abst 0.2.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.
- data/lib/abst.rb +38 -0
- data/lib/config.rb +21 -0
- data/lib/include/array.rb +45 -0
- data/lib/include/bisect.rb +95 -0
- data/lib/include/cache.rb +60 -0
- data/lib/include/compatibility.rb +3 -0
- data/lib/include/complex.rb +10 -0
- data/lib/include/float.rb +19 -0
- data/lib/include/fundamental.rb +903 -0
- data/lib/include/graph.rb +11 -0
- data/lib/include/group.rb +35 -0
- data/lib/include/integer.rb +385 -0
- data/lib/include/matrix.rb +143 -0
- data/lib/include/polynomial.rb +137 -0
- data/lib/include/prime.rb +556 -0
- data/lib/include/prime_mpqs.rb +483 -0
- data/lib/include/rational.rb +155 -0
- data/lib/include/residue.rb +27 -0
- data/lib/include/ring.rb +21 -0
- data/lib/include/sequence.rb +54 -0
- data/lib/include/set.rb +67 -0
- data/lib/include/vector.rb +90 -0
- data/test/test_all.rb +15 -0
- data/test/test_array.rb +10 -0
- data/test/test_bisect.rb +43 -0
- data/test/test_combination.rb +6 -0
- data/test/test_float.rb +13 -0
- data/test/test_fundamental.rb +406 -0
- data/test/test_group.rb +10 -0
- data/test/test_integer.rb +161 -0
- data/test/test_matrix.rb +54 -0
- data/test/test_polynomial.rb +107 -0
- data/test/test_prime.rb +153 -0
- data/test/test_prime_mpqs.rb +24 -0
- data/test/test_rational.rb +33 -0
- data/test/test_sequence.rb +55 -0
- data/test/test_set.rb +36 -0
- data/test/test_vector.rb +37 -0
- metadata +98 -0
data/lib/abst.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'include/compatibility'
|
2
|
+
|
3
|
+
module Abst
|
4
|
+
ABST_ROOT = File.dirname(__FILE__) + '/'
|
5
|
+
|
6
|
+
# Integer block byte size
|
7
|
+
BASE_BYTE = 1.size
|
8
|
+
INFINITY = Float::INFINITY
|
9
|
+
|
10
|
+
I = Complex::I
|
11
|
+
end
|
12
|
+
|
13
|
+
require_relative 'config'
|
14
|
+
|
15
|
+
require_relative 'include/bisect'
|
16
|
+
require_relative 'include/cache'
|
17
|
+
|
18
|
+
require_relative 'include/group'
|
19
|
+
require_relative 'include/ring'
|
20
|
+
|
21
|
+
require_relative 'include/array'
|
22
|
+
require_relative 'include/complex'
|
23
|
+
require_relative 'include/float'
|
24
|
+
require_relative 'include/fundamental'
|
25
|
+
require_relative 'include/graph'
|
26
|
+
require_relative 'include/integer'
|
27
|
+
require_relative 'include/prime'
|
28
|
+
require_relative 'include/prime_mpqs'
|
29
|
+
require_relative 'include/rational'
|
30
|
+
require_relative 'include/residue'
|
31
|
+
require_relative 'include/sequence'
|
32
|
+
require_relative 'include/set'
|
33
|
+
|
34
|
+
require_relative 'include/vector'
|
35
|
+
require_relative 'include/polynomial'
|
36
|
+
require_relative 'include/matrix'
|
37
|
+
|
38
|
+
include Abst if Abst::AUTO_INCLUDE
|
data/lib/config.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Abst
|
2
|
+
# Precompute primes under the value
|
3
|
+
PRIME_CACHE_LIMIT = 1_000_000
|
4
|
+
|
5
|
+
# System cache files will be created in this directory
|
6
|
+
DATA_DIR = ABST_ROOT + 'data/'
|
7
|
+
|
8
|
+
# User cache files will be created in this directory
|
9
|
+
CACHE_DIR = ABST_ROOT + 'cache/'
|
10
|
+
|
11
|
+
# Cache primes
|
12
|
+
PRIMES_LIST = DATA_DIR + 'primes_list'
|
13
|
+
|
14
|
+
# Bit size of Fixnum
|
15
|
+
# integer e s.t. (2 ** e) is not Fixnum and (2 ** e - 1) is Fixnum
|
16
|
+
FIXNUM_BIT_SIZE = 30
|
17
|
+
|
18
|
+
THREAD_NUM = defined?(JRUBY_VERSION) ? 6 : 1
|
19
|
+
|
20
|
+
AUTO_INCLUDE = false
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Array
|
2
|
+
def each_coefficient(init = nil)
|
3
|
+
return Enumerator.new(self, :each_coefficient, init) unless block_given?
|
4
|
+
|
5
|
+
init = Array.new(self.size, 0) unless init
|
6
|
+
current = init.dup
|
7
|
+
|
8
|
+
loop do
|
9
|
+
yield current.dup
|
10
|
+
|
11
|
+
# next coef
|
12
|
+
self.size.times do |i|
|
13
|
+
if self[i] == current[i]
|
14
|
+
return if i == self.size - 1
|
15
|
+
current[i] = 0
|
16
|
+
else
|
17
|
+
current[i] += 1
|
18
|
+
break
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_set(sortable = true, &compare)
|
25
|
+
return SortableSet.new(self, &compare) if sortable
|
26
|
+
return Set.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
# to_vector
|
30
|
+
def to_v(coef_class = nil)
|
31
|
+
vector = Abst::Vector(coef_class || self[0].class, self.size)
|
32
|
+
return vector.new(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
# to_matrix
|
36
|
+
def to_m(coef_class = nil)
|
37
|
+
matrix = Abst::Matrix(coef_class || self[0][0].class, self.size, self[0].size)
|
38
|
+
return matrix.new(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create integer from factorization of it
|
42
|
+
def to_i
|
43
|
+
return self.map{|p, e| p ** e}.inject(&:*)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Bisect
|
2
|
+
module_function
|
3
|
+
|
4
|
+
def bisect_left(list, item, lo = 0, hi = list.size)
|
5
|
+
if block_given?
|
6
|
+
while lo < hi
|
7
|
+
i = (lo + hi - 1) >> 1
|
8
|
+
|
9
|
+
if 0 <= yield(list[i], item)
|
10
|
+
hi = i
|
11
|
+
else
|
12
|
+
lo = i + 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
else
|
16
|
+
while lo < hi
|
17
|
+
i = (lo + hi - 1) >> 1
|
18
|
+
|
19
|
+
if 0 <= (list[i] <=> item)
|
20
|
+
hi = i
|
21
|
+
else
|
22
|
+
lo = i + 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return hi
|
28
|
+
end
|
29
|
+
|
30
|
+
def bisect_right(list, item, lo = 0, hi = list.size)
|
31
|
+
if block_given?
|
32
|
+
while lo < hi
|
33
|
+
i = (lo + hi - 1) >> 1
|
34
|
+
|
35
|
+
if 0 < yield(list[i], item)
|
36
|
+
hi = i
|
37
|
+
else
|
38
|
+
lo = i + 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
while lo < hi
|
43
|
+
i = (lo + hi - 1) >> 1
|
44
|
+
|
45
|
+
if 0 < (list[i] <=> item)
|
46
|
+
hi = i
|
47
|
+
else
|
48
|
+
lo = i + 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
return lo
|
54
|
+
end
|
55
|
+
|
56
|
+
def insert_left(list, item, lo = 0, hi = list.size, &block)
|
57
|
+
i = bisect_left(list, item, lo, hi, &block)
|
58
|
+
list.insert(i, item)
|
59
|
+
end
|
60
|
+
|
61
|
+
def insert_right(list, item, lo = 0, hi = list.size, &block)
|
62
|
+
i = bisect_right(list, item, lo, hi, &block)
|
63
|
+
list.insert(i, item)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Locate the leftmost value exactly equal to item
|
67
|
+
def index(list, item, &block)
|
68
|
+
i = bisect_left(list, item, &block)
|
69
|
+
return list[i] == item ? i : nil
|
70
|
+
end
|
71
|
+
|
72
|
+
# Find rightmost value less than item
|
73
|
+
def find_lt(list, item, &block)
|
74
|
+
i = bisect_left(list, item, &block)
|
75
|
+
return list[i - 1] unless 0 == i
|
76
|
+
end
|
77
|
+
|
78
|
+
# Find rightmost value less than or equal to item
|
79
|
+
def find_le(list, item, &block)
|
80
|
+
i = bisect_right(list, item, &block)
|
81
|
+
return list[i - 1] unless 0 == i
|
82
|
+
end
|
83
|
+
|
84
|
+
# Find leftmost value greater than item
|
85
|
+
def find_gt(list, item, &block)
|
86
|
+
i = bisect_right(list, item, &block)
|
87
|
+
return list[i] unless list.size == i
|
88
|
+
end
|
89
|
+
|
90
|
+
# Find leftmost item greater than or equal to item
|
91
|
+
def find_ge(list, item, &block)
|
92
|
+
i = bisect_left(list, item, &block)
|
93
|
+
return list[i] unless list.size == i
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Abst
|
2
|
+
class Cache
|
3
|
+
PREFIX = 'abst_cache_'
|
4
|
+
CACHE_DIR = CACHE_DIR
|
5
|
+
|
6
|
+
def self.mkdir(dir)
|
7
|
+
pdir = File.dirname(dir)
|
8
|
+
mkdir(pdir) unless FileTest.exist?(pdir)
|
9
|
+
Dir::mkdir(dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.[]=(cache_id, data)
|
13
|
+
if nil == data
|
14
|
+
delete(cache_id)
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
path = get_path(cache_id)
|
19
|
+
dir = File.dirname(path)
|
20
|
+
|
21
|
+
mkdir(dir) unless FileTest.exist?(dir)
|
22
|
+
open(path, "w") {|io| Marshal.dump(data, io)}
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.[](cache_id)
|
26
|
+
path = get_path(cache_id)
|
27
|
+
|
28
|
+
return nil unless FileTest.exist?(path)
|
29
|
+
open(path) {|io| return Marshal.load(io)}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.delete(cache_id)
|
33
|
+
path = get_path(cache_id)
|
34
|
+
|
35
|
+
File.delete(path) if FileTest.exist?(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.clear
|
39
|
+
raise NotImplementedError
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.exist?(cache_id)
|
43
|
+
return FileTest.exist?(get_path(cache_id))
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.get_path(cache_id)
|
47
|
+
cache_id = cache_id.to_s
|
48
|
+
unless /^[a-z0-9_]+$/ =~ cache_id
|
49
|
+
raise ArgumentError, "Invalid cache_id. (Only a-z 0-9 and _ are available)"
|
50
|
+
end
|
51
|
+
|
52
|
+
return self::CACHE_DIR + self::PREFIX + cache_id
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class SystemCache < Cache
|
57
|
+
PREFIX = ''
|
58
|
+
CACHE_DIR = DATA_DIR
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Float
|
2
|
+
def self.one
|
3
|
+
return 1.0
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.zero
|
7
|
+
return 0.0
|
8
|
+
end
|
9
|
+
|
10
|
+
def inverse
|
11
|
+
return 1.0 / self
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return:: formatted string
|
15
|
+
def to_fs
|
16
|
+
return self.to_s.gsub(/(?<=\d)(?=(\d\d\d)+\.)/, ' ')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,903 @@
|
|
1
|
+
module Abst
|
2
|
+
module_function
|
3
|
+
|
4
|
+
# Right-Left Binary Power
|
5
|
+
# Param:: group element g
|
6
|
+
# integer n
|
7
|
+
# Euclidean domain element mod
|
8
|
+
# Return:: g ** n % mod
|
9
|
+
def right_left_power(g, n, mod = nil)
|
10
|
+
rslt = g.class.one
|
11
|
+
return rslt if 0 == n
|
12
|
+
|
13
|
+
if n < 0
|
14
|
+
n = -n
|
15
|
+
g **= (-1)
|
16
|
+
end
|
17
|
+
|
18
|
+
g %= mod if mod
|
19
|
+
|
20
|
+
loop do
|
21
|
+
rslt *= g if n.odd?
|
22
|
+
rslt %= mod if mod
|
23
|
+
|
24
|
+
n >>= 1
|
25
|
+
break if 0 == n
|
26
|
+
|
27
|
+
g = g ** 2
|
28
|
+
g %= mod if mod
|
29
|
+
end
|
30
|
+
|
31
|
+
return rslt
|
32
|
+
end
|
33
|
+
|
34
|
+
# Left-Right Binary Power
|
35
|
+
# Param:: group element g
|
36
|
+
# integer n
|
37
|
+
# Euclidean domain element mod
|
38
|
+
# Return:: g ** n % mod
|
39
|
+
def left_right_power(g, n, mod = nil)
|
40
|
+
return g.class.one if 0 == n
|
41
|
+
|
42
|
+
if n < 0
|
43
|
+
n = -n
|
44
|
+
g **= (-1)
|
45
|
+
end
|
46
|
+
|
47
|
+
g %= mod if mod
|
48
|
+
e = ilog2(n)
|
49
|
+
|
50
|
+
rslt = g
|
51
|
+
while 0 != e
|
52
|
+
e -= 1
|
53
|
+
|
54
|
+
rslt *= rslt
|
55
|
+
rslt %= mod if mod
|
56
|
+
|
57
|
+
if 1 == n[e]
|
58
|
+
rslt *= g
|
59
|
+
rslt %= mod if mod
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return rslt
|
64
|
+
end
|
65
|
+
alias power left_right_power
|
66
|
+
module_function :power
|
67
|
+
|
68
|
+
# Left-Right Base 2**k Power
|
69
|
+
# Param:: group element g
|
70
|
+
# integer n
|
71
|
+
# Euclidean domain element mod
|
72
|
+
# base bit size k
|
73
|
+
# Return:: g ** n % mod
|
74
|
+
def left_right_base2k_power(g, n, mod = nil, k = nil)
|
75
|
+
rslt = g.class.one
|
76
|
+
return rslt if 0 == n
|
77
|
+
|
78
|
+
# Initialize
|
79
|
+
if n < 0
|
80
|
+
n = -n
|
81
|
+
g **= (-1)
|
82
|
+
end
|
83
|
+
|
84
|
+
g %= mod if mod
|
85
|
+
|
86
|
+
unless k
|
87
|
+
e = ilog2(n)
|
88
|
+
optim = [0, 8, 24, 69, 196, 538, 1433, 3714]
|
89
|
+
|
90
|
+
if e <= optim.last
|
91
|
+
k = Bisect.bisect_left(optim, e)
|
92
|
+
else
|
93
|
+
k = optim.size
|
94
|
+
k += 1 until e <= k * (k + 1) * (1 << (k << 1)) / ((1 << (k + 1)) - k - 2)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# convert n into base 2**k
|
99
|
+
digits = []
|
100
|
+
mask = (1 << k) - 1
|
101
|
+
until 0 == n
|
102
|
+
digits.unshift(n & mask)
|
103
|
+
n >>= k
|
104
|
+
end
|
105
|
+
|
106
|
+
# Precomputations
|
107
|
+
z_powers = [nil, g]
|
108
|
+
g_square = g * g
|
109
|
+
g_square %= mod if mod
|
110
|
+
3.step((1 << k) - 1, 2) do |i|
|
111
|
+
z_powers[i] = z_powers[i - 2] * g_square
|
112
|
+
z_powers[i] %= mod if mod
|
113
|
+
end
|
114
|
+
|
115
|
+
digits.each do |a|
|
116
|
+
# Multiply
|
117
|
+
if 0 == a
|
118
|
+
k.times do
|
119
|
+
rslt = rslt ** 2
|
120
|
+
rslt %= mod if mod
|
121
|
+
end
|
122
|
+
else
|
123
|
+
t = 0
|
124
|
+
t += 1 while 0 == a[t]
|
125
|
+
a >>= t if 0 < t
|
126
|
+
|
127
|
+
(k - t).times do
|
128
|
+
rslt = rslt ** 2
|
129
|
+
rslt %= mod if mod
|
130
|
+
end
|
131
|
+
rslt *= z_powers[a]
|
132
|
+
rslt %= mod if mod
|
133
|
+
t.times do
|
134
|
+
rslt = rslt ** 2
|
135
|
+
rslt %= mod if mod
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
return rslt
|
141
|
+
end
|
142
|
+
|
143
|
+
# GCD
|
144
|
+
# Param:: a and b are member of a Euclidean domain
|
145
|
+
# Return:: gcd of a and b
|
146
|
+
def gcd(a, b)
|
147
|
+
until b.zero?
|
148
|
+
a, b = b, a % b
|
149
|
+
end
|
150
|
+
|
151
|
+
return a
|
152
|
+
end
|
153
|
+
|
154
|
+
def lcm(a, b)
|
155
|
+
return a * b / gcd(a, b)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Param:: integer a, b
|
159
|
+
# Return:: gcd of a and b
|
160
|
+
def lehmer_gcd(a, b)
|
161
|
+
a = -a if a < 0
|
162
|
+
b = -b if b < 0
|
163
|
+
a, b = b, a if a < b
|
164
|
+
|
165
|
+
until 0 == b
|
166
|
+
return gcd(a, b) if b.instance_of?(Fixnum)
|
167
|
+
|
168
|
+
# Get most significant digits of a and b
|
169
|
+
shift_size = (a < b ? b : a).bit_size - FIXNUM_BIT_SIZE
|
170
|
+
a_ = a >> shift_size
|
171
|
+
b_ = b >> shift_size
|
172
|
+
|
173
|
+
_A = 1
|
174
|
+
_B = 0 # a_ == msd(a) * _A + msd(b) * _B
|
175
|
+
_C = 0
|
176
|
+
_D = 1 # b_ == msd(a) * _C + msd(b) * _D
|
177
|
+
|
178
|
+
# Always
|
179
|
+
# a_ + _B <= msd(a * _A + b * _B) < a_ + _A AND
|
180
|
+
# b_ + _C <= msd(a * _C + b * _D) < a_ + _D
|
181
|
+
# OR
|
182
|
+
# a_ + _B > msd(a * _A + b * _B) >= a_ + _A AND
|
183
|
+
# b_ + _C > msd(a * _C + b * _D) >= a_ + _D
|
184
|
+
|
185
|
+
# Test quotient
|
186
|
+
until 0 == b_ + _C or 0 == b_ + _D
|
187
|
+
q1 = (a_ + _A) / (b_ + _C)
|
188
|
+
q2 = (a_ + _B) / (b_ + _D)
|
189
|
+
break if q1 != q2
|
190
|
+
|
191
|
+
# Euclidean step
|
192
|
+
_A, _C = _C, _A - q1 * _C
|
193
|
+
_B, _D = _D, _B - q1 * _D
|
194
|
+
a_, b_ = b_, a_ - q1 * b_
|
195
|
+
end
|
196
|
+
|
197
|
+
# Multi-precision step
|
198
|
+
if 0 == _B
|
199
|
+
a, b = b, a % b
|
200
|
+
else
|
201
|
+
a, b = a * _A + b * _B, a * _C + b * _D
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
return a
|
206
|
+
end
|
207
|
+
|
208
|
+
# Binary GCD
|
209
|
+
# Param:: integer a, b
|
210
|
+
# Return:: gcd of a and b
|
211
|
+
def binary_gcd(a, b)
|
212
|
+
a = -a if a < 0
|
213
|
+
b = -b if b < 0
|
214
|
+
a, b = b, a if a < b
|
215
|
+
return a if 0 == b
|
216
|
+
|
217
|
+
# Reduce size once
|
218
|
+
a, b = b, a % b
|
219
|
+
return a if 0 == b
|
220
|
+
|
221
|
+
# Compute powers of 2
|
222
|
+
k = 0
|
223
|
+
k += 1 while 0 == a[k] and 0 == b[k]
|
224
|
+
if 0 < k
|
225
|
+
a >>= k
|
226
|
+
b >>= k
|
227
|
+
end
|
228
|
+
|
229
|
+
# Remove initial power of 2
|
230
|
+
a >>= 1 while a.even?
|
231
|
+
b >>= 1 while b.even?
|
232
|
+
|
233
|
+
loop do
|
234
|
+
# Subtract (Here a and b are both odd.)
|
235
|
+
t = a - b
|
236
|
+
return a << k if 0 == t
|
237
|
+
|
238
|
+
count = 0
|
239
|
+
count += 1 while 0 == t[count]
|
240
|
+
t >>= count
|
241
|
+
|
242
|
+
(0 < t) ? a = t : b = -t
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Param:: a and b are member of a Euclidean domain
|
247
|
+
# Return:: (u, v, d) s.t. a*u + b*v = gcd(a, b) = d
|
248
|
+
def extended_gcd(a, b)
|
249
|
+
u0 = a.class.one
|
250
|
+
u1 = a.class.zero
|
251
|
+
|
252
|
+
return u0, u1, a if b.zero?
|
253
|
+
|
254
|
+
d0 = a # d0 = a * u0 + b * v0
|
255
|
+
d1 = b # d1 = a * u1 + b * v1
|
256
|
+
|
257
|
+
loop do
|
258
|
+
q, r = d0.divmod(d1)
|
259
|
+
|
260
|
+
return u1, (d1 - a * u1) / b, d1 if r.zero?
|
261
|
+
|
262
|
+
d0, d1 = d1, r
|
263
|
+
u0, u1 = u1, u0 - q * u1
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Param:: non-negative integer a, b
|
268
|
+
# Return:: (u, v, d) s.t. a*u + b*v = gcd(a, b) = d
|
269
|
+
def extended_lehmer_gcd(a, b)
|
270
|
+
d0 = a
|
271
|
+
u0 = 1 # d0 = a * u0 + b * v0
|
272
|
+
d1 = b
|
273
|
+
u1 = 0 # d1 = a * u1 + b * v1
|
274
|
+
|
275
|
+
loop do
|
276
|
+
if d1.instance_of?(Fixnum)
|
277
|
+
_u, _v, d = extended_gcd(d0, d1)
|
278
|
+
|
279
|
+
# here
|
280
|
+
# d == _u * d0 + _v * d1
|
281
|
+
# d0 == u0 * a + v0 * b
|
282
|
+
# d1 == u1 * a + v1 * b
|
283
|
+
|
284
|
+
u = _u * u0 + _v * u1
|
285
|
+
v = (d - u * a) / b
|
286
|
+
|
287
|
+
return u, v, d
|
288
|
+
end
|
289
|
+
|
290
|
+
# Get most significant digits of d0 and d1
|
291
|
+
shift_size = (d0 < d1 ? d1 : d0).bit_size - FIXNUM_BIT_SIZE
|
292
|
+
a_ = d0 >> shift_size
|
293
|
+
b_ = d1 >> shift_size
|
294
|
+
|
295
|
+
# Initialize (Here a_ and b_ are next value of d0, d1)
|
296
|
+
_A = 1
|
297
|
+
_B = 0 # a_ == msd(d0) * _A + msd(d1) * _B
|
298
|
+
_C = 0
|
299
|
+
_D = 1 # b_ == msd(d0) * _C + msd(d1) * _D
|
300
|
+
|
301
|
+
# Test Quotient
|
302
|
+
until 0 == b_ + _C or 0 == b_ + _D
|
303
|
+
q1 = (a_ + _B) / (b_ + _D)
|
304
|
+
q2 = (a_ + _A) / (b_ + _C)
|
305
|
+
break if q1 != q2
|
306
|
+
|
307
|
+
# Euclidean step
|
308
|
+
_A, _C = _C, _A - q1 * _C
|
309
|
+
_B, _D = _D, _B - q1 * _D
|
310
|
+
a_, b_ = b_, a_ - q1 * b_
|
311
|
+
end
|
312
|
+
|
313
|
+
# Multi-precision step
|
314
|
+
if 0 == _B
|
315
|
+
q, r = d0.divmod(d1)
|
316
|
+
d0, d1 = d1, r
|
317
|
+
u0, u1 = u1, u0 - q * u1
|
318
|
+
else
|
319
|
+
d0, d1 = d0 * _A + d1 * _B, d0 * _C + d1 * _D
|
320
|
+
u0, u1 =u0 * _A + u1 * _B, u0 * _C + u1 * _D
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Param:: non-negative integer a, b
|
326
|
+
# Return:: (u, v, d) s.t. a*u + b*v = gcd(a, b) = d
|
327
|
+
def extended_binary_gcd(a, b)
|
328
|
+
if a < b
|
329
|
+
a, b = b, a
|
330
|
+
exchange_flag_1 = true
|
331
|
+
end
|
332
|
+
|
333
|
+
if 0 == b
|
334
|
+
return 0, 1, a if exchange_flag_1
|
335
|
+
return 1, 0, a
|
336
|
+
end
|
337
|
+
|
338
|
+
# Reduce size once
|
339
|
+
_Q, r = a.divmod(b)
|
340
|
+
if 0 == r
|
341
|
+
return 1, 0, b if exchange_flag_1
|
342
|
+
return 0, 1, b
|
343
|
+
end
|
344
|
+
a, b = b, r
|
345
|
+
|
346
|
+
# Compute power of 2
|
347
|
+
_K = 0
|
348
|
+
_K += 1 while 0 == a[_K] and 0 == b[_K]
|
349
|
+
if 0 < _K
|
350
|
+
a >>= _K
|
351
|
+
b >>= _K
|
352
|
+
end
|
353
|
+
|
354
|
+
if b.even?
|
355
|
+
a, b = b, a
|
356
|
+
exchange_flag_2 = true
|
357
|
+
end
|
358
|
+
|
359
|
+
# Initialize
|
360
|
+
u = 1
|
361
|
+
d = a # d == a * u + b * v, (v = 0)
|
362
|
+
u_ = 0
|
363
|
+
d_ = b # d_ == a * u_ + b * v_, (v_ = 1)
|
364
|
+
|
365
|
+
# Remove intial power of 2
|
366
|
+
while d.even?
|
367
|
+
d >>= 1
|
368
|
+
u += b if u.odd?
|
369
|
+
u >>= 1
|
370
|
+
end
|
371
|
+
|
372
|
+
loop do
|
373
|
+
# Substract
|
374
|
+
next_u = u - u_
|
375
|
+
next_d = d - d_ # next_d == a * next_u + b * next_v
|
376
|
+
next_u += b if next_u < 0
|
377
|
+
|
378
|
+
break if 0 == next_d
|
379
|
+
|
380
|
+
# Remove powers of 2
|
381
|
+
while next_d.even?
|
382
|
+
next_d >>= 1
|
383
|
+
next_u += b if next_u.odd?
|
384
|
+
next_u >>= 1
|
385
|
+
end
|
386
|
+
|
387
|
+
if 0 < next_d
|
388
|
+
u = next_u
|
389
|
+
d = next_d
|
390
|
+
else
|
391
|
+
u_ = b - next_u
|
392
|
+
d_ = -next_d
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
v = (d - a * u) / b
|
397
|
+
|
398
|
+
u, v = v, u if exchange_flag_2
|
399
|
+
d <<= _K
|
400
|
+
u, v = v, u - v * _Q
|
401
|
+
u, v = v, u if exchange_flag_1
|
402
|
+
|
403
|
+
return u, v, d
|
404
|
+
end
|
405
|
+
|
406
|
+
# Param:: integer n
|
407
|
+
# positive integer mod which is reratively prime with n
|
408
|
+
# Return:: inverse of n modulo mod. (1 <= inverse < mod)
|
409
|
+
def inverse(n, mod)
|
410
|
+
u, _, d = extended_gcd(n, mod)
|
411
|
+
|
412
|
+
raise ArgumentError, 'n and mod must be reratively prime.' unless 1 == d
|
413
|
+
|
414
|
+
return u % mod
|
415
|
+
end
|
416
|
+
|
417
|
+
# Chinese Remainder Theorem using Algorithm 1.3.12 of [CCANT]
|
418
|
+
# Param:: array of pair integer s.t. [[x_1, m_1], [x_2, m_2], ... , [x_k, m_k]]
|
419
|
+
# m_i are pairwise coprime
|
420
|
+
# Return:: integer x s.t. x = x_i (mod m_i) for all i
|
421
|
+
# and 0 <= x < m_1 * m_2 * ... * m_k
|
422
|
+
# Example: chinese_remainder_theorem([(1,2),(2,3),(3,5)]) return 23
|
423
|
+
def chinese_remainder_theorem(list)
|
424
|
+
x, m = list.shift
|
425
|
+
|
426
|
+
list.each do |xi, mi|
|
427
|
+
u, v, = extended_gcd(m, mi)
|
428
|
+
x = u * m * xi + v * mi * x
|
429
|
+
m *= mi
|
430
|
+
x %= m
|
431
|
+
end
|
432
|
+
|
433
|
+
return x
|
434
|
+
end
|
435
|
+
|
436
|
+
# Param:: integer a, b, a_, b_ s.t. (real number x s.t. a/b <= x <= a_/b_)
|
437
|
+
# Return:: continueed fraction expansion of x
|
438
|
+
# and lower and upper bounds for this next partial quotient.
|
439
|
+
# Format: [[continueed fraction expansion...], [lower, upper]]
|
440
|
+
def continued_fraction(a, b, a_, b_)
|
441
|
+
# Initialize
|
442
|
+
q = q_ = nil
|
443
|
+
rslt = []
|
444
|
+
|
445
|
+
until 0 == b or 0 == b_
|
446
|
+
# Euclidean step
|
447
|
+
q, r = a.divmod(b)
|
448
|
+
|
449
|
+
r_ = a_ - b_ * q
|
450
|
+
if r_ < 0 or b_ <= r_
|
451
|
+
q_ = a_ / b_
|
452
|
+
break
|
453
|
+
end
|
454
|
+
|
455
|
+
rslt.push(q)
|
456
|
+
a , b = b , r
|
457
|
+
a_, b_ = b_, r_
|
458
|
+
end
|
459
|
+
|
460
|
+
if 0 == b
|
461
|
+
return rslt, [] if 0 == b_
|
462
|
+
return rslt, [a_ / b_, INFINITY]
|
463
|
+
end
|
464
|
+
|
465
|
+
return rslt, [a / b, INFINITY] if 0 == b_
|
466
|
+
|
467
|
+
q, q_ = q_, q if q > q_
|
468
|
+
return rslt, [q, q_]
|
469
|
+
end
|
470
|
+
|
471
|
+
# Param:: odd prime p
|
472
|
+
# Return:: primitive root modulo p
|
473
|
+
def primitive_root(p)
|
474
|
+
p_m1 = p - 1
|
475
|
+
check_order = factorize(p_m1)
|
476
|
+
check_order.shift
|
477
|
+
half_p_m1 = p_m1 >> 1
|
478
|
+
check_order.map!{|f| half_p_m1 / f.first}
|
479
|
+
|
480
|
+
a = 1
|
481
|
+
loop do
|
482
|
+
a += 1
|
483
|
+
|
484
|
+
next unless -1 == kronecker_symbol(a, p)
|
485
|
+
check = true
|
486
|
+
check_order.each do |e|
|
487
|
+
if p_m1 == power(a, e, p)
|
488
|
+
check = false
|
489
|
+
break
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
return a if check
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
# Param:: integer n, m
|
498
|
+
# Return:: kronecker symbol (n|m)
|
499
|
+
def kronecker_symbol(n, m)
|
500
|
+
if 0 == m
|
501
|
+
return (-1 == n or 1 == n) ? 1 : 0
|
502
|
+
end
|
503
|
+
|
504
|
+
return 0 if n.even? and m.even?
|
505
|
+
|
506
|
+
m8 = [0, 1, 0, -1, 0, -1, 0, 1]
|
507
|
+
|
508
|
+
# Remove 2's from m
|
509
|
+
count = 0
|
510
|
+
count += 1 while 0 == m[count]
|
511
|
+
m >>= count
|
512
|
+
rslt = count.even? ? 1 : m8[n & 7]
|
513
|
+
|
514
|
+
if m < 0
|
515
|
+
m = -m
|
516
|
+
rslt = -rslt if n < 0
|
517
|
+
end
|
518
|
+
n %= m
|
519
|
+
|
520
|
+
until 0 == n
|
521
|
+
count = 0
|
522
|
+
count += 1 while 0 == n[count]
|
523
|
+
n >>= count
|
524
|
+
rslt *= m8[m & 7] if count.odd?
|
525
|
+
|
526
|
+
# Apply reciprocity
|
527
|
+
rslt = -rslt if 1 == n[1] and 1 == m[1]
|
528
|
+
n, m = m % n, n
|
529
|
+
end
|
530
|
+
|
531
|
+
return (1 == m) ? rslt : 0
|
532
|
+
end
|
533
|
+
alias legendre_symbol kronecker_symbol
|
534
|
+
alias jacobi_symbol kronecker_symbol
|
535
|
+
module_function :jacobi_symbol, :legendre_symbol
|
536
|
+
|
537
|
+
# Param:: integer n
|
538
|
+
# odd prime p
|
539
|
+
# positive integer exp
|
540
|
+
# (if 1 < exp then n must be relatively prime with p)
|
541
|
+
# Return:: the square root of n mod (p ** exp) if exists else nil
|
542
|
+
def mod_sqrt(n, p, exp = 1, return_list = false)
|
543
|
+
if 1 < exp or return_list
|
544
|
+
x = mod_sqrt(n, p)
|
545
|
+
return x unless x
|
546
|
+
return [x] if 1 == exp
|
547
|
+
raise ArgumentError, "if 1 < exp then n must be relatively prime with p" if 0 == x
|
548
|
+
|
549
|
+
rslt = [x] if return_list
|
550
|
+
p_power = p
|
551
|
+
z = extended_lehmer_gcd(x << 1, p)[0]
|
552
|
+
(exp - 1).times do
|
553
|
+
x += (n - x ** 2) / p_power * z % p * p_power
|
554
|
+
p_power *= p
|
555
|
+
rslt.push(x) if return_list
|
556
|
+
end
|
557
|
+
|
558
|
+
return return_list ? rslt : x
|
559
|
+
end
|
560
|
+
|
561
|
+
unless (k = kronecker_symbol(n, p)) == 1
|
562
|
+
return nil if -1 == k
|
563
|
+
return 0
|
564
|
+
end
|
565
|
+
|
566
|
+
if 0 < p & 6
|
567
|
+
return power(n, (p >> 2) + 1, p) if p[1] == 1
|
568
|
+
n %= p
|
569
|
+
x = power(n, (p >> 3) + 1, p)
|
570
|
+
return x if x ** 2 % p == n
|
571
|
+
return x * power(2, p >> 2, p) % p
|
572
|
+
end
|
573
|
+
|
574
|
+
# get q and e s.t. p - 1 == 2**e * q with q odd
|
575
|
+
e = 0
|
576
|
+
q = p - 1
|
577
|
+
e += 1 while 0 == q[e]
|
578
|
+
q >>= e
|
579
|
+
|
580
|
+
# Find generator
|
581
|
+
g = 2
|
582
|
+
g += 1 until -1 == kronecker_symbol(g, p)
|
583
|
+
z = power(g, q, p) # |<z>| == 2 ** e
|
584
|
+
|
585
|
+
# Initialize
|
586
|
+
temp = power(n, q >> 1, p)
|
587
|
+
x = n * temp % p # n ** ((q + 1) / 2) mod p
|
588
|
+
b = x * temp % p # n ** q mod p
|
589
|
+
|
590
|
+
# always
|
591
|
+
# n * b == x ** 2
|
592
|
+
until 1 == b
|
593
|
+
# Find exponent f s.t. b ** (2 ** f) == 1 (mod p)
|
594
|
+
f = 0
|
595
|
+
b_ = b
|
596
|
+
until 1 == b_
|
597
|
+
b_ = b_ ** 2 % p
|
598
|
+
f += 1
|
599
|
+
end
|
600
|
+
|
601
|
+
# Reduce exponent
|
602
|
+
(e - f - 1).times { z = z ** 2 % p }
|
603
|
+
e = f
|
604
|
+
x = x * z % p
|
605
|
+
z = z ** 2 % p
|
606
|
+
b = b * z % p
|
607
|
+
end
|
608
|
+
|
609
|
+
return x
|
610
|
+
end
|
611
|
+
|
612
|
+
# Param:: positive integer d
|
613
|
+
# prime number p (d < p)
|
614
|
+
# Return:: integer solution (x, y) to the Diophantine equation
|
615
|
+
# x ** 2 + d * y ** 2 = p if exists else nil
|
616
|
+
def cornacchia(d, p)
|
617
|
+
return nil if -1 == kronecker_symbol(-d, p)
|
618
|
+
|
619
|
+
# Compute square root
|
620
|
+
x0 = mod_sqrt(-d, p)
|
621
|
+
x0 = p - x0 if x0 <= p >> 1
|
622
|
+
a = p
|
623
|
+
b = x0
|
624
|
+
border = isqrt(p)
|
625
|
+
|
626
|
+
# Euclidean algorithm
|
627
|
+
while border < b
|
628
|
+
a, b = b, a % b
|
629
|
+
end
|
630
|
+
|
631
|
+
# Test solution
|
632
|
+
c, r = (p - b ** 2).divmod(d)
|
633
|
+
return nil if 0 != r
|
634
|
+
|
635
|
+
q = c.square?
|
636
|
+
return nil unless q
|
637
|
+
|
638
|
+
return [b, q]
|
639
|
+
end
|
640
|
+
|
641
|
+
# Param:: d is a negative integer s.t. d = 0 or 1 mod 4
|
642
|
+
# prime number p (|d| < 4p)
|
643
|
+
# Return:: integer solution (x, y) to the Diophantine equation
|
644
|
+
# x ** 2 + |d| * y ** 2 = 4p if exists else nil
|
645
|
+
def modified_cornacchia(d, p)
|
646
|
+
# Case p == 2
|
647
|
+
if 2 == p
|
648
|
+
return nil unless q = (d + 8).square?
|
649
|
+
return q, 1
|
650
|
+
end
|
651
|
+
|
652
|
+
# Test if residue
|
653
|
+
return nil if -1 == kronecker_symbol(d, p)
|
654
|
+
|
655
|
+
# Compute square root
|
656
|
+
x0 = mod_sqrt(d, p)
|
657
|
+
x0 = p - x0 if 0 != x0 - d % 2
|
658
|
+
|
659
|
+
a = p << 1
|
660
|
+
b = x0
|
661
|
+
l = isqrt(p << 2)
|
662
|
+
|
663
|
+
# Euclidean algorithm
|
664
|
+
a, b = b, a % b while b < l
|
665
|
+
|
666
|
+
# Test solution
|
667
|
+
c, r = (4 * p - b**2).divmod(-d)
|
668
|
+
return nil if 0 != r
|
669
|
+
return nil unless q = c.square?
|
670
|
+
return b, q
|
671
|
+
end
|
672
|
+
|
673
|
+
# Integer Square Root
|
674
|
+
# Param:: positive integer n
|
675
|
+
# Return:: integer part of the square root of n
|
676
|
+
# i.e. the number m s.t. m ** 2 <= n < (m + 1) ** 2
|
677
|
+
def isqrt(n)
|
678
|
+
x = 1 << ((ilog2(n) >> 1) + 1)
|
679
|
+
|
680
|
+
loop do
|
681
|
+
# Newtonian step
|
682
|
+
next_x = (x + n / x) >> 1
|
683
|
+
return x if x <= next_x
|
684
|
+
|
685
|
+
x = next_x
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
# Integer Power Root
|
690
|
+
# Param:: positive integer n
|
691
|
+
# positive integer pow
|
692
|
+
# Return:: integer part of the power root of n
|
693
|
+
# i.e. the number m s.t. m ** pow <= n < (m + 1) ** pow
|
694
|
+
# if return_power is true then return n ** pow
|
695
|
+
def iroot(n, pow, return_power = false)
|
696
|
+
# get integer e s.t. (2 ** (e - 1)) ** pow <= n < (2 ** e) ** pow
|
697
|
+
e = ilog2(n) / pow + 1 # == Rational(ilog2(n) + 1, pow).ceil
|
698
|
+
|
699
|
+
x = 1 << e # == 2 ** e
|
700
|
+
z = nil
|
701
|
+
q = n >> (e * (pow - 1)) # == n / (x ** (pow - 1))
|
702
|
+
|
703
|
+
loop do
|
704
|
+
# Newtonian step
|
705
|
+
x += (q - x) / pow
|
706
|
+
z = x ** (pow - 1)
|
707
|
+
q = n / z
|
708
|
+
|
709
|
+
break if x <= q
|
710
|
+
end
|
711
|
+
|
712
|
+
return x, x * z if return_power
|
713
|
+
return x
|
714
|
+
end
|
715
|
+
|
716
|
+
# Param:: positive integer n > 1
|
717
|
+
# Return:: p if n is of the form p^k with p prime else false
|
718
|
+
def prime_power?(n)
|
719
|
+
if n.even?
|
720
|
+
return n.power_of?(2) ? 2 : false
|
721
|
+
end
|
722
|
+
|
723
|
+
p = n
|
724
|
+
loop do
|
725
|
+
rslt, witness = miller_rabin(p, 10, true)
|
726
|
+
if rslt
|
727
|
+
# Final test
|
728
|
+
redo unless prime?(p)
|
729
|
+
return n.power_of?(p) ? p : false
|
730
|
+
end
|
731
|
+
|
732
|
+
d = lehmer_gcd(power(witness, p, p) - witness, p)
|
733
|
+
return false if 1 == d or d == p
|
734
|
+
p = d
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
# Param:: positive integer n
|
739
|
+
# boolean largest_exp
|
740
|
+
# Return:: integer x, k s.t. n == x ** k
|
741
|
+
# if n == 1 then x, k == 1, 1
|
742
|
+
# (2 <= k if exist else x, k == n, 1)
|
743
|
+
# if largest_exp is true then return largest k
|
744
|
+
def power_detection(n, largest_exp = true)
|
745
|
+
x, k = n, 1
|
746
|
+
|
747
|
+
limit = ilog2(n)
|
748
|
+
(2..limit).each_prime do |exp|
|
749
|
+
break if limit < exp
|
750
|
+
|
751
|
+
root, pow = iroot(n, exp, true)
|
752
|
+
if pow == n
|
753
|
+
return root, exp unless largest_exp
|
754
|
+
|
755
|
+
n = x = root
|
756
|
+
k *= exp
|
757
|
+
limit = ilog2(n)
|
758
|
+
redo
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
return x, k
|
763
|
+
end
|
764
|
+
|
765
|
+
# Param:: positive integer n
|
766
|
+
# Return:: integer e s.t. 2 ** e <= n < 2 ** (e + 1)
|
767
|
+
def ilog2(n)
|
768
|
+
bits = (n.size - BASE_BYTE) << 3
|
769
|
+
return bits + Bisect.bisect_right(powers_of_2, n >> bits) - 1
|
770
|
+
end
|
771
|
+
|
772
|
+
$powers_of_2 = nil
|
773
|
+
# Return:: array power of 2 s.t. [1, 2, 4, 8, 16, 32, ...]
|
774
|
+
def powers_of_2
|
775
|
+
unless $powers_of_2
|
776
|
+
$powers_of_2 = [1]
|
777
|
+
((BASE_BYTE << 3) - 1).times do |i|
|
778
|
+
$powers_of_2[i + 1] = $powers_of_2[i] << 1
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
return $powers_of_2
|
783
|
+
end
|
784
|
+
|
785
|
+
# Param:: positive integer m
|
786
|
+
# Return:: continued fraction of sqrt m
|
787
|
+
# [n, [a_1, a_2, ..., a_i]] s.t. sqrt m = n + frac{1}{a1 + frac{1}{a2 + ...}}
|
788
|
+
# n is integer part and a_1...a_i are repeating part
|
789
|
+
# Example::continued_fraction_of_sqrt(2) -> [1, [2]]
|
790
|
+
# continued_fraction_of_sqrt(3) -> [1, [1, 2]]
|
791
|
+
# continued_fraction_of_sqrt(4) -> [2, []]
|
792
|
+
def continued_fraction_of_sqrt(m)
|
793
|
+
if r = m.square?
|
794
|
+
return [r, []]
|
795
|
+
end
|
796
|
+
|
797
|
+
# Initialize
|
798
|
+
rslt = [isqrt(m), []]
|
799
|
+
|
800
|
+
a = 1
|
801
|
+
_B = b = -rslt[0]
|
802
|
+
c = 1
|
803
|
+
|
804
|
+
loop do
|
805
|
+
# inverse
|
806
|
+
a, b, c = a * c, -b * c, a**2 * m - b**2
|
807
|
+
|
808
|
+
# reduction
|
809
|
+
t = gcd(gcd(a, b), c)
|
810
|
+
a /= t
|
811
|
+
b /= t
|
812
|
+
c /= t
|
813
|
+
|
814
|
+
t = (isqrt(m * a ** 2) + b) / c
|
815
|
+
rslt[1].push(t)
|
816
|
+
b -= c * t
|
817
|
+
|
818
|
+
return rslt if 1 == a and _B == b and 1 == c
|
819
|
+
end
|
820
|
+
end
|
821
|
+
|
822
|
+
# Param:: positive non-square integer n
|
823
|
+
# Return:: least positive integer a, b s.t. a**2 - n * b**2 == 1 or -1
|
824
|
+
def bhaskara_brouncker(n)
|
825
|
+
t = sqrt = isqrt(n)
|
826
|
+
u, u1 = 0, sqrt
|
827
|
+
c, c1 = 1, n - sqrt ** 2
|
828
|
+
a, a1 = 1, sqrt
|
829
|
+
b, b1 = 0, 1
|
830
|
+
|
831
|
+
until 1 == c1
|
832
|
+
t = (sqrt + u1) / c1
|
833
|
+
u1, u = t * c1 - u1, u1
|
834
|
+
c1, c = c + t * (u - u1), c1
|
835
|
+
a1, a = a + t * a1, a1
|
836
|
+
b1, b = b + t * b1, b1
|
837
|
+
end
|
838
|
+
|
839
|
+
return a1, b1
|
840
|
+
end
|
841
|
+
|
842
|
+
# primitive Pythagorean number
|
843
|
+
# Param:: integer max_c
|
844
|
+
# Return:: array of primitive Pythagorean numbers s.t. [[a, b, c], ...]
|
845
|
+
# a**2 + b**2 == c**2 and a < b < c <= max_c
|
846
|
+
def pythagorean(max_c)
|
847
|
+
return Enumerator.new(self, :pythagorean, max_c) unless block_given?
|
848
|
+
return [] if max_c <= 4
|
849
|
+
|
850
|
+
1.upto(isqrt(max_c - 1)) do |m|
|
851
|
+
mm = m ** 2
|
852
|
+
s = m.even? ? 1 : 2
|
853
|
+
s.step(m, 2) do |n|
|
854
|
+
next unless gcd(m, n) == 1
|
855
|
+
|
856
|
+
nn = n ** 2
|
857
|
+
c = mm + nn
|
858
|
+
break if max_c < c
|
859
|
+
|
860
|
+
a = mm - nn
|
861
|
+
b = (m * n) << 1
|
862
|
+
a, b = b, a if b < a
|
863
|
+
|
864
|
+
yield [a, b, c]
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
# Param:: positive integer n
|
870
|
+
# Return:: the value of moebius function for n
|
871
|
+
def mobius(n)
|
872
|
+
return 1 if 1 == n
|
873
|
+
return 0 unless n.squarefree?
|
874
|
+
return factorize(n).size.odd? ? -1 : 1
|
875
|
+
end
|
876
|
+
alias moebius mobius
|
877
|
+
|
878
|
+
# Param:: Integer base (2 <= base < p)
|
879
|
+
# Integer m (2 <= m < p)
|
880
|
+
# prime p
|
881
|
+
# Return:: Integer e s.t. base ** e == m (mod p)
|
882
|
+
def dlog_rho(base, m, p)
|
883
|
+
raise NotImplementedError
|
884
|
+
end
|
885
|
+
|
886
|
+
# Param:: Integer base (2 <= base < p)
|
887
|
+
# Integer m (2 <= m < p)
|
888
|
+
# prime p
|
889
|
+
# Return:: Integer e s.t. base ** e == m (mod p)
|
890
|
+
def dlog_icm(base, m, p)
|
891
|
+
# Select factor base
|
892
|
+
|
893
|
+
# Select smooth elements on factor base
|
894
|
+
|
895
|
+
# Solve DL of factor base for base
|
896
|
+
|
897
|
+
# Find smooth element h s.t. h == m * base ** f where f is integer
|
898
|
+
|
899
|
+
# Solve DL of h for factor base
|
900
|
+
|
901
|
+
raise NotImplementedError
|
902
|
+
end
|
903
|
+
end
|