primes-utils 2.6.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/primes/utils.rb CHANGED
@@ -1,473 +1,520 @@
1
- # need rubygems to load gems, and rational for 'gcd' method for 1.8
2
- %w/rubygems rational/.each{|r| require r} if RUBY_VERSION =~ /^(1.8)/
1
+ # Enable YJIT if using CRuby >= 3.3"
2
+ RubyVM::YJIT.enable if RUBY_ENGINE == "ruby" and RUBY_VERSION.to_f >= 3.3
3
3
 
4
- require "primes/utils/version"
4
+ require "bitarray"
5
5
 
6
6
  module Primes
7
7
  module Utils
8
8
  # Upon loading, determine if platform has cli command 'factor'
9
- private
9
+
10
10
  @@os_has_factor = false
11
11
  begin
12
- if `factor 10`.split(' ') == ["10:", "2", "5"]
12
+ if `factor 10`.split(' ') == ['10:', '2', '5']
13
13
  @@os_has_factor = true
14
14
  end
15
15
  rescue
16
16
  @@os_has_factor = false
17
17
  end
18
18
 
19
- public
19
+ begin RUBY = RUBY_ENGINE rescue RUBY = 'ruby'.freeze end
20
+ #RUBY = `ruby -v`.split[0] # alternative old way
21
+
20
22
  if @@os_has_factor # for platforms with cli 'factor' command
21
23
 
22
- def prime?
23
- `factor #{self.abs}`.split(' ').size == 2
24
+ # Return prime factors of n in form [[-1,1],[p1,e1],...[pn,en]]
25
+ # Use Linux|Unix coreutils cli command 'factor' for speed and large numbers
26
+ def factors
27
+ factors = self < 0 ? [-1] : []
28
+ factors += `factor #{abs}`.split(' ')[1..-1].map(&:to_i)
29
+ factors.group_by { |prm| prm }.map { |prm, exp| [prm, exp.size] }
24
30
  end
25
31
 
26
- def factors(p=0) # p is unused dummy variable for method consistency
27
- factors = `factor #{self.abs}`.split(' ')[1..-1].map(&:to_i)
28
- h = Hash.new(0); factors.each {|f| h[f] +=1}; h.to_a.sort
29
- end
32
+ alias prime_division factors
30
33
 
31
- def primesf(start_num=0)
32
- # List primes within a number range: end_num - start_num
33
- # Uses 'prime?' to check primality of prime candidates in range
34
- sozdata = sozcore1(self, start_num, true) # true for primes list
35
- pcs_in_range, r, mod, modk, rescnt, residues, primes = sozdata
34
+ puts "Using cli 'factor' for factors|prime_division"
36
35
 
37
- pcs_in_range.times do # list primes from this num pcs in range
38
- prime = modk + residues[r]
39
- primes << prime if prime.prime?
40
- r +=1; if r > rescnt; r=1; modk +=mod end
41
- end
42
- primes
43
- end
36
+ end # use pure ruby versions for platforms without cli command 'factor'
44
37
 
45
- def primescntf(start_num=0)
46
- # Count primes within a number range: end_num - start_num
47
- # Uses 'prime?' to check primality of prime candidates in range
48
- sozdata = sozcore1(self, start_num, false) # false for primes count
49
- pcs_in_range, r, mod, modk, rescnt, residues, primescnt = sozdata
38
+ # Return prime factors of n in form [[-1,1],[p1,e1],..[pn,en]]
39
+ # Adaptively selects optimum PG of reduced factored number, if possible
40
+ def factors1
41
+ modpg, rescnt = 210, (48 + 4) # P7's modulus and residues count
42
+ residues = [2,3,5,7, 11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,
43
+ 97,101,103,107,109,113,121,127,131,137,139,143,149,151,157,163,
44
+ 167,169,173,179,181,187,191,193,197,199,209,211]
50
45
 
51
- pcs_in_range.times do # count primes from this num pcs in range
52
- primescnt +=1 if (modk + residues[r]).prime?
53
- r +=1; if r > rescnt; r=1; modk +=mod end
54
- end
55
- primescnt
56
- end
57
-
58
- puts "Using cli 'factor' for prime? primesf primescntf factors|prime_division"
59
-
60
- else # use pure ruby versions for platforms without cli command 'factor'
61
-
62
- def prime? # Uses P7 Strictly Prime Generator
63
- residues = [1,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,
64
- 83,89,97,101,103,107,109,113,121,127,131,137,139,143,149,151,157,
65
- 163,167,169,173,179,181,187,191,193,197,199,209,211]
66
- mod=210; # rescnt=48
67
-
68
- n = self.abs
69
- is_a_pc = residues.include?(n%mod) # true if n is a prime candidate
70
- return false unless (n > 1 and is_a_pc) or [2,3,5,7].include? n
71
- return true if n <= 211 and not [121,143,169,187,209].include? n
72
-
73
- sqrtN = Math.sqrt(n).to_i
74
- p=11 # first test prime pj
75
- while p <= sqrtN
76
- return false if
77
- n%(p) == 0 or n%(p+2) ==0 or n%(p+6) == 0 or n%(p+8) ==0 or
78
- n%(p+12) == 0 or n%(p+18) ==0 or n%(p+20) == 0 or n%(p+26) ==0 or
79
- n%(p+30) == 0 or n%(p+32) ==0 or n%(p+36) == 0 or n%(p+42) ==0 or
80
- n%(p+48) == 0 or n%(p+50) ==0 or n%(p+56) == 0 or n%(p+60) ==0 or
81
- n%(p+62) == 0 or n%(p+68) ==0 or n%(p+72) == 0 or n%(p+78) ==0 or
82
- n%(p+86) == 0 or n%(p+90) ==0 or n%(p+92) == 0 or n%(p+96) ==0 or
83
- n%(p+98) == 0 or n%(p+102)==0 or n%(p+110)== 0 or n%(p+116)==0 or
84
- n%(p+120)== 0 or n%(p+126)==0 or n%(p+128)== 0 or n%(p+132)==0 or
85
- n%(p+138)== 0 or n%(p+140)==0 or n%(p+146)== 0 or n%(p+152)==0 or
86
- n%(p+156)== 0 or n%(p+158)==0 or n%(p+162)== 0 or n%(p+168)==0 or
87
- n%(p+170)== 0 or n%(p+176)==0 or n%(p+180)== 0 or n%(p+182)==0 or
88
- n%(p+186)== 0 or n%(p+188)==0 or n%(p+198)== 0 or n%(p+200)==0
89
- p += mod # first prime candidate for next kth residues group
90
- end
91
- true # n is prime (100%|deterministically)
92
- end
46
+ factors = self < 0 ? [-1] : [] # returns [] for 0|1; [-1, 1] for negatives
47
+ num = self.abs # factor only non-negative integers
93
48
 
94
- def factors(p=13)
95
- # Return prime factors of n in form [[p1,e1],[p2,e2]..[pn,en]]
96
- # Uses P13 SP PG as default Prime Generator
97
- seeds = [2, 3, 5, 7, 11, 13, 17, 19]
98
- p = 13 unless seeds.include? p
99
-
100
- primes = seeds[0..seeds.index(p)]
101
- mod = primes.reduce(:*) # modulus: modPn = 2*3*5*7*..*Pn
102
- residues, rescnt = make_residues_rescnt(mod)
103
-
104
- n = self.abs # number to factor
105
- factors = [] # init empty factors array
106
-
107
- return [] if n < 2
108
- return [[n,1]] if primes.include? n
109
- primes.each {|p| while n%p == 0; factors << p; n /= p end }
110
-
111
- sqrtN = Math.sqrt(n).to_i
112
- modk,r=0,1
113
- while (p = modk+residues[r]) <= sqrtN
114
- if n%p == 0
115
- factors << p; r -=1; n /= p; sqrtN = Math.sqrt(n).to_i
116
- end
117
- r +=1; if r > rescnt; r=1; modk +=mod end
118
- end
119
- factors << n if n > 1
120
- h=Hash.new(0); factors.each {|f| h[f] +=1}; h.to_a.sort
121
- end
122
-
123
- puts "Using pure ruby versions for all methods"
49
+ unless num.prime? || (num | 1) == 1 # skip factoring if num is prime, 0, or 1
50
+ modk, r, r0 = 0, 0, 4 # r0 is index for P7's first residue 11
51
+ until num.prime? || num == 1 # find factors until num is prime or 1
52
+ while prime = modk + residues[r]
53
+ (factors << prime; num /= prime; break) if (num % prime).zero?
54
+ (r = r0; modk += modpg) if (r = r.succ) == rescnt
55
+ end end end
56
+ factors << num if num > 1
57
+ factors.group_by{ |prm| prm}.map{ |prm, exp| [prm, exp.size] }
124
58
  end
125
59
 
126
- # Replace slow ruby library method prime_division with faster version
127
- alias prime_division factors
60
+ # Use pure Ruby version of `factor` if not in OS.
61
+ alias prime_division factors1 unless @@os_has_factor
128
62
 
129
- def primenth(p=0)
130
- # Return value of nth prime
131
- # Adaptively selects best SP PG, unless valid input PG given at runtime
63
+ # Return value of nth prime for self > 0, 0 if self = 0, or nil if self < 0
64
+ # Adaptively selects best SP PG, unless valid input PG given at runtime
65
+ def primenth(p = 0)
66
+ return nil if (n = self) < 0
132
67
  seeds = [2, 3, 5, 7, 11, 13]
133
- (primes=seeds[0..seeds.index(p)]; mod=primes.reduce(:*)) if seeds.include? p
68
+ return n > 0 ? seeds[n - 1] : 0 if n <= seeds.size
134
69
 
135
- n = self.abs # the desired nth prime
136
- return n > 0 ? seeds[n-1] : 0 if n <= seeds.size
70
+ start_num, nth, nthflag = set_start_value(n, true)
71
+ return start_num if nthflag # output nthprime value if n a ref prime key
72
+ end_num = approximate_nth(n) # close approx to nth >= real nth
137
73
 
138
- start_num, nth, nthflag = set_start_value(n,true)
139
- return start_num if nthflag # output nthprime value if n a ref prime key
74
+ (primes = seeds[0..seeds.index(p)]; modpg = primes.reduce(:*)) if seeds.include? p
75
+ primes, modpg = select_pg(end_num, start_num) unless primes
140
76
 
141
- num = approximate_nth(n) # close approx to nth >= real nth
142
- primes, mod = select_pg(num, start_num) unless primes
143
- prms, m, modk, residues, rescnt, pcs2start, * = sozcore2(num, start_num, mod)
144
- return unless prms # exit gracefully if sozcore2 mem error
77
+ prms, m, _, residues, pcs2start, * = sozcore2(end_num, start_num, modpg)
78
+ return unless prms # exit gracefully if sozcore2 mem error
145
79
 
146
80
  # starting at start_num's location, find nth prime within given range
147
- prmcnt = n > nth ? nth-1 : primes.size
148
- pcnt = prmcnt + prms[m..-1].count(1) # number of primes upto nth approx
149
- return puts "#{pcnt} not enough primes, approx nth too small." if pcnt < n
150
- while prmcnt < n; prmcnt +=1 if prms[m] == 1; m +=1 end
151
- k, r = (m + pcs2start).divmod rescnt
152
- mod*k + residues[r]
81
+ pcnt = n > nth ? nth - 1 : primes.size
82
+ max = prms.size
83
+ while pcnt < n && m < max; pcnt = pcnt.succ if prms[m].zero?; m = m.succ end
84
+ return puts "#{pcnt} not enough primes, ~nth val too small." if pcnt < n
85
+ k, r = (m + pcs2start - 1).divmod residues.size
86
+ modpg * k + residues[r]
153
87
  end
154
88
 
155
- alias nthprime primenth # to make life easier
89
+ alias nthprime primenth # to make life easier
156
90
 
157
- def primes(start_num=0)
158
- # List primes between a number range: end_num - start_num
159
- # Adaptively selects Strictly Prime (SP) Prime Generator
160
- num = self.abs; start_num = start_num.abs
161
- num, start_num = start_num, num if start_num > num
91
+ # List primes between a number range: end_num - start_num
92
+ # Adaptively selects Strictly Prime (SP) Prime Generator
93
+ def primes(start_num = 0)
94
+ end_num, start_num = check_inputs(self, start_num)
162
95
 
163
- primes, mod = select_pg(num, start_num) # adaptively select PG
164
- prms, m, modk, residues, rescnt, x, maxprms, r = sozcore2(num, start_num, mod)
165
- return unless prms # exit gracefully if sozcore2 mem error
96
+ primes, modpg = select_pg(end_num, start_num) # adaptively select PG
97
+ prms, m, modk, residues, _, r = sozcore2(end_num, start_num, modpg)
98
+ return unless prms # exit gracefully if sozcore2 mem error
99
+ rescnt, modpg, maxprms = residues.size, residues[-1] - 1, prms.size
166
100
 
167
- # init 'primes' w/any excluded primes in range then extract primes from prms
168
- primes.select! {|p| p >= start_num && p <= num}
169
- while m < maxprms # list primes from sieved pcs in prms for range
101
+ # init 'primes' w/any excluded primes in range, extract primes from prms
102
+ primes.select! { |p| p >= start_num && p <= end_num }
103
+
104
+ # Find, numerate, and store primes from sieved pcs in prms for range
105
+ while m < maxprms
170
106
  begin
171
- primes << modk + residues[r] if prms[m] == 1
107
+ primes << modk + residues[r] if prms[m].zero?; m = m.succ
172
108
  rescue Exception
173
- return puts "ERROR3: not enough memory to store all primes in output array."
109
+ return puts 'ERROR3: not enough sys memory for primes output array.'
174
110
  end
175
- r +=1; if r > rescnt; r=1; modk +=mod end
176
- m +=1
111
+ (r = 0; modk += modpg) if (r = r.succ) == rescnt
177
112
  end
178
113
  primes
179
114
  end
180
115
 
181
- def primescnt(start_num=0)
182
- # Count primes between a number range: end_num - start_num
183
- # Adaptively selects Strictly Prime (SP) Prime Generator
184
- num = self.abs; start_num = start_num.abs
185
- num, start_num = start_num, num if start_num > num
116
+ # Count primes between a number range: end_num - start_num
117
+ # Adaptively selects Strictly Prime (SP) Prime Generator
118
+ def primescnt(start_num = 0)
119
+ end_num, start_num = check_inputs(self, start_num)
186
120
 
187
- if start_num < 3 # for all primes upto num
188
- start_num, nth, nthflag = set_start_value(num,false)
189
- return nth unless nthflag # output num's key|count if a ref nth value
121
+ nthflag, nth = 0, 0
122
+ if start_num < 3 # for all primes upto num
123
+ start_num, nth, nthflag = set_start_value(end_num, false) # closest nth value
124
+ return nth unless nthflag # output num's key|count if ref nth value
190
125
  end
191
126
 
192
- primes,mod = select_pg(num, start_num) # adaptively select PG
193
- prms, m, * = sozcore2(num, start_num, mod)
194
- return unless prms # exit gracefully if sozcore2 mem error
127
+ primes, modpg = select_pg(end_num, start_num) # adaptively select PG
128
+ prms, m, _ = sozcore2(end_num, start_num, modpg)
129
+ return unless prms # exit gracefully if sozcore2 mem error
195
130
 
196
- # init prmcnt for any excluded primes in range then count primes in prms
197
- prmcnt = primes.count {|p| p >= start_num && p <= num}
198
- prmcnt = nth-1 if nthflag && nth > 0 # start count for small range
199
- prmcnt + prms[m..-1].count(1)
131
+ # init prmcnt for any modulus primes in range; count primes in prms
132
+ prmcnt = primes.count { |p| p >= start_num && p <= end_num }
133
+ prmcnt = nth - 1 if nthflag && (nth > 0) # start count for small range
134
+ max = prms.size
135
+ while m < max; prmcnt = prmcnt.succ if prms[m].zero?; m = m.succ end
136
+ prmcnt
200
137
  end
201
138
 
202
- # Miller-Rabin prime test in Ruby
203
- # From: http://en.wikipedia.org/wiki/Miller-Rabin_primality_test
204
- # Ruby Rosetta Code: http://rosettacode.org/wiki/Miller-Rabin_primality_test
205
- # I modified the Rosetta Code, as shown below
206
-
207
- require 'openssl'
208
- def primemr?(k=20) # increase k for more reliability
209
- n = self.abs
210
- return true if [2,3].include? n
211
- return false unless [1,5].include?(n%6) and n > 1
212
-
213
- d = n - 1
214
- s = 0
215
- (d >>= 1; s += 1) while d.even?
216
- k.times do
217
- a = 2 + rand(n-4)
218
- x = a.to_bn.mod_exp(d,n) # x = (a**d) mod n
219
- next if x == 1 or x == n-1
220
- (s-1).times do
221
- x = x.mod_exp(2,n) # x = (x**2) mod n
222
- return false if x == 1
223
- break if x == n-1
224
- end
225
- return false if x != n-1
139
+ # List primes within a number range: end_num - start_num
140
+ # Uses 'primemr' to check primality of prime candidates in range
141
+ def primesmr(start_num = 0)
142
+ end_num, start_num = check_inputs(self, start_num)
143
+ r, modk, residues, primes = sozcore1(end_num, start_num)
144
+ rescnt, modpg = residues.size, residues[-1] - 1
145
+
146
+ while end_num >= (pc = modk + residues[r])
147
+ primes << pc if pc.primemr?
148
+ (r = 0; modk += modpg) if (r = r.succ) == rescnt
226
149
  end
227
- true # n is prime (with high probability)
150
+ primes
228
151
  end
229
152
 
230
- def primesmr(start_num=0)
231
- # List primes within a number range: end_num - start_num
232
- # Uses 'primemr' to check primality of prime candidates in range
233
- sozdata = sozcore1(self, start_num, true) # true for primes
234
- pcs_in_range, r, mod, modk, rescnt, residues, primes = sozdata
153
+ # Count primes within a number range: end_num - start_num
154
+ # Uses 'primemr' to check primality of prime candidates in range
155
+ def primescntmr(start_num = 0)
156
+ end_num, start_num = check_inputs(self, start_num)
235
157
 
236
- pcs_in_range.times do # list primes from this num pcs in range
237
- prime = modk + residues[r]
238
- primes << prime if prime.primemr?
239
- r +=1; if r > rescnt; r=1; modk +=mod end
158
+ nthflag, nth = 0, 0
159
+ if start_num < 3 # for all primes upto num
160
+ start_num, nth, nthflag = set_start_value(end_num, false) # closest nth value
161
+ return nth unless nthflag # output num's key|count if ref nth value
240
162
  end
241
- primes
242
- end
243
163
 
244
- def primescntmr(start_num=0)
245
- # Count primes within a number range: end_num - start_num
246
- # Uses 'primemr' to check primality of prime candidates in range
247
- sozdata = sozcore1(self, start_num, false) # false for primescnt
248
- pcs_in_range, r, mod, modk, rescnt, residues, primescnt = sozdata
164
+ r, modk, residues, mod_primes = sozcore1(end_num, start_num)
165
+ rescnt, modpg, primescnt = residues.size, residues[-1] - 1, mod_primes.size
166
+ primescnt = nth - 1 if nthflag && (nth > 0) # set count for nth prime < num
249
167
 
250
- pcs_in_range.times do # count primes from this num pcs in range
251
- primescnt +=1 if (modk + residues[r]).primemr?
252
- r +=1; if r > rescnt; r=1; modk +=mod end
168
+ while end_num >= (pc = modk + residues[r])
169
+ primescnt = primescnt.succ if pc.primemr?
170
+ (r = 0; modk += modpg) if (r = r.succ) == rescnt
253
171
  end
254
172
  primescnt
255
173
  end
256
174
 
257
- def primes_utils # display list of available methods
258
- methods = %w/prime? primemr? primes primesf primesmr primescnt
259
- primescntf primescntmr primenth|nthprime factors|prime_division primes_utils/
260
- (methods - (@@os_has_factor ? [] : %w/primesf primescntf/)).join(" ")
175
+ # PGT and Miller-Rabin combined primality tests for random n
176
+ def prime? (k = 5) # Can change k higher for mr_prime?
177
+ #Use PGT residue checks for small values < PRIMES.last**2
178
+ return PRIMES.include? self if self <= PRIMES.last
179
+ return false if MODPN.gcd(self) != 1
180
+ return true if self < PRIMES_LAST_SQRD
181
+ primemr?(k)
182
+ end
183
+
184
+ # Returns the next prime number for +self+ >= 0, or nil if n < 0
185
+ def next_prime
186
+ return nil if (n = self) < 0
187
+ return (n >> 1) + 2 if n <= 2 # return 2 if n is 0|1
188
+ n = n + 1 | 1 # 1st odd number > n
189
+ until (res = n % 6) & 0b11 == 1; n += 2 end # n first P3 pc >= n, w/residue 1 or 5
190
+ inc = (res == 1) ? 4 : 2 # set its P3 PGS value, inc by 2 and 4
191
+ until n.primemr?; n += inc; inc ^= 0b110 end # find first prime P3 pc
192
+ n
193
+ end
194
+
195
+ # Returns the previous prime number < +self+, or nil if self <= 2
196
+ def prev_prime
197
+ return nil if (n = self) < 3
198
+ return (n >> 1) + 1 if n <= 5
199
+ n = n - 2 | 1 # 1st odd number < n
200
+ until (res = n % 6) & 0b11 == 1; n -= 2 end # n first P3 pc <= n, w/residue 1 or 5
201
+ dec = (res == 1) ? 2 : 4 # set its P3 PGS value, dec by 2 and 4
202
+ until n.primemr?; n -= dec; dec ^= 0b110 end # find first prime P3 pc
203
+ n
204
+ end
205
+
206
+ def primes_utils
207
+ # display list of available methods
208
+ methods = %w[prime? primemr? primes primesmr primescnt
209
+ primescntmr primenth|nthprime factors|prime_division
210
+ factors1 next_prime prev_prime primes_utils].join(" ")
211
+ end
212
+
213
+ # Returns true if +self+ is a prime number, else returns false.
214
+ def primemr? (k = 5) # k is default number of random bases
215
+ return false if self < 2 # return false for 0|1 and negatives
216
+ neg_one_mod = n = d = self - 1 # these are even as self is always odd
217
+ d >>= 2 while (d & 0b11) == 0; d >>= (d & 1)^1 # make d odd number
218
+ # wits = [range, [wit_prms]] or nil
219
+ wits = WITNESS_RANGES.find { |range, wits| range > self }
220
+ witnesses = wits ? wits[1] : k.times.map{ rand(self - 4) + 2 }
221
+ witnesses.each do |b|
222
+ next if (b % self).zero? # **skip base if a multiple of input**
223
+ y = b.pow(d, self) # y = (b**d) mod self
224
+ s = d
225
+ until y == 1 || y == neg_one_mod || s == n
226
+ y = y.pow(2, self) # y = (y**2) mod self
227
+ s <<= 1
228
+ end
229
+ return false unless y == neg_one_mod || s.odd?
230
+ end
231
+ true
261
232
  end
262
233
 
263
234
  private
264
- def make_residues_rescnt(mod)
265
- residues=[1]; 3.step(mod,2) {|r| residues << r if mod.gcd(r) == 1}
266
- residues << mod+1
267
- [residues, residues.size-1] # return residues array and rescnt
235
+
236
+ # Best known deterministic witnnesses for given range and set of bases
237
+ # https://miller-rabin.appspot.com/
238
+ # https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
239
+ WITNESS_RANGES = {
240
+ 341_531 => [9345883071009581737],
241
+ 1_050_535_501 => [336781006125, 9639812373923155],
242
+ 350_269_456_337 => [4230279247111683200, 14694767155120705706, 16641139526367750375],
243
+ 55_245_642_489_451 => [2, 141889084524735, 1199124725622454117, 11096072698276303650],
244
+ 7_999_252_175_582_851 => [2, 4130806001517, 149795463772692060, 186635894390467037, 3967304179347715805],
245
+ 585_226_005_592_931_977 => [2, 123635709730000, 9233062284813009, 43835965440333360, 761179012939631437, 1263739024124850375],
246
+ 18_446_744_073_709_551_615 => [2, 325, 9375, 28178, 450775, 9780504, 1795265022],
247
+ 318_665_857_834_031_151_167_461 => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37],
248
+ 3_317_044_064_679_887_385_961_981 => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
249
+ }
250
+
251
+ MODPN = 232862364358497360900063316880507363070 # 101# (101 primorial) is largest for u128
252
+ PRIMES = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103]
253
+ PRIMES_LAST_SQRD = PRIMES.last ** 2
254
+
255
+ def check_inputs(end_num, start_num)
256
+ raise "invalid negative input(s)" if end_num < 0 || start_num < 0
257
+ end_num, start_num = start_num, end_num if start_num > end_num
258
+ [end_num, start_num]
268
259
  end
269
260
 
270
- # lte= true: first output element is number of pcs <= num
271
- # lte=false: num pcs <, residue index, and resgroup value, for (start_)num pc
272
- def pcs_to_num(num,mod,rescnt,residues,lte)
273
- num -=1; lte ? (num |=1; k=num.abs/mod) : k = (num-1).abs/mod
274
- modk = mod*k; r=1
275
- r +=1 while num >= modk+residues[r]
276
- [rescnt*k + r-1, r, modk] # [num pcs, r index, num modulus]
261
+ # Returns for SP PG mod value array of residues [r1, r2,..mod-1, mod+1]
262
+ def make_residues(modpg)
263
+ return [ 7, 11, 13, 17, 19, 23, 29, 31] if modpg == 30
264
+ return [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
265
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 121, 127, 131, 137,
266
+ 139, 143, 149, 151, 157, 163, 167, 169, 173, 179, 181, 187, 191,
267
+ 193, 197, 199, 209, 211] if modpg == 210
268
+ residues = []
269
+ pc, inc, midmod = 13, 4, modpg >> 1
270
+ while pc < midmod
271
+ residues << pc << (modpg - pc) if modpg.gcd(pc) == 1
272
+ pc += inc; inc ^= 0b110
273
+ end
274
+ residues.sort << (modpg - 1) << (modpg + 1)
277
275
  end
278
276
 
279
- # Use default SP Prime Generator to parametize the pcs within a range
280
- # inputs: end_num|start_num of range; method_flag to numerate|count primes
277
+ # lte = true: first output element is number of pcs <= num
278
+ # lte = false: num pcs <, residue index, resgroup val, for (start_)num pc
279
+ def pcs_to_num(num, residues, lte)
280
+ modpg, rescnt = residues[-1] - 1, residues.size
281
+ num -= 1; lte ? (num |= 1; k = num.abs/modpg) : k = (num - 1).abs/modpg
282
+ modk = modpg * k; r = 0
283
+ r = r.succ while num >= modk + residues[r]
284
+ [rescnt * k + r, r, modk] # [num pcs, r index, num modulus]
285
+ end
286
+
287
+ def pcs_to_nums(end_num, start_num, residues)
288
+ modpg, rescnt = residues[-1] - 1, residues.size
289
+ end_num = 2 if end_num < residues[0]
290
+ start_num = 2 if start_num < residues[0]
291
+ start_num -= 1; k1 = (start_num - 1)/modpg; modk1 = modpg * k1
292
+ end_num -= 1; k2 = (end_num |= 1 )/modpg; modk2 = modpg * k2
293
+ r1 = 0; r1 = r1.succ while start_num >= modk1 + residues[r1]
294
+ r2 = 0; r2 = r2.succ while end_num >= modk2 + residues[r2]
295
+ pcs2end = k2 * rescnt + r2; pcs2start = k1 * rescnt + r1
296
+ pcs_in_range = pcs2end - pcs2start
297
+ [pcs2end, pcs2start, r1, modk1, pcs_in_range]
298
+ end
299
+
300
+ # Use default SP Prime Generator to parametize the pcs within range
301
+ # inputs: end_num|start_num of range
281
302
  # outputs: maxpcs-m - number of pcs in the range
282
303
  # r - residue index value for start_num pc of range
283
- # mod - mod value for PG
284
304
  # modk - base value for start_num's resgroup
285
- # rescnt - number of residues for PG
286
- # residues - array of residues plus mod+1 for PG
287
- # primes array|primes.size - primes array or size based on method_flag
288
- def sozcore1(num, start_num, method_flag)
289
- num = num.abs; start_num = start_num.abs
290
- num, start_num = start_num, num if start_num > num
291
-
292
- primes = [2,3,5,7,11,13] # excluded primes for P13 default SP PG
293
- mod = primes.reduce(:*) # P13 modulus: 2*3*5*7*11*13 = 30030
294
- residues, rescnt = make_residues_rescnt(mod)
295
- maxpcs,* = pcs_to_num(num,mod,rescnt,residues,true) # num pcs <= end_num
296
-
297
- # init 'primes' w/any excluded primes in the range, or [] if none
298
- primes.select! {|p| p >= start_num && p <= num}
299
-
300
- # compute parameters for start_num pc, then create output parameters array
301
- m, r, modk = pcs_to_num(start_num, mod, rescnt, residues, false)
302
- [maxpcs-m, r, mod, modk, rescnt, residues, method_flag ? primes : primes.size]
305
+ # residues - array of residues for PG: [r1..modpg-1, modpg+1]
306
+ # primes array|primes.size - based on method_flag
307
+ def sozcore1(end_num, start_num)
308
+ range = end_num - start_num
309
+ modpg = range < 10_000 ? 210 : 30030
310
+ residues = make_residues(modpg) # chosen PG residues
311
+ primes = PRIMES.select { |p| p < residues[0] && (p >= start_num && p <= end_num) }
312
+ start_num = 2 if start_num < residues[0]
313
+ k = (start_num - 2) / modpg; modk = k * modpg; r = 0
314
+ while (start_num - 1) >= modk + residues[r]; r = r.succ end
315
+ [r, modk, residues, primes]
303
316
  end
304
317
 
305
318
  # Perform SoZ with given Prime Generator and return array of parameters
306
- # inputs: end_num and start_num of range and mod value for PG
319
+ # inputs: end_num and start_num of range and modulus value for PG
307
320
  # outputs: prms - binary (0,1) array of pcs within a range or to end_num
308
321
  # m - num of pcs in prms < start_num; so prms[m] = start_num
309
- # modks - mod value for start_num's resgroup
310
- # residues - array of residues plus mod+1 for PG
311
- # rescnt - number of residues for PG
322
+ # modks - modulus value for start_num's resgroup
323
+ # residues - array of residues for PG: [r1..modpg-1, modpg+1]
312
324
  # pcs2start- number of pcs < start_num pc
313
- # maxprms - number of pcs to find primes from; prms array size
314
- # rs - residue index location for first pc >= start_num
315
- def sozcore2(num, start_num, mod)
316
- residues, rescnt = make_residues_rescnt(mod) # parameters for the PG
317
- maxprms,* = pcs_to_num(num,mod,rescnt,residues,true) # num pcs <= end_num
318
-
319
- # for start_num pc, find num pcs <, residue index, and resgroup mod value
320
- pcs2start, rs, modks = pcs_to_num(start_num, mod, rescnt, residues, false)
321
-
322
- sqrtN = Math.sqrt(num).to_i # sqrt of end_num (end of range)
323
- pcs2sqrtN,* = pcs_to_num(sqrtN,mod,rescnt,residues,true) # num pcs <= sqrtN
324
-
325
- split_arrays = start_num > sqrtN # flag, true if two arrays used for sieve
326
- maxpcs = maxprms # init array size for all pcs to end_num
327
- if split_arrays # if start_num > sqrtN create two arrays
328
- maxpcs = pcs2sqrtN # number of pcs|array size, for pcs <= sqrtN
329
- max_range = maxprms-pcs2start # number of pcs in range start_num to end_num
330
- prms_range = array_check(max_range,1) # array to represent pcs in range
331
- return puts "ERROR1: range size too big for available memory." unless prms_range
325
+ # rs - residue index location for first pc >= start_num
326
+ def sozcore2(end_num, start_num, modpg)
327
+ residues = make_residues(modpg); rescnt = residues.size
328
+ maxpcs, pcs2start, rs, modks, pcs_range = pcs_to_nums(end_num, start_num, residues)
329
+ sqrtN = Integer.sqrt(end_num)
330
+ pcs2sqrtN, _ = pcs_to_nums(sqrtN, 0, residues) # num pcs <= sqrtN
331
+
332
+ m = pcs2start # index to start retrieving primes in prms array
333
+ split_arrays = (start_num > sqrtN) # flag, true for split arrays
334
+ if split_arrays # if start_num > sqrtN create two arrays
335
+ maxpcs = pcs2sqrtN # array size now for primary sieve array prms
336
+ prms_range = array_check(pcs_range) # array for pcs in range
337
+ raise 'ERROR1: range too big for free sys mem.' unless prms_range
338
+ m = 0 # index to start retrieving primes in split array
332
339
  end
333
- prms = array_check(maxpcs,1) # array for pcs upto sqrtN, or end_num
334
- return puts "ERROR2: end_num too big for available memory." unless prms
335
-
336
- # residues offsets to compute a pcs address in its resgroup in prms
337
- pos =[]; rescnt.times {|i| pos[residues[i]] = i-1}
338
-
339
- # Sieve of Zakiya (SoZ) to eliminate nonprimes from prms and prms_range
340
- modk,r,k=0,0,0
341
- pcs2sqrtN.times do |i| # sieve primes from pcs upto sqrt(end_num)
342
- r +=1; if r > rescnt; r=1; modk +=mod; k +=1 end
343
- next unless prms[i] == 1 # when a prime location found
344
- prm_r = residues[r] # its residue value is saved
345
- prime = modk + prm_r # its value is numerated
346
- prmstep = prime * rescnt # its primestep computed
347
- kcon = k * prmstep # its inner loop constant computed
348
- residues[1..-1].each do |ri|# now perform sieve with it
340
+ prms = array_check(maxpcs) # array for pcs upto sqrtN, or end_num
341
+ raise 'ERROR2: end_num too big for available sys mem.' unless prms
342
+
343
+ # Sieve of Zakiya (SoZ) to eliminate nonprimes from prms, prms_range
344
+ pcs2sqrtN.times do |i| # sieve primes from pcs upto sqrt(end_num)
345
+ next unless prms[i].zero? # if pc not prime, get next one
346
+ prm_r = residues[i % rescnt] # save its residue value
347
+ prime = modpg*(k=i/rescnt)+prm_r # numerate its value; set k resgroup value
348
+ rem = start_num % prime # prime's distance to start_num
349
+ next unless (prime - rem <= end_num - start_num) || rem == 0 # skip prime mults not in range
350
+ prmstep = prime * rescnt # compute its primestep
351
+ residues.each do |ri| # find|mark its multiples
349
352
  # convert (prime * (modk + ri)) pc value to its address in prms
350
- # computed as nonprm = (k*(prime + ri) + kn)*rescnt + pos[rr]
351
- kn,rr = (prm_r * ri).divmod mod # residues product res[group|track]
352
- nonprm = kcon + (k*ri + kn)*rescnt + pos[rr]# 1st prime multiple address with ri
353
- while nonprm < maxpcs; prms[nonprm]=0; nonprm +=prmstep end
354
- if split_arrays # when start_num > sqrtN
355
- nonprm = (pcs2start - nonprm)%prmstep # (start_num - last multiple) pcs
356
- nonprm = prmstep - nonprm if nonprm != 0 # location in range, or beyond
357
- while nonprm < max_range; prms_range[nonprm]=0; nonprm += prmstep end
358
- end
359
- end
360
- end
361
- # determine prms array parameters and starting location value m for start_num
362
- split_arrays ? (prms = prms_range; maxprms = max_range; m=0) : m = pcs2start
363
- [prms, m, modks, residues, rescnt, pcs2start, maxprms, rs] # parameters output
353
+ kn, rr = (prm_r * ri - 2).divmod modpg
354
+ mult = mult1 = (k*(prime + ri) + kn)*rescnt + residues.index(rr+2) # 1st prime mult
355
+ while mult < maxpcs; prms[mult] = 1; mult += prmstep end
356
+ if split_arrays # when start_num > sqrtN(pcs2sqrtN+1)
357
+ mult = (pcs2start - mult1) % prmstep # (start_num - last mult) pcs
358
+ mult = prmstep - mult if mult != 0 # location in range, or beyond
359
+ while mult < pcs_range; prms_range[mult] = 1; mult += prmstep end
360
+ end end end
361
+ # select prms array and start location val m for start_num in it
362
+ [(split_arrays ? prms_range : prms), m, modks, residues, pcs2start, rs]
364
363
  end
365
364
 
366
- def approximate_nth(n) # approximate nthprime value >= real value
367
- b = 0.5722*n**0.0088 # derived equation to compute close nth values
368
- a = b*(Math.log(Math.log(n)))
369
- (n*(Math.log(n)+a)+3).to_i # use nth approximation as end_num of range
365
+ def approximate_nth(n)
366
+ b = 0.5722 * n**0.0088
367
+ a = b * Math.log(log_n = Math.log(n))
368
+ (n * (log_n + a) + 3).to_i
370
369
  end
371
370
 
372
- def set_start_value(n, hshflag) # find largest index nthprime|val <= n
371
+ # Find largest index nthprime|val <= n; return [start_num, nth, f/t]
372
+ def set_start_value(n, hshflag)
373
373
  if hshflag
374
- return [nths[n], 0, true] if nths.has_key? n # if n is key in nths table
375
- nth = nths.keys.sort.reverse.detect {|k| k < n} # find largest indexed key < n
376
- [nth ? nths[nth] : 0, nth ||= n+1, false] # [start_num, nth, false]
374
+ nth = nths.keys.sort.reverse.find { |k| k <= n } # largest key <= n
375
+ [nth ? nths[nth] : 0, nth || (n + 1), nth == n]
377
376
  else
378
- return [0,nths.key(n),false] if nths.has_value? n # if n is value in nths table
379
- v=val= nths.values.sort.reverse.detect {|v| v < n} # find largest indexed val < n
380
- [v ||= 0, val ? nths.key(val) : 0, true] # [start_num, nth, true]
377
+ val = nths.values.sort.reverse.find { |v| v <= n } # largest val <= n
378
+ [val || 0, val ? nths.key(val) : 0, val != n]
381
379
  end
382
380
  end
383
381
 
384
- def nths # hash table index of reference nth primes
385
- nths={1000000 => 15485863, 5000000 => 86028121, 7500000 => 132276691,
386
- 10000000 => 179424673, 12500000 => 227254201, 15000000 => 275604541,
387
- 18500000 => 344032387, 25000000 => 472882027, 31000000 => 593441843,
388
- 37500000 => 725420401, 43500000 => 848321917, 50000000 => 982451653,
389
- 56000000 => 1107029837, 62500000 => 1242809749, 68500000 => 1368724829,
390
- 75000000 => 1505776939, 81500000 => 1643429659, 87500000 => 1770989609,
391
- 93500000 => 1898979367, 100000000 => 2038074743, 106500000 => 2177624377,
392
- 112500000 => 2306797469, 125000000 => 2576983867, 137500000 => 2848518523,
393
- 150000000 => 3121238909, 162500000 => 3395057291, 175000000 => 3669829403,
394
- 187500000 => 3945592087, 200000000 => 4222234741, 212500000 => 4499683009,
395
- 225000000 => 4777890881, 237500000 => 5056862311, 250000000 => 5336500537,
396
- 262500000 => 5616787769, 275000000 => 5897707297, 287500000 => 6179208157,
397
- 300000000 => 6461335109, 312500000 => 6743943629, 325000000 => 7027107881,
398
- 337500000 => 7310793337, 350000000 => 7594955549, 362500000 => 7879581839,
399
- 375000000 => 8164628191, 387500000 => 8450100349, 400000000 => 8736028057,
400
- 412500000 => 9022375487, 425000000 => 9309109471, 437500000 => 9596238593,
401
- 450000000 => 9883692017, 462500000 =>10171564687, 475000000 =>10459805417,
402
- 487500000 =>10748372137, 500000000 =>11037271757, 512500000 =>11326513039,
403
- 525000000 =>11616020609, 537500000 =>11905863799, 550000000 =>12196034771,
404
- 562500000 =>12486465863, 575000000 =>12777222833, 587500000 =>13068237251,
405
- 600000000 =>13359555403, 612500000 =>13651119389, 625000000 =>13942985677,
406
- 637500000 =>14235122851, 650000000 =>14527476781, 662500000 =>14820071503,
407
- 675000000 =>15112928683, 687500000 =>15406031899, 700000000 =>15699342107,
408
- 712500000 =>15992957251, 725000000 =>16286768243, 737500000 =>16580801137,
409
- 750000000 =>16875026921, 762500000 =>17169527171, 775000000 =>17464243799,
410
- 787500000 =>17759139259, 800000000 =>18054236957, 812500000 =>18349591409,
411
- 825000000 =>18645104897, 837500000 =>18940846207, 850000000 =>19236701629,
412
- 862500000 =>19532780327, 875000000 =>19829092147, 887500000 =>20125592731,
413
- 900000000 =>20422213579, 912500000 =>20719050323, 925000000 =>21016060633,
414
- 937500000 =>21313231963, 950000000 =>21610588367, 962500000 =>21908128993,
415
- 975000000 =>22205818561, 987500000 =>22503733657, 1000000000=>22801763489,
416
- 1012500000=>23099993743, 1025000000=>23398391231, 1037500000=>23696858797,
417
- 1050000000=>23995554823, 1062500000=>24294392179, 1075000000=>24593421187,
418
- 1087500000=>24892587403, 1100000000=>25191867719, 1112500000=>25491361037,
419
- 1125000000=>25790970053, 1137500000=>26090709563, 1150000000=>26390560513,
420
- 1162500000=>26690560601, 1175000000=>26990744987, 1187500000=>27291009337,
421
- 1200000000=>27591444869, 1212500000=>27892051267, 1225000000=>28192760279,
422
- 1237500000=>28493648629, 1250000000=>28794583627, 1262500000=>29095694269,
423
- 1275000000=>29396966971, 1287500000=>29698366099, 1300000000=>29999858327,
424
- 1312500000=>30301430881, 1325000000=>30603183581, 1337500000=>30905024497,
425
- 1350000000=>31207047449, 1362500000=>31509131153, 1375000000=>31811397571,
426
- 1387500000=>32113702069, 1400000000=>32416190071, 1412500000=>32718790873,
427
- 1425000000=>33021414143, 1437500000=>33324275711, 1450000000=>33627220709,
428
- 1462500000=>33930284893, 1475000000=>34233442279, 1487500000=>34536683891,
429
- 1500000000=>34840062373, 1512500000=>35143545889, 1525000000=>35447088559,
430
- 1537500000=>35750747297, 1550000000=>36054501641, 1562500000=>36358440731,
431
- 1575000000=>36662430631, 1587500000=>36966563321, 1600000000=>37270791697,
432
- 1612500000=>37575137933, 1625000000=>37879532671, 1637500000=>38184009763,
433
- 1650000000=>38488677419, 1662500000=>38793413899, 1675000000=>39098225629,
434
- 1687500000=>39403174463, 1700000000=>39708229123, 1712500000=>40013309359,
435
- 1725000000=>40318523009, 1737500000=>40623800311, 1750000000=>40929166261,
436
- 1762500000=>41234743751, 1775000000=>41540289619, 1787500000=>41845958971,
437
- 1800000000=>42151671491, 1812500000=>42457500313, 1825000000=>42763499629,
438
- 1837500000=>43069571603, 1850000000=>43375710643, 1862500000=>43681898699,
439
- 1875000000=>43988172667, 1887500000=>44294549347, 1900000000=>44601021791,
440
- 1912500000=>44907564593, 1925000000=>45214177441, 1937500000=>45520935011,
441
- 1950000000=>45827700419, 1962500000=>46134655219, 1975000000=>46441643177,
442
- 1987500000=>46748693981, 2000000000=>47055833459, 2012500000=>47363059687
443
- }
444
- end
445
-
446
- def select_pg(num, start_num) # adaptively select PG
447
- range = num - start_num
382
+ def nths # hash table index of reference nth primes
383
+ { 1_000_000 => 15_485_863, 5_000_000 => 86_028_121,
384
+ 7_500_000 => 132_276_691, 10_000_000 => 179_424_673,
385
+ 12_500_000 => 227_254_201, 15_000_000 => 275_604_541,
386
+ 18_500_000 => 344_032_387, 25_000_000 => 472_882_027,
387
+ 31_000_000 => 593_441_843, 37_500_000 => 725_420_401,
388
+ 43_500_000 => 848_321_917, 50_000_000 => 982_451_653,
389
+ 56_000_000 => 1_107_029_837, 62_500_000 => 1_242_809_749,
390
+ 68_500_000 => 1_368_724_829, 75_000_000 => 1_505_776_939,
391
+ 81_500_000 => 1_643_429_659, 87_500_000 => 1_770_989_609,
392
+ 93_500_000 => 1_898_979_367, 100_000_000 => 2_038_074_743,
393
+ 106_500_000 => 2_177_624_377, 112_500_000 => 2_306_797_469,
394
+ 125_000_000 => 2_576_983_867, 137_500_000 => 2_848_518_523,
395
+ 150_000_000 => 3_121_238_909, 162_500_000 => 3_395_057_291,
396
+ 175_000_000 => 3_669_829_403, 187_500_000 => 3_945_592_087,
397
+ 200_000_000 => 4_222_234_741, 212_500_000 => 4_499_683_009,
398
+ 225_000_000 => 4_777_890_881, 237_500_000 => 5_056_862_311,
399
+ 250_000_000 => 5_336_500_537, 262_500_000 => 5_616_787_769,
400
+ 275_000_000 => 5_897_707_297, 287_500_000 => 6_179_208_157,
401
+ 300_000_000 => 6_461_335_109, 312_500_000 => 6_743_943_629,
402
+ 325_000_000 => 7_027_107_881, 337_500_000 => 7_310_793_337,
403
+ 350_000_000 => 7_594_955_549, 362_500_000 => 7_879_581_839,
404
+ 375_000_000 => 8_164_628_191, 387_500_000 => 8_450_100_349,
405
+ 400_000_000 => 8_736_028_057, 412_500_000 => 9_022_375_487,
406
+ 425_000_000 => 9_309_109_471, 437_500_000 => 9_596_238_593,
407
+ 450_000_000 => 9_883_692_017, 462_500_000 => 10_171_564_687,
408
+ 475_000_000 => 10_459_805_417, 487_500_000 => 10_748_372_137,
409
+ 500_000_000 => 11_037_271_757, 512_500_000 => 11_326_513_039,
410
+ 525_000_000 => 11_616_020_609, 537_500_000 => 11_905_863_799,
411
+ 550_000_000 => 12_196_034_771, 562_500_000 => 12_486_465_863,
412
+ 575_000_000 => 12_777_222_833, 587_500_000 => 13_068_237_251,
413
+ 600_000_000 => 13_359_555_403, 612_500_000 => 13_651_119_389,
414
+ 625_000_000 => 13_942_985_677, 637_500_000 => 14_235_122_851,
415
+ 650_000_000 => 14_527_476_781, 662_500_000 => 14_820_071_503,
416
+ 675_000_000 => 15_112_928_683, 687_500_000 => 15_406_031_899,
417
+ 700_000_000 => 15_699_342_107, 712_500_000 => 15_992_957_251,
418
+ 725_000_000 => 16_286_768_243, 737_500_000 => 16_580_801_137,
419
+ 750_000_000 => 16_875_026_921, 762_500_000 => 17_169_527_171,
420
+ 775_000_000 => 17_464_243_799, 787_500_000 => 17_759_139_259,
421
+ 800_000_000 => 18_054_236_957, 812_500_000 => 18_349_591_409,
422
+ 825_000_000 => 18_645_104_897, 837_500_000 => 18_940_846_207,
423
+ 850_000_000 => 19_236_701_629, 862_500_000 => 19_532_780_327,
424
+ 875_000_000 => 19_829_092_147, 887_500_000 => 20_125_592_731,
425
+ 900_000_000 => 20_422_213_579, 912_500_000 => 20_719_050_323,
426
+ 925_000_000 => 21_016_060_633, 937_500_000 => 21_313_231_963,
427
+ 950_000_000 => 21_610_588_367, 962_500_000 => 21_908_128_993,
428
+ 975_000_000 => 22_205_818_561, 987_500_000 => 22_503_733_657,
429
+ 1_000_000_000 => 22_801_763_489, 1_012_500_000 => 23_099_993_743,
430
+ 1_025_000_000 => 23_398_391_231, 1_037_500_000 => 23_696_858_797,
431
+ 1_050_000_000 => 23_995_554_823, 1_062_500_000 => 24_294_392_179,
432
+ 1_075_000_000 => 24_593_421_187, 1_087_500_000 => 24_892_587_403,
433
+ 1_100_000_000 => 25_191_867_719, 1_112_500_000 => 25_491_361_037,
434
+ 1_125_000_000 => 25_790_970_053, 1_137_500_000 => 26_090_709_563,
435
+ 1_150_000_000 => 26_390_560_513, 1_162_500_000 => 26_690_560_601,
436
+ 1_175_000_000 => 26_990_744_987, 1_187_500_000 => 27_291_009_337,
437
+ 1_200_000_000 => 27_591_444_869, 1_212_500_000 => 27_892_051_267,
438
+ 1_225_000_000 => 28_192_760_279, 1_237_500_000 => 28_493_648_629,
439
+ 1_250_000_000 => 28_794_583_627, 1_262_500_000 => 29_095_694_269,
440
+ 1_275_000_000 => 29_396_966_971, 1_287_500_000 => 29_698_366_099,
441
+ 1_300_000_000 => 29_999_858_327, 1_312_500_000 => 30_301_430_881,
442
+ 1_325_000_000 => 30_603_183_581, 1_337_500_000 => 30_905_024_497,
443
+ 1_350_000_000 => 31_207_047_449, 1_362_500_000 => 31_509_131_153,
444
+ 1_375_000_000 => 31_811_397_571, 1_387_500_000 => 32_113_702_069,
445
+ 1_400_000_000 => 32_416_190_071, 1_412_500_000 => 32_718_790_873,
446
+ 1_425_000_000 => 33_021_414_143, 1_437_500_000 => 33_324_275_711,
447
+ 1_450_000_000 => 33_627_220_709, 1_462_500_000 => 33_930_284_893,
448
+ 1_475_000_000 => 34_233_442_279, 1_487_500_000 => 34_536_683_891,
449
+ 1_500_000_000 => 34_840_062_373, 1_512_500_000 => 35_143_545_889,
450
+ 1_525_000_000 => 35_447_088_559, 1_537_500_000 => 35_750_747_297,
451
+ 1_550_000_000 => 36_054_501_641, 1_562_500_000 => 36_358_440_731,
452
+ 1_575_000_000 => 36_662_430_631, 1_587_500_000 => 36_966_563_321,
453
+ 1_600_000_000 => 37_270_791_697, 1_612_500_000 => 37_575_137_933,
454
+ 1_625_000_000 => 37_879_532_671, 1_637_500_000 => 38_184_009_763,
455
+ 1_650_000_000 => 38_488_677_419, 1_662_500_000 => 38_793_413_899,
456
+ 1_675_000_000 => 39_098_225_629, 1_687_500_000 => 39_403_174_463,
457
+ 1_700_000_000 => 39_708_229_123, 1_712_500_000 => 40_013_309_359,
458
+ 1_725_000_000 => 40_318_523_009, 1_737_500_000 => 40_623_800_311,
459
+ 1_750_000_000 => 40_929_166_261, 1_762_500_000 => 41_234_743_751,
460
+ 1_775_000_000 => 41_540_289_619, 1_787_500_000 => 41_845_958_971,
461
+ 1_800_000_000 => 42_151_671_491, 1_812_500_000 => 42_457_500_313,
462
+ 1_825_000_000 => 42_763_499_629, 1_837_500_000 => 43_069_571_603,
463
+ 1_850_000_000 => 43_375_710_643, 1_862_500_000 => 43_681_898_699,
464
+ 1_875_000_000 => 43_988_172_667, 1_887_500_000 => 44_294_549_347,
465
+ 1_900_000_000 => 44_601_021_791, 1_912_500_000 => 44_907_564_593,
466
+ 1_925_000_000 => 45_214_177_441, 1_937_500_000 => 45_520_935_011,
467
+ 1_950_000_000 => 45_827_700_419, 1_962_500_000 => 46_134_655_219,
468
+ 1_975_000_000 => 46_441_643_177, 1_987_500_000 => 46_748_693_981,
469
+ 2_000_000_000 => 47_055_833_459, 2_012_500_000 => 47_363_059_687,
470
+ 2_125_000_000 => 50_131_763_837, 2_250_000_000 => 53_215_141_519,
471
+ 2_375_000_000 => 56_305_859_821, 2_500_000_000 => 59_403_556_879,
472
+ 2_625_000_000 => 62_507_768_977, 2_750_000_000 => 65_618_159_808,
473
+ 2_875_000_000 => 68_734_481_527, 3_000_000_000 => 71_856_445_751,
474
+ 3_125_000_000 => 74_983_924_661, 3_250_000_000 => 78_116_541_127,
475
+ 3_375_000_000 => 81_254_172_953, 3_500_000_000 => 84_396_675_733,
476
+ 3_625_000_000 => 87_543_835_147, 3_750_000_000 => 90_695_492_941,
477
+ 3_875_000_000 => 93_851_412_433, 4_000_000_000 => 97_011_687_217,
478
+ 4_125_000_000 =>100_175_917_301, 4_250_000_000 => 103_344_103_553,
479
+ 4_375_000_000 =>106_516_393_597, 4_500_000_000 => 109_692_247_799,
480
+ 4_625_000_000 =>112_871_634_437, 4_750_000_000 => 116_054_419_753,
481
+ 4_875_000_000 =>119_240_825_947, 5_000_000_000 => 122_430_513_841,
482
+ 5_125_000_000 =>125_623_420_333, 5_250_000_000 => 128_819_622_391,
483
+ 5_375_000_000 =>132_018_808_321, 5_500_000_000 => 135_221_143_753,
484
+ 5_625_000_000 =>138_426_461_137, 5_750_000_000 => 141_634_567_969,
485
+ 5_875_000_000 =>144_845_535_431, 6_000_000_000 => 148_059_109_201,
486
+ 6_125_000_000 =>151_275_700_969, 6_250_000_000 => 154_494_952_609,
487
+ 6_375_000_000 =>157_716_628_943, 6_500_000_000 => 160_940_840_461,
488
+ 6_625_000_000 =>164_167_763_329, 6_750_000_000 => 167_397_013_051,
489
+ 6_875_000_000 =>170_628_613_009, 7_000_000_000 => 173_862_636_221 }
490
+
491
+ def select_pg(end_num, start_num) # adaptively select PG
492
+ range = end_num - start_num
448
493
  pg = 5
449
- if start_num <= Math.sqrt(num).to_i # for one array of primes upto N
450
- pg = 7 if num > 50*10**4
451
- pg = 11 if num > 305*10**5
452
- else # for split array cases
453
- pg = 7 if ((10**6...10**7).cover? range and start_num < 10**8) or
454
- ((10**7...10**8).cover? range and start_num < 10**10) or
455
- ((10**8...10**9).cover? range and start_num < 10**12) or
456
- (range >= 10**9 and start_num < 10**14)
457
- pg = 11 if ((10**8...10**9).cover? range and start_num < 10**10) or
458
- (range >= 10**9 and start_num < 10**11)
494
+ if start_num <= Integer.sqrt(end_num) # for one array of primes upto N
495
+ pg = 7 if end_num > 50 * 10**4
496
+ pg = 11 if end_num > 305 * 10**5
497
+ else # for split array cases
498
+ pg = 7 if (range.between?(10**6, 10**7 - 1) && start_num < 10**8) ||
499
+ (range.between?(10**7, 10**8 - 1) && start_num < 46 * 10**8) ||
500
+ (range.between?(10**8, 10**9 - 1) && start_num < 16 * 10**10) ||
501
+ (range >= 10**9 && start_num < 26 * 10**12)
502
+ pg = 11 if (range.between?(10**8, 10**9 - 1) && start_num < 55 * 10**7) ||
503
+ (range >= 10**9 && start_num < 45 * 10**9)
459
504
  end
460
- primes = [2,3,5,7,11,13].select! {|p| p <= pg}
461
- [primes, primes.reduce(:*)] # [excluded primes, mod] for PG
462
- end
463
-
464
- def array_check(n,v) # catch out-of-memory errors on array creation
465
- Array.new(n,v) rescue return # return an array or nil
505
+ primes = [2, 3, 5, 7, 11, 13].select! { |p| p <= pg }
506
+ [primes, primes.reduce(:*)] # [base primes, mod] for PG
466
507
  end
467
508
 
509
+ def array_check(len) # for out-of-memory errors on primes array creation
510
+ begin
511
+ Array.new(len, 0) # use Array when enough mem for given length
512
+ rescue Exception
513
+ return BitArray.new(len) # use BitArray when memory-error for Array
514
+ end end
468
515
  end
469
516
  end
470
517
 
471
518
  class Integer; include Primes::Utils end
472
519
 
473
- puts "Available methods are: #{0.primes_utils}" # display methods upon loading
520
+ puts "Available methods are: #{0.primes_utils}" # display methods upon loading