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
@@ -0,0 +1,483 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Abst
|
4
|
+
module_function
|
5
|
+
|
6
|
+
class MPQS
|
7
|
+
@@kronecker_table = nil
|
8
|
+
@@fixed_factor_base = [-1, 2, 3, 5, 7, 11, 13].freeze
|
9
|
+
@@fixed_factor_base_log = ([nil] + @@fixed_factor_base[1..-1].map {|p| Math.log(p)}).freeze
|
10
|
+
@@mpqs_parameter_map = [[100,20]] * 9 + [
|
11
|
+
[100, 20], # 9 -digits
|
12
|
+
[100, 21], # 10
|
13
|
+
[100, 22], # 11
|
14
|
+
[100, 24], # 12
|
15
|
+
[100, 26], # 13
|
16
|
+
[100, 29], # 14
|
17
|
+
[100, 32], # 15
|
18
|
+
[200, 35], # 16
|
19
|
+
[300, 40], # 17
|
20
|
+
[300, 60], # 18
|
21
|
+
[300, 80], # 19
|
22
|
+
[300, 100], # 20
|
23
|
+
[300, 100], # 21
|
24
|
+
[300, 120], # 22
|
25
|
+
[300, 140], # 23
|
26
|
+
[600, 160], # 24
|
27
|
+
[900, 180], # 25
|
28
|
+
[1000, 200], # 26
|
29
|
+
[1000, 220], # 27
|
30
|
+
[2000, 240], # 28
|
31
|
+
[2000, 260], # 29
|
32
|
+
[2000, 325], # 30
|
33
|
+
[2000, 355], # 31
|
34
|
+
[2000, 375], # 32
|
35
|
+
[3000, 400], # 33
|
36
|
+
[2000, 425], # 34
|
37
|
+
[2000, 550], # 35
|
38
|
+
[3000, 650], # 36
|
39
|
+
[5000, 750], # 37
|
40
|
+
[4000, 850], # 38
|
41
|
+
[4000, 950], # 39
|
42
|
+
[5000, 1000], # 40
|
43
|
+
[14000, 1150], # 41
|
44
|
+
[15000, 1300], # 42
|
45
|
+
[15000, 1600], # 43
|
46
|
+
[15000, 1900], # 44
|
47
|
+
[15000, 2200], # 45
|
48
|
+
[20000, 2500], # 46
|
49
|
+
[25000, 2500], # 47
|
50
|
+
[27500, 2700], # 48
|
51
|
+
[30000, 2800], # 49
|
52
|
+
[35000, 2900], # 50
|
53
|
+
[40000, 3000], # 51
|
54
|
+
[50000, 3200], # 52
|
55
|
+
[50000, 3500]] # 53
|
56
|
+
|
57
|
+
#@@proc_time = Hash.new(0)
|
58
|
+
#def self.get_times
|
59
|
+
# return @@proc_time
|
60
|
+
#end
|
61
|
+
|
62
|
+
def self.kronecker_table
|
63
|
+
unless @@kronecker_table
|
64
|
+
target = [3, 5, 7, 11, 13]
|
65
|
+
@@kronecker_table = 4.times.map{Hash.new}
|
66
|
+
(17..3583).each_prime do |p|
|
67
|
+
k = target.map {|b| Abst.kronecker_symbol(p, b)}
|
68
|
+
@@kronecker_table[(p & 6) >> 1][k] ||= p
|
69
|
+
end
|
70
|
+
@@kronecker_table[0][[1, 1, 1, 1, 1]] = 1
|
71
|
+
end
|
72
|
+
|
73
|
+
return @@kronecker_table
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize(n, thread_num)
|
77
|
+
#@@proc_time[:init] -= Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
78
|
+
@original_n = n
|
79
|
+
@thread_num = [thread_num, 1].max
|
80
|
+
@big_prime = {}
|
81
|
+
@big_prime_mutex = Mutex.new
|
82
|
+
|
83
|
+
decide_multiplier(n)
|
84
|
+
decide_parameter
|
85
|
+
select_factor_base
|
86
|
+
some_precomputations
|
87
|
+
|
88
|
+
@d = Abst.isqrt(Abst.isqrt(@n >> 1) / @sieve_range)
|
89
|
+
@d -= (@d & 3) + 1
|
90
|
+
|
91
|
+
@matrix_left = []
|
92
|
+
@matrix_right = []
|
93
|
+
@mask = 1
|
94
|
+
@check_list = Array.new(@factor_base_size)
|
95
|
+
#@@proc_time[:init] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
96
|
+
end
|
97
|
+
|
98
|
+
def decide_multiplier(n)
|
99
|
+
t = [3, 5, 7, 11, 13].map {|p| Abst.kronecker_symbol(n, p)}
|
100
|
+
multiplier = self.class.kronecker_table[(n & 6) >> 1][t]
|
101
|
+
@n = n * multiplier
|
102
|
+
end
|
103
|
+
|
104
|
+
def decide_parameter
|
105
|
+
digit = Math.log(@n, 10).floor
|
106
|
+
parameter = @@mpqs_parameter_map[digit] ? @@mpqs_parameter_map[digit].dup : @@mpqs_parameter_map.last.dup
|
107
|
+
parameter[0] = (parameter[0] * 2).floor
|
108
|
+
@sieve_range, @factor_base_size = parameter
|
109
|
+
@sieve_range_2 = @sieve_range << 1
|
110
|
+
end
|
111
|
+
|
112
|
+
def select_factor_base
|
113
|
+
@factor_base = @@fixed_factor_base.dup
|
114
|
+
(17..INFINITY).each_prime do |p|
|
115
|
+
if 1 == Abst.kronecker_symbol(@n, p)
|
116
|
+
@factor_base.push(p)
|
117
|
+
break if @factor_base_size <= @factor_base.size
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def some_precomputations
|
123
|
+
size = @@fixed_factor_base_log.size
|
124
|
+
@factor_base_log = @@fixed_factor_base_log + @factor_base[size..-1].map {|p| Math.log(p)}
|
125
|
+
|
126
|
+
@power_limit = Array.new(@factor_base_size)
|
127
|
+
@mod_sqrt_cache = Array.new(@factor_base_size)
|
128
|
+
2.upto(@factor_base_size - 1) do |i|
|
129
|
+
p = @factor_base[i]
|
130
|
+
@power_limit[i] = (@factor_base_log.last / @factor_base_log[i]).floor
|
131
|
+
@mod_sqrt_cache[i] = [nil] + Abst.mod_sqrt(@n, p, @power_limit[i], true)
|
132
|
+
end
|
133
|
+
|
134
|
+
target = Math.log(@n) / 2 + Math.log(@sieve_range) - 1
|
135
|
+
@closenuf = target - 1.8 * Math.log(@factor_base.last)
|
136
|
+
end
|
137
|
+
|
138
|
+
def find_factor
|
139
|
+
if 1 == @thread_num
|
140
|
+
find_factor_single_thread
|
141
|
+
else
|
142
|
+
sieve_thread_num = [@thread_num - 2, 1].max
|
143
|
+
find_factor_multi_thread(sieve_thread_num)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def find_factor_single_thread
|
148
|
+
r_list = []
|
149
|
+
factorization = []
|
150
|
+
big_prime_sup = []
|
151
|
+
|
152
|
+
loop do
|
153
|
+
# Create polynomial
|
154
|
+
a, b, c, d = next_poly
|
155
|
+
|
156
|
+
# Sieve
|
157
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
158
|
+
sieve_rslt = sieve(a, b, c, d)
|
159
|
+
#@@proc_time[:sieve] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
160
|
+
next if sieve_rslt.empty?
|
161
|
+
f, big, r = eliminate_big_primes(sieve_rslt)
|
162
|
+
next if f.empty?
|
163
|
+
|
164
|
+
# Gaussian elimination
|
165
|
+
factorization += f
|
166
|
+
r_list += r
|
167
|
+
big_prime_sup += big
|
168
|
+
|
169
|
+
#@@proc_time[:gaussian] -= Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
170
|
+
eliminated = gaussian_elimination(f)
|
171
|
+
#@@proc_time[:gaussian] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
172
|
+
eliminated.each do |row|
|
173
|
+
x = y = 1
|
174
|
+
f = Array.new(@factor_base_size, 0)
|
175
|
+
factorization.size.times do |i|
|
176
|
+
next if row[i] == 0
|
177
|
+
x = x * r_list[i] % @n
|
178
|
+
f = f.zip(factorization[i]).map{|e1, e2| e1 + e2}
|
179
|
+
y = y * big_prime_sup[i] % @n
|
180
|
+
end
|
181
|
+
|
182
|
+
2.upto(@factor_base_size - 1) do |i|
|
183
|
+
y = y * Abst.power(@factor_base[i], f[i] >> 1, @n) % @n
|
184
|
+
end
|
185
|
+
y = (y << (f[1] >> 1)) % @n
|
186
|
+
|
187
|
+
z = Abst.lehmer_gcd(x - y, @original_n)
|
188
|
+
return z if 1 < z and z < @original_n
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def find_factor_multi_thread(sieve_thread_num)
|
194
|
+
queue_poly = SizedQueue.new(sieve_thread_num)
|
195
|
+
queue_sieve_rslt = SizedQueue.new(sieve_thread_num)
|
196
|
+
|
197
|
+
# Create thread make polynomials
|
198
|
+
th_make_poly = Thread.new do
|
199
|
+
loop { queue_poly.push next_poly }
|
200
|
+
end
|
201
|
+
|
202
|
+
thg_sieve = ThreadGroup.new
|
203
|
+
# Create threads for sieve
|
204
|
+
sieve_thread_num.times do
|
205
|
+
thread = Thread.new do
|
206
|
+
loop do
|
207
|
+
a, b, c, d = queue_poly.shift
|
208
|
+
|
209
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
210
|
+
# Sieve
|
211
|
+
rslt = sieve(a, b, c, d)
|
212
|
+
#@@proc_time[:sieve] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
213
|
+
|
214
|
+
queue_sieve_rslt.push rslt unless rslt.empty?
|
215
|
+
end
|
216
|
+
end
|
217
|
+
thg_sieve.add thread
|
218
|
+
end
|
219
|
+
|
220
|
+
r_list = []
|
221
|
+
factorization = []
|
222
|
+
big_prime_sup = []
|
223
|
+
loop do
|
224
|
+
sieve_rslt = queue_sieve_rslt.shift
|
225
|
+
next if sieve_rslt.empty?
|
226
|
+
|
227
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
228
|
+
f, big, r = eliminate_big_primes(sieve_rslt)
|
229
|
+
next if f.empty?
|
230
|
+
|
231
|
+
#p [factorization.size, r_list.size, big_prime_sup.size]
|
232
|
+
# Gaussian elimination
|
233
|
+
factorization.concat f
|
234
|
+
r_list.concat r
|
235
|
+
big_prime_sup.concat big
|
236
|
+
|
237
|
+
eliminated = gaussian_elimination(f)
|
238
|
+
#@@proc_time[:gaussian] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
239
|
+
eliminated.each do |row|
|
240
|
+
x = y = 1
|
241
|
+
f = Array.new(@factor_base_size, 0)
|
242
|
+
factorization.size.times do |i|
|
243
|
+
next if row[i] == 0
|
244
|
+
x = x * r_list[i] % @n
|
245
|
+
f = f.zip(factorization[i]).map{|e1, e2| e1 + e2}
|
246
|
+
y = y * big_prime_sup[i] % @n
|
247
|
+
end
|
248
|
+
|
249
|
+
2.upto(@factor_base_size - 1) do |i|
|
250
|
+
y = y * Abst.power(@factor_base[i], f[i] >> 1, @n) % @n
|
251
|
+
end
|
252
|
+
y = (y << (f[1] >> 1)) % @n
|
253
|
+
|
254
|
+
z = Abst.lehmer_gcd(x - y, @original_n)
|
255
|
+
return z if 1 < z and z < @original_n
|
256
|
+
end
|
257
|
+
end
|
258
|
+
ensure
|
259
|
+
thg_sieve.list.each {|th| th.kill}
|
260
|
+
th_make_poly.kill
|
261
|
+
end
|
262
|
+
|
263
|
+
def eliminate_big_primes(sieve_rslt)
|
264
|
+
sieve_rslt_with_big_prime = sieve_rslt.select{|f, re, d, r| 1 != re}
|
265
|
+
sieve_rslt.select!{|f, re, d, r| 1 == re}
|
266
|
+
|
267
|
+
temp_f = sieve_rslt.map(&:first)
|
268
|
+
temp_r = sieve_rslt.map(&:last)
|
269
|
+
temp_big = sieve_rslt.map{|f, re, d, r| d}
|
270
|
+
sieve_rslt_with_big_prime.each do |f, re, d, r|
|
271
|
+
unless @big_prime[re]
|
272
|
+
@big_prime[re] = [f, r, d]
|
273
|
+
else
|
274
|
+
temp_f << (@big_prime[re][0].zip(f).map{|e1, e2| e1 + e2})
|
275
|
+
temp_big << (re * d * @big_prime[re][2])
|
276
|
+
temp_r << (r * @big_prime[re][1])
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
return temp_f, temp_big, temp_r
|
281
|
+
end
|
282
|
+
|
283
|
+
# Return:: a, b,c
|
284
|
+
def next_poly
|
285
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
286
|
+
@d = d = next_d
|
287
|
+
a = d ** 2
|
288
|
+
h1 = Abst.power(@n, (d >> 2) + 1, d)
|
289
|
+
h2 = ((@n - h1 ** 2) / d) * Abst.extended_lehmer_gcd(h1 << 1, d)[0] % d
|
290
|
+
b = h1 + h2 * d
|
291
|
+
b = a - b if b.even?
|
292
|
+
c = ((b ** 2 - @n) >> 2) / a
|
293
|
+
|
294
|
+
#@@proc_time[:make_poly_2] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
295
|
+
return a, b, c, d
|
296
|
+
end
|
297
|
+
|
298
|
+
def next_d
|
299
|
+
d = @d + 4
|
300
|
+
if d < Abst.primes_list.last
|
301
|
+
plist = Abst.primes_list
|
302
|
+
(d..plist.last).each_prime do |p|
|
303
|
+
return p if p[1] == 1 and Abst.kronecker_symbol(@n, p) == 1
|
304
|
+
end
|
305
|
+
d += 4
|
306
|
+
end
|
307
|
+
|
308
|
+
loop do
|
309
|
+
return d if Abst.kronecker_symbol(@n, d) == 1 and Abst.power(@n, d >> 1, d) == 1
|
310
|
+
d += 4
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def sieve(a, b, c, d)
|
315
|
+
a2 = a << 1
|
316
|
+
lo = -(b / a2) - @sieve_range + 1
|
317
|
+
|
318
|
+
sieve = Array.new(@sieve_range_2, 0)
|
319
|
+
|
320
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
321
|
+
# Sieve by 2
|
322
|
+
# 0.upto(@sieve_range_2 - 1) do |i|
|
323
|
+
# count = 1
|
324
|
+
# count += 1 while sieve[i][2][count] == 0
|
325
|
+
# sieve[i][1] += @factor_base_log[1] * count
|
326
|
+
# end
|
327
|
+
|
328
|
+
# Sieve by 3, 5, 7, 11, ...
|
329
|
+
# 2.upto(@factor_base_size - 1) do |i|
|
330
|
+
4.upto(@factor_base_size - 1) do |i|
|
331
|
+
p = @factor_base[i]
|
332
|
+
a_inverse = Abst.extended_lehmer_gcd(a2, p ** @power_limit[i])[0]
|
333
|
+
pe = 1
|
334
|
+
e = 1
|
335
|
+
|
336
|
+
power_limit_i = @power_limit[i]
|
337
|
+
factor_base_log_i = @factor_base_log[i]
|
338
|
+
mod_sqrt_cache_i = @mod_sqrt_cache[i]
|
339
|
+
while e <= power_limit_i
|
340
|
+
pe *= p
|
341
|
+
sqrt = mod_sqrt_cache_i[e]
|
342
|
+
|
343
|
+
t = sqrt
|
344
|
+
s = ((t - b) * a_inverse - lo) % pe
|
345
|
+
s.step(@sieve_range_2 - 1, pe) do |j|
|
346
|
+
sieve[j] += factor_base_log_i
|
347
|
+
end
|
348
|
+
|
349
|
+
t = pe - sqrt
|
350
|
+
s = ((t - b) * a_inverse - lo) % pe
|
351
|
+
s.step(@sieve_range_2 - 1, pe) do |j|
|
352
|
+
sieve[j] += factor_base_log_i
|
353
|
+
end
|
354
|
+
|
355
|
+
e += 1
|
356
|
+
end
|
357
|
+
end
|
358
|
+
#@@proc_time[:sieve_a] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
359
|
+
|
360
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
361
|
+
# select trial division target
|
362
|
+
td_target = []
|
363
|
+
sieve.each.with_index do |sum_of_log, idx|
|
364
|
+
if @closenuf < sum_of_log
|
365
|
+
x = idx + lo
|
366
|
+
t = a * x
|
367
|
+
td_target.push([(t << 1) + b, (t + b) * x + c])
|
368
|
+
end
|
369
|
+
end
|
370
|
+
#@@proc_time[:sieve_slct] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
371
|
+
|
372
|
+
# trial division on factor base
|
373
|
+
rslt = []
|
374
|
+
#temp = Time.now.to_i + Time.now.usec.to_f / 10 ** 6
|
375
|
+
td_target.each do |r, s|
|
376
|
+
f, re = trial_division_on_factor_base(s, @factor_base)
|
377
|
+
f[1] += 2
|
378
|
+
rslt.push [f, re, d, r]
|
379
|
+
end
|
380
|
+
#@@proc_time[:sieve_td] += Time.now.to_i + Time.now.usec.to_f / 10 ** 6 - temp
|
381
|
+
|
382
|
+
return rslt
|
383
|
+
end
|
384
|
+
|
385
|
+
def gaussian_elimination(m)
|
386
|
+
elim_start = @matrix_left.size
|
387
|
+
temp = Array.new(m.size)
|
388
|
+
m.size.times do |i|
|
389
|
+
temp[i] = @mask
|
390
|
+
@mask <<= 1
|
391
|
+
end
|
392
|
+
rslt = @matrix_right += temp
|
393
|
+
m = @matrix_left.concat(m.map{|row| row.reverse_each.map{|i| i[0]}})
|
394
|
+
|
395
|
+
height = m.size
|
396
|
+
width = @factor_base_size
|
397
|
+
|
398
|
+
i = 0
|
399
|
+
width.times do |j|
|
400
|
+
unless @check_list[j]
|
401
|
+
# Find non-zero entry
|
402
|
+
row = nil
|
403
|
+
elim_start.upto(height - 1) do |i2|
|
404
|
+
if 1 == m[i2][j]
|
405
|
+
row = i2
|
406
|
+
break
|
407
|
+
end
|
408
|
+
end
|
409
|
+
next unless row
|
410
|
+
|
411
|
+
@check_list[j] = row
|
412
|
+
|
413
|
+
# Swap?
|
414
|
+
if i < row
|
415
|
+
m.insert(i, m.delete_at(row))
|
416
|
+
rslt.insert(i, rslt.delete_at(row))
|
417
|
+
end
|
418
|
+
|
419
|
+
elim_start += 1
|
420
|
+
end
|
421
|
+
|
422
|
+
# Eliminate
|
423
|
+
m_i = m[i]
|
424
|
+
(row ? (row + 1) : elim_start).upto(height - 1) do |i2|
|
425
|
+
next if m[i2][j] == 0
|
426
|
+
|
427
|
+
m_i2 = m[i2]
|
428
|
+
(j + 1).upto(width - 1) do |j2|
|
429
|
+
m_i2[j2] ^= 1 if 1 == m_i[j2]
|
430
|
+
end
|
431
|
+
rslt[i2] ^= rslt[i]
|
432
|
+
end
|
433
|
+
|
434
|
+
i += 1
|
435
|
+
end
|
436
|
+
|
437
|
+
t = height - i
|
438
|
+
m.pop(t)
|
439
|
+
return rslt.pop(t)
|
440
|
+
end
|
441
|
+
|
442
|
+
def trial_division_on_factor_base(n, factor_base)
|
443
|
+
factor = Array.new(@factor_base_size, 0)
|
444
|
+
if n < 0
|
445
|
+
factor[0] = 1
|
446
|
+
n = -n
|
447
|
+
end
|
448
|
+
|
449
|
+
div_count = 1
|
450
|
+
div_count += 1 while n[div_count] == 0
|
451
|
+
factor[1] = div_count
|
452
|
+
n >>= div_count
|
453
|
+
|
454
|
+
i = 2
|
455
|
+
while i < @factor_base_size
|
456
|
+
d = factor_base[i]
|
457
|
+
q, r = n.divmod(d)
|
458
|
+
if 0 == r
|
459
|
+
n = q
|
460
|
+
div_count = 1
|
461
|
+
loop do
|
462
|
+
q, r = n.divmod(d)
|
463
|
+
break unless 0 == r
|
464
|
+
|
465
|
+
n = q
|
466
|
+
div_count += 1
|
467
|
+
end
|
468
|
+
|
469
|
+
factor[i] = div_count
|
470
|
+
end
|
471
|
+
|
472
|
+
i += 1
|
473
|
+
end
|
474
|
+
|
475
|
+
return factor, n
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def mpqs(n, thread_num = Abst::THREAD_NUM)
|
480
|
+
mpqs = MPQS.new(n, thread_num)
|
481
|
+
return mpqs.find_factor
|
482
|
+
end
|
483
|
+
end
|