faster_prime 1.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +5 -0
- data/README.md +68 -0
- data/Rakefile +10 -0
- data/faster_prime.gemspec +24 -0
- data/lib/faster_prime.rb +45 -0
- data/lib/faster_prime/core_ext.rb +17 -0
- data/lib/faster_prime/primality_test.rb +557 -0
- data/lib/faster_prime/prime_factorization.rb +458 -0
- data/lib/faster_prime/sieve.rb +174 -0
- data/lib/faster_prime/utils.rb +225 -0
- metadata +83 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
# Sieve of Atkin
|
2
|
+
# http://cr.yp.to/papers/primesieves.pdf
|
3
|
+
|
4
|
+
require "faster_prime/utils"
|
5
|
+
|
6
|
+
module FasterPrime
|
7
|
+
module Sieve
|
8
|
+
module_function
|
9
|
+
|
10
|
+
TBL = []
|
11
|
+
[
|
12
|
+
[:mark_type_1, ->(f, g) { 4*f*f + g*g }, 15, 30, 1, 4],
|
13
|
+
[:mark_type_2, ->(f, g) { 3*f*f + g*g }, 10, 30, 7, 12],
|
14
|
+
[:mark_type_3, ->(f, g) { 3*f*f - g*g }, 10, 30, 11, 12],
|
15
|
+
].each do |type, expr, fmax, gmax, n, mod|
|
16
|
+
gmax.times do |g|
|
17
|
+
fmax.times do |f|
|
18
|
+
d = expr[f, g] % 60
|
19
|
+
TBL << [type, f, g, d] if d % mod == n && d.gcd(60) == 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
MODULO60 = [1, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 53, 59]
|
25
|
+
|
26
|
+
def each(ubound = nil, &blk)
|
27
|
+
if ubound && SMALL_PRIMES.last >= ubound
|
28
|
+
SMALL_PRIMES.each(&blk)
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
u = SMALL_PRIMES.last / 60 * 60
|
33
|
+
SMALL_PRIMES.pop until SMALL_PRIMES.last < u
|
34
|
+
SMALL_PRIMES.each(&blk)
|
35
|
+
|
36
|
+
b = Utils.integer_square_root(ubound * 3000) if ubound
|
37
|
+
b = 100000 if !b || b > 100000
|
38
|
+
l = u / 60
|
39
|
+
|
40
|
+
# Algorithm 3.1, 3.2, 3.3
|
41
|
+
flags = [0] * b
|
42
|
+
while true
|
43
|
+
# step
|
44
|
+
flags.fill(0)
|
45
|
+
|
46
|
+
# sieve
|
47
|
+
TBL.each do |name, f, g, d|
|
48
|
+
send(name, flags, f, g, d, l, b)
|
49
|
+
end
|
50
|
+
|
51
|
+
# squarefree
|
52
|
+
squarefree(flags, l, b)
|
53
|
+
|
54
|
+
# enumerate primes
|
55
|
+
if ubound && ubound < 60 * (l + b)
|
56
|
+
enumerate(flags, l, b) do |p|
|
57
|
+
return if p >= ubound
|
58
|
+
yield p
|
59
|
+
end
|
60
|
+
else
|
61
|
+
enumerate(flags, l, b, &blk)
|
62
|
+
end
|
63
|
+
|
64
|
+
l += b
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def enumerate(flags, l, b)
|
69
|
+
b.times do |x|
|
70
|
+
base = 60 * (l + x)
|
71
|
+
flag = flags[x]
|
72
|
+
MODULO60.each do |d|
|
73
|
+
next if flag[d] == 0
|
74
|
+
p = base + d
|
75
|
+
SMALL_PRIMES << p
|
76
|
+
yield p
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def squarefree(flags, l, b)
|
82
|
+
SMALL_PRIMES.each do |q|
|
83
|
+
q2 = q * q
|
84
|
+
break if q2 > 60 * (l + b)
|
85
|
+
next if q < 7
|
86
|
+
minus_inv_60 = -Utils.mod_inv(60, q2)
|
87
|
+
MODULO60.each do |d|
|
88
|
+
# 60(l+x)+d == 0 (mod q2)
|
89
|
+
# x = -d * 60^{-1} - l (mod q2)
|
90
|
+
x = (d * minus_inv_60 - l) % q2
|
91
|
+
m = ~(1 << d)
|
92
|
+
while x < b
|
93
|
+
flags[x] &= m
|
94
|
+
x += q2
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Algorithm 4.1
|
101
|
+
def mark_type_1(flags, f, g, d, l, b)
|
102
|
+
x, y0 = f, g
|
103
|
+
k0 = (4 * f * f + g * g - d) / 60 - l
|
104
|
+
m = 1 << d
|
105
|
+
while k0 < l + b
|
106
|
+
k0 += 2*x + 15
|
107
|
+
x += 15
|
108
|
+
end
|
109
|
+
while true
|
110
|
+
x -= 15
|
111
|
+
k0 -= 2*x + 15
|
112
|
+
return if x <= 0
|
113
|
+
while k0 < 0
|
114
|
+
k0 += y0 + 15
|
115
|
+
y0 += 30
|
116
|
+
end
|
117
|
+
k, y = k0, y0
|
118
|
+
while k < b
|
119
|
+
flags[k] ^= m # mark
|
120
|
+
k += y + 15
|
121
|
+
y += 30
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Algorithm 4.2
|
127
|
+
def mark_type_2(flags, f, g, d, l, b)
|
128
|
+
x, y0 = f, g
|
129
|
+
k0 = (3 * f * f + g * g - d) / 60 - l
|
130
|
+
m = 1 << d
|
131
|
+
while k0 < b
|
132
|
+
k0 += x + 5
|
133
|
+
x += 10
|
134
|
+
end
|
135
|
+
while true
|
136
|
+
x -= 10
|
137
|
+
k0 -= x + 5
|
138
|
+
return if x <= 0
|
139
|
+
while k0 < 0
|
140
|
+
k0 += y0 + 15
|
141
|
+
y0 += 30
|
142
|
+
end
|
143
|
+
k, y = k0, y0
|
144
|
+
while k < b
|
145
|
+
flags[k] ^= m # mark
|
146
|
+
k += y + 15
|
147
|
+
y += 30
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Algorithm 4.3
|
153
|
+
def mark_type_3(flags, f, g, d, l, b)
|
154
|
+
x, y0 = f, g
|
155
|
+
k0 = (3 * f * f - g * g - d) / 60 - l
|
156
|
+
m = 1 << d
|
157
|
+
while true
|
158
|
+
while k0 >= b
|
159
|
+
return if x <= y0
|
160
|
+
k0 -= y0 + 15
|
161
|
+
y0 += 30
|
162
|
+
end
|
163
|
+
k, y = k0, y0
|
164
|
+
while k >= 0 && y < x
|
165
|
+
flags[k] ^= m # mark
|
166
|
+
k -= y + 15
|
167
|
+
y += 30
|
168
|
+
end
|
169
|
+
k0 += x + 5
|
170
|
+
x += 10
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module FasterPrime
|
2
|
+
SMALL_PRIMES = []
|
3
|
+
SMALL_PRIME_TABLE = []
|
4
|
+
|
5
|
+
def self.setup_table(n)
|
6
|
+
n = (n + 59) / 60 * 60
|
7
|
+
SMALL_PRIMES.clear << 2
|
8
|
+
n /= 2
|
9
|
+
SMALL_PRIME_TABLE.replace([true] * n)
|
10
|
+
i = 1
|
11
|
+
while i < n
|
12
|
+
if SMALL_PRIME_TABLE[i]
|
13
|
+
SMALL_PRIMES << j = i * 2 + 1
|
14
|
+
k = i + j
|
15
|
+
while k < n
|
16
|
+
SMALL_PRIME_TABLE[k] = false
|
17
|
+
k += j
|
18
|
+
end
|
19
|
+
end
|
20
|
+
i += 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
setup_table(100000)
|
24
|
+
|
25
|
+
module Utils
|
26
|
+
module_function
|
27
|
+
|
28
|
+
FLOAT_BIGNUM = Float::RADIX ** (Float::MANT_DIG - 1)
|
29
|
+
# Find the largest integer m such that m <= sqrt(n)
|
30
|
+
def integer_square_root(n)
|
31
|
+
if n < FLOAT_BIGNUM
|
32
|
+
Math.sqrt(n).floor
|
33
|
+
else
|
34
|
+
# newton method
|
35
|
+
a, b = n, 1
|
36
|
+
a, b = a / 2, b * 2 until a <= b
|
37
|
+
a = b + 1
|
38
|
+
a, b = b, (b + n / b) / 2 until a <= b
|
39
|
+
a
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Find b such that a * b = 1 (mod n)
|
44
|
+
def mod_inv(a, n)
|
45
|
+
raise ArgumentError, "not coprime for mod_inv" if a == 0 || a.gcd(n) != 1
|
46
|
+
|
47
|
+
# Extended GCD Algorithm:
|
48
|
+
# Find $$(x, y)$$ such that $$ax + by = gcd(a, b), x > 0 and y > 0$$
|
49
|
+
x0, x1 = 1, 0
|
50
|
+
y0, y1 = 0, 1
|
51
|
+
b = n
|
52
|
+
while b != 0
|
53
|
+
q = a / b
|
54
|
+
a, b = b, a % b
|
55
|
+
x0, x1 = x1, x0 - x1 * q
|
56
|
+
y0, y1 = y1, y0 - y1 * q
|
57
|
+
end
|
58
|
+
|
59
|
+
x0 % n
|
60
|
+
end
|
61
|
+
|
62
|
+
# Calculate a^b mod n
|
63
|
+
def mod_pow(a, b, n)
|
64
|
+
r = 1
|
65
|
+
return 1 if b == 0
|
66
|
+
len = b.bit_length
|
67
|
+
len.times do |i|
|
68
|
+
if b[i] == 1
|
69
|
+
r = (a * r) % n
|
70
|
+
return r if i == len - 1
|
71
|
+
end
|
72
|
+
a = (a * a) % n
|
73
|
+
end
|
74
|
+
|
75
|
+
raise "assert not reached"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Find x such that x^2 = a (mod prime)
|
79
|
+
# See ``Complexity of finding square roots'' section of wikipedia:
|
80
|
+
# http://en.wikipedia.org/wiki/Quadratic_residue
|
81
|
+
def mod_sqrt(a, prime)
|
82
|
+
return 0 if a == 0
|
83
|
+
|
84
|
+
case
|
85
|
+
when prime == 2
|
86
|
+
a.odd? ? 1 : 0
|
87
|
+
|
88
|
+
when prime % 4 == 3
|
89
|
+
mod_pow(a, (prime + 1) / 4, prime)
|
90
|
+
|
91
|
+
when prime % 8 == 5
|
92
|
+
# Let v = (2a)^((prime-5)/8) and r = (2av^2 - 1)av.
|
93
|
+
#
|
94
|
+
# 2^((prime-1)/2) = -1 because (2|prime) = -1.
|
95
|
+
# a^((prime-1)/2) = 1 because (a|prime) = 1.
|
96
|
+
# So, (2a)^((prime-1)/2) = -1.
|
97
|
+
#
|
98
|
+
# r = ((2a)^((prime-1)/4) - 1) * a * (2a)^((prime-5)/8)
|
99
|
+
# r^2 = ((2a)^((prime-1)/2) - 2(2a)^((prime-1)/4) + 1)
|
100
|
+
# * a^2 * (2a)^((prime-5)/4)
|
101
|
+
# = -2(2a)^((prime-1)/4) * a^2 * (2a)^((prime-5)/4)
|
102
|
+
# = -a * (2a)^((prime-1)/2)
|
103
|
+
# = a
|
104
|
+
#
|
105
|
+
# So r is the solution of x^2 = a (mod prime).
|
106
|
+
v = mod_pow(a * 2, (prime - 5) / 8, prime)
|
107
|
+
(2 * a * v * v - 1) * a * v % prime
|
108
|
+
|
109
|
+
else # prime % 8 == 1
|
110
|
+
# Use Tonelli–Shanks algorithm, see:
|
111
|
+
# http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
|
112
|
+
|
113
|
+
# Calculate (q, s) such that prime - 1 = 2^s * q and q is odd
|
114
|
+
s, q = 3, prime - 1
|
115
|
+
s += 1 until q[s] == 1
|
116
|
+
q >>= s
|
117
|
+
|
118
|
+
# Find quadratic nonresidue z.
|
119
|
+
z = (2...prime).find {|z_| kronecker(z_, prime) != 1 }
|
120
|
+
|
121
|
+
# The main part of the algorithm
|
122
|
+
c = mod_pow(z, q, prime)
|
123
|
+
x = mod_pow(a, (q - 1) / 2, prime)
|
124
|
+
r = a * x % prime # a^((q+1)/2)
|
125
|
+
t = r * x % prime # a^q
|
126
|
+
until t == 1
|
127
|
+
i, d = 0, t
|
128
|
+
i, d = i + 1, d * d % prime while d != 1
|
129
|
+
b = mod_pow(c, 1 << (s - i - 1), prime)
|
130
|
+
c = b * b % prime
|
131
|
+
r = r * b % prime
|
132
|
+
t = t * c % prime
|
133
|
+
s = i
|
134
|
+
end
|
135
|
+
r
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# p-adic valuation: Find the highest exponent v such that p^v divides n
|
140
|
+
def valuation(n, p)
|
141
|
+
e = 0
|
142
|
+
while n % p == 0
|
143
|
+
n /= p
|
144
|
+
e += 1
|
145
|
+
end
|
146
|
+
e
|
147
|
+
end
|
148
|
+
|
149
|
+
# Enumerate divisors of n, including n itself
|
150
|
+
def each_divisor(n)
|
151
|
+
return enum_for(:each_divisor, n) unless block_given?
|
152
|
+
t = 1
|
153
|
+
while t * t < n
|
154
|
+
yield t if n % t == 0
|
155
|
+
t += 1
|
156
|
+
end
|
157
|
+
yield t if n == t * t
|
158
|
+
while t > 1
|
159
|
+
t -= 1
|
160
|
+
yield n / t if n % t == 0
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Calculate the number of positive integers less than or equal to n that
|
165
|
+
# are coprime to n
|
166
|
+
def totient(n)
|
167
|
+
n = n.abs
|
168
|
+
raise "n must not be zero" if n == 0
|
169
|
+
r = n
|
170
|
+
PrimeFactorization.prime_factorization(n) do |prime, exp|
|
171
|
+
r = r * (prime - 1) / prime
|
172
|
+
end
|
173
|
+
r
|
174
|
+
end
|
175
|
+
|
176
|
+
# Finds the least primitive root modulo n.
|
177
|
+
# Algorithm 1.4.4 (Primitive Root).
|
178
|
+
def primitive_root(n)
|
179
|
+
t = Utils.totient(n)
|
180
|
+
ps = PrimeFactorization.prime_factorization(t).to_a
|
181
|
+
2.upto(t) do |a|
|
182
|
+
found = true
|
183
|
+
ps.each do |q,|
|
184
|
+
if mod_pow(a, t / q, n) == 1
|
185
|
+
found = false
|
186
|
+
break
|
187
|
+
end
|
188
|
+
end
|
189
|
+
return a if found
|
190
|
+
end
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
|
194
|
+
# Calculate Kronecker symbol (a|b) in O((log a)(log b)), see pp. 29--30,
|
195
|
+
# Cohen, Henri (1993), A Course in Computational Algebraic Number Theory
|
196
|
+
def kronecker(a, b)
|
197
|
+
return a.abs != 1 ? 0 : 1 if b == 0
|
198
|
+
return 0 if a.even? && b.even?
|
199
|
+
|
200
|
+
k = 1
|
201
|
+
|
202
|
+
case a & 7
|
203
|
+
when 1, 7 then b = b / 2 while b.even?
|
204
|
+
when 3, 5 then b, k = b / 2, -k while b.even?
|
205
|
+
end
|
206
|
+
if b < 0
|
207
|
+
b = -b
|
208
|
+
k = -k if a < 0
|
209
|
+
end
|
210
|
+
|
211
|
+
while a != 0
|
212
|
+
case b & 7
|
213
|
+
when 1, 7 then a = a / 2 while a.even?
|
214
|
+
when 3, 5 then a, k = a / 2, -k while a.even?
|
215
|
+
end
|
216
|
+
k = -k if 2 & a & b == 2
|
217
|
+
a = a.abs
|
218
|
+
a, b = b % a, a
|
219
|
+
a -= b if a > b / 2
|
220
|
+
end
|
221
|
+
|
222
|
+
return b > 1 ? 0 : k
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: faster_prime
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yusuke Endoh
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-01-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: This provides `Integer#prime?`, `Integer#prime_division`, and `Prime#each`
|
42
|
+
that are almost compatible to and faster than `lib/prime.rb`.
|
43
|
+
email:
|
44
|
+
- mame@ruby-lang.org
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- faster_prime.gemspec
|
54
|
+
- lib/faster_prime.rb
|
55
|
+
- lib/faster_prime/core_ext.rb
|
56
|
+
- lib/faster_prime/primality_test.rb
|
57
|
+
- lib/faster_prime/prime_factorization.rb
|
58
|
+
- lib/faster_prime/sieve.rb
|
59
|
+
- lib/faster_prime/utils.rb
|
60
|
+
homepage: https://github.com/mame/faster_prime
|
61
|
+
licenses: []
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.7.3
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: A faster substitute for `lib/prime.rb`.
|
83
|
+
test_files: []
|