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 +5 -0
- data/README.md +73 -29
- data/lib/primes/utils.rb +293 -119
- data/lib/primes/utils/version.rb +1 -1
- data/primes-utils.gemspec +3 -2
- metadata +72 -48
- checksums.yaml +0 -7
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# primes-utils
|
2
2
|
|
3
|
-
|
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
|
-
|
43
|
-
Miller-Rabin
|
44
|
-
|
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
|
-
|
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
|
-
|
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.
|
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.
|
93
|
-
1.
|
103
|
+
0.primesf => []
|
104
|
+
1.primesmr => []
|
94
105
|
```
|
95
106
|
|
96
|
-
**
|
107
|
+
**primescnt(start=0), primescntf(start=0), primescntmr(start=0)**
|
97
108
|
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
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
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
176
|
+
GPLv2+
|
data/lib/primes/utils.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require "primes/utils/version"
|
2
|
-
require
|
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
|
-
|
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,
|
34
|
-
89,97,101,103,107,109,113,121,127,131,137,139,143,149,151,157,
|
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
|
-
|
97
|
+
true
|
64
98
|
end
|
65
99
|
|
66
|
-
def factors(p=13)
|
67
|
-
|
68
|
-
|
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
|
-
|
71
|
-
|
72
|
-
residues
|
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=
|
136
|
+
def primenth(p=7)
|
102
137
|
# Return value of nth prime
|
103
|
-
# Uses
|
104
|
-
|
105
|
-
|
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
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
primes = seeds[0..seeds.index(p)]
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
prms=
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
#
|
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
|
173
|
-
|
174
|
-
|
175
|
-
rescnt = residues.size
|
176
|
-
|
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
|
-
|
207
|
-
modk = mod*
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
-
|
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)
|
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)
|
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
|
data/lib/primes/utils/version.rb
CHANGED
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 = "
|
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
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
48
|
-
|
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
|
-
-
|
61
|
-
metadata: {}
|
71
|
+
licenses:
|
72
|
+
- GPLv2+
|
62
73
|
post_install_message:
|
63
74
|
rdoc_options: []
|
64
|
-
|
75
|
+
|
76
|
+
require_paths:
|
65
77
|
- lib
|
66
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
-
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
68
81
|
- - ">="
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
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:
|
101
|
+
rubygems_version: 1.8.25
|
79
102
|
signing_key:
|
80
|
-
specification_version:
|
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
|