primes-utils 1.1.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -7,3 +7,8 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
11
+ .directory
12
+ *.odt
13
+ *.odt#
14
+ *.pdf
data/README.md CHANGED
@@ -1,6 +1,16 @@
1
1
  # primes-utils
2
2
 
3
- primes-utils is a Rubygem which provides a suite of extremely fast (relative to Ruby's standard library) utility methods for testing and generating primes.
3
+ ## Introduction
4
+
5
+ `primes-utils` is a Rubygem which provides a suite of extremely fast (relative to Ruby's standard library) utility methods for testing and generating primes.
6
+
7
+ For details on best use practices and implementation details see:
8
+
9
+ `PRIMES-UTILS HANDBOOK`
10
+
11
+ https://www.scribd.com/doc/266461408/Primes-Utils-Handbook
12
+
13
+ Periodically check for updates.
4
14
 
5
15
  ## Installation
6
16
 
@@ -39,10 +49,9 @@ This replaces the `prime?` method in the `prime.rb` standard library.
39
49
 
40
50
  **primemr?(k=20)**
41
51
 
42
- Determine if the absolute value of an integer is prime using Miller-Rabin test. Return 'true' or 'false'.
43
- Miller-Rabin here is super fast, but probabilistic (not deterministic), primality test.
44
- https://en.wikipedia.org/wiki/Miller-Rabin_primality_test
45
- The method's reliability can be increased by increasing the default input parameter of k=20.
52
+ Using Miller-Rabin primality test for integers, return 'true' or 'false'.
53
+ Miller-Rabin [6] is super fast, but probabilistic (not deterministic), primality test.
54
+ The reliability can be increased by increasing the default input parameter of k=20.
46
55
 
47
56
  ```
48
57
  1111111111111111111.primemr? => true
@@ -58,7 +67,7 @@ The method's reliability can be increased by increasing the default input parame
58
67
 
59
68
  Determine the prime factorization of the absolute value of an integer.
60
69
  This replaces the `prime division` method in the `prime.rb` standard library.
61
- Returns an array of arrays of factors and exponents: [[2,4],[3,2],[5,1]] => (2^4)(3^2)(5^1) = (16)(9)(5) = 720
70
+ Output is array of arrays of factors and exponents: [[p1,e1],[p2,e2]..[pn,en]]
62
71
  Default Strictly Prime (SP) Prime Generator (PG) used here is P13.
63
72
  Can change SP PG used on input. Acceptable primes range: [3 - 19].
64
73
 
@@ -73,50 +82,83 @@ Can change SP PG used on input. Acceptable primes range: [3 - 19].
73
82
  1.factors => []
74
83
  ```
75
84
 
76
- **primes(start=0)**
85
+ **primes(start=0), primesf(start=0), primesmr(start=0)**
77
86
 
78
- Create an array of primes from the absolute value range (|start| - |end|).
79
- The order of the range doesn't matter if both given: start.primes end <=> end.prime start
87
+ Return an array of primes within the absolute value range `(|start| - |end|)`.
88
+ The order of the range doesn't matter if both given: `start.primes end <=> end.prime start`
80
89
  If only one parameter used, then all the primes upto that number will be returned.
90
+ See `PRIMES-UTILS HANDBOOK` for details on best use practices.
81
91
 
82
92
  ```
83
93
  50.primes => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
84
- 50.primes 125 => [53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113]
94
+ 50.primesf 125 => [53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113]
85
95
  300.primes 250 => [251, 257, 263, 269, 271, 277, 281, 283, 293]
96
+ n=10**100; (n-250).primesmr(n+250) => []
86
97
  541.primes.size => 100
87
98
  1000.primes(5000).size => 501
88
99
  (prms = 1000000.primes(1000100)).size => 6
89
100
  prms.size => 6
90
101
  prms => [1000003, 1000033, 1000037, 1000039, 1000081, 1000099]
91
102
  -10.primes -50 => [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
92
- 0.primes => []
93
- 1.primes => []
103
+ 0.primesf => []
104
+ 1.primesmr => []
94
105
  ```
95
106
 
96
- **primenth(p=11) or nthprime(p=11)**
107
+ **primescnt(start=0), primescntf(start=0), primescntmr(start=0)**
97
108
 
98
- Return the value of the nth (absolute value) prime.
99
- Default Strictly Prime (SP) Prime Generator (PG) used here is P11.
100
- Can change SP PG used on input. Acceptable primes range: [3 - 19].
109
+ Provide count of primes within the absolute value range `(|start| - |end|)`.
110
+ The order of the range doesn't matter if both given: `start.primes end <=> end.prime start`
111
+ If only one parameter used, the count of all the primes upto that number will be returned.
112
+ See `PRIMES-UTILS HANDBOOK` for details on best use practices.
113
+
114
+ ```
115
+ 100000.primescnt => 9592
116
+ 100000.primescntf 100500 => 40
117
+ n=10**400; (n-500).primescntmr(n+500) => 1
118
+ -10.primescnt -50 => 11
119
+ 0.primescntf => 0
120
+ 1.primescntmr => 0
121
+ ```
122
+
123
+ **primenth(p=7) or nthprime(p=7)**
124
+
125
+ Return the value of the (absolute value of) nth prime.
126
+ Default Strictly Prime (SP) Prime Generator (PG) used here is P7.
127
+ Can change SP PG used on input. Acceptable primes range: [3 - 13].
128
+ Currently, parameters are set so that the 1122951705th prime is max.
129
+ An error message will be given if requested nth prime is > than max.
101
130
 
102
131
  ```
103
132
  1000000.primenth => 15485863
104
133
  1500000.nthprime => 23879519
105
- 2000000.nthprime 13 => 32452843
134
+ 2000000.nthprime 11 => 32452843
106
135
  -500000.nthprime => 7368787
136
+ 1122951705.nthprime => 25741879847
137
+ 1122951706.primenth => "1122951705 not enough primes, nth approx too small"
107
138
  0.nthprime => 0
108
139
  ```
109
140
 
110
- ## Coding Implementations
111
- The methods `primemr`, `nthprime/primenth`, and `primes` are coded in pure ruby.
112
- The methods `prime?` and `prime_division/factors` have two implementations.
113
- Each has a pure ruby implementation, and also a hybrid implementation which uses the
114
- Unix cli command `factor` if its available on the host OS. `factor` [5] is an extremely fast
115
- C coded factoring algorithm, part of the GNU Core Utilities package [4].
141
+ **primes_utils**
116
142
 
117
- Upon loading, the gem tests if the desired `factor` command exists on the host OS.
118
- If so, it wraps a system call to it and uses it for `prime?` and `prime_division/factors`.
119
- If not, it uses a fast pure ruby implementation for each method based on the Sieve of Zakiya (SoZ)[1][2][3].
143
+ Displays a list of all the `primes-utils` methods available for your system.
144
+ Use as `x.primes_utils` where x is any `class Integer` value.
145
+
146
+ ```
147
+ 0.primes_utils => "prime? primemr? primes primesf primesmr primescnt primescntf primescntmr primenth|nthprime factors|prime_division"
148
+ ```
149
+
150
+ ## Coding Implementations
151
+ The methods `primemr?`, `nthprime/primenth`, `primes`, `primescnt`, `primesmr`, and `primescnt` are coded in pure ruby.
152
+ The methods `prime?` and `prime_division|factors` have two implementations.
153
+ Each has a pure ruby implementation, and a hybrid implementation using the Unix cli command `factor` if its available on the host OS.
154
+ The methods `primesf` and `primescntf` use the `factor` version of `prime?` and are created if it exits.
155
+ `factor` [5] is an extremely fast C coded factoring algorithm, part of the GNU Core Utilities package [4].
156
+
157
+ Upon loading, the gem tests if the command `factor` exists on the host OS.
158
+ If so, it performs a system call to it within `prime?` and `prime_division/factors`, which uses its output.
159
+ If not, each method uses a fast pure ruby implementation based on the Sieve of Zakiya (SoZ)[1][2][3].
160
+
161
+ All the `primes-utils` methods are `instance_methods` for `class Integer`.
120
162
 
121
163
  ## Author
122
164
  Jabari Zakiya
@@ -124,9 +166,11 @@ Jabari Zakiya
124
166
  ## References
125
167
  [1]https://www.scribd.com/doc/150217723/Improved-Primality-Testing-and-Factorization-in-Ruby-revised
126
168
  [2]https://www.scribd.com/doc/228155369/The-Segmented-Sieve-of-Zakiya-SSoZ
127
- [3]https://www.scribd.com/doc/73385696/The-Sieve-of-Zakiya
128
- [4]https://en.wikipedia.org/wiki/GNU_Core_Utilities
169
+ [3]https://www.scribd.com/doc/73385696/The-Sieve-of-Zakiya
170
+ [4]https://en.wikipedia.org/wiki/GNU_Core_Utilities
129
171
  [5]https://en.wikipedia.org/wiki/Factor_(Unix)
172
+ [6]https://en.wikipedia.org/wiki/Miller-Rabin_primality_test
173
+
130
174
 
131
175
  ## License
132
- GPL 2.0 or later.
176
+ GPLv2+
data/lib/primes/utils.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  require "primes/utils/version"
2
- require 'rational' if RUBY_VERSION =~ /^(1.8)/ # for 'gcd' method
2
+ require "rational" if RUBY_VERSION =~ /^(1.8)/ # for 'gcd' method
3
3
 
4
4
  module Primes
5
5
  module Utils
6
+ # Upon loading, determine if platform has cli command 'factor'
6
7
  private
7
8
  os_has_factor = false
8
9
  begin
@@ -13,6 +14,11 @@ module Primes
13
14
  os_has_factor = false
14
15
  end
15
16
 
17
+ # Methods primes* and primescnt* use a number range: end_num - start_num
18
+ # Use as: end_num.primes*(start_num) (or vice versa) or end_num.primes
19
+ # If start_num omitted, the method will find all primes <= end_num
20
+ # If start_num > self, values are switched to make end_num > start_num
21
+
16
22
  public
17
23
  if os_has_factor # for platforms with cli 'factor' command
18
24
 
@@ -25,15 +31,43 @@ module Primes
25
31
  h = Hash.new(0); factors.each {|f| h[f] +=1}; h.to_a.sort
26
32
  end
27
33
 
28
- puts "Using cli 'factor' for prime?/factors/prime_division"
34
+ def primesf(start_num=0)
35
+ # Find primes within a number range: end_num - start_num
36
+ # Uses 'prime?' to check primality of prime candidates in range
37
+ sozdata = sozcore1(self, start_num, true) # true for primes
38
+ return sozdata[1] if sozdata[0]
39
+ pcs_in_range, r, mod, modk, rescnt, residues, primes = sozdata[1..-1]
40
+ pcs_in_range.times do # find primes from this num pcs in range
41
+ prime = modk + residues[r]
42
+ primes << prime if prime.prime?
43
+ r +=1; if r > rescnt; r=1; modk +=mod end
44
+ end
45
+ primes
46
+ end
47
+
48
+ def primescntf(start_num=0)
49
+ # Count primes within a number range: end_num - start_num
50
+ # Uses 'prime?' to check primality of prime candidates in range
51
+ sozdata = sozcore1(self, start_num, false) # false for primescnt
52
+ return sozdata[1] if sozdata[0]
53
+ pcs_in_range, r, mod, modk, rescnt, residues, primes = sozdata[1..-1]
54
+ primescnt = primes.size
55
+ pcs_in_range.times do # count primes from this num pcs in range
56
+ primescnt +=1 if (modk + residues[r]).prime?
57
+ r +=1; if r > rescnt; r=1; modk +=mod end
58
+ end
59
+ primescnt
60
+ end
61
+
62
+ puts "Using cli 'factor' for prime? primesf primescntf factors|prime_division"
29
63
 
30
64
  else # use pure ruby versions for platforms without cli command 'factor'
31
65
 
32
- def prime?
33
- residues = [1,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,
34
- 89,97,101,103,107,109,113,121,127,131,137,139,143,149,151,157,163,
35
- 167,169,173,179,181,187,191,193,197,199,209,211]
36
- mod=210; rescnt=48
66
+ def prime? # Uses P7 Strictly Prime Generator
67
+ residues = [1,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,
68
+ 83,89,97,101,103,107,109,113,121,127,131,137,139,143,149,151,157,
69
+ 163,167,169,173,179,181,187,191,193,197,199,209,211]
70
+ mod=210; # rescnt=48
37
71
 
38
72
  n = self.abs
39
73
  return true if [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
@@ -60,20 +94,21 @@ module Primes
60
94
  n%(p+186)== 0 or n%(p+188)==0 or n%(p+198)== 0 or n%(p+200)==0
61
95
  p += mod # first prime candidate for next kth residues group
62
96
  end
63
- return true
97
+ true
64
98
  end
65
99
 
66
- def factors(p=13) # P13 is default prime generator here
67
- seeds = [2, 3, 5, 7, 11, 13, 17, 19]
68
- return 'PRIME OPTION NOT A SEEDS PRIME' if !seeds.include? p
100
+ def factors(p=13)
101
+ # Return prime factors of n in form [[p1,e1],[p2.e2]..[pn.en]]
102
+ # Uses sozP13 Sieve of Zakiya (SoZ) as default Prime Generator
103
+ seeds = [2, 3, 5, 7, 11, 13, 17, 19]
104
+ p = 13 if !seeds.include? p
69
105
 
70
- # find primes <= Pn, compute modPn then Prime Gen residues for Pn
71
- primes = seeds[0..seeds.index(p)]; mod = primes.reduce(:*)
72
- residues=[1]; 3.step(mod,2) {|i| residues << i if mod.gcd(i) == 1}
73
- residues << mod+1; rescnt = residues.size-1
106
+ primes = seeds[0..seeds.index(p)]
107
+ mod = primes.reduce(:*) # modulus: modPn = 2*3*5*7*..*Pn
108
+ residues, rescnt = make_residues_rescnt(mod)
74
109
 
75
- n = self.abs
76
- factors = []
110
+ n = self.abs # number to factor
111
+ factors = [] # init empty factors array
77
112
 
78
113
  return [] if n == 0
79
114
  return [[n,1]] if primes.include? n
@@ -92,76 +127,49 @@ module Primes
92
127
  h=Hash.new(0); factors.each {|f| h[f] +=1}; h.to_a.sort
93
128
  end
94
129
 
95
- puts "Using pure ruby versions"
130
+ puts "Using pure ruby versions for all methods"
96
131
  end
97
132
 
98
133
  # Replace slow ruby library method prime_division with faster version
99
134
  alias prime_division factors
100
135
 
101
- def primenth(p=11) # return value of nth prime
136
+ def primenth(p=7)
102
137
  # Return value of nth prime
103
- # Uses sozP11 Sieve of Zakiya (SoZ) as default prime sieve
104
-
105
- seeds = [2, 3, 5, 7, 11, 13, 17, 19]
106
- primes = []
138
+ # Uses sozP7 Sieve of Zakiya (SoZ) as default Prime Generator
139
+ seeds = [2, 3, 5, 7, 11, 13]
140
+ p = 7 if !seeds.include? p
107
141
 
108
142
  n = self.abs # the desired nth prime
109
- return n != 0 ? seeds[n-1] : 0 if n < seeds.size
110
-
111
- # compute approximate value of (>) nth prime to count up to
112
- numb = (n*(Math.log(n)+2)).to_i
113
-
114
- # find primes <= Pn, compute modPn then Prime Gen residues for Pn
115
- primes = seeds[0..seeds.index(p)]; mod = primes.reduce(:*)
116
- residues=[1]; 3.step(mod,2) {|i| residues << i if mod.gcd(i) == 1}
117
- residues << mod+1; rescnt = residues.size-1
118
-
119
- num = numb-1 | 1 # if N even number then subtract 1
120
- k=num/mod; modk = mod*k; r=1 # kth residue group; base num value
121
- while num >= modk+residues[r]; r += 1 end # compute extra prms size
122
- maxprms = k*rescnt + r-1 # max size of prime candidates array
123
- prms=Array.new(maxprms,true) # set all prime candidates to True
124
-
125
- # array of residues offsets to compute nonprimes positions in prms
126
- pos =[]; rescnt.times {|i| pos[residues[i]] = i-1};
127
-
128
- # sieve (SoZ) to eliminate nonprimes from prms
129
- sqrtN = Math.sqrt(num).to_i
130
- modk,r,k=0,0,0
131
- prms.each do |prime|
132
- r +=1; if r > rescnt; r=1; modk +=mod; k +=1 end
133
- next unless prime
134
- res_r = residues[r]
135
- prime = modk + res_r
136
- break if prime > sqrtN
137
- prmstep = prime * rescnt
138
- residues[1..-1].each do |ri|
139
- # compute (prime * (modk + ri)) position index in prms
140
- kk,rr = (res_r * ri).divmod mod # residues product res[group|track]
141
- nonprm = (k*(prime + ri) + kk)*rescnt + pos[rr]
142
- while nonprm < maxprms; prms[nonprm]=nil; nonprm +=prmstep end
143
- end
144
- end
145
- # the prms array now has all the primes positions for primes r1..N
146
- # count up to nth prime, compute value from address, and output
147
- prmcnt = primes.size # number of primes up to Pn
148
- m=0
149
- while prmcnt < n; prmcnt +=1 if prms[m]; m +=1 end
150
- k,rr = m.divmod rescnt
151
- return (mod*k + residues[rr])
143
+ return n != 0 ? seeds[n-1] : 0 if n <= seeds.size
144
+
145
+ start_num, nth, nthflag = set_start_value(n, nths)
146
+ return start_num if nthflag # output nthprime if nth ref value
147
+
148
+ num = approximate_nth(n) # close approx to nth >= real nth
149
+ primes = seeds[0..seeds.index(p)]
150
+
151
+ ks, mod, residues, maxprms,
152
+ max_range, prms, prms_range = sozcore2(num, start_num, primes)
153
+
154
+ rescnt = residues[1..-1].size
155
+ pcs2ks = rescnt*ks
156
+ # starting at start_num's location, find nth prime within given range
157
+ prms = prms_range if ks > 0
158
+ prmcnt = n > nth ? nth-1 : primes.size
159
+ m=0; r=1; modk = mod*ks # find number of pcs, m, in ks < start_num
160
+ while modk + residues[r] < start_num; r += 1; m +=1 end
161
+ pcnt = prmcnt + prms[m..-1].count(1) # number of primes upto nth approx
162
+ return "#{pcnt} not enough primes, nth approx too small" if pcnt < n
163
+ while prmcnt < n; prmcnt +=1 if prms[m] == 1; m +=1 end
164
+ k, r = (m+pcs2ks).divmod rescnt
165
+ mod*k + residues[r]
152
166
  end
153
167
 
154
168
  alias nthprime primenth # to make life easier
155
169
 
156
170
  def primes(start_num=0)
157
171
  # Find primes between a number range: end_num - start_num
158
- # Use as: end_num.primes(start_num) or end_num.primes
159
- # If start_num omitted, will find all primes <= end_num
160
- # If start_num > self, values are switched to continue
161
- # Output is an array of the primes within the given range
162
- # To find count of these primes do: end_num.primes(start_num).size
163
- # This method uses the P13 Strictly Prime (SP) Prime Generator (PG)
164
-
172
+ # Uses the P13 Strictly Prime (SP) Prime Generator
165
173
  num = self.abs; start_num = start_num.abs
166
174
  num, start_num = start_num, num if start_num > num
167
175
 
@@ -169,54 +177,43 @@ module Primes
169
177
  plast = primes.last # last prime in primes
170
178
  return primes.select {|p| p >= start_num && p <= num} if num <= plast
171
179
 
172
- mod = primes.reduce(:*) # P13 modulus value 2*3*5*7*11*13
173
-
174
- residues=[1]; plast.step(mod,2) {|i| residues << i if mod.gcd(i) == 1}
175
- rescnt = residues.size # number of residues
176
- residues << mod+1 # to make algorithm easier
177
-
178
- num = num-1 | 1 # if N even number then subtract 1
179
- k=num/mod; modk = mod*k; r=1 # kth residue group; base num value
180
- while num >= modk+residues[r]; r += 1 end # compute extra prms size
181
- maxprms = k*rescnt + r-1 # max size of prime candidates array
182
- prms=Array.new(maxprms,true) # set all prime candidates to True
183
-
184
- # hash of residues offsets to compute nonprimes positions in prms
185
- pos =[]; rescnt.times {|i| pos[residues[i]] = i-1};
186
-
187
- # sieve (SoZ) to eliminate nonprimes from prms
188
- sqrtN= Math.sqrt(num).to_i
189
- modk,r,k=0,0,0
190
- prms.each do |prime|
191
- r +=1; if r > rescnt; r=1; modk +=mod; k +=1 end
192
- next unless prime
193
- res_r = residues[r]
194
- prime = modk + res_r
195
- break if prime > sqrtN
196
- prmstep = prime * rescnt
197
- residues[1..-1].each do |ri|
198
- # compute (prime * (modk + ri)) position index in prms
199
- kk,rr = (res_r * ri).divmod mod # residues product res[group|track]
200
- nonprm = (k*(prime + ri) + kk)*rescnt + pos[rr] # 1st prime multiple
201
- while nonprm < maxprms; prms[nonprm]=nil; nonprm +=prmstep end
202
- end
203
- end
204
- # the prms array now has all the primes positions for primes r1..N
180
+ ks, mod, residues, maxprms,
181
+ max_range, prms, prms_range = sozcore2(num, start_num, primes)
182
+
183
+ rescnt = residues[1..-1].size
184
+ # starting at start_num's location, extract primes within given range
205
185
  primes = start_num <= plast ? primes.drop_while {|p| p < start_num} : []
206
- k = (start_num-2).abs/mod # start_num's residue group value
207
- modk = mod*k # start_num's mod group value
208
- m = rescnt*k # number of prms elements up to kth resgroup
209
- r = 0 # begin processing at start of resgroup
210
- while m < maxprms
186
+ if ks > 0; maxprms = max_range; prms = prms_range end
187
+ m=0; r=1; modk = mod*ks; # find number of pcs, m, in ks < start_num
188
+ while modk + residues[r] < start_num; r +=1; m +=1 end
189
+ (maxprms-m).times do # find primes from this num pcs within range
190
+ primes << modk + residues[r] if prms[m] == 1
211
191
  r +=1; if r > rescnt; r=1; modk +=mod end
212
- if prms[m]
213
- prime = modk + residues[r]
214
- primes << prime if prime >= start_num
215
- end
216
- m +=1
192
+ m +=1
217
193
  end
218
194
  primes
219
195
  end
196
+
197
+ def primescnt(start_num=0)
198
+ # Count primes between a number range: end_num - start_num
199
+ # Uses the P13 Strictly Prime (SP) Prime Generator
200
+ num = self.abs; start_num = start_num.abs
201
+ num, start_num = start_num, num if start_num > num
202
+
203
+ primes = [2,3,5,7,11,13] # P13 excluded primes lists
204
+ plast = primes.last # last prime in primes
205
+ return primes.select {|p| p >= start_num && p <= num}.size if num <= plast
206
+
207
+ ks, mod, residues, maxprms,
208
+ max_range, prms, prms_range = sozcore2(num, start_num, primes)
209
+
210
+ # starting at start_num's location, count primes within given range
211
+ primes = start_num <= plast ? primes.drop_while {|p| p < start_num} : []
212
+ prms = prms_range if ks > 0
213
+ m=0; r=1; modk = mod*ks # find number of pcs, m, in ks < start_num
214
+ while modk + residues[r] < start_num; r +=1; m +=1 end
215
+ primecnt = primes.size + prms[m..-1].count(1)
216
+ end
220
217
 
221
218
  # Miller-Rabin prime test in Ruby
222
219
  # From: http://en.wikipedia.org/wiki/Miller-Rabin_primality_test
@@ -234,10 +231,10 @@ module Primes
234
231
  (d >>= 1; s += 1) while d.even?
235
232
  k.times do
236
233
  a = 2 + rand(n-4)
237
- x = a.to_bn.mod_exp(d,n) #x = (a**d) mod n
234
+ x = a.to_bn.mod_exp(d,n) # x = (a**d) mod n
238
235
  next if x == 1 or x == n-1
239
236
  (s-1).times do
240
- x = x.mod_exp(2,n) #x = (x**2) mod n
237
+ x = x.mod_exp(2,n) # x = (x**2) mod n
241
238
  return false if x == 1
242
239
  break if x == n-1
243
240
  end
@@ -245,7 +242,184 @@ module Primes
245
242
  end
246
243
  true # with high probability
247
244
  end
245
+
246
+ def primesmr(start_num=0)
247
+ # Find primes within a number range: end_num - start_num
248
+ # Uses 'primemr' to check primality of prime candidates in range
249
+ sozdata = sozcore1(self, start_num, true) # true for primes
250
+ return sozdata[1] if sozdata[0]
251
+ pcs_in_range, r, mod, modk, rescnt, residues, primes = sozdata[1..-1]
252
+ pcs_in_range.times do # find primes from this num pcs in range
253
+ prime = modk + residues[r]
254
+ primes << prime if prime.primemr?
255
+ r +=1; if r > rescnt; r=1; modk +=mod end
256
+ end
257
+ primes
258
+ end
259
+
260
+ def primescntmr(start_num=0)
261
+ # Count primes within a number range: end_num - start_num
262
+ # Uses 'primemr' to check primality of prime candidates in range
263
+ sozdata = sozcore1(self, start_num, false) # false for primescnt
264
+ return sozdata[1] if sozdata[0]
265
+ pcs_in_range, r, mod, modk, rescnt, residues, primes = sozdata[1..-1]
266
+ primescnt = primes.size
267
+ pcs_in_range.times do # count primes from this num pcs in range
268
+ primescnt +=1 if (modk + residues[r]).primemr?
269
+ r +=1; if r > rescnt; r=1; modk +=mod end
270
+ end
271
+ primescnt
272
+ end
273
+
274
+ def primes_utils
275
+ "prime? primemr? primes primesmr primescnt primescntmr primenth|nthprime factors|prime_division"
276
+ end
277
+
278
+ if os_has_factor
279
+ def primes_utils
280
+ "prime? primemr? primes primesf primesmr primescnt primescntf primescntmr primenth|nthprime factors|prime_division"
281
+ end
282
+ end
283
+
284
+ private
285
+ def make_residues_rescnt(mod)
286
+ residues=[1]; 3.step(mod,2) {|i| residues << i if mod.gcd(i) == 1}
287
+ residues << mod+1
288
+ [residues, residues.size-1] # return residues array and rescnt
289
+ end
290
+
291
+ def pcs_to_num(num,mod,rescnt,residues) # find number of pcs upto num
292
+ num = num-1 | 1 # if N even number then subtract 1
293
+ k=num/mod; modk = mod*k; r=1 # kth residue group; base num value
294
+ while num >= modk+residues[r]; r += 1 end # compute extra prms size
295
+ k*rescnt + r-1 # max number of pcs <= num
296
+ end
297
+
298
+ def pcs_to_start_num(start_num, mod, rescnt, residues)
299
+ k = (start_num-2).abs/mod # start_num's residue group value
300
+ modk = mod*k # start_num's mod group value
301
+ m = rescnt*k # number of pcs upto kth resgroup
302
+ r = 1 # find r,m for first pc >= start_num
303
+ while modk + residues[r] < start_num; r +=1; m +=1 end
304
+ [m, r, modk] # parameters for 1st pc >= start_num
305
+ end
306
+
307
+ def sozcore1(num, start_num, method_flag)
308
+ # Uses the P13 Strictly Prime (SP) Prime Generator
309
+ num = num.abs; start_num = start_num.abs
310
+ num, start_num = start_num, num if start_num > num
311
+
312
+ primes = [2,3,5,7,11,13] # P13 excluded primes lists
313
+ plast = primes.last # last prime in primes
314
+ if num <= plast
315
+ primes = primes.select {|p| p >= start_num && p <= num}
316
+ return [true, method_flag ? primes : primes.size]
317
+ end
318
+ mod = primes.reduce(:*) # P13 modulus: 2*3*5*7*11*13 = 30030
319
+ residues, rescnt = make_residues_rescnt(mod)
320
+ maxpcs = pcs_to_num(num,mod,rescnt,residues) # num of pcs <= end_num
321
+
322
+ # compute parameters for start location and number of pcs within range
323
+ primes = start_num <= plast ? primes.drop_while {|p| p < start_num} : []
324
+ m, r, modk = pcs_to_start_num(start_num, mod, rescnt, residues)
325
+ [false, maxpcs-m, r, mod, modk, rescnt, residues, primes]
326
+ end
327
+
328
+ def sozcore2(num, start_num, primes)
329
+ mod = primes.reduce(:*) # modulus: modPn = 2*3*5*7*..*Pn
330
+ residues, rescnt = make_residues_rescnt(mod)
331
+ maxprms = pcs_to_num(num,mod,rescnt,residues) # num of pcs <= end_num
332
+
333
+ sqrtN = Math.sqrt(num).to_i # sqrt of end_num (end of range)
334
+ pcs2sqrtN = pcs_to_num(sqrtN,mod,rescnt,residues) # num of pcs <= sqrtN
335
+
336
+ ks = (start_num-2).abs/mod # start_num's resgroup value
337
+ maxpcs = maxprms # if ks = 0 use this for prms array
338
+ if ks > 0 # if start_num > mod+1
339
+ maxpcs = pcs2sqrtN # find primes in pcs upto sqrtN
340
+ pcs2ks = rescnt*ks # number of pcs upto ks resgroup
341
+ max_range = maxprms-pcs2ks # number of pcs from ks resgroup to end_num
342
+ prms_range = Array.new(max_range,1) # pc array to generate range primes
343
+ end
344
+ prms=Array.new(maxpcs,1) # for primes upto sqrtN and end_num if ks=0
345
+
346
+ # hash of residues offsets to compute nonprimes positions in prms
347
+ pos =[]; rescnt.times {|i| pos[residues[i]] = i-1}
348
+
349
+ # Sieve of Zakiya (SoZ) to eliminate nonprimes from prms and prms_range
350
+ modk,r,k=0,0,0
351
+ pcs2sqrtN.times do |i| # sieve primes from pcs upto sqrt(end_num)
352
+ r +=1; if r > rescnt; r=1; modk +=mod; k +=1 end
353
+ next unless prms[i]==1
354
+ res_r = residues[r]
355
+ prime = modk + res_r
356
+ prmstep = prime * rescnt
357
+ residues[1..-1].each do |ri|
358
+ # compute (prime * (modk + ri)) position index in prms
359
+ kk,rr = (res_r * ri).divmod mod # residues product res[group|track]
360
+ nonprm = (k*(prime + ri) + kk)*rescnt + pos[rr] # 1st prime multiple
361
+ while nonprm < maxpcs; prms[nonprm]=0; nonprm +=prmstep end
362
+ if ks > 0
363
+ nonprm = (pcs2ks - nonprm)%prmstep
364
+ nonprm = prmstep - nonprm if nonprm != 0
365
+ while nonprm < max_range; prms_range[nonprm]=0; nonprm += prmstep end
366
+ end
367
+ end
368
+ end
369
+ [ks, mod, residues, maxprms, max_range, prms, prms_range]
370
+ end
371
+
372
+ def approximate_nth(n, b=0.686) # approximate nthprime value >= real value
373
+ b=0.6863 if n > 1064000000 # good upto nth <= 1122951705
374
+ a = b*(Math.log(Math.log(n)))
375
+ (n*(Math.log(n)+a)+1).to_i
376
+ end
377
+
378
+ def set_start_value(n, nths) # find closest nth|nthprime val <= requested
379
+ nthkeys = nths.keys.sort
380
+ return [nths[n], 0, true] if nthkeys.include? n # if nth in nths table
381
+ start_num, nth = 0, nthkeys[0]
382
+ nthkeys.each {|i| start_num, nth = nths[i], i if n > i}
383
+ [start_num, nth, false]
384
+ end
385
+
386
+ def nths # hash table index of reference nth primes
387
+ nths={1000000 => 15485863, 5000000 => 86028121, 7500000 => 132276691,
388
+ 10000000 => 179424673, 12500000 => 227254201, 25000000 => 472882027,
389
+ 37500000 => 725420401, 50000000 => 982451653, 62500000 => 1242809749,
390
+ 75000000 => 1505776939, 87500000 => 1770989609, 100000000 => 2038074743,
391
+ 112500000 => 2306797469, 125000000 => 2576983867, 137500000 => 2848518523,
392
+ 150000000 => 3121238909, 162500000 => 3395057291, 175000000 => 3669829403,
393
+ 187500000 => 3945592087, 200000000 => 4222234741, 212500000 => 4499683009,
394
+ 225000000 => 4777890881, 237500000 => 5056862311, 250000000 => 5336500537,
395
+ 262500000 => 5616787769, 275000000 => 5897707297, 287500000 => 6179208157,
396
+ 300000000 => 6461335109, 312500000 => 6743943629, 325000000 => 7027107881,
397
+ 337500000 => 7310793337, 350000000 => 7594955549, 362500000 => 7879581839,
398
+ 375000000 => 8164628191, 387500000 => 8450100349, 400000000 => 8736028057,
399
+ 412500000 => 9022375487, 425000000 => 9309109471, 437500000 => 9596238593,
400
+ 450000000 => 9883692017, 462500000 =>10171564687, 475000000 =>10459805417,
401
+ 487500000 =>10748372137, 500000000 =>11037271757, 512500000 =>11326513039,
402
+ 525000000 =>11616020609, 537500000 =>11905863799, 550000000 =>12196034771,
403
+ 562500000 =>12486465863, 575000000 =>12777222833, 587500000 =>13068237251,
404
+ 600000000 =>13359555403, 612500000 =>13651119389, 625000000 =>13942985677,
405
+ 637500000 =>14235122851, 650000000 =>14527476781, 662500000 =>14820071503,
406
+ 675000000 =>15112928683, 687500000 =>15406031899, 700000000 =>15699342107,
407
+ 712500000 =>15992957251, 725000000 =>16286768243, 737500000 =>16580801137,
408
+ 750000000 =>16875026921, 762500000 =>17169527171, 775000000 =>17464243799,
409
+ 787500000 =>17759139259, 800000000 =>18054236957, 812500000 =>18349591409,
410
+ 825000000 =>18645104897, 837500000 =>18940846207, 850000000 =>19236701629,
411
+ 862500000 =>19532780327, 875000000 =>19829092147, 887500000 =>20125592731,
412
+ 900000000 =>20422213579, 912500000 =>20719050323, 925000000 =>21016060633,
413
+ 937500000 =>21313231963, 950000000 =>21610588367, 962500000 =>21908128993,
414
+ 975000000 =>22205818561, 987500000 =>22503733657, 1000000000=>22801763489,
415
+ 1012500000=>23099993743, 1025000000=>23398391231, 1037500000=>23696858797,
416
+ 1050000000=>23995554823, 1062500000=>24294392179, 1075000000=>24593421187,
417
+ 1087500000=>24892587403, 1100000000=>25191867719, 1112500000=>25491361037
418
+ }
419
+ end
248
420
  end
249
421
  end
250
422
 
251
- class Integer; include Primes::Utils end
423
+ class Integer; include Primes::Utils end
424
+
425
+ puts "Available methods are: #{0.primes_utils}" # display methods upon loading
@@ -1,5 +1,5 @@
1
1
  module Primes
2
2
  module Utils
3
- VERSION = "1.1.1"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
data/primes-utils.gemspec CHANGED
@@ -10,14 +10,15 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["jzakiya@gmail.com"]
11
11
 
12
12
  spec.summary = %q{suite of extremely fast utility methods for testing and generating primes}
13
- spec.description = %q{Methods: prime?, primemr?, primes, nthprime/primenth, factors/prime_division}
13
+ spec.description = %q{Methods: prime?, primemr?, primes, primesf, primesmr, primescnt, primescntf, primescntmr, nthprime/primenth, factors/prime_division}
14
14
  spec.homepage = "https://github.com/jzakiya/primes-utils"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  spec.bindir = "exe"
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
- spec.license = "GPL-2.0"
20
+ spec.license = "GPLv2+"
21
+ spec.required_ruby_version = ">= 1.8.7"
21
22
 
22
23
  #if spec.respond_to?(:metadata)
23
24
  # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
metadata CHANGED
@@ -1,51 +1,63 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: primes-utils
3
- version: !ruby/object:Gem::Version
4
- version: 1.1.1
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 2
8
+ - 0
9
+ - 0
10
+ version: 2.0.0
5
11
  platform: ruby
6
- authors:
12
+ authors:
7
13
  - Jabari Zakiya
8
14
  autorequire:
9
15
  bindir: exe
10
16
  cert_chain: []
11
- date: 2015-04-06 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
17
+
18
+ date: 2015-05-25 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
14
21
  name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.9'
20
- type: :development
21
22
  prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.9'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '10.0'
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 29
29
+ segments:
30
+ - 1
31
+ - 9
32
+ version: "1.9"
34
33
  type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rake
35
37
  prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '10.0'
41
- description: 'Methods: prime?, primemr?, primes, nthprime/primenth, factors/prime_division'
42
- email:
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 35
44
+ segments:
45
+ - 10
46
+ - 0
47
+ version: "10.0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ description: "Methods: prime?, primemr?, primes, primesf, primesmr, primescnt, primescntf, primescntmr, nthprime/primenth, factors/prime_division"
51
+ email:
43
52
  - jzakiya@gmail.com
44
53
  executables: []
54
+
45
55
  extensions: []
56
+
46
57
  extra_rdoc_files: []
47
- files:
48
- - ".gitignore"
58
+
59
+ files:
60
+ - .gitignore
49
61
  - CODE_OF_CONDUCT.md
50
62
  - Gemfile
51
63
  - README.md
@@ -56,28 +68,40 @@ files:
56
68
  - lib/primes/utils/version.rb
57
69
  - primes-utils.gemspec
58
70
  homepage: https://github.com/jzakiya/primes-utils
59
- licenses:
60
- - GPL-2.0
61
- metadata: {}
71
+ licenses:
72
+ - GPLv2+
62
73
  post_install_message:
63
74
  rdoc_options: []
64
- require_paths:
75
+
76
+ require_paths:
65
77
  - lib
66
- required_ruby_version: !ruby/object:Gem::Requirement
67
- requirements:
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
68
81
  - - ">="
69
- - !ruby/object:Gem::Version
70
- version: '0'
71
- required_rubygems_version: !ruby/object:Gem::Requirement
72
- requirements:
82
+ - !ruby/object:Gem::Version
83
+ hash: 57
84
+ segments:
85
+ - 1
86
+ - 8
87
+ - 7
88
+ version: 1.8.7
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
73
92
  - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
76
98
  requirements: []
99
+
77
100
  rubyforge_project:
78
- rubygems_version: 2.4.6
101
+ rubygems_version: 1.8.25
79
102
  signing_key:
80
- specification_version: 4
103
+ specification_version: 3
81
104
  summary: suite of extremely fast utility methods for testing and generating primes
82
105
  test_files: []
106
+
83
107
  has_rdoc:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: b3c9d1ce71f37ddfef893df9081068e4bed16c1f
4
- data.tar.gz: 8c43034d10f57ad8e4753e866c35c52b43da8d37
5
- SHA512:
6
- metadata.gz: f12686ee34059845f8c0525283b1d83a4076676606acce3acbd1b3c58baacd375648c8cb926aaa61b2cb69d9df109f939ec7977e7d158044bf0e028f9df4374f
7
- data.tar.gz: 0e6d049b7a9787bce8ceb27cafc224d1a0e05dd8ae7438ed36ae6ecd1916351bd1dedc944e0abc8ac3a77e400edee4d902fe79d9ad71fde02a641199aea44e09