kryptools 0.9.5__tar.gz → 0.9.6__tar.gz
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.
- {kryptools-0.9.5 → kryptools-0.9.6}/PKG-INFO +1 -1
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/factor.py +36 -16
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/factor_ecm.py +40 -8
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/factor_fmt.py +17 -3
- kryptools-0.9.6/kryptools/factor_pm1.py +77 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/factor_qs.py +34 -9
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/primes.py +13 -9
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools.egg-info/PKG-INFO +1 -1
- {kryptools-0.9.5 → kryptools-0.9.6}/pyproject.toml +1 -1
- kryptools-0.9.5/kryptools/factor_pm1.py +0 -58
- {kryptools-0.9.5 → kryptools-0.9.6}/LICENSE +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/README.md +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/Zmod.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/__init__.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/dlp.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/dlp_ic.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/dlp_qs.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/dlp_rho.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/ec.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/factor_dix.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/la.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/lat.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/nt.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools/poly.py +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools.egg-info/SOURCES.txt +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-0.9.5 → kryptools-0.9.6}/setup.cfg +0 -0
|
@@ -4,7 +4,7 @@ Factorization of integers:
|
|
|
4
4
|
divisors(n) unsorted list of all divisors of the integer n
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from math import isqrt, gcd, prod
|
|
7
|
+
from math import isqrt, gcd, prod, floor, log10
|
|
8
8
|
from .primes import sieve_eratosthenes, is_prime
|
|
9
9
|
from .factor_pm1 import _pm1_parameters, factor_pm1
|
|
10
10
|
from .factor_ecm import _ecm_parameters, factor_ecm
|
|
@@ -14,8 +14,10 @@ from .factor_ecm import _ecm_parameters, factor_ecm
|
|
|
14
14
|
# Factoring
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def _factor_fermat(n: int,
|
|
17
|
+
def _factor_fermat(n: int, maxsteps: int = 10, verbose: int = 0) -> list:
|
|
18
18
|
"Fermat method"
|
|
19
|
+
if verbose:
|
|
20
|
+
print(f"Factoring (Fermat, steps={maxsteps}): {n} ({floor(log10(n)) + 1} digits)")
|
|
19
21
|
parameters = {11: (12, 6), 23: (12, 0),
|
|
20
22
|
5: (6, 3), 17: (6, 3),
|
|
21
23
|
19: (4, 2), 7: (4, 0),
|
|
@@ -23,12 +25,23 @@ def _factor_fermat(n: int, steps: int = 10) -> list:
|
|
|
23
25
|
start = isqrt(n - 1) + 1
|
|
24
26
|
step, mod = parameters[n % 24]
|
|
25
27
|
start += (mod - start) % step
|
|
26
|
-
|
|
28
|
+
if verbose > 1:
|
|
29
|
+
print("Working ", end= "")
|
|
30
|
+
maxa = (n + 9) // 6 + 1
|
|
31
|
+
if maxsteps:
|
|
32
|
+
maxa = min(maxa, start + maxsteps * step + 1)
|
|
33
|
+
for a in range(start, maxa, step):
|
|
34
|
+
if verbose > 1:
|
|
35
|
+
print(".", end= "")
|
|
27
36
|
b = isqrt(a * a - n)
|
|
28
37
|
if b * b == a * a - n:
|
|
38
|
+
if verbose > 1:
|
|
39
|
+
print("")
|
|
29
40
|
return a - b
|
|
41
|
+
if verbose > 1:
|
|
42
|
+
print("")
|
|
30
43
|
|
|
31
|
-
def factorint(n: int, verbose: int = 0) -> dict:
|
|
44
|
+
def factorint(n: int, verbose: int = 0, trial_bnd: int = 2500) -> dict:
|
|
32
45
|
"Factor an ineger `n` into prime factors."
|
|
33
46
|
prime_factors = {}
|
|
34
47
|
if not isinstance(n, int):
|
|
@@ -46,7 +59,7 @@ def factorint(n: int, verbose: int = 0) -> dict:
|
|
|
46
59
|
for m in mm:
|
|
47
60
|
if m in prime_factors:
|
|
48
61
|
prime_factors[m] += k
|
|
49
|
-
elif is_prime(m):
|
|
62
|
+
elif is_prime(m, trialdivision = False):
|
|
50
63
|
prime_factors[m] = k
|
|
51
64
|
else:
|
|
52
65
|
if m in remaining_factors:
|
|
@@ -56,8 +69,10 @@ def factorint(n: int, verbose: int = 0) -> dict:
|
|
|
56
69
|
else:
|
|
57
70
|
new_factors[m] = k
|
|
58
71
|
|
|
72
|
+
if verbose:
|
|
73
|
+
print(f"Factoring: {n} ({floor(log10(n)) + 1} digits)")
|
|
59
74
|
# trial division
|
|
60
|
-
B =
|
|
75
|
+
B = max(3, trial_bnd)
|
|
61
76
|
factorbase = sieve_eratosthenes(B)
|
|
62
77
|
for p in factorbase:
|
|
63
78
|
k = 0
|
|
@@ -70,13 +85,14 @@ def factorint(n: int, verbose: int = 0) -> dict:
|
|
|
70
85
|
return prime_factors
|
|
71
86
|
if verbose:
|
|
72
87
|
print("Trial division found:", list(prime_factors))
|
|
73
|
-
if is_prime(n):
|
|
88
|
+
if is_prime(n, trialdivision = False):
|
|
74
89
|
prime_factors[n] = 1
|
|
75
90
|
return prime_factors
|
|
76
91
|
remaining_factors = { n: 1 }
|
|
77
92
|
|
|
78
93
|
# https://gitlab.inria.fr/zimmerma/ecm/
|
|
79
94
|
ECM_PARAMETERS = [
|
|
95
|
+
[ 5000, 130000, 54],
|
|
80
96
|
[ 11000, 1900000, 74],
|
|
81
97
|
[ 50000, 13000000, 221],
|
|
82
98
|
[ 250000, 130000000, 453],
|
|
@@ -88,8 +104,12 @@ def factorint(n: int, verbose: int = 0) -> dict:
|
|
|
88
104
|
[260000000, 3200000000000, 47173],
|
|
89
105
|
[850000000, 16000000000000, 77666]
|
|
90
106
|
]
|
|
107
|
+
fermat_steps = 10
|
|
91
108
|
|
|
92
|
-
for parameters in ECM_PARAMETERS:
|
|
109
|
+
for round, parameters in enumerate(ECM_PARAMETERS):
|
|
110
|
+
if verbose:
|
|
111
|
+
print(f"Round {round+1}")
|
|
112
|
+
fermat_steps += 10
|
|
93
113
|
B1, B2, num_curves = parameters
|
|
94
114
|
num_curves *= 2
|
|
95
115
|
D = isqrt(B2)
|
|
@@ -97,20 +117,20 @@ def factorint(n: int, verbose: int = 0) -> dict:
|
|
|
97
117
|
pm1_parameters = _pm1_parameters(10 * B1, B2, primes = primes)
|
|
98
118
|
ecm_parameters = tuple([B1, B2, num_curves] + list(_ecm_parameters(B1, B2, D, primes = primes)))
|
|
99
119
|
|
|
100
|
-
methods = {_factor_fermat: "
|
|
120
|
+
methods = {_factor_fermat: "fmt", factor_pm1: "pm1", factor_ecm: "ecm"} #, factor_qs: "qs"}
|
|
101
121
|
while remaining_factors:
|
|
102
122
|
new_factors = {}
|
|
103
123
|
for method in [ _factor_fermat, factor_pm1, factor_ecm ]: # , factor_qs ]:
|
|
104
124
|
factors = list(remaining_factors)
|
|
105
125
|
for m in factors:
|
|
106
|
-
if
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
tmp = factor_pm1(m, pm1_parameters = pm1_parameters)
|
|
126
|
+
if method == _factor_fermat:
|
|
127
|
+
tmp = _factor_fermat(m, maxsteps = fermat_steps, verbose = max(0, verbose - 1))
|
|
128
|
+
elif method == factor_pm1:
|
|
129
|
+
tmp = factor_pm1(m, pm1_parameters = pm1_parameters, verbose = max(0, verbose - 1))
|
|
110
130
|
elif method == factor_ecm:
|
|
111
|
-
tmp = factor_ecm(m, ecm_parameters = ecm_parameters)
|
|
131
|
+
tmp = factor_ecm(m, ecm_parameters = ecm_parameters, verbose = max(0, verbose - 1))
|
|
112
132
|
else:
|
|
113
|
-
tmp = method(m)
|
|
133
|
+
tmp = method(m, verbose = max(0, verbose - 1))
|
|
114
134
|
if tmp:
|
|
115
135
|
tmp2 = m // tmp
|
|
116
136
|
g = gcd(tmp, tmp2)
|
|
@@ -125,7 +145,7 @@ def factorint(n: int, verbose: int = 0) -> dict:
|
|
|
125
145
|
tmp3.append(x)
|
|
126
146
|
else:
|
|
127
147
|
tmp3 = [tmp, tmp2]
|
|
128
|
-
if verbose > 0: print("Factors found (
|
|
148
|
+
if verbose > 0: print(f"Factors found ({methods[method]}): ", tmp3)
|
|
129
149
|
add_factors(m, tmp3)
|
|
130
150
|
if not new_factors:
|
|
131
151
|
break
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Integer factorization: Lentra's ECM
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from math import gcd, isqrt, log
|
|
5
|
+
from math import gcd, isqrt, log, floor, log10
|
|
6
6
|
from random import randint, seed
|
|
7
7
|
from .primes import sieve_eratosthenes
|
|
8
8
|
seed(0)
|
|
@@ -35,19 +35,21 @@ def mult(k, P, c2, p):
|
|
|
35
35
|
return dbl(P, c2, p)
|
|
36
36
|
Q = dbl(P, c2, p)
|
|
37
37
|
R = P
|
|
38
|
-
for i in bin(k)[3
|
|
38
|
+
for i in bin(k)[3:-1]:
|
|
39
39
|
if i == "1":
|
|
40
40
|
R = add(Q, R, P, p)
|
|
41
41
|
Q = dbl(Q, c2, p)
|
|
42
42
|
else:
|
|
43
43
|
Q = add(R, Q, P, p)
|
|
44
44
|
R = dbl(R, c2, p)
|
|
45
|
-
|
|
45
|
+
if k % 2:
|
|
46
|
+
return add(Q, R, P, p)
|
|
47
|
+
return dbl(R, c2, p)
|
|
46
48
|
|
|
47
49
|
# Crandall and Pomerance: Primes (doi=10.1007/0-387-28979-8)
|
|
48
50
|
# Algorithm 7.4.4 (Inversionless ECM)
|
|
49
51
|
|
|
50
|
-
def _ecm_parameters(B1: int, B2: int = None, D: int = None, primes: tuple = None):
|
|
52
|
+
def _ecm_parameters(B1: int, B2: int|None = None, D: int|None = None, primes: tuple|None = None):
|
|
51
53
|
"Precompute parameters for the ECM method."
|
|
52
54
|
|
|
53
55
|
# Stage-one/two limits must be even
|
|
@@ -84,36 +86,59 @@ def _ecm_parameters(B1: int, B2: int = None, D: int = None, primes: tuple = None
|
|
|
84
86
|
return D, stage_one, stage_two_deltas
|
|
85
87
|
|
|
86
88
|
|
|
87
|
-
def factor_ecm(n: int, B1: int =
|
|
89
|
+
def factor_ecm(n: int, B1: int|None = None, B2: int|None = None, curves: int = 150, ecm_parameters: tuple|None = None, verbose: int = 0):
|
|
88
90
|
"Factors a number n using Lentsta's ECM method."
|
|
89
91
|
|
|
90
92
|
if ecm_parameters:
|
|
91
93
|
B1, B2, curves, D, stage_one, stage_two_deltas = ecm_parameters
|
|
92
94
|
else:
|
|
95
|
+
if not B1:
|
|
96
|
+
B1 = max(2, isqrt(n//200))
|
|
97
|
+
if not B2:
|
|
98
|
+
B2 = 100 * B1
|
|
93
99
|
D, stage_one, stage_two_deltas = _ecm_parameters(B1, B2)
|
|
94
100
|
|
|
101
|
+
if verbose:
|
|
102
|
+
print(f"Factoring (ECM, B1={B1}, B2={B2}): {n} ({floor(log10(n)) + 1} digits)")
|
|
103
|
+
if verbose > 1:
|
|
104
|
+
print("Working ", end= "")
|
|
95
105
|
for _ in range(curves):
|
|
96
106
|
# find a random curve
|
|
107
|
+
if verbose > 1:
|
|
108
|
+
print("C", end= "") # stage one
|
|
97
109
|
sigma = randint(6, n - 1)
|
|
98
110
|
u = (sigma**2 - 5) % n
|
|
99
111
|
v = (4 * sigma) % n
|
|
100
112
|
try:
|
|
101
113
|
c = (pow(v - u, 3, n) * (3 * u + v) * pow(4 * u**3 * v, -1, n) - 2) % n
|
|
102
114
|
except:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
if verbose > 1:
|
|
116
|
+
print("\nFactor found.")
|
|
117
|
+
m = gcd(u, n)
|
|
118
|
+
if m > 1:
|
|
119
|
+
return m
|
|
120
|
+
m = gcd(v, n)
|
|
121
|
+
if m > 1:
|
|
122
|
+
return m
|
|
123
|
+
return 2
|
|
124
|
+
c2 = (c - 2) % n
|
|
106
125
|
# and a point on the curve (or its twist)
|
|
107
126
|
Q = (pow(u, 3, n), pow(v, 3, n))
|
|
108
127
|
# Stage one
|
|
128
|
+
if verbose > 1:
|
|
129
|
+
print("1", end= "")
|
|
109
130
|
Q = mult(stage_one, Q, c2, n)
|
|
110
131
|
if Q[1] == 0:
|
|
111
132
|
continue
|
|
112
133
|
g = gcd(Q[1], n)
|
|
113
134
|
if g > 1:
|
|
135
|
+
if verbose > 1:
|
|
136
|
+
print("\nFactor found.")
|
|
114
137
|
return g
|
|
115
138
|
|
|
116
139
|
# Stage two
|
|
140
|
+
if verbose > 1:
|
|
141
|
+
print("2", end= "")
|
|
117
142
|
S = [dbl(Q, c2, n)]
|
|
118
143
|
S.append(dbl(S[0], c2, n))
|
|
119
144
|
|
|
@@ -136,9 +161,16 @@ def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm
|
|
|
136
161
|
break
|
|
137
162
|
g =gcd(g,n)
|
|
138
163
|
if 1 < g:
|
|
164
|
+
if verbose > 1:
|
|
165
|
+
print("\nFactor found.")
|
|
139
166
|
return g
|
|
140
167
|
R, T = add(R, S[D - 1], T, n), R
|
|
141
168
|
|
|
142
169
|
g = gcd(g, n)
|
|
143
170
|
if 1 < g < n:
|
|
171
|
+
if verbose > 1:
|
|
172
|
+
print("\nFactor found.")
|
|
144
173
|
return g
|
|
174
|
+
if verbose > 1:
|
|
175
|
+
print(f"\nNo factor after trying {curves} curves.")
|
|
176
|
+
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Integer factorization: Fermat's method
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from math import isqrt
|
|
5
|
+
from math import isqrt, log10, floor, log10
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def factor_fermat(n: int) -> list:
|
|
8
|
+
def factor_fermat(n: int, maxsteps: int|None = None, verbose: int = 0) -> list:
|
|
9
9
|
"""Find factors of n using the method of Fermat."""
|
|
10
|
+
if verbose:
|
|
11
|
+
print(f"Factoring (Fermat): {n} ({floor(log10(n)) + 1} digits)")
|
|
10
12
|
factors = []
|
|
11
13
|
parameters = {11: (12, 6), 23: (12, 0),
|
|
12
14
|
5: (6, 3), 17: (6, 3),
|
|
@@ -20,11 +22,23 @@ def factor_fermat(n: int) -> list:
|
|
|
20
22
|
start = isqrt(n - 1) + 1
|
|
21
23
|
step, mod = parameters[n % 24]
|
|
22
24
|
start += (mod - start) % step
|
|
23
|
-
|
|
25
|
+
if verbose > 1:
|
|
26
|
+
print("Working ", end= "")
|
|
27
|
+
maxa = (n + 9) // 6 + 1
|
|
28
|
+
if maxsteps:
|
|
29
|
+
maxsteps = max(1, maxsteps)
|
|
30
|
+
maxa = min(maxa, start + maxsteps * step + 1)
|
|
31
|
+
for a in range(start, maxa, step):
|
|
32
|
+
if verbose > 1:
|
|
33
|
+
print(".", end= "")
|
|
24
34
|
b = isqrt(a * a - n)
|
|
25
35
|
if b * b == a * a - n:
|
|
26
36
|
factors.append(a - b)
|
|
27
37
|
factors.append(a + b)
|
|
38
|
+
if verbose > 1:
|
|
39
|
+
print("")
|
|
28
40
|
return factors
|
|
29
41
|
factors.append(n)
|
|
42
|
+
if verbose > 1:
|
|
43
|
+
print("")
|
|
30
44
|
return factors
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integer factorization: Pollard's p-1 method
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from math import gcd, log, floor, log10
|
|
6
|
+
from .primes import sieve_eratosthenes
|
|
7
|
+
|
|
8
|
+
def _pm1_parameters(B1: int, B2: int|None = None, primes: tuple = None):
|
|
9
|
+
"Precompute parameters for the p-1 method."
|
|
10
|
+
if not B2:
|
|
11
|
+
B2 = 100 * B1
|
|
12
|
+
if not primes:
|
|
13
|
+
primes = sieve_eratosthenes(B2)
|
|
14
|
+
|
|
15
|
+
stage_one = 1
|
|
16
|
+
stage_two_deltas = []
|
|
17
|
+
pp = 0
|
|
18
|
+
for p in primes:
|
|
19
|
+
if p <= B1:
|
|
20
|
+
stage_one *= p**int(log(B1, p))
|
|
21
|
+
elif p <= B2:
|
|
22
|
+
stage_two_deltas.append(p - pp) # pylint: disable=W0631
|
|
23
|
+
pp = p
|
|
24
|
+
if p > B2:
|
|
25
|
+
break
|
|
26
|
+
|
|
27
|
+
return stage_one, stage_two_deltas
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def factor_pm1(n: int, B1: int = 10000, B2: int|None = None, x: int = 2, pm1_parameters: tuple = None, verbose: int = 0):
|
|
31
|
+
"Factors a number n using Pollard's p-1 method."
|
|
32
|
+
if verbose:
|
|
33
|
+
print(f"Factoring (Pollard p-1, B1={B1}, B2={B2}): {n} ({floor(log10(n)) + 1} digits)")
|
|
34
|
+
|
|
35
|
+
if pm1_parameters:
|
|
36
|
+
stage_one, stage_two_deltas = pm1_parameters
|
|
37
|
+
else:
|
|
38
|
+
stage_one, stage_two_deltas = _pm1_parameters(B1, B2)
|
|
39
|
+
|
|
40
|
+
# stage one
|
|
41
|
+
if verbose > 1:
|
|
42
|
+
print("Working 1", end= "")
|
|
43
|
+
x = pow(x, stage_one, n)
|
|
44
|
+
g = gcd(x - 1, n)
|
|
45
|
+
if g == n:
|
|
46
|
+
return None
|
|
47
|
+
if 1 < g:
|
|
48
|
+
return g
|
|
49
|
+
|
|
50
|
+
if verbose > 1:
|
|
51
|
+
print("2", end= "")
|
|
52
|
+
saved = {}
|
|
53
|
+
y = pow(x, stage_two_deltas[0], n)
|
|
54
|
+
D = (y - 1) % n
|
|
55
|
+
for k, d in enumerate(stage_two_deltas[1:]):
|
|
56
|
+
if not d in saved:
|
|
57
|
+
saved[d] = pow(x, d, n)
|
|
58
|
+
y = y * saved[d] % n
|
|
59
|
+
D = D * (y - 1) % n
|
|
60
|
+
if k % 20 == 0:
|
|
61
|
+
g = gcd(D, n)
|
|
62
|
+
if g == n:
|
|
63
|
+
if verbose > 1:
|
|
64
|
+
print("")
|
|
65
|
+
return None
|
|
66
|
+
if 1 < g:
|
|
67
|
+
if verbose > 1:
|
|
68
|
+
print("\nFactor found.")
|
|
69
|
+
return g
|
|
70
|
+
g = gcd(D, n)
|
|
71
|
+
if 1 < g < n:
|
|
72
|
+
if verbose > 1:
|
|
73
|
+
print("\nFactor found.")
|
|
74
|
+
return g
|
|
75
|
+
if verbose > 1:
|
|
76
|
+
print("")
|
|
77
|
+
return None
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Integer factorization: Quadratic sieve
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from math import isqrt, gcd, sqrt, log, exp, ceil
|
|
5
|
+
from math import isqrt, gcd, sqrt, log, exp, ceil, floor, log10
|
|
6
6
|
from .primes import sieve_eratosthenes
|
|
7
7
|
from .nt import legendre_symbol, sqrt_mod
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@ def bytetest(a: bytes, i: int) -> bytes:
|
|
|
24
24
|
return (a[i1] & 2**i0) != 0
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def factor_qs(n: int) -> list:
|
|
27
|
+
def factor_qs(n: int, verbose: int = 0) -> list:
|
|
28
28
|
"""Find factors of n using the quadratic sieve."""
|
|
29
29
|
# first determine the bound B for the factorbase: Choosing B=p^(1/u) Canfield-Erdös-Pomerance gives us
|
|
30
30
|
# the expected running time |B|^2 u^u = u^(u+2) p^(2/u)/log(n). There is no explicit expression for the optimum, hence
|
|
@@ -37,10 +37,16 @@ def factor_qs(n: int) -> list:
|
|
|
37
37
|
B = int(exp(log(n) / u))
|
|
38
38
|
# B = int(exp(0.5 * sqrt( log(n) * log(log(n)) )*( 1 + 1/log(log(n)) )))
|
|
39
39
|
|
|
40
|
+
if verbose:
|
|
41
|
+
print(f"Factoring (QS, B={B}): {n} ({floor(log10(n)) + 1} digits)")
|
|
42
|
+
if verbose > 1:
|
|
43
|
+
print(f"Setting up factorbase: B={B}", end="")
|
|
40
44
|
factorbase = []
|
|
41
45
|
for p in sieve_eratosthenes(B): # compute the factorbase
|
|
42
46
|
ls = legendre_symbol(n, p)
|
|
43
47
|
if ls == 0: # we already found a factor;-)
|
|
48
|
+
if verbose > 1:
|
|
49
|
+
print("\nFound a small prime factor.")
|
|
44
50
|
if n == p:
|
|
45
51
|
return n
|
|
46
52
|
return p
|
|
@@ -49,6 +55,8 @@ def factor_qs(n: int) -> list:
|
|
|
49
55
|
lf = len(factorbase) # length of the factorbase (including -1)
|
|
50
56
|
lfb = ceil((lf+1) / 8) # the number of bytes we need to store a relation
|
|
51
57
|
|
|
58
|
+
if verbose > 1:
|
|
59
|
+
print(f" len={lf}", end ="")
|
|
52
60
|
factorbase_log = [log(p) for p in factorbase] # we add these up to test if a number will probably factor
|
|
53
61
|
factorbase_root = [0] * lf # compute the roots of n mod p
|
|
54
62
|
for i, p in enumerate(factorbase):
|
|
@@ -66,13 +74,18 @@ def factor_qs(n: int) -> list:
|
|
|
66
74
|
return m
|
|
67
75
|
m2 = 2 * m
|
|
68
76
|
|
|
77
|
+
if verbose > 1:
|
|
78
|
+
print(" done.")
|
|
79
|
+
|
|
69
80
|
relation_no = -1
|
|
70
81
|
relations = [None] * (lf + 1) # here we will store the relations in case we found a B-smooth number
|
|
71
82
|
values = [None] * (lf + 1) # here we will store the values leading to the B-smooth numbers
|
|
72
83
|
|
|
73
84
|
# set up the sieve
|
|
74
|
-
|
|
75
|
-
sieve_bound =
|
|
85
|
+
expected_trys = int(exp(u*log(u/2)/2))+1 # expected number of trys to find a relation for sieved values
|
|
86
|
+
sieve_bound = max(100, expected_trys * lf)
|
|
87
|
+
sieve_step = sieve_bound // 10
|
|
88
|
+
|
|
76
89
|
# start values for the iterators in the positive/negative direction
|
|
77
90
|
iterator_p = [ [0, 0] for _ in range(lf) ]
|
|
78
91
|
iterator_m = [ [0, 0] for _ in range(lf) ]
|
|
@@ -122,10 +135,10 @@ def factor_qs(n: int) -> list:
|
|
|
122
135
|
# print(f'{j:3}', ' '.join(f'{b:08b}' for b in reversed(relation)))
|
|
123
136
|
if index == lf: # the new relation is linearly dependent: we have found a linear combination of the 0 vector
|
|
124
137
|
# now we need to determine the factors
|
|
125
|
-
u = 1 # product over all values m
|
|
126
|
-
v = 1 # sqrt of the product over all f(m
|
|
138
|
+
u = 1 # product over all values m + j such that f(m+j) is B-smooth
|
|
139
|
+
v = 1 # sqrt of the product over all f(m+j) which are B-smooth
|
|
127
140
|
w = 1 # save nonsquare terms for the next round
|
|
128
|
-
for i in range(lf):
|
|
141
|
+
for i in range(lf + 1):
|
|
129
142
|
if bytetest(relation, lfb * 8 + i): # select the relations which sum to the 0 vector
|
|
130
143
|
ui = m + values[i]
|
|
131
144
|
u = (u * ui) % n
|
|
@@ -137,14 +150,20 @@ def factor_qs(n: int) -> list:
|
|
|
137
150
|
res = gcd(u - v, n)
|
|
138
151
|
if 1 < res < n:
|
|
139
152
|
return res
|
|
153
|
+
if verbose > 1:
|
|
154
|
+
print("X", end="")
|
|
140
155
|
relation_no -= 1 # this one did not work, try again
|
|
141
156
|
else:
|
|
142
157
|
relations[index] = relation
|
|
158
|
+
if verbose > 1:
|
|
159
|
+
print(".", end="")
|
|
143
160
|
return None
|
|
144
161
|
|
|
145
|
-
|
|
162
|
+
for _ in range(expected_trys * lf * 100):
|
|
146
163
|
factors = {} # store the index of the primes dividing j
|
|
147
164
|
|
|
165
|
+
if verbose > 1:
|
|
166
|
+
print("S", end="")
|
|
148
167
|
do_sieve()
|
|
149
168
|
|
|
150
169
|
for j in sorted(factors.keys(),key=abs):
|
|
@@ -157,7 +176,7 @@ def factor_qs(n: int) -> list:
|
|
|
157
176
|
v *= -1
|
|
158
177
|
for i in primes:
|
|
159
178
|
p = factorbase[i]
|
|
160
|
-
k = 1 # per our sieve we already know that p
|
|
179
|
+
k = 1 # per our sieve we already know that p divides v
|
|
161
180
|
v //= p
|
|
162
181
|
while v % p == 0: # divide by p as many times as possible
|
|
163
182
|
k = (k + 1) % 2 # we only want to know if the exponent is even or odd
|
|
@@ -167,8 +186,14 @@ def factor_qs(n: int) -> list:
|
|
|
167
186
|
if v == 1:
|
|
168
187
|
res = process_relation(j, mask)
|
|
169
188
|
if res:
|
|
189
|
+
if verbose:
|
|
190
|
+
print(f"\nFactor found after {relation_no} relations.")
|
|
170
191
|
return res
|
|
171
192
|
del factors[j]
|
|
172
193
|
else:
|
|
173
194
|
del factors[j] # the number is unlikely to factor
|
|
174
195
|
sieve_bound += sieve_step # increase the sieve and continue
|
|
196
|
+
if verbose > 1:
|
|
197
|
+
print("")
|
|
198
|
+
if verbose:
|
|
199
|
+
print("QS failed.")
|
|
@@ -125,12 +125,15 @@ def iroot(k: int, n: int) -> int:
|
|
|
125
125
|
return n
|
|
126
126
|
if k == 2:
|
|
127
127
|
return isqrt(n)
|
|
128
|
-
|
|
128
|
+
r = isqrt(n)
|
|
129
|
+
while r**k >= n:
|
|
130
|
+
rr = r
|
|
131
|
+
r = isqrt(r)
|
|
129
132
|
r =rr + 1
|
|
130
133
|
k1 = k-1
|
|
131
134
|
while rr < r:
|
|
132
135
|
r = rr
|
|
133
|
-
rr = (k1 *
|
|
136
|
+
rr = (k1 * rr + n // rr ** k1) // k # Newton iteration
|
|
134
137
|
return r
|
|
135
138
|
|
|
136
139
|
def perfect_power(n: int) -> tuple|None:
|
|
@@ -184,15 +187,16 @@ def lucas_test(n: int) -> bool:
|
|
|
184
187
|
Qk = pow(Qk, 2, n)
|
|
185
188
|
return False
|
|
186
189
|
|
|
187
|
-
def is_prime(n: int) -> bool:
|
|
190
|
+
def is_prime(n: int, trialdivision: bool = True) -> bool:
|
|
188
191
|
"""Test if an integer `n` if probable prime."""
|
|
189
192
|
# Test small primes
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
if trialdivision:
|
|
194
|
+
for p in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
|
|
195
|
+
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
|
|
196
|
+
211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293,
|
|
197
|
+
307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397):
|
|
198
|
+
if n % p == 0:
|
|
199
|
+
return n == p
|
|
196
200
|
if n < 18446744073709551616: # https://miller-rabin.appspot.com
|
|
197
201
|
return miller_rabin_test(n, [2, 325, 9375, 28178, 450775, 9780504, 1795265022])
|
|
198
202
|
if n < 3317044064679887385961981:
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Integer factorization: Pollard's p-1 method
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from math import gcd, log
|
|
6
|
-
from .primes import sieve_eratosthenes
|
|
7
|
-
|
|
8
|
-
def _pm1_parameters(B1: int, B2: int = None, primes: tuple = None):
|
|
9
|
-
"Precompute parameters for the P-1 method."
|
|
10
|
-
if not B2:
|
|
11
|
-
B2 = 100 * B1
|
|
12
|
-
if not primes:
|
|
13
|
-
primes = sieve_eratosthenes(B2)
|
|
14
|
-
|
|
15
|
-
stage_one = 1
|
|
16
|
-
for p in primes:
|
|
17
|
-
if p > B1:
|
|
18
|
-
break
|
|
19
|
-
stage_one *= p**int(log(B1, p))
|
|
20
|
-
|
|
21
|
-
stage_two_deltas = []
|
|
22
|
-
for q in primes:
|
|
23
|
-
if q <= B1:
|
|
24
|
-
continue
|
|
25
|
-
if q > B2:
|
|
26
|
-
break
|
|
27
|
-
stage_two_deltas.append(q - p) # pylint: disable=W0631
|
|
28
|
-
|
|
29
|
-
return stage_one, stage_two_deltas
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def factor_pm1(n: int, B1: int = 11000, B2: int = 1900000, x: int = 2, pm1_parameters: tuple = None):
|
|
33
|
-
"Factors a number n using Pollard's P-1 method."
|
|
34
|
-
|
|
35
|
-
if pm1_parameters:
|
|
36
|
-
stage_one, stage_two_deltas = pm1_parameters
|
|
37
|
-
else:
|
|
38
|
-
stage_one, stage_two_deltas = _pm1_parameters(B1, B2)
|
|
39
|
-
|
|
40
|
-
g = gcd(pow(x, stage_one, n) - 1, n)
|
|
41
|
-
if 1 < g < n:
|
|
42
|
-
return g
|
|
43
|
-
k = 0
|
|
44
|
-
for d in stage_two_deltas:
|
|
45
|
-
saved = {}
|
|
46
|
-
if not d in saved:
|
|
47
|
-
y = pow(x, d, n)
|
|
48
|
-
saved[d] = y
|
|
49
|
-
x = (x * saved[d]) % n
|
|
50
|
-
k += 1
|
|
51
|
-
if k % 20 == 0:
|
|
52
|
-
g = gcd(x-1, n)
|
|
53
|
-
if 1 < g < n:
|
|
54
|
-
return g
|
|
55
|
-
g = gcd(x-1, n)
|
|
56
|
-
if 1 < g < n:
|
|
57
|
-
return g
|
|
58
|
-
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|