number-theory 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/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:
|