number-theory 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9284bb03268cfa67e75ffef537016a2a4d8d8533
4
+ data.tar.gz: 4b8ee777a7acd1c3dad895d76b8220c400231c30
5
+ SHA512:
6
+ metadata.gz: fbde0ca1af6d1e0f089b04b7801aa8a5c7b28ff39ee3e7d18637b14327ea0dcadfad18e9b030c6c968b7504fa23b246367e33057c611ecdcb49ea272c7fde285
7
+ data.tar.gz: 0ce4ba32f0a9d869d818d9a228be60a36278bd027cdf9c0847755128efa141c1d1c25e5413e8673d678e5b3bc7bd63b9686319168daf981f47d864bad591dbab
@@ -0,0 +1,15 @@
1
+ module NumberTheory
2
+
3
+ begin
4
+ require 'narray'
5
+ rescue LoadError
6
+ raise LoadError, "You must have NArray installed"
7
+ end
8
+
9
+ # Load modules
10
+ require File.dirname(__FILE__) + '/number-theory/primes.rb'
11
+ require File.dirname(__FILE__) + '/number-theory/divisors.rb'
12
+ require File.dirname(__FILE__) + '/number-theory/utils.rb'
13
+ require File.dirname(__FILE__) + '/number-theory/congruences.rb'
14
+
15
+ end
@@ -0,0 +1,82 @@
1
+ require_relative 'divisors'
2
+
3
+ module NumberTheory
4
+
5
+ module Congruences
6
+ ##
7
+ # Returns all the incongruent solutions to ax ≡ c (mod m) in ascending order
8
+ #
9
+ # == Example
10
+ # >> Congruences.congruences_solver(893, 266, 2432) # this solves 893x ≡ 266 (mod 2432)
11
+ # => [82, 210, 338, 466, 594, 722, 850, 978, 1106, 1234, 1362, 1490, 1618, 1746, 1874, 2002, 2130, 2258, 2386]
12
+ #
13
+ def self.linear_congruences_solver(a, c, m)
14
+ return [] if a == 0 || m == 0
15
+ # Assuming both a and m are positive integers
16
+ # If negative values are passed, they will be treated as positive integers
17
+ a = 0 - a if a < 0
18
+ m = 0 - m if m < 0
19
+ arr = Divisors.extended_euclidean_algo(a, m)
20
+ gcd = arr[0]
21
+ if c % gcd != 0
22
+ [] # i.e no solution
23
+ else
24
+ first = (arr[1] * c / gcd) % m
25
+ sol = [first]
26
+ i = 1
27
+ while i < gcd do
28
+ x = first + i * m / gcd
29
+ x -= m if x >= m
30
+ sol << x
31
+ i+=1
32
+ end
33
+ sol.sort
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Returns an integer d satifying the following simultaneous congruences
39
+ # x ≡ r1 (mod m1)
40
+ # x ≡ r2 (mod m2)
41
+ # x ≡ r3 (mod m3)
42
+ # and 0 <= d < m1*m2*m3
43
+ # == Example
44
+ # >> Congruences.chinese_remainder_theorem([2, 3, 2], [3, 5, 7])
45
+ # => 23
46
+ # the above solves
47
+ # # x ≡ 2 (mod 3)
48
+ # # x ≡ 3 (mod 5)
49
+ # # x ≡ 2 (mod 7)
50
+ def self.chinese_remainder_theorem(remainders, modulos)
51
+ m1 = modulos[0].abs
52
+ m2 = modulos[1].abs
53
+ m3 = modulos[2].abs
54
+ r1 = remainders[0]
55
+ r2 = remainders[1]
56
+ r3 = remainders[2]
57
+ arr = Divisors.extended_euclidean_algo(m1, m2)
58
+ g1 = arr[0]
59
+ g2 = Divisors.euclidean_algo(m2, m3)
60
+ g3 = Divisors.euclidean_algo(m3, m1)
61
+ if g1 != 1 || g2 != 1 || g3 != 1
62
+ false
63
+ else
64
+ # x ≡ r1 (mod m1) ...... (1)
65
+ # x ≡ r2 (mod m2) ...... (2)
66
+ # x ≡ r3 (mod m3) ...... (3)
67
+ # From (1) and (2), we have m1*q1 + r1 ≡ r2 (mod m2)
68
+ # which means m1*q1 - m2*q2 = r2 - r1
69
+ # So, we can solve this linear congruence equation for x0 which equals m1*q1 + r1
70
+ # Then, we can assume x0 + m1*m2*y ≡ r3 (mod m3)
71
+ # which means m1*m2*y - m3*q3 = r3 - x0
72
+ # So, we have the following solution
73
+ q1 = arr[1] * (r2 - r1)
74
+ x0 = m1 * q1 + r1
75
+ arr2 = Divisors.extended_euclidean_algo(m1*m2, m3)
76
+ x = (arr2[1] * (r3 - x0) * m1 * m2 + x0) % (m1*m2*m3) # Make sure 0 <= x < m1*m2*m3
77
+ x
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,200 @@
1
+ require_relative 'utils'
2
+ require_relative 'primes'
3
+
4
+ module NumberTheory
5
+
6
+ module Divisors
7
+
8
+ ##
9
+ # Returns the greatest integer k such that
10
+ # d^k divides n.
11
+ #
12
+ # == Example
13
+ # >> Divisors.multiplicity(1000,5)
14
+ # => 3
15
+ #
16
+ def self.multiplicity(n, d)
17
+ return 0 if n % d != 0
18
+ m, res = n, 0
19
+ while m % d == 0
20
+ m /= d
21
+ res += 1
22
+ end
23
+ res
24
+ end
25
+
26
+ ##
27
+ # Returns the ordered list of the divisors of n (1 and n included).
28
+ #
29
+ # == Example
30
+ # >> Divisors.divisors(100)
31
+ # => [1, 2, 4, 5, 10, 20, 25, 50, 100]
32
+ #
33
+ def self.divisors(n)
34
+ factors = Primes.factor(n)
35
+ ps = factors.keys.sort!
36
+ self._divisors(0, factors, ps).sort!.uniq
37
+ end
38
+
39
+ ## Helper function for divisors
40
+ def self._divisors(n = 0, factors, ps) # :nodoc:
41
+ give = []
42
+ if n == ps.size
43
+ give << 1
44
+ else
45
+ pows = [1]
46
+ factors[ps[n]].times {pows << pows[-1]*ps[n]}
47
+ for q in self._divisors(n+1, factors, ps)
48
+ for p in pows
49
+ give << p * q
50
+ end
51
+ end
52
+ end
53
+ give
54
+ end
55
+
56
+ ##
57
+ # Return sigma_0(n), i.e. the number of divisors of n.
58
+ #
59
+ # == Example
60
+ # >> Divisors.divcount(100)
61
+ # => 9
62
+ #
63
+ def self.divcount(n)
64
+ return nil if n < 0
65
+ return 1 if n == 1
66
+ divcount = 1
67
+ Primes.factor(n).values.each {|n| divcount *= (n+1)}
68
+ divcount
69
+ end
70
+
71
+ ##
72
+ # Returns sigma_k(n), i.e. the sum of the k-th powers of
73
+ # the divisors of n.
74
+ #
75
+ # == Example
76
+ # >> Divisors.divisor_sigma(10, 2)
77
+ # => 130
78
+ #
79
+ def self.divisor_sigma(n, k)
80
+ return self.divcount(n) if k == 0
81
+ res = 0
82
+ for i in self.divisors(n)
83
+ res += i**k
84
+ end
85
+ res
86
+ end
87
+
88
+ ##
89
+ # Returns true if n is a perfect number, false otherwise.
90
+ #
91
+ # == Example
92
+ # >> Divisors.perfect?(6)
93
+ # => true
94
+ #
95
+ def self.perfect? (n)
96
+ self.divisor_sigma(n, 1) == 2*n
97
+ end
98
+
99
+
100
+ ##
101
+ # Returns the valuer of phi(n), the Euler phi function; i.e.
102
+ # the number of integers in [1..n] comprime to n.
103
+ #
104
+ # == Example
105
+ # >> Divisors.euler_phi(30)
106
+ # => 8
107
+ #
108
+ # >> Divisors.euler_phi(37)
109
+ # => 36
110
+ #
111
+ # == Algorithm
112
+ # n is first factored, then the result is computed as
113
+ # n * prod_{p} (1 - 1/p)
114
+ # where the product spans over all the prime factors of n
115
+ #
116
+ def self.euler_phi(n)
117
+ return 0 if n < 1
118
+ res = n
119
+ Primes.factor(n).keys.each do |i|
120
+ res *= i - 1
121
+ res /= i
122
+ end
123
+ res
124
+ end
125
+
126
+ ##
127
+ # Returns true if a positive integer is square free;
128
+ # returns false otherwise
129
+ #
130
+ # A positive integer 'n' is said to be square free
131
+ # if no prime factor appears more than once
132
+ # in the list of prime factors for 'n'
133
+ #
134
+ # == Example
135
+ # >> Divisors.square_free?(10)
136
+ # => true
137
+ #
138
+ # The integer 1 is a special case since it is
139
+ # both a prefect square, and square free.
140
+ #
141
+ def self.square_free?(n)
142
+ return false if n <= 0
143
+ (Primes.factor(n)).each_value { |value| return false if value >= 2 }
144
+ true
145
+ end
146
+
147
+ ##
148
+ # Returns the greatest common divisor of a and b
149
+ # == Example
150
+ # >> Divisors.euclidean_algo(20, 15) = 5
151
+ def self.euclidean_algo(a, b)
152
+ a = 0 - a if a < 0
153
+ b = 0 - b if b < 0
154
+ if a == 0 || b == 0
155
+ 0
156
+ else
157
+ r = a - (a/b)*b
158
+ while r!=0 do
159
+ a = b
160
+ b = r
161
+ r = a - (a/b)*b
162
+ end
163
+ b
164
+ end
165
+ end
166
+
167
+ ##
168
+ # Returns the greatest common divisor of a and b and the corresponding Bézout coefficients
169
+ # for ax + by = g where g is the greatest common divisor of a and b
170
+ # == Example
171
+ # >> Divisors.extended_euclidean_algo(20, 15) = [5, 1, -1]
172
+ def self.extended_euclidean_algo(a, b)
173
+ a = 0 - a if a < 0
174
+ b = 0 - b if b < 0
175
+ if a == 0 || b == 0
176
+ [0, 0, 0]
177
+ else
178
+ r = a - (a/b)*b
179
+ s0 = 1; t0 = 0
180
+ s1 = 0; t1 = 1
181
+ s2 = s0 - (a/b)*s1; t2 = t0 - (a/b)*t1
182
+ while r!=0 do
183
+ a = b
184
+ b = r
185
+ r = a - (a/b)*b
186
+ s0 = s1; t0 = t1
187
+ s1 = s2; t1 = t2
188
+ s2 = s0 - (a/b)*s1; t2 = t0 - (a/b)*t1
189
+ end
190
+ ans = []
191
+ ans << b
192
+ ans << s1
193
+ ans << t1
194
+ ans
195
+ end
196
+ end
197
+
198
+ end
199
+
200
+ end
@@ -0,0 +1,400 @@
1
+ module NumberTheory
2
+
3
+ module Primes
4
+
5
+ ##
6
+ # Returns true if n is (probably) prime, false otherwise.
7
+ # For n < 10^15 the answer is accurate; greater n values
8
+ # reported as primes have a (very) small probability of
9
+ # actually being pseudoprimes.
10
+ #
11
+ # == Example
12
+ # >> Primes.prime?(12)
13
+ # => false
14
+ #
15
+ # >> Primes.prime?(1882341361)
16
+ # => true
17
+ #
18
+ # == Algorithm
19
+ # For n < 10^9 the function just test by trial division;
20
+ # for greater n values it performs a Miller-Rabin a
21
+ # pseudoprime test.
22
+ #
23
+ # If n < 341550071728321 the test is deterministic, otherwise
24
+ # there's a probability smaller than 7 x 10^-31 that a composite
25
+ # number is reported as prime.
26
+ #
27
+ def self.prime?(n)
28
+ if n < 10**9 or n % 2 == 0
29
+ self._trial_division(n)
30
+ else
31
+ self._miller_rabin(n)
32
+ end
33
+ end
34
+
35
+ ## helper method for prime?
36
+ def self._trial_division(n)
37
+ return false if n < 2 or n == 4
38
+ return true if n == 2 or n == 3 or n == 5
39
+
40
+ mods30 = [1, 7, 11, 13, 17, 19, 23, 29]
41
+ return false if not mods30.include?(n % 30)
42
+
43
+ 3.step(n**0.5, 2) { |x| return false if n % x == 0 }
44
+ true
45
+ end
46
+
47
+ ## helper method for prime?
48
+ def self._miller_rabin(n, runs = 50) # :nodoc:
49
+ if n < 341550071728321
50
+ for a in [2, 3, 5, 7, 11, 13, 17] do
51
+ return false if self._witness(a, n)
52
+ end
53
+ true
54
+ else
55
+ runs.times do
56
+ a = 1 + Random.rand(n)
57
+ return false if self._witness(a, n)
58
+ end
59
+ true
60
+ end
61
+ end
62
+
63
+ ## helper method for _miller_rabin
64
+ def self._witness(a, n)
65
+ t = Divisors.multiplicity(n - 1, 2)
66
+ u = (n - 1) / (2**t)
67
+ x1 = Utils.mod_exp(a, u, n)
68
+ t.times do |i|
69
+ x0 = x1
70
+ x1 = (x0 * x0) % n
71
+ return true if x1 == 1 and x0 != 1 and x0 != n-1
72
+ end
73
+ return true if x1 != 1
74
+ false
75
+ end
76
+
77
+ ##
78
+ # Memoized list of primes.
79
+ #
80
+ # primes_list returns a list of the prime numbers
81
+ # between low and high (extremes included).
82
+ #
83
+ # The computation is memoized in @primes_bit_array,
84
+ # @upper_limit is the current highest prime in the list.
85
+ # Later requests are serverd either by using the memoized
86
+ # list, or extending it (if high > @upper_limit)
87
+ #
88
+ # == Example
89
+ # >> Primes.primes_list(30)
90
+ # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
91
+ #
92
+ # == Algorithm
93
+ # Primes are computed using a plain version of the Sieve
94
+ # of Eratosthenes; the method uses the bit-array structure
95
+ # implemented in the NArray gem
96
+ #
97
+ @primes_bit_array = nil
98
+ @upper_limit = 0
99
+
100
+ def self.primes_list(low = 2, high)
101
+ self._initialize_bit_array(high) if not @primes_bit_array
102
+ if high > @upper_limit
103
+ self._extend_bit_array(high)
104
+ end
105
+ primes = NArray.object(high + 1).indgen!
106
+ ret = primes[@primes_bit_array[0..high]].to_a
107
+ return ret[ret.index{ |i| i >= low}..-1 ]
108
+ end
109
+
110
+ ## extend the memoized bit array up to high
111
+ def self._extend_bit_array(high)
112
+ arr = NArray.byte(high + 1)
113
+ arr[0..@upper_limit] = @primes_bit_array[0..@upper_limit]
114
+ arr[@upper_limit+1..-1] = NArray.byte(high - @upper_limit).fill(1)[0..-1]
115
+ sq = (high**0.5).ceil
116
+ 2.upto(sq) do |i|
117
+ if arr[i] == 1
118
+ j = i*i
119
+ j += i while j < @upper_limit
120
+ while j <= high
121
+ arr[j] = 0
122
+ j += i
123
+ end
124
+ end
125
+ end
126
+ @primes_bit_array = arr
127
+ @upper_limit = high
128
+ end
129
+
130
+ ## for the first computation of the bit array
131
+ def self._initialize_bit_array (high)
132
+ arr = NArray.byte(high + 1).fill(1)
133
+ arr[0], arr[1] = 0, 0
134
+ sq = (high**0.5).ceil
135
+ 2.upto(sq) do |i|
136
+ if arr[i] == 1
137
+ j = i**2
138
+ while j <= high
139
+ arr[j] = 0
140
+ j += i
141
+ end
142
+ end
143
+ end
144
+ @primes_bit_array = arr
145
+ @upper_limit = high
146
+ end
147
+
148
+ ##
149
+ # Returns the nth prime number.
150
+ #
151
+ # == Example
152
+ # >> Primes.nthprime(10**4)
153
+ # => 104729
154
+ #
155
+ # == Algorithm
156
+ # Since it can be proved that the nth prime number is
157
+ # not greater than n * (log n + log log n)
158
+ # the method returns the nth element of
159
+ # primes_list(n * (log n + log log n))
160
+ #
161
+ def self.nthprime(n)
162
+ return [2, 3, 5, 7, 11, 13][n - 1] if n < 7
163
+ lim = n * (Math.log(n) + Math.log(Math.log(n))).to_i
164
+ self.primes_list(lim + 1)[n - 1]
165
+ end
166
+
167
+ ##
168
+ # Returns the value of primepi(n), i.e. the number
169
+ # of primes smaller than n
170
+ #
171
+ # == Example
172
+ # >> Primes.primepi(100000)
173
+ # => 9592
174
+ #
175
+ def self.primepi(n)
176
+ return 0 if n < 2
177
+ self.primes_list(n).size
178
+ end
179
+
180
+
181
+ ##
182
+ # Factors n and returns an hash containing the prime
183
+ # factors of n as keys and their respective multiplicities
184
+ # as values. If limit is passed, no factors greater
185
+ # than limit are searched, and some of the keys could be
186
+ # composite.
187
+ #
188
+ # == Example
189
+ # >> Primes.factor(1690)
190
+ # => {2=>1, 5=>1, 13=>2}
191
+ #
192
+ # >> Primes.factor(1690, 10)
193
+ # => {2=>1, 5=>1, 169=>1}
194
+ #
195
+ # >> Primes.factor(79103835773176077140539788299)
196
+ # => {3267000013=>1, 4093082899=>1, 5915587277=>1}
197
+ #
198
+ # == Algorithm
199
+ # The procedure first searches for small factors of n
200
+ # in the primes list provided by the primes_list method.
201
+ # Then, if needed, the procedure switches between Pollard's Rho method
202
+ # and Pollard's p - 1 method until n is fully factored.
203
+ #
204
+ def self.factor(n, opts = {})
205
+ defaults = { limit: nil, use_rho: true, use_pm1: true}
206
+ options = defaults.merge!(opts)
207
+
208
+ limit = options[:limit]
209
+ use_rho = options[:use_rho]
210
+ use_pm1 = options[:use_pm1]
211
+
212
+ return nil if n < 1
213
+ return {1 => 1} if n == 1
214
+ return {n => 1} if self.prime?(n)
215
+
216
+ if limit
217
+ factors, m = self._trial(n, limit)
218
+ return factors if m == 1
219
+ return factors.merge({m => 1})
220
+ else
221
+ factors, m = self._trial(n, 100000)
222
+ return factors if m == 1
223
+ return factors.merge({m => 1}) if self.prime?(m)
224
+
225
+ if not (use_pm1 or use_rho)
226
+ return factors.merge({m => 1})
227
+ end
228
+
229
+ count = 1
230
+ while m > 1
231
+
232
+ ## pollard rho
233
+ if use_rho
234
+ rounds = [10**3, 10**count].max
235
+ div = self._pollard_rho(m, rounds)
236
+ if div
237
+ if self.prime?(div)
238
+ fac = {div => 1}
239
+ else
240
+ fac = self.factor(div)
241
+ end
242
+ factors.merge!(fac) { |_, v1, v2| v1 + v2 }
243
+ m /= div
244
+ return factors.merge({m => 1}) if self.prime?(m)
245
+ end
246
+ end
247
+
248
+ ## pollard p - 1
249
+ if use_pm1
250
+ bound = [10**3, 10**count].max
251
+ div = self._pollard_pm1(m, bound)
252
+ if div
253
+ if self.prime?(div)
254
+ fac = {div => 1}
255
+ else
256
+ fac = self.factor(div)
257
+ end
258
+ factors.merge!(fac) { |_, v1, v2| v1 + v2 }
259
+ m /= div
260
+ return factors.merge({m => 1}) if self.prime?(m)
261
+ end
262
+ end
263
+
264
+ count += 1
265
+ end
266
+ return factors
267
+ end
268
+ end
269
+
270
+ ## search for small factor of n (upto lim)
271
+ def self._trial(n, lim)
272
+ factors = {}
273
+ for p in self.primes_list(lim)
274
+ if n % p == 0
275
+ t = Divisors.multiplicity(n, p)
276
+ factors[p] = t
277
+ n /= p**t
278
+ end
279
+ end
280
+ return factors, n
281
+ end
282
+
283
+ ## Pollard's rho method
284
+ def self._pollard_rho(n, max_rounds, retries = 5)
285
+ v, a, i = 2, -1, 0
286
+ retries.times do
287
+ u, f = v, ->(x) { (x*x + a) % n }
288
+ max_rounds.times do
289
+ u, v = f.call(u), f.call(f.call(v))
290
+ g = n.gcd(u - v)
291
+ next if g == 1
292
+ break if g == n
293
+ return g
294
+ end
295
+ v, a = rand(n - 1), 1 + rand(n - 4)
296
+ end
297
+ return nil
298
+ end
299
+
300
+ ## Pollard's p - 1 method
301
+ def self._pollard_pm1 (n, bound, max_rounds = 8)
302
+ primes = self.primes_list(bound)
303
+ max_rounds.times do
304
+ c = 2
305
+ primes.each do |p|
306
+ Math.log(bound, p).floor.times { c = Utils.mod_exp(c, p, n) }
307
+ end
308
+ g = n.gcd(c-1)
309
+ return g if g != 1 and g != n
310
+ end
311
+ return nil
312
+ end
313
+
314
+ ##
315
+ # Returns the smallest prime number greater than n.
316
+ #
317
+ # == Example
318
+ # >> Primes.nextprime(1000)
319
+ # => 1009
320
+ #
321
+ # == Algorithm
322
+ # Starting from n, tests primality for each successive number
323
+ # equals to 1 or 5 (mod 6). Return the first prime
324
+ # found.
325
+ #
326
+ def self.nextprime(n)
327
+ if n < 10007
328
+ return self.primes_list(10007).find { |x| x > n }
329
+ else
330
+ p = n + 1
331
+ p += 1 while p % 6 != 1 and p % 6 != 5
332
+ flip = (p % 6 == 1 ? 4 : 2)
333
+ while not self.prime?(p)
334
+ p += flip
335
+ flip = 6 - flip
336
+ end
337
+ return p
338
+ end
339
+ end
340
+
341
+ ##
342
+ # Returns the greatest prime number smaller than +n+.
343
+ #
344
+ # == Example
345
+ # >> Primes.prevprime(1000)
346
+ # => 997
347
+ #
348
+ # == Algorithm
349
+ # Starting from n, tests primality for each previous number
350
+ # equals to 1 or 5 (mod 6). Return the first prime
351
+ # found.
352
+ def self.prevprime(n)
353
+ return nil if n < 3
354
+ p = n-1
355
+ p -= 1 while p % 6 != 1 and p % 6 != 5
356
+ flip = (p % 6 == 1 ? 2 : 4)
357
+ while not self.prime?(p)
358
+ p -= flip
359
+ flip = 6 - flip
360
+ end
361
+ return p
362
+ end
363
+
364
+ ##
365
+ # Returns a random prime between low and high;
366
+ # and nil if there's not one.
367
+ #
368
+ # == Example
369
+ # >> Primes.randprime(1000)
370
+ # => 631
371
+ #
372
+ # >> Primes.randprime(10000, 20000)
373
+ # => 15569
374
+ #
375
+ def self.randprime(low = 1, high)
376
+ p = self.nextprime(low + rand(high - low + 1))
377
+ p = self.prevprime(p) if p > high
378
+ return nil if p < low
379
+ return p
380
+ end
381
+
382
+ ##
383
+ # Returns the primorial of n, i.e. the product
384
+ # of the first n prime numbers.
385
+ #
386
+ # == Example
387
+ # >> Primes.primorial(10)
388
+ # => 6469693230
389
+ #
390
+ def self.primorial(n)
391
+ return nil if n < 0
392
+ return [2, 6, 30, 210, 2310][n - 1] if n < 7
393
+ upp = n * (Math.log(n) + Math.log(Math.log(n)))
394
+ primes = self.primes_list(upp)[0..n-1]
395
+ primes.inject {|a, b| a * b}
396
+ end
397
+
398
+ end
399
+
400
+ end
@@ -0,0 +1,68 @@
1
+ module NumberTheory
2
+
3
+ def self.time_once
4
+ start = Time.now
5
+ yield
6
+ (Time.now - start).round(3)
7
+ end
8
+
9
+ module Utils
10
+
11
+ ##
12
+ # Returns a^b (mod m). Negative exponents are allowed.
13
+ #
14
+ # == Example
15
+ # >> Utils.mod_exp(12, 34, 107)
16
+ # => 61
17
+ #
18
+ # == Algorithm
19
+ #
20
+ #
21
+ #
22
+ def self.mod_exp(a, b, m)
23
+ if b >= 0
24
+ res = 1
25
+ while b > 0
26
+ res = (res * a) % m if b & 1 != 0
27
+ b >>= 1
28
+ a = (a * a) % m
29
+ end
30
+ res
31
+ else
32
+ self.mod_exp(self.mod_inv(a, m), -b, m)
33
+ end
34
+ end
35
+
36
+ ##
37
+ # Returns the modular inverse of a, i.e. the number b
38
+ # such that a * b = 1 (mod m)
39
+ #
40
+ # == Example
41
+ # >> Utils.mod_inv(121, 107)
42
+ # => 23
43
+ #
44
+ # == Algorithm
45
+ #
46
+ #
47
+ #
48
+ #
49
+ def self.mod_inv(a, m)
50
+ return 0 if a % m == 0
51
+ g, a, y = self._eca(a, m)
52
+ return g != 1 ? 0 : a % m
53
+ end
54
+
55
+ ## helper function for mod_inv
56
+ def self._eca(a, b)
57
+ return b, 0, 1 if a == 0
58
+ g, y, x = self._eca(b % a, a)
59
+ return g, x - y * (b / a).floor, y
60
+ end
61
+
62
+
63
+ end
64
+
65
+
66
+ end
67
+
68
+
@@ -0,0 +1,25 @@
1
+ require 'test/unit'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'number-theory'))
3
+
4
+ include NumberTheory
5
+
6
+ class TestCongruences < Test::Unit::TestCase
7
+ def test_linear_congruences_solver
8
+ # test linear_congruences_solver(a, c, m) which solves ax ≡ c (mod m)
9
+ assert_equal [], Congruences.linear_congruences_solver(3, 5, 6)
10
+ assert_equal [], Congruences.linear_congruences_solver(0, 5, 6)
11
+ assert_equal [1], Congruences.linear_congruences_solver(3, -5, 8)
12
+ p Congruences.linear_congruences_solver(-3, -5, -8)
13
+ assert_equal [1], Congruences.linear_congruences_solver(-3, -5, -8) # treating both a and m are positive integers
14
+ assert_equal 1078, Congruences.linear_congruences_solver(316258250, 2156, 1160718174).size
15
+ assert_equal [82, 210, 338, 466, 594, 722, 850, 978, 1106, 1234, 1362, 1490, 1618, 1746, 1874, 2002, 2130, 2258, 2386], Congruences.linear_congruences_solver(893, 266, 2432)
16
+ end
17
+
18
+ def test_chinese_remainder_theorem
19
+ assert_equal 23, Congruences.chinese_remainder_theorem([2, 3, 2], [3, 5, 7])
20
+ assert_equal false, Congruences.chinese_remainder_theorem([2, 3, 2], [3, 6, 7]) # as gcd(3, 6) = 2 which is not 1
21
+ assert_equal 1000, Congruences.chinese_remainder_theorem([10, 4, 12], [11, 12, 13])
22
+ assert_equal 8083600090651983754, Congruences.chinese_remainder_theorem([10, 4, 12], [1160718174, 1160718175, 13])
23
+ assert_equal 6725167681631185062030019654, Congruences.chinese_remainder_theorem([10, 4, 12], [2160718174, 2160718175, 2160718177])
24
+ end
25
+ end
@@ -0,0 +1,80 @@
1
+ require 'test/unit'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'number-theory'))
3
+
4
+ include NumberTheory
5
+
6
+ class TestDivisors < Test::Unit::TestCase
7
+
8
+ def test_divisors
9
+ assert_equal(Divisors.divisors(32), [1, 2, 4, 8, 16, 32])
10
+ assert_equal(Divisors.divisors(210), [1, 2, 3, 5, 6, 7, 10, 14, 15, 21, 30, 35, 42, 70, 105, 210])
11
+ end
12
+
13
+ def test_divcount
14
+ assert_equal(Divisors.divcount(210), 16)
15
+ assert_equal(Divisors.divcount(32000), 36)
16
+ assert_equal(Divisors.divcount(3861000), 256)
17
+ assert_equal(Divisors.divcount(4900265546530028103000), 12288)
18
+ end
19
+
20
+ def test_multiplicity
21
+ assert_equal(Divisors.multiplicity(18, 2), 1)
22
+ assert_equal(Divisors.multiplicity(34992, 3), 7)
23
+ assert_equal(Divisors.multiplicity(8152454278732958496, 19), 11)
24
+ end
25
+
26
+ def test_divisor_sigma
27
+ assert_equal(Divisors.divisor_sigma(16,1), 31)
28
+ assert_equal(Divisors.divisor_sigma(20,2), 546)
29
+ assert_equal(Divisors.divisor_sigma(28,5), 17766056)
30
+ assert_equal(Divisors.divisor_sigma(15,7), 170939688)
31
+ end
32
+
33
+ def test_euler_phi
34
+ assert_equal(Divisors.euler_phi(12), 4)
35
+ assert_equal(Divisors.euler_phi(19), 18)
36
+ assert_equal(Divisors.euler_phi(25555), 19296)
37
+ assert_equal(Divisors.euler_phi(87654), 25032)
38
+ end
39
+
40
+ def test_perfect?
41
+ assert(Divisors.perfect?(6))
42
+ assert(Divisors.perfect?(496))
43
+ assert(Divisors.perfect?(2305843008139952128))
44
+ end
45
+
46
+ def test_square_free?
47
+ assert(Divisors.square_free?(1))
48
+ assert(Divisors.square_free?(110))
49
+ assert(Divisors.square_free?(3226340895))
50
+ assert(Divisors.square_free?(3226340897))
51
+ assert(!Divisors.square_free?(3226340896))
52
+ assert(!Divisors.square_free?(18))
53
+ assert(!Divisors.square_free?(0))
54
+ assert(!Divisors.square_free?(-110))
55
+ end
56
+
57
+ def test_euclidean_algo
58
+ assert_equal 1, Divisors.euclidean_algo(1, 2) # Trivial case
59
+ assert_equal 5, Divisors.euclidean_algo(20, 15) # Trivial case
60
+ assert_equal 13, Divisors.euclidean_algo(13, 13) # a = b
61
+ assert_equal 100, Divisors.euclidean_algo(1000, 100) # one is a mulitple of the other
62
+ assert_equal 0, Divisors.euclidean_algo(1000, 0) # one number is zero
63
+ assert_equal 1, Divisors.euclidean_algo(-3, -7) # treat -ve numbers as +ve ones
64
+ assert_equal 1, Divisors.euclidean_algo(99991, 200) # with a prime number
65
+ assert_equal 1078, Divisors.euclidean_algo(1160718174, 316258250)
66
+ end
67
+
68
+ def test_extended_euclidean_algo
69
+ assert_equal [1, 1, 0], Divisors.extended_euclidean_algo(1, 2) # Trivial case
70
+ assert_equal [5, 1, -1], Divisors.extended_euclidean_algo(20, 15) # Trivial case
71
+ assert_equal [13, 0, 1], Divisors.extended_euclidean_algo(13, 13) # a = b
72
+ assert_equal [100, 0, 1], Divisors.extended_euclidean_algo(1000, 100) # one is a mulitple of the other
73
+ assert_equal [0, 0, 0], Divisors.extended_euclidean_algo(1000, 0) # one number is zero
74
+ assert_equal [1, -2, 1], Divisors.extended_euclidean_algo(-3, -7) # treat -ve numbers as +ve ones
75
+ assert_equal [1, -89, 44496], Divisors.extended_euclidean_algo(99991, 200) # with a prime number
76
+ assert_equal [1078, 144397, -529960], Divisors.extended_euclidean_algo(1160718174, 316258250)
77
+ assert_equal [1078, -529960, 144397], Divisors.extended_euclidean_algo(316258250, 1160718174)
78
+ end
79
+
80
+ end
@@ -0,0 +1,84 @@
1
+ require 'test/unit'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'number-theory'))
3
+
4
+ include NumberTheory
5
+
6
+ class TestPrimes < Test::Unit::TestCase
7
+
8
+ def test__trial_division
9
+ assert(!Primes._trial_division(1))
10
+ assert(Primes._trial_division(2))
11
+ assert(!Primes._trial_division(24680))
12
+ assert(Primes._trial_division(982451653))
13
+ assert(Primes._trial_division(1882341361))
14
+ end
15
+
16
+ def test__miller_rabin
17
+ assert(!Primes._miller_rabin(24680))
18
+ assert(Primes._miller_rabin(982451653))
19
+ assert(Primes._miller_rabin(1882341361))
20
+ assert(Primes._miller_rabin(9007199254740881))
21
+ assert(Primes._miller_rabin(9007199254740881))
22
+ end
23
+
24
+ def test_prime?
25
+ assert(!Primes.prime?(1))
26
+ assert(Primes.prime?(2))
27
+ assert(!Primes.prime?(24680))
28
+ assert(Primes.prime?(982451653))
29
+ assert(Primes.prime?(1882341361))
30
+ assert(Primes.prime?(9007199254740881))
31
+ assert(Primes.prime?(66405897020462343733))
32
+ assert(Primes.prime?(416064700201658306196320137931))
33
+ assert(Primes.prime?(6847944682037444681162770672798288913849))
34
+ assert(Primes.prime?(95647806479275528135733781266203904794419563064407))
35
+ assert(Primes.prime?(669483106578092405936560831017556154622901950048903016651289))
36
+ assert(Primes.prime?(2367495770217142995264827948666809233066409497699870112003149352380375124855230068487109373226251983))
37
+ end
38
+
39
+ def test_primes_list
40
+ assert_equal(Primes.primes_list(10), [2, 3, 5, 7])
41
+ assert_equal(Primes.primes_list(10, 30), [11, 13, 17, 19, 23, 29])
42
+ assert_equal(Primes.primes_list(10**3,10**4).size, 1229 - 168)
43
+ assert_equal(Primes.primes_list(10**5).size, 9592)
44
+ end
45
+
46
+ def test_primepi
47
+ assert_equal(Primes.primepi(10), 4)
48
+ assert_equal(Primes.primepi(10**3), 168)
49
+ assert_equal(Primes.primepi(10**6), 78498)
50
+ end
51
+
52
+ def test_factor
53
+ assert_equal(Primes.factor(10), {2=>1, 5=>1})
54
+ assert_equal(Primes.factor(10125000), {2=>3, 3=>4, 5=>6})
55
+ assert_equal(Primes.factor(10125001), {10125001=>1})
56
+ assert_equal(Primes.factor(79103835773176077140539788299), {3267000013=>1, 4093082899=>1, 5915587277=>1})
57
+ assert_equal(Primes.factor(323424426232167763068694468589), {5915587277=>1, 54673257461630679457=>1})
58
+ assert_equal(Primes.factor(12932983746293756928584532764589230), {2=>1, 5=>1, 73=>1, 383=>1, 63564265087747=>1, 727719592270351=>1})
59
+ end
60
+
61
+ def test_nextprime
62
+ assert_equal(Primes.nextprime(5), 7)
63
+ assert_equal(Primes.nextprime(98765432100), 98765432137)
64
+ assert_equal(Primes.nextprime(9876543210000000000), 9876543210000000029)
65
+ end
66
+
67
+ def test_prevprime
68
+ assert_equal(Primes.prevprime(1234567890), 1234567811)
69
+ assert_equal(Primes.prevprime(1234567890000000000), 1234567889999999953)
70
+ end
71
+
72
+ def test_nthprimes
73
+ assert_equal(Primes.nthprime(10), 29)
74
+ assert_equal(Primes.nthprime(1000), 7919)
75
+ assert_equal(Primes.nthprime(10000), 104729)
76
+ end
77
+
78
+ def test_primorial
79
+ assert_equal(Primes.primorial(5), 2310)
80
+ assert_equal(Primes.primorial(17), 1922760350154212639070)
81
+ assert_equal(Primes.primorial(25), 2305567963945518424753102147331756070)
82
+ end
83
+
84
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/unit'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'number-theory'))
3
+
4
+ include NumberTheory
5
+
6
+ class TestUtils < Test::Unit::TestCase
7
+
8
+ def test_mod_exp
9
+ assert_equal(Utils.mod_exp(123, 0, 234), 1)
10
+ assert_equal(Utils.mod_exp(4, 13, 497), 445)
11
+ assert_equal(Utils.mod_exp(1777, 1885, 100000001), 2452785)
12
+ assert_equal(Utils.mod_exp(54321, 98765, 2147483647), 252276857)
13
+ assert_equal(Utils.mod_exp(4, -13, 497), 86)
14
+ assert_equal(Utils.mod_exp(1777, -1885, 100000001), 9159955)
15
+ assert_equal(Utils.mod_exp(54321, -98765, 2147483647), 1899303417)
16
+ end
17
+
18
+ def test_mod_inv
19
+ assert_equal(Utils.mod_inv(3, 17), 6)
20
+ assert_equal(Utils.mod_inv(116003, 1500450271), 287173581)
21
+ assert_equal(Utils.mod_inv(5915587277, 54673257461630679457), 50186303895605983691)
22
+ end
23
+
24
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: number-theory
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alberto Donizetti
8
+ - Danny Siu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-01-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: narray
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0.6'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0.6'
28
+ description: A number theory library written in pure Ruby. Provides methods for primality
29
+ test, factoring integers, and more.
30
+ email: dannysiu392005@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/number-theory.rb
36
+ - lib/number-theory/congruences.rb
37
+ - lib/number-theory/divisors.rb
38
+ - lib/number-theory/primes.rb
39
+ - lib/number-theory/utils.rb
40
+ - test/test_congruences.rb
41
+ - test/test_divisors.rb
42
+ - test/test_primes.rb
43
+ - test/test_utils.rb
44
+ homepage: https://github.com/dannysiu392005/ruby-number-theory
45
+ licenses:
46
+ - GNU GPL 3
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.6.7
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: A Ruby Number Theory Library
68
+ test_files:
69
+ - test/test_congruences.rb
70
+ - test/test_divisors.rb
71
+ - test/test_primes.rb
72
+ - test/test_utils.rb
73
+ has_rdoc: