abst 0.2.0

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