abst 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|