primes-utils 1.1.1 → 2.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/.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
|