kryptools 0.9.5__tar.gz → 0.9.7__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.
Files changed (30) hide show
  1. {kryptools-0.9.5 → kryptools-0.9.7}/PKG-INFO +1 -1
  2. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/factor.py +44 -17
  3. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/factor_dix.py +2 -2
  4. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/factor_ecm.py +48 -12
  5. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/factor_fmt.py +17 -3
  6. kryptools-0.9.7/kryptools/factor_pm1.py +83 -0
  7. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/factor_qs.py +34 -9
  8. kryptools-0.9.7/kryptools/factor_rho.py +58 -0
  9. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/primes.py +13 -9
  10. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools.egg-info/PKG-INFO +1 -1
  11. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools.egg-info/SOURCES.txt +1 -0
  12. {kryptools-0.9.5 → kryptools-0.9.7}/pyproject.toml +1 -1
  13. kryptools-0.9.5/kryptools/factor_pm1.py +0 -58
  14. {kryptools-0.9.5 → kryptools-0.9.7}/LICENSE +0 -0
  15. {kryptools-0.9.5 → kryptools-0.9.7}/README.md +0 -0
  16. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/Zmod.py +0 -0
  17. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/__init__.py +0 -0
  18. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/dlp.py +0 -0
  19. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/dlp_bsgs.py +0 -0
  20. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/dlp_ic.py +0 -0
  21. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/dlp_qs.py +0 -0
  22. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/dlp_rho.py +0 -0
  23. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/ec.py +0 -0
  24. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/la.py +0 -0
  25. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/lat.py +0 -0
  26. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/nt.py +0 -0
  27. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools/poly.py +0 -0
  28. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools.egg-info/dependency_links.txt +0 -0
  29. {kryptools-0.9.5 → kryptools-0.9.7}/kryptools.egg-info/top_level.txt +0 -0
  30. {kryptools-0.9.5 → kryptools-0.9.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.9.5
3
+ Version: 0.9.7
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  Project-URL: Homepage, https://github.com/teschlg/kryptools
@@ -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, steps: int = 10) -> list:
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
- for a in range(start, min(start + steps * step,(n + 9) // 6) + 1, step):
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 = 2500
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,29 +104,40 @@ 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
+ fermat_steps += 10
93
111
  B1, B2, num_curves = parameters
112
+ p_max = isqrt(max(remaining_factors))+1
113
+ B1 = min(B1, p_max//50)
114
+ B1 += B1 & 1
115
+ B2 = min(B2, p_max)
116
+ B2 += B2 & 1
94
117
  num_curves *= 2
95
118
  D = isqrt(B2)
119
+
120
+ if verbose:
121
+ print(f"Round {round+1} (B1={B1})")
122
+
96
123
  primes = sieve_eratosthenes(B1 - 1 + ((B2 - B1 + 1) // (2 * D) + 1) * 2 * D)
97
- pm1_parameters = _pm1_parameters(10 * B1, B2, primes = primes)
124
+ pm1_parameters = tuple([10 * B1, B2] + list(_pm1_parameters(10 * B1, B2, primes = primes)))
98
125
  ecm_parameters = tuple([B1, B2, num_curves] + list(_ecm_parameters(B1, B2, D, primes = primes)))
99
126
 
100
- methods = {_factor_fermat: "fm", factor_pm1: "pm1", factor_ecm: "ecm"} #, factor_qs: "qs"}
127
+ methods = {_factor_fermat: "fmt", factor_pm1: "pm1", factor_ecm: "ecm"} #, factor_qs: "qs"}
101
128
  while remaining_factors:
102
129
  new_factors = {}
103
130
  for method in [ _factor_fermat, factor_pm1, factor_ecm ]: # , factor_qs ]:
104
131
  factors = list(remaining_factors)
105
132
  for m in factors:
106
- if verbose > 1:
107
- print("Factoring: ",m, "method", methods[method])
108
- if method == factor_pm1:
109
- tmp = factor_pm1(m, pm1_parameters = pm1_parameters)
133
+ if method == _factor_fermat:
134
+ tmp = _factor_fermat(m, maxsteps = fermat_steps, verbose = max(0, verbose - 1))
135
+ elif method == factor_pm1:
136
+ tmp = factor_pm1(m, pm1_parameters = pm1_parameters, verbose = max(0, verbose - 1))
110
137
  elif method == factor_ecm:
111
- tmp = factor_ecm(m, ecm_parameters = ecm_parameters)
138
+ tmp = factor_ecm(m, ecm_parameters = ecm_parameters, verbose = max(0, verbose - 1))
112
139
  else:
113
- tmp = method(m)
140
+ tmp = method(m, verbose = max(0, verbose - 1))
114
141
  if tmp:
115
142
  tmp2 = m // tmp
116
143
  g = gcd(tmp, tmp2)
@@ -125,7 +152,7 @@ def factorint(n: int, verbose: int = 0) -> dict:
125
152
  tmp3.append(x)
126
153
  else:
127
154
  tmp3 = [tmp, tmp2]
128
- if verbose > 0: print("Factors found (", methods[method] ,"): ", tmp3)
155
+ if verbose > 0: print(f"Factors found ({methods[method]}): ", tmp3)
129
156
  add_factors(m, tmp3)
130
157
  if not new_factors:
131
158
  break
@@ -7,7 +7,7 @@ from .primes import sieve_eratosthenes
7
7
  from .nt import legendre_symbol
8
8
  from .factor_qs import bytexor, byteset, bytetest
9
9
 
10
- def is_smooth(n: int, factorbase: list, lfb: int) -> bytes or None:
10
+ def is_smooth(n: int, factorbase: list, lfb: int) -> bytes|None:
11
11
  """Try to factor n with respect to a given factorbase.
12
12
  Upon success a bytestring, whose bits are the exponents with repect to the factorbase mod 2, is returned.
13
13
  Otherwise None."""
@@ -26,7 +26,7 @@ def is_smooth(n: int, factorbase: list, lfb: int) -> bytes or None:
26
26
  return None # the number factors if at the end nothing is left
27
27
  return factors
28
28
 
29
- def factor_dixon(n: int) -> list:
29
+ def factor_dixon(n: int) -> int|None:
30
30
  """Find factors of n using the method of Dixon."""
31
31
  # first determine the bound B for the factorbase: Choosing B=p^(1/u) Canfield-Erdös-Pomerance gives us
32
32
  # 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
@@ -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,27 +35,30 @@ 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
- return R
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) -> tuple:
51
53
  "Precompute parameters for the ECM method."
52
54
 
53
55
  # Stage-one/two limits must be even
54
- B1 += B1 & 1
56
+ if B1 % 2:
57
+ raise ValueError("B1 must be even.")
55
58
  if not B2:
56
59
  B2 = 100 * B1
57
- else:
58
- B2 += B2 & 1
60
+ if B2 % 2:
61
+ raise ValueError("B2 must be even.")
59
62
  if not D:
60
63
  D = min(isqrt(B2), B1 // 2 - 1)
61
64
  if not primes:
@@ -84,36 +87,61 @@ def _ecm_parameters(B1: int, B2: int = None, D: int = None, primes: tuple = None
84
87
  return D, stage_one, stage_two_deltas
85
88
 
86
89
 
87
- def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple|None = None):
90
+ def factor_ecm(n: int, B1: int|None = None, B2: int|None = None, curves: int = 150, ecm_parameters: tuple|None = None, verbose: int = 0) -> int|None:
88
91
  "Factors a number n using Lentsta's ECM method."
89
92
 
90
93
  if ecm_parameters:
91
94
  B1, B2, curves, D, stage_one, stage_two_deltas = ecm_parameters
92
95
  else:
96
+ if not B1:
97
+ B1 = min(1000_000, isqrt(n)//200 + 2)
98
+ # Stage-one/two limits must be even
99
+ B1 += B1 & 1
100
+ if not B2:
101
+ B2 = 100 * B1
93
102
  D, stage_one, stage_two_deltas = _ecm_parameters(B1, B2)
94
103
 
104
+ if verbose:
105
+ print(f"Factoring (ECM, B1={B1}, B2={B2}): {n} ({floor(log10(n)) + 1} digits)")
106
+ if verbose > 1:
107
+ print("Working ", end= "")
95
108
  for _ in range(curves):
96
109
  # find a random curve
110
+ if verbose > 1:
111
+ print("C", end= "") # stage one
97
112
  sigma = randint(6, n - 1)
98
113
  u = (sigma**2 - 5) % n
99
114
  v = (4 * sigma) % n
100
115
  try:
101
116
  c = (pow(v - u, 3, n) * (3 * u + v) * pow(4 * u**3 * v, -1, n) - 2) % n
102
117
  except:
103
- m = gcd(4 * u**3 * v, n)
104
- return m
105
- c2 = c - 2 % n
118
+ if verbose > 1:
119
+ print("\nFactor found.")
120
+ m = gcd(u, n)
121
+ if m > 1:
122
+ return m
123
+ m = gcd(v, n)
124
+ if m > 1:
125
+ return m
126
+ return 2
127
+ c2 = (c - 2) % n
106
128
  # and a point on the curve (or its twist)
107
129
  Q = (pow(u, 3, n), pow(v, 3, n))
108
130
  # Stage one
131
+ if verbose > 1:
132
+ print("1", end= "")
109
133
  Q = mult(stage_one, Q, c2, n)
110
134
  if Q[1] == 0:
111
135
  continue
112
136
  g = gcd(Q[1], n)
113
137
  if g > 1:
138
+ if verbose > 1:
139
+ print("\nFactor found.")
114
140
  return g
115
141
 
116
142
  # Stage two
143
+ if verbose > 1:
144
+ print("2", end= "")
117
145
  S = [dbl(Q, c2, n)]
118
146
  S.append(dbl(S[0], c2, n))
119
147
 
@@ -134,11 +162,19 @@ def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm
134
162
  break
135
163
  if g == 0:
136
164
  break
137
- g =gcd(g,n)
165
+ g = gcd(g,n)
138
166
  if 1 < g:
167
+ if verbose > 1:
168
+ print("\nFactor found.")
139
169
  return g
140
170
  R, T = add(R, S[D - 1], T, n), R
141
171
 
142
172
  g = gcd(g, n)
143
173
  if 1 < g < n:
174
+ if verbose > 1:
175
+ print("\nFactor found.")
144
176
  return g
177
+
178
+ if verbose > 1:
179
+ print(f"\nNo factor after trying {curves} curves.")
180
+
@@ -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
- for a in range(start, (n + 9) // 6 + 1, step):
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,83 @@
1
+ """
2
+ Integer factorization: Pollard's p-1 method
3
+ """
4
+
5
+ from math import gcd, log, isqrt, floor, log10
6
+ from .primes import sieve_eratosthenes
7
+
8
+ def _pm1_parameters(B1: int, B2: int|None = None, primes: tuple = None) -> tuple:
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|None = None, B2: int|None = None, x: int = 2, pm1_parameters: tuple = None, verbose: int = 0) -> int|None:
31
+ "Factors a number n using Pollard's p-1 method."
32
+ if pm1_parameters:
33
+ B1, B2, stage_one, stage_two_deltas = pm1_parameters
34
+ else:
35
+ if not B1:
36
+ B1 = min(1000_000, isqrt(n)//50 + 2)
37
+ stage_one, stage_two_deltas = _pm1_parameters(B1, B2)
38
+
39
+ if verbose:
40
+ print(f"Factoring (Pollard p-1, B1={B1}, B2={B2}): {n} ({floor(log10(n)) + 1} digits)")
41
+
42
+ # stage one
43
+ if verbose > 1:
44
+ print("Working 1", end= "")
45
+ x = pow(x, stage_one, n)
46
+ g = gcd(x - 1, n)
47
+ if g == n:
48
+ if verbose > 1:
49
+ print("")
50
+ return None
51
+ if 1 < g:
52
+ if verbose > 1:
53
+ print("\nFactor found.")
54
+ return g
55
+
56
+ if verbose > 1:
57
+ print("2", end= "")
58
+ saved = {}
59
+ y = pow(x, stage_two_deltas[0], n)
60
+ D = (y - 1) % n
61
+ for k, d in enumerate(stage_two_deltas[1:]):
62
+ if not d in saved:
63
+ saved[d] = pow(x, d, n)
64
+ y = y * saved[d] % n
65
+ D = D * (y - 1) % n
66
+ if k % 20 == 0:
67
+ g = gcd(D, n)
68
+ if g == n:
69
+ if verbose > 1:
70
+ print("")
71
+ return None
72
+ if 1 < g:
73
+ if verbose > 1:
74
+ print("\nFactor found.")
75
+ return g
76
+ g = gcd(D, n)
77
+ if 1 < g < n:
78
+ if verbose > 1:
79
+ print("\nFactor found.")
80
+ return g
81
+ if verbose > 1:
82
+ print("")
83
+ 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) -> int|None:
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
- sieve_step = 100
75
- sieve_bound = sieve_step
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 - j such that f(m-j) is B-smooth
126
- v = 1 # sqrt of the product over all f(m-j) which are B-smooth
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
- while True:
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 devides v
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.")
@@ -0,0 +1,58 @@
1
+ """
2
+ Integer factorization: Pollard's rho method
3
+ """
4
+
5
+ from math import gcd, isqrt, floor, log10
6
+ from random import randint
7
+
8
+
9
+ def factor_rho(n: int, x: int = 2, maxinit: int = 10, maxiter: int = 1000, brent: bool = True, verbose: int = 0) -> int|None:
10
+ """Factor n using the Pollard's rho method with start value x."""
11
+ if not maxiter:
12
+ maxiter = 10 * isqrt(n) # stop iterating and try a different start value
13
+
14
+ def f(x: int) -> int:
15
+ return (pow(x, 2, n) + 2) % n
16
+
17
+ if verbose:
18
+ print(f"Factoring (Pollard rho, maxinit={maxinit}, maxiter={maxiter}): {n} ({floor(log10(n)) + 1} digits)")
19
+
20
+ if verbose > 1:
21
+ print("Working ", end= "")
22
+ for _ in range(maxinit):
23
+ if verbose > 1:
24
+ print("X", end= "")
25
+ if not brent: # Floyd's cycle detection algorithm
26
+ i = 1
27
+ x = f(x) # x_1=f(x_0)
28
+ y = f(x) # y_1=f(f(x_0))
29
+ while gcd(x - y, n) == 1 and i < maxiter:
30
+ if verbose > 1 and i % 100 == 0:
31
+ print(".", end= "")
32
+ i += 1
33
+ x = f(x) # f(x_j)
34
+ y = f(y)
35
+ y = f(y) # f(f(y_j))
36
+ else: # Brent's cycle detection algorithm
37
+ i = 1 # search for a cycle length k < 2^i
38
+ k = 1 # cycle length
39
+ y = f(x) # f(x_0)
40
+ while gcd(x - y, n) == 1 and i < maxiter:
41
+ if verbose > 1 and (i+k) % 100 == 0:
42
+ print(".", end= "")
43
+ if i == k: # start a new power of 2
44
+ x = y
45
+ i *= 2
46
+ k = 0
47
+ y = f(y)
48
+ k += 1
49
+ # we found a collission (or hit max)
50
+ tmp = gcd(x - y, n)
51
+ if 1 < tmp < n:
52
+ if verbose > 1:
53
+ print("\nFactor found.")
54
+ return tmp
55
+ x = randint(1, n - 1) # new x_0
56
+ if verbose > 1:
57
+ print("")
58
+
@@ -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
- rr = isqrt(n)
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 * r + n // r ** k1) // k # Newton iteration
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
- 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,
191
- 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
192
- 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293,
193
- 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397):
194
- if n % p == 0:
195
- return n == p
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.9.5
3
+ Version: 0.9.7
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  Project-URL: Homepage, https://github.com/teschlg/kryptools
@@ -15,6 +15,7 @@ kryptools/factor_ecm.py
15
15
  kryptools/factor_fmt.py
16
16
  kryptools/factor_pm1.py
17
17
  kryptools/factor_qs.py
18
+ kryptools/factor_rho.py
18
19
  kryptools/la.py
19
20
  kryptools/lat.py
20
21
  kryptools/nt.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "0.9.5"
3
+ version = "0.9.7"
4
4
  authors = [
5
5
  { name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
6
6
  ]
@@ -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