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.
@@ -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: []