number-theory 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/number-theory.rb +15 -0
- data/lib/number-theory/congruences.rb +82 -0
- data/lib/number-theory/divisors.rb +200 -0
- data/lib/number-theory/primes.rb +400 -0
- data/lib/number-theory/utils.rb +68 -0
- data/test/test_congruences.rb +25 -0
- data/test/test_divisors.rb +80 -0
- data/test/test_primes.rb +84 -0
- data/test/test_utils.rb +24 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -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
|
data/test/test_primes.rb
ADDED
@@ -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
|
data/test/test_utils.rb
ADDED
@@ -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:
|