Skip to content

Commit 2c439f5

Browse files
committed
Update trial division algorithm and apply Euler's criterion for prime selection in QS algorithm.
1 parent f8a109c commit 2c439f5

File tree

1 file changed

+48
-103
lines changed

1 file changed

+48
-103
lines changed

qs.sage

Lines changed: 48 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2,114 +2,59 @@
22
# Code taken from https://facthacks.cr.yp.to/facthacks.sage
33
# FactHacks is joint work by Daniel J. Bernstein, Nadia Heninger, and Tanja Lange.
44

5-
from gmpy2 import gcd, isqrt, is_prime, next_prime
5+
from gmpy2 import gcd, isqrt, is_prime, next_prime, is_power
66
import itertools
7-
8-
9-
def prebuilt_params(bits):
10-
"""
11-
Bounds estimation
12-
borrowed from msieve/mpqs.c
13-
"""
14-
if bits <= 64:
15-
return [100, 40, 1 * 65536]
16-
if bits <= 128:
17-
return [450, 40, 1 * 65536]
18-
if bits <= 183:
19-
return [2000, 40, 1 * 65536]
20-
if bits <= 200:
21-
return [3000, 50, 1 * 65536]
22-
if bits <= 212:
23-
return [5400, 50, 3 * 65536]
24-
if bits <= 233:
25-
return [10000, 100, 3 * 65536]
26-
if bits <= 249:
27-
return [27000, 100, 3 * 65536]
28-
if bits <= 266:
29-
return [50000, 100, 3 * 65536]
30-
if bits <= 283:
31-
return [55000, 80, 3 * 65536]
32-
if bits <= 298:
33-
return [60000, 80, 9 * 65536]
34-
if bits <= 315:
35-
return [80000, 150, 9 * 65536]
36-
if bits <= 332:
37-
return [100000, 150, 9 * 65536]
38-
if bits <= 348:
39-
return [140000, 150, 9 * 65536]
40-
if bits <= 363:
41-
return [210000, 150, 13 * 65536]
42-
if bits <= 379:
43-
return [300000, 150, 17 * 65536]
44-
if bits <= 395:
45-
return [400000, 150, 21 * 65536]
46-
if bits <= 415:
47-
return [500000, 150, 25 * 65536] # beyond this point you're crazy
48-
if bits <= 440:
49-
return [700000, 150, 33 * 65536]
50-
if bits <= 465:
51-
return [900000, 150, 50 * 65536]
52-
if bits <= 490:
53-
return [1100000, 150, 75 * 65536]
54-
if bits <= 512:
55-
return [1300000, 150, 100 * 65536]
56-
return [1300000, 150, 100 * 65536]
57-
58-
59-
def trial_division_minus_even_powers(n,P):
60-
a = []
61-
r = n
62-
pw = 0
63-
if r & 1 == 0:
64-
while r & 1 == 0:
7+
import sys
8+
9+
10+
def trial_division_minus_even_powers(n, P):
11+
a, r, pw = [], n, 0
12+
while (r & 1) == 0:
13+
pw += 1
14+
r >>= 1
15+
if (pw & 1 == 1): a.append(2)
16+
for i in range(1, len(P)):
17+
pw, p = 0, P[i]
18+
while (r % p) == 0:
6519
pw += 1
66-
r >>= 1
67-
if pw & 1 != 0:
68-
if 2 not in a:
69-
a.append(2)
70-
l=len(P)
71-
i=0
72-
while (i < l) and (P[i] <= n):
73-
if r % P[i] == 0:
74-
pw = 0
75-
while r % P[i] == 0:
76-
pw += 1
77-
r //= P[i]
78-
if pw & 1 != 0:
79-
if P[i] not in a:
80-
a.append(P[i])
81-
else:
82-
i += 1
83-
return [a,r,n]
20+
r //= p
21+
if (pw & 1 == 1): a.append(p)
22+
return a, r, n
8423

8524

8625
def minifactor(x, P):
87-
if not is_prime(x) and not is_square(x):
88-
p = trial_division_minus_even_powers(x, P)
89-
if p[1] == 1: # if 1 x is B-smooth
90-
return p
91-
92-
93-
def QS(N,differences,y):
94-
i2N = isqrt(N)
95-
X = [int(a * a - N) for a in range(i2N + 1, i2N + differences)]
96-
P = list(primes(2, y))
97-
F = list(filter(None, map(minifactor, X, itertools.repeat(P, len(X)))))
98-
99-
M = matrix(GF(2), len(F), len(P), lambda i, j:P[j] in F[i][0])
100-
for K in M.left_kernel().basis():
101-
I = [f for f, k in zip(F, K) if k == 1]
102-
x = product([int(isqrt(f[2] + N)) for f in I])
103-
y = isqrt(product([f[2] for f in I]))
104-
g0, g1 = gcd(N, x - y), gcd(N, x + y)
105-
if N > g0 > 1 or N > g1 > 1:
106-
return g0, g1
107-
26+
p = trial_division_minus_even_powers(x, P)
27+
if p[1] == 1: return p # if 1 then x is b-smooth
28+
29+
30+
def eulercriterion(N, p):
31+
return pow(N, (p - 1) >> 1, p)
32+
33+
34+
def QS(N, B1):
35+
sys.stderr.write("max prime: %d\n" % B1)
36+
P = [p for p in list(primes(2, B1)) if eulercriterion(N, p) == 1]
37+
i2n, B2, X, offset, lp = isqrt(N), 65536, [], 0, len(P)
38+
while True:
39+
S = i2n + offset
40+
sys.stderr.write("Relations matix: [cols: %d x rows: %d]\n" % (lp, offset + B2))
41+
X += [int(a * a - N) for a in range(S + 1, S + B2)]
42+
X = list(filter(lambda x:not is_power(x) and not is_prime(x), X))
43+
F = list(filter(None, map(minifactor, X, itertools.repeat(P, len(X)))))
44+
M = matrix(GF(2), len(F), lp, lambda i, j:P[j] in F[i][0])
45+
sys.stderr.write("performing linear algebra:\n")
46+
for K in M.left_kernel().basis():
47+
I = [f for f, k in zip(F, K) if k == 1]
48+
if len(I) > 0:
49+
sys.stderr.write("On Vector basis, taking square roots x and y.\n")
50+
x = product([isqrt(f[2] + N) for f in I])
51+
y = isqrt(product([f[2] for f in I]))
52+
g0, g1 = int(gcd(N, x - y)), int(gcd(N, x + y))
53+
if N > g0 > 1: return g0, N // g0
54+
if N > g1 > 1: return g1, N // g1
55+
offset += B2
10856

10957
if __name__ == "__main__":
11058
N = int(sys.argv[1])
111-
y ,_ ,diff = prebuilt_params(N.bit_length())
112-
r = QS(N, diff * 10, y * 10)
113-
if r != None:
114-
print("%d, %d" % r)
115-
59+
B1 = int(exp(sqrt(log(N) * log(log(N)))) ** (1 / sqrt(2)))
60+
print(QS(N, B1))

0 commit comments

Comments
 (0)