kryptools 0.9.8__tar.gz → 0.9.10__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 (42) hide show
  1. {kryptools-0.9.8 → kryptools-0.9.10}/PKG-INFO +3 -1
  2. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/Zmod.py +27 -4
  3. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/__init__.py +3 -2
  4. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/ec.py +5 -1
  5. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor.py +10 -5
  6. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_ecm.py +0 -1
  7. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_fmt.py +5 -1
  8. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_pm1.py +1 -1
  9. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_rho.py +0 -1
  10. kryptools-0.9.10/kryptools/intfuncs.py +106 -0
  11. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/la.py +3 -3
  12. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/nt.py +24 -3
  13. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/primes.py +7 -41
  14. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/PKG-INFO +3 -1
  15. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/SOURCES.txt +14 -1
  16. {kryptools-0.9.8 → kryptools-0.9.10}/pyproject.toml +3 -1
  17. kryptools-0.9.10/tests/test_Zmod.py +66 -0
  18. kryptools-0.9.10/tests/test_dlog.py +18 -0
  19. kryptools-0.9.10/tests/test_ec.py +27 -0
  20. kryptools-0.9.10/tests/test_factor.py +18 -0
  21. kryptools-0.9.10/tests/test_factor_fmt.py +12 -0
  22. kryptools-0.9.10/tests/test_factor_qs.py +9 -0
  23. kryptools-0.9.10/tests/test_intfuncs.py +78 -0
  24. kryptools-0.9.10/tests/test_la.py +20 -0
  25. kryptools-0.9.10/tests/test_lat.py +15 -0
  26. kryptools-0.9.10/tests/test_nt.py +96 -0
  27. kryptools-0.9.10/tests/test_poly.py +15 -0
  28. kryptools-0.9.10/tests/test_primes.py +104 -0
  29. {kryptools-0.9.8 → kryptools-0.9.10}/LICENSE +0 -0
  30. {kryptools-0.9.8 → kryptools-0.9.10}/README.md +0 -0
  31. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp.py +0 -0
  32. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_bsgs.py +0 -0
  33. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_ic.py +0 -0
  34. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_qs.py +0 -0
  35. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/dlp_rho.py +0 -0
  36. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_dix.py +0 -0
  37. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/factor_qs.py +0 -0
  38. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/lat.py +0 -0
  39. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools/poly.py +0 -0
  40. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/dependency_links.txt +0 -0
  41. {kryptools-0.9.8 → kryptools-0.9.10}/kryptools.egg-info/top_level.txt +0 -0
  42. {kryptools-0.9.8 → kryptools-0.9.10}/setup.cfg +0 -0
@@ -1,9 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.9.8
3
+ Version: 0.9.10
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
+ License: MIT
6
7
  Project-URL: Homepage, https://github.com/teschlg/kryptools
8
+ Project-URL: Source, https://github.com/teschlg/kryptools
7
9
  Project-URL: Issues, https://github.com/teschlg/kryptools/issues
8
10
  Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
9
11
  Classifier: Programming Language :: Python :: 3
@@ -4,7 +4,7 @@ Ring of intergers modulo `n`.
4
4
 
5
5
  from math import gcd
6
6
  from .factor import factorint
7
-
7
+ from .intfuncs import prime_power
8
8
 
9
9
  class Zmod:
10
10
  """
@@ -25,12 +25,18 @@ class Zmod:
25
25
  """
26
26
 
27
27
  def __init__(self, n: int, short: bool = True):
28
+ if not isinstance(n, int) or n < 1:
29
+ raise ValueError(f"{n} is not a positive integer.")
28
30
  self.n = n
29
31
  self.short = short
30
32
  self.group_order = 0
31
33
  self.factors = {} # factoring of the group order
32
34
 
33
- def __call__(self, x: int):
35
+ def __call__(self, x: int|list|tuple):
36
+ if isinstance(x, list):
37
+ return [ZmodPoint(xx, self) for xx in x]
38
+ elif isinstance(x, tuple):
39
+ return (ZmodPoint(xx, self) for xx in x)
34
40
  return ZmodPoint(x, self)
35
41
 
36
42
  def __eq__(self, other):
@@ -62,8 +68,25 @@ class Zmod:
62
68
  self.group_order *= p**k
63
69
  return self.group_order
64
70
 
65
- def generator(self, all = False) -> int:
71
+ def is_cyclic(self) -> bool:
72
+ """Test if the group Z_n^* is cyclic."""
73
+ n = self.n
74
+ if n < 8:
75
+ return True
76
+ if not n % 2:
77
+ n //= 2
78
+ if not n % 2:
79
+ return False
80
+ if prime_power(n):
81
+ return True
82
+ return False
83
+
84
+ def generator(self, all = False) -> int|None:
66
85
  """Return a generator of the group Z_n^*."""
86
+ if not self.is_cyclic():
87
+ if all:
88
+ return []
89
+ return None
67
90
  for a in range(2, self.n):
68
91
  if gcd(a, self.n) > 1:
69
92
  continue
@@ -76,7 +99,7 @@ class Zmod:
76
99
 
77
100
  def star(self) -> int:
78
101
  """Return a list of all elements of the group Z_n^*."""
79
- elements = [ 1 ]
102
+ elements = [ self(1) ]
80
103
  for a in range(2, self.n):
81
104
  if gcd(a, self.n) > 1:
82
105
  continue
@@ -5,11 +5,12 @@ Implemenation of same basic algorithms used in cryptography.
5
5
  __author__ = "Gerald Teschl"
6
6
  __copyright__ = "Copyright 2024, Gerald Teschl"
7
7
  __license__ = "MIT License"
8
- __version__ = "0.9.8"
8
+ __version__ = "0.9.10"
9
9
  __email__ = "Gerald.Teschl@univie.ac.at"
10
10
 
11
- from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, order, crt
11
+ from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, is_carmichael_number, order, crt
12
12
  from .primes import sieve_eratosthenes, prime_pi, is_prime, next_prime, previous_prime, random_prime, random_strongprime, is_safeprime, random_safeprime, is_blumprime, random_blumprime, miller_rabin_test, lucas_test
13
+ from .intfuncs import iroot, ilog, perfect_square, perfect_power, prime_power
13
14
  from .factor import factorint, divisors
14
15
  from .dlp import dlog
15
16
  from .ec import EC_Weierstrass
@@ -71,6 +71,10 @@ class EC_Weierstrass():
71
71
  return False
72
72
 
73
73
  def __contains__(self, P: "ECPoint") -> bool:
74
+ if not isinstance(P, ECPoint) or P.curve != self:
75
+ return False
76
+ if P.x is None and P.y is None:
77
+ return True
74
78
  return P.y**2 == P.x**3 + self.a * P.x + self.b
75
79
 
76
80
  def info(self):
@@ -268,7 +272,7 @@ class ECPoint:
268
272
  else:
269
273
  y = (curve.p - y1) % curve.p
270
274
  self.y = curve.gf(y)
271
- if not self in curve:
275
+ if self not in curve:
272
276
  raise ValueError("Point not on curve!")
273
277
 
274
278
  def __repr__(self):
@@ -116,7 +116,7 @@ def factorint(n: int, verbose: int = 0, trial_bnd: int = 2500) -> dict:
116
116
  B2 += B2 & 1
117
117
  num_curves *= 2
118
118
  D = isqrt(B2)
119
-
119
+
120
120
  if verbose:
121
121
  print(f"Round {round+1} (B1={B1})")
122
122
 
@@ -172,16 +172,21 @@ def factorint(n: int, verbose: int = 0, trial_bnd: int = 2500) -> dict:
172
172
 
173
173
  def divisors(n:int, proper:bool = False) -> int:
174
174
  """Returns an unsorted list of all divisors of an integer `n`."""
175
+ if not isinstance(n, int):
176
+ raise ValueError("Number must be an integer!")
177
+ n = abs(n)
178
+ if proper:
179
+ divisors = []
180
+ else:
181
+ divisors = [1]
182
+ if n <= 1:
183
+ return divisors
175
184
  facctordict= factorint(n)
176
185
  primes = [p for p in facctordict]
177
186
  nprimes = len(primes)
178
187
  multiplicites = [facctordict[p] for p in primes]
179
188
 
180
189
  exponents = [0] * nprimes
181
- if proper:
182
- divisors = []
183
- else:
184
- divisors = [1]
185
190
  i = 0
186
191
  while True:
187
192
  exponents[i] += 1
@@ -177,4 +177,3 @@ def factor_ecm(n: int, B1: int|None = None, B2: int|None = None, curves: int = 1
177
177
 
178
178
  if verbose > 1:
179
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, log10, floor, log10
5
+ from math import isqrt, floor, log10
6
6
 
7
7
 
8
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 not isinstance(n, int) or n < 1:
11
+ raise ValueError("Number to be factored must be a positive integer!")
10
12
  if verbose:
11
13
  print(f"Factoring (Fermat): {n} ({floor(log10(n)) + 1} digits)")
12
14
  factors = []
@@ -19,6 +21,8 @@ def factor_fermat(n: int, maxsteps: int|None = None, verbose: int = 0) -> list:
19
21
  while n % p == 0:
20
22
  factors.append(p)
21
23
  n //= p
24
+ if n == 1:
25
+ return factors
22
26
  start = isqrt(n - 1) + 1
23
27
  step, mod = parameters[n % 24]
24
28
  start += (mod - start) % step
@@ -59,7 +59,7 @@ def factor_pm1(n: int, B1: int|None = None, B2: int|None = None, x: int = 2, pm1
59
59
  y = pow(x, stage_two_deltas[0], n)
60
60
  D = (y - 1) % n
61
61
  for k, d in enumerate(stage_two_deltas[1:]):
62
- if not d in saved:
62
+ if d not in saved:
63
63
  saved[d] = pow(x, d, n)
64
64
  y = y * saved[d] % n
65
65
  D = D * (y - 1) % n
@@ -55,4 +55,3 @@ def factor_rho(n: int, x: int = 2, maxinit: int = 10, maxiter: int = 1000, brent
55
55
  x = randint(1, n - 1) # new x_0
56
56
  if verbose > 1:
57
57
  print("")
58
-
@@ -0,0 +1,106 @@
1
+ """
2
+ Integer Functions:
3
+ iroot(k, n) integer k'th root of n
4
+ ilog(b, n) integer log of n with respect ot base b
5
+ perfect_square(n) test if a number is a perfect square
6
+ perfect_power(n) test if a number is a perfect power
7
+ prime_power(n) test if a number is a prime power
8
+ """
9
+ from math import isqrt
10
+ from .primes import sieve_eratosthenes, is_prime
11
+
12
+
13
+ def iroot(k: int, n: int) -> int:
14
+ """For a given positive integers `n` and `k`, finds the largest integer `r` such that `r**k <= n`."""
15
+ if not isinstance(k, int) or k < 1:
16
+ raise ValueError(f"k={k} must be a positive integer.")
17
+ if not isinstance(n, int) or n < 0:
18
+ raise ValueError(f"n={n} must be a nonegative integer.")
19
+ if n <= 1 or k == 1:
20
+ return n
21
+ if k == 2:
22
+ return isqrt(n)
23
+ rr = n
24
+ kk = k
25
+ while kk > 1:
26
+ kk //= 2
27
+ rr = isqrt(rr)
28
+ r = rr + 1
29
+ k1 = k-1
30
+ while rr < r:
31
+ r = rr
32
+ rr = (k1 * rr + n // rr ** k1) // k # Newton iteration
33
+ return r
34
+
35
+ def ilog(b: int, n:int) -> int:
36
+ """For a given positive integer `n`, finds the largest integer `l` such that `b**l <= n`."""
37
+ # https://stackoverflow.com/questions/39190815/how-to-make-perfect-power-algorithm-more-efficient/39191163#39191163
38
+ if not isinstance(b, int) or b < 2:
39
+ raise ValueError(f"base b={b} must a positive integer larger then one.")
40
+ if not isinstance(n, int) or n <= 0:
41
+ raise ValueError(f"n={n} must be a positive integer.")
42
+ if n == 1:
43
+ return 0
44
+ if b == 2:
45
+ return n.bit_length() - 1
46
+ lo, blo, hi, bhi = 0, 1, 1, b
47
+ while bhi < n:
48
+ lo, blo, hi, bhi = hi, bhi, hi + hi, bhi * bhi
49
+ while 1 < (hi - lo):
50
+ mid = (lo + hi) // 2
51
+ bmid = blo * pow(b, mid - lo)
52
+ if n < bmid:
53
+ hi, bhi = mid, bmid
54
+ elif bmid < n:
55
+ lo, blo = mid, bmid
56
+ else: return mid
57
+ if bhi == n: return hi
58
+ return lo
59
+
60
+
61
+ def perfect_square(n: int) -> int|None:
62
+ """Returns the root if `n` is a perfect square and None else."""
63
+ if not isinstance(n, int) or n < 2:
64
+ return None
65
+ s = isqrt(n)
66
+ if s*s == n:
67
+ return s
68
+
69
+ def perfect_power(n: int) -> tuple|None:
70
+ """Returns integers `(m, p)` with `m**p == n` if `n` is a perfect power and None else."""
71
+ if not isinstance(n, int) or abs(n) < 2:
72
+ return None
73
+ sign = 1
74
+ if n < 0:
75
+ sign = -1
76
+ n = abs(n)
77
+ for p in sieve_eratosthenes(n.bit_length() - 1):
78
+ m = iroot(p, n)
79
+ if pow(m, p) == n:
80
+ if sign == 1:
81
+ return m, p
82
+ if p > 2:
83
+ return -m, p
84
+
85
+ def prime_power(n: int) -> tuple|None:
86
+ """Returns integers `(p, k)` with `p**k == n` if `n` is a prime power and None else."""
87
+ if not isinstance(n, int) or n < 2:
88
+ return None
89
+ if is_prime(n):
90
+ return (n, 1)
91
+ r = n
92
+ k = 1
93
+ while r.bit_length() > 20:
94
+ k += 1
95
+ r = iroot(k, n)
96
+ if r**k == n and is_prime(r):
97
+ return (r, k)
98
+ for p in sieve_eratosthenes(r - 1):
99
+ k = 0
100
+ while n % p == 0:
101
+ k += 1
102
+ n //= p
103
+ if k >= 1:
104
+ if n == 1:
105
+ return (p, k)
106
+ return None
@@ -69,7 +69,7 @@ class Matrix:
69
69
  if isinstance(item, int):
70
70
  i, j = divmod(item, self.cols)
71
71
  return self.matrix[i][j]
72
- return self.__class__([self.matrix[k // self.cols][k % self.cols] for k in range(self.cols * self.rows)[item]])
72
+ return [self.matrix[k // self.cols][k % self.cols] for k in range(self.cols * self.rows)[item]]
73
73
 
74
74
  def __setitem__(self, item, value):
75
75
  if isinstance(item, tuple):
@@ -187,12 +187,12 @@ class Matrix:
187
187
  def __mul__(self, other) -> "Matrix":
188
188
  if isinstance(other, Matrix):
189
189
  return self.multiply(other)
190
- if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
190
+ if isinstance(other, Number) or type(other) is type(self.matrix[0][0]):
191
191
  return self.__class__([ [item * other for item in row] for row in self.matrix ])
192
192
  return NotImplemented
193
193
 
194
194
  def __rmul__(self, other) -> "Matrix":
195
- if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
195
+ if isinstance(other, Number) or type(other) is type(self.matrix[0][0]):
196
196
  return self.__class__([ [item * other for item in row] for row in self.matrix ])
197
197
  return NotImplemented
198
198
 
@@ -9,6 +9,7 @@ Number theory tools:
9
9
  euler_phi(n) Euler phi function of n
10
10
  carmichael_lambda(n) Carmichael lambda function of n
11
11
  moebius_mu(n) Moebius function of n
12
+ is_carmichael_number(n) tests if a number is a Carmichael number
12
13
  order(a, n) oder of a in the multiplicative group Z_n^*
13
14
  crt([a1, a2, ...],[m1, m2, ...]) Chinese Remainder Theorem
14
15
  """
@@ -73,6 +74,8 @@ def convergents(cont_frac: list[int]) -> float:
73
74
 
74
75
  def legendre_symbol(a: int, p: int) -> int:
75
76
  """Compute the Legendre symbol of `a` with respect to the prime `p`."""
77
+ if not isinstance(p, int) or p < 2:
78
+ raise ValueError(f"{p} must be a prime.")
76
79
  a = a % p
77
80
  if a == 0:
78
81
  return 0
@@ -81,7 +84,9 @@ def legendre_symbol(a: int, p: int) -> int:
81
84
  return -1
82
85
 
83
86
  def jacobi_symbol(a: int, n: int) -> int:
84
- """Compute the Jacobi symbol of `a` with respect to the integer `n`."""
87
+ """Compute the Jacobi symbol of `a` with respect to the odd integer `n`."""
88
+ if not isinstance(n, int) or n < 1 or not n % 2:
89
+ raise ValueError(f"{n} must be a positive odd integer.")
85
90
  # Crandall/Pomerance Algorithm 2.3.5
86
91
  a = a % n
87
92
  t = 1
@@ -143,6 +148,8 @@ def carmichael_lambda(n: int) -> int:
143
148
  """Carmichael's lambda function of `n`."""
144
149
  if not isinstance(n, int) or n < 1:
145
150
  raise ValueError(f"{n} is not a positive integer")
151
+ if n == 1:
152
+ return 1
146
153
  k = factorint(n)
147
154
  lam_all = [] # values corresponding to the prime factors
148
155
  for p in k:
@@ -168,6 +175,15 @@ def moebius_mu(n: int) -> int:
168
175
  return -1
169
176
  return 1
170
177
 
178
+ def is_carmichael_number(n: int) -> bool:
179
+ """Tests if a number is a Carmichael number using Korselt's criterion."""
180
+ if n < 561:
181
+ return False
182
+ factors = factorint(n)
183
+ if len(factors) == 1: # prime
184
+ return False
185
+ return max(factors.values()) == 1 and not any((n-1) % (p-1) for p in factors)
186
+
171
187
  # Order in Z_p^*
172
188
 
173
189
  def order(a: int, n: int, factor=False) -> int:
@@ -215,7 +231,12 @@ def crt(a: list[int], m: list[int], coprime = True) -> int:
215
231
  if l != len(a):
216
232
  raise ValueError("The lists of numbers and modules must have equal length.")
217
233
 
218
- if not coprime: # make a copy
234
+ if not coprime:
235
+ if not all( (isinstance(mi, int) and mi > 1) for mi in m):
236
+ raise ValueError("Moduli must be positive integers larger than one.")
237
+ if not all( isinstance(ai, int) for ai in a):
238
+ raise ValueError("Residues must be integers.")
239
+ # make a copy
219
240
  m = [ mi for mi in m ]
220
241
  a = [ ai for ai in a ]
221
242
  for i in range(l):
@@ -224,7 +245,7 @@ def crt(a: list[int], m: list[int], coprime = True) -> int:
224
245
  if g == 1: # moduli are coprime, nothing to be done
225
246
  continue
226
247
  if (a[i] - a[j]) % g:
227
- raise ValueError(f"Congruences not solvable!")
248
+ raise ValueError("Congruences not solvable!")
228
249
  for p, k in factorint(g).items():
229
250
  p = p**k # remove this factor from one of the equations
230
251
  if gcd(m[i] // p, p) == 1:
@@ -13,9 +13,6 @@ Tools for prime numbers:
13
13
  random_blumprime(l) find a pseudorandom Blum prime with bit length at least l
14
14
  miller_rabin_test(n, b) Miller-Rabin primality test with base b
15
15
  lucas_test(n) strong Lucas test
16
- perfect_square(n) test if a number is a perfect square
17
- perfect_power(n) test if a number is a perfect power
18
- iroot(k, n) integer k'th root of n
19
16
  """
20
17
  from math import isqrt, gcd, floor, ceil
21
18
  from random import randint
@@ -72,7 +69,7 @@ def miller_rabin_test(n: int, bases: list[int] | int) -> bool:
72
69
  if a == 0:
73
70
  continue
74
71
  b = pow(a, m, n)
75
- if b == 1 or b == n - 1:
72
+ if b in (1, n - 1):
76
73
  continue
77
74
  for _ in range(1, k):
78
75
  b = pow(b, 2, n)
@@ -110,40 +107,6 @@ def _lucas_sequence(n, D, k):
110
107
  Qk %= n
111
108
  return U % n, V % n, Qk
112
109
 
113
-
114
- def perfect_square(n: int) -> int|None:
115
- """Returns the root if `n` is a perfect square and None else."""
116
- s = isqrt(n)
117
- if s*s == n:
118
- return s
119
-
120
- def iroot(k: int, n: int) -> int:
121
- """Compute the integer k'th root of n."""
122
- if not isinstance(k, int) or n < 1:
123
- raise ValueError(f"{k} is not a positive integer")
124
- if k == 1:
125
- return n
126
- if k == 2:
127
- return isqrt(n)
128
- r = isqrt(n)
129
- while r**k >= n:
130
- rr = r
131
- r = isqrt(r)
132
- r =rr + 1
133
- k1 = k-1
134
- while rr < r:
135
- r = rr
136
- rr = (k1 * rr + n // rr ** k1) // k # Newton iteration
137
- return r
138
-
139
- def perfect_power(n: int) -> tuple|None:
140
- """Returns integers `(m, p)` with `m**p == n` if `n` is a perfect power and None else."""
141
- for p in sieve_eratosthenes(n.bit_length() - 1):
142
- m = iroot(p, n)
143
- if pow(m, p) == n:
144
- return (m, p)
145
-
146
-
147
110
  # https://en.wikipedia.org/wiki/Lucas_pseudoprime#Implementing_a_Lucas_probable_prime_test
148
111
 
149
112
  def lucas_test(n: int) -> bool:
@@ -152,9 +115,10 @@ def lucas_test(n: int) -> bool:
152
115
  return False
153
116
  if n % 2 == 0:
154
117
  return n == 2
155
- if perfect_square(n) is not None: # the search for D will not succeed in this case
118
+ s = isqrt(n)
119
+ if s * s == n: # the search for D will not succeed in this case
156
120
  return False
157
-
121
+
158
122
  # write n = k 2^s - 1
159
123
  k = (n + 1) // 2
160
124
  s = 1
@@ -163,7 +127,7 @@ def lucas_test(n: int) -> bool:
163
127
  s += 1
164
128
 
165
129
  # Selfridge method for choosing D
166
-
130
+
167
131
  D = 5
168
132
  while True:
169
133
  g = gcd(abs(D), n)
@@ -189,6 +153,8 @@ def lucas_test(n: int) -> bool:
189
153
 
190
154
  def is_prime(n: int, trialdivision: bool = True) -> bool:
191
155
  """Test if an integer `n` if probable prime."""
156
+ if not isinstance(n, int) or n < 2:
157
+ return False
192
158
  # Test small primes
193
159
  if trialdivision:
194
160
  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,
@@ -1,9 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.9.8
3
+ Version: 0.9.10
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
+ License: MIT
6
7
  Project-URL: Homepage, https://github.com/teschlg/kryptools
8
+ Project-URL: Source, https://github.com/teschlg/kryptools
7
9
  Project-URL: Issues, https://github.com/teschlg/kryptools/issues
8
10
  Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
9
11
  Classifier: Programming Language :: Python :: 3
@@ -16,6 +16,7 @@ kryptools/factor_fmt.py
16
16
  kryptools/factor_pm1.py
17
17
  kryptools/factor_qs.py
18
18
  kryptools/factor_rho.py
19
+ kryptools/intfuncs.py
19
20
  kryptools/la.py
20
21
  kryptools/lat.py
21
22
  kryptools/nt.py
@@ -24,4 +25,16 @@ kryptools/primes.py
24
25
  kryptools.egg-info/PKG-INFO
25
26
  kryptools.egg-info/SOURCES.txt
26
27
  kryptools.egg-info/dependency_links.txt
27
- kryptools.egg-info/top_level.txt
28
+ kryptools.egg-info/top_level.txt
29
+ tests/test_Zmod.py
30
+ tests/test_dlog.py
31
+ tests/test_ec.py
32
+ tests/test_factor.py
33
+ tests/test_factor_fmt.py
34
+ tests/test_factor_qs.py
35
+ tests/test_intfuncs.py
36
+ tests/test_la.py
37
+ tests/test_lat.py
38
+ tests/test_nt.py
39
+ tests/test_poly.py
40
+ tests/test_primes.py
@@ -1,11 +1,12 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "0.9.8"
3
+ version = "0.9.10"
4
4
  authors = [
5
5
  { name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
6
6
  ]
7
7
  description = "Implemenation of same basic algorithms used in cryptography."
8
8
  readme = "README.md"
9
+ license = { text = "MIT" }
9
10
  requires-python = ">=3.10"
10
11
  classifiers = [
11
12
  "Programming Language :: Python :: 3",
@@ -15,5 +16,6 @@ classifiers = [
15
16
 
16
17
  [project.urls]
17
18
  Homepage = "https://github.com/teschlg/kryptools"
19
+ Source = "https://github.com/teschlg/kryptools"
18
20
  Issues = "https://github.com/teschlg/kryptools/issues"
19
21
  Docs = "https://github.com/teschlg/kryptools/tree/main/doc"
@@ -0,0 +1,66 @@
1
+ import pytest
2
+ from math import gcd
3
+ from kryptools import Zmod, euler_phi
4
+
5
+ def test_Zmod_ops():
6
+ for n in range(2, 10):
7
+ Z_n = Zmod(n)
8
+ for a in range(n):
9
+ aa = Z_n(a)
10
+ bool(aa) == bool(a)
11
+ for i in range(4):
12
+ assert aa ** i == Z_n(pow(a, i, n))
13
+ if i == 0 or gcd(a, n) == 1:
14
+ assert aa ** -i == Z_n(pow(a, -i, n))
15
+ else:
16
+ with pytest.raises(ValueError):
17
+ aa ** -i
18
+ if gcd(a, n) == 1:
19
+ assert aa ** aa.order() == Z_n(1)
20
+ else:
21
+ with pytest.raises(ValueError):
22
+ aa.order()
23
+ for b in range(n):
24
+ bb = Z_n(b)
25
+ assert aa + bb == Z_n(a + b)
26
+ assert aa - bb == Z_n(a - b)
27
+ assert aa * bb == Z_n(a * b)
28
+ if gcd(b, n) == 1:
29
+ assert aa / bb == Z_n( a * pow(b, -1, n) )
30
+
31
+ def test_Zmod_methods():
32
+ Z_5 = Zmod(5)
33
+ assert Z_5(3).order() == 4
34
+ assert Z_5(1).is_generator() == False
35
+ assert Z_5(3).is_generator() == True
36
+ assert Z_5.generator(all = True) == Z_5([2, 3])
37
+ assert Z_5.star() == Z_5([1, 2, 3, 4])
38
+ assert [Z_5(i).sharp() for i in range(Z_5.n)] == [0, 1, 2, -2, -1]
39
+ assert [abs(Z_5(i)) for i in range(Z_5.n)] == [0, 1, 2, 2, 1]
40
+ assert str(Z_5(3)) == "3"
41
+ Z_5.short = False
42
+ assert str(Z_5(6)) == "1 (mod 5)"
43
+
44
+ Z_6 = Zmod(6)
45
+ assert Z_6(5).order() == 2
46
+ with pytest.raises(ValueError):
47
+ Z_6(3).order()
48
+ assert Z_6(1).is_generator() == False
49
+ assert Z_6(5).is_generator() == True
50
+ assert Z_6.generator(all = True) == Z_6([5])
51
+ assert Z_6.star() == Z_6([1, 5])
52
+ assert str(Z_6(3)) == "3"
53
+
54
+ def test_Zmod_order():
55
+ for n in range(2,10):
56
+ Z_n = Zmod(n)
57
+ assert Z_n.order() == euler_phi(n)
58
+
59
+ OEIS_A033948 = [ 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 17, 18, 19, 22, 23, 25, 26, 27,
60
+ 29, 31, 34, 37, 38, 41, 43, 46, 47, 49, 50, 53, 54, 58, 59, 61, 62, 67, 71, 73, 74,
61
+ 79, 81, 82, 83, 86, 89, 94, 97, 98, 101, 103, 106, 107, 109, 113, 118, 121, 122, 125,
62
+ 127, 131, 134, 137, 139 ]
63
+
64
+ def test_Zmod_cyclic():
65
+ for n in range(2, OEIS_A033948[-1]+1):
66
+ assert Zmod(n).is_cyclic() == (n in OEIS_A033948)
@@ -0,0 +1,18 @@
1
+ import pytest
2
+ from random import randint, seed
3
+ from kryptools import dlog
4
+
5
+ seed(0)
6
+
7
+ with pytest.raises(ValueError):
8
+ dlog(1, 3, 10)
9
+
10
+ def test_dlogt():
11
+ for data in (
12
+ [557639, 278819, 2],
13
+ [24570203447, 12285101723, 2],
14
+ [28031135240181527, 14015567620090763, 2] ):
15
+ p, m, a = data # m is the order of a in Z_p
16
+ x = randint(2, m - 1)
17
+ b = pow(a, x, p)
18
+ assert dlog(a, b, p) == x
@@ -0,0 +1,27 @@
1
+ import pytest
2
+ from random import randint, seed
3
+ from kryptools import EC_Weierstrass
4
+ seed(0)
5
+
6
+
7
+ def test_EC():
8
+ ec = EC_Weierstrass(239, 3, 1)
9
+ O = ec(None, None) # point at infinity
10
+ assert O in ec
11
+ P = ec.random()
12
+ assert P in ec
13
+ Q = ec.random()
14
+ assert Q in ec
15
+ assert P + O == P
16
+ assert O + P == P
17
+ assert P - P == O
18
+ assert P + P == 2 * P
19
+ R = O
20
+ for i in range(5):
21
+ assert i * P == R
22
+ R += P
23
+ assert P + Q == Q + P
24
+ assert Q.order() * Q == O
25
+ k = randint(1, ec.order())
26
+ R = k * Q
27
+ assert R.dlog(Q) == k
@@ -0,0 +1,18 @@
1
+ import pytest
2
+ from kryptools import factorint, is_prime
3
+
4
+ def myprod(factors: dict) -> int:
5
+ n = 1
6
+ for p in factors:
7
+ assert is_prime(p)
8
+ n *= p**factors[p]
9
+ return n
10
+
11
+
12
+ def test_factorint():
13
+ assert factorint(0) == {0: 1}
14
+ assert factorint(1) == {}
15
+ for n in ( 1489576198567193874913874619387459183543154617315437135656,
16
+ 2**128 - 1,
17
+ 4521089809**7 ):
18
+ assert n == myprod(factorint(n))
@@ -0,0 +1,12 @@
1
+ import pytest
2
+ from math import prod
3
+ from kryptools import is_prime
4
+ from kryptools.factor_fmt import factor_fermat
5
+
6
+
7
+ def test_fermat():
8
+ for n in range(1,1000):
9
+ factors = factor_fermat(n)
10
+ assert n == prod(factors)
11
+ if len(factors) == 1:
12
+ assert is_prime(n)
@@ -0,0 +1,9 @@
1
+ import pytest
2
+ from kryptools import is_prime
3
+ from kryptools.factor_qs import factor_qs
4
+
5
+
6
+ def test_qs():
7
+ for n in [ 832283, 2**32+1 ]:
8
+ m = factor_qs(n)
9
+ assert n % m == 0 and 1 < m and m < n
@@ -0,0 +1,78 @@
1
+ import pytest
2
+ from random import randint, seed
3
+ from math import gcd
4
+ from kryptools import sieve_eratosthenes, factorint
5
+ from kryptools import iroot, ilog, perfect_square, perfect_power, prime_power
6
+ seed(0)
7
+
8
+
9
+ def test_iroot():
10
+ assert iroot(3, 0) == 0
11
+ assert iroot(3, 1) == 1
12
+ assert iroot(1, 3) == 3
13
+ for _ in range(100):
14
+ n = randint(0, 10**10)
15
+ for k in range(2, 5):
16
+ r = iroot(k, n)
17
+ assert r ** k <= n and (r+1) ** k > n
18
+
19
+ def test_ilog():
20
+ for n in range(1, 10000):
21
+ for b in range(2,5):
22
+ l = ilog(b, n)
23
+ assert b**l <= n and b**(l+1) > n
24
+ for _ in range(1000):
25
+ n = randint(1, 10**10)
26
+ for b in range(2,5):
27
+ l = ilog(b, n)
28
+ assert b**l <= n and b**(l+1) > n
29
+
30
+ def test_perfect_square():
31
+ for n in (-1, 0, 1):
32
+ assert perfect_square(n) == None
33
+ for n in range(2, 1000):
34
+ factors = factorint(n)
35
+ res = perfect_square(n)
36
+ if all( not (k % 2) for k in factors.values()):
37
+ assert isinstance(res, int)
38
+ assert res**2 == n
39
+ else:
40
+ assert res == None
41
+
42
+ def test_perfect_power():
43
+ for n in (-1, 0, 1):
44
+ assert perfect_power(n) == None
45
+ for n in range(2, 1000):
46
+ factors = factorint(n)
47
+ res = perfect_power(n)
48
+ g = gcd(*factors.values())
49
+ if g > 1:
50
+ assert isinstance(res, tuple)
51
+ assert res[0]**res[1] == n
52
+ n = -n
53
+ res = perfect_power(n)
54
+ while not g % 2:
55
+ g //= 2
56
+ if g > 1:
57
+ assert isinstance(res, tuple)
58
+ assert res[0]**res[1] == n
59
+ else:
60
+ assert res == None
61
+ else:
62
+ assert res == None
63
+ assert perfect_power(-n) == None
64
+
65
+ def test_prime_power():
66
+ for n in (-1, 0, 1):
67
+ assert prime_power(n) == None
68
+ for p in sieve_eratosthenes(30):
69
+ for k in range(1,6):
70
+ assert prime_power(p**k) == (p, k)
71
+ for n in range(2, 1000):
72
+ factors = factorint(n)
73
+ res = prime_power(n)
74
+ if len(factors) > 1:
75
+ assert res is None
76
+ else:
77
+ p, k = res
78
+ assert n == p**k
@@ -0,0 +1,20 @@
1
+ import pytest
2
+ from fractions import Fraction
3
+ from kryptools import Matrix, Zmod
4
+
5
+
6
+ def test_Matrix():
7
+ M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 12]])
8
+ assert len(M) == 3 * 3
9
+ assert M[0] == 1
10
+ assert M[:] == [1, 2, 3, 4, 5, 6, 7, 8, 12]
11
+ assert 2 * M - M == M
12
+ M.map(Fraction)
13
+ Mi = M.inv()
14
+ assert M * Mi == M.eye()
15
+
16
+ Z_11 = Zmod(11)
17
+ assert 2 * M - M == M
18
+ M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 12]], ring=Z_11)
19
+ Mi = M.inv()
20
+ assert M * Mi == M.eye()
@@ -0,0 +1,15 @@
1
+ import pytest
2
+ from fractions import Fraction
3
+ from kryptools import Matrix, gram_schmidt, random_unimodular_matrix
4
+
5
+
6
+ def test_gram_schmidt():
7
+ V = Matrix([[5, 8], [0, 1]], ring=Fraction)
8
+ Vs, M = gram_schmidt(V)
9
+ assert V == Vs * M
10
+
11
+ def test_random_unimodular_matrix():
12
+ for i in range(2, 5):
13
+ U = random_unimodular_matrix(i)
14
+ U.map(Fraction)
15
+ assert U.det() in (-1, 1)
@@ -0,0 +1,96 @@
1
+ import pytest
2
+ from random import randint, seed
3
+ from math import prod, lcm
4
+ from fractions import Fraction
5
+ from kryptools import sieve_eratosthenes, is_prime
6
+ from kryptools import egcd, crt, cf, convergents, legendre_symbol, jacobi_symbol, carmichael_lambda, euler_phi, moebius_mu, is_carmichael_number
7
+ seed(0)
8
+
9
+
10
+ def test_egcd():
11
+ for a in range(100):
12
+ for b in range(100):
13
+ g, x, y = egcd(a, b)
14
+ assert x * a + y * b == g
15
+
16
+ def test_crt():
17
+ maxsize = 1000
18
+ numtests = 100
19
+ primes = sieve_eratosthenes(11)
20
+ n = prod(primes)
21
+ for _ in range(numtests):
22
+ x = randint(0, n-1)
23
+ assert x == crt([x % p for p in primes], primes)
24
+ for _ in range(numtests):
25
+ m = []
26
+ for _ in range(10):
27
+ m.append(randint(2, maxsize))
28
+ M = lcm(*m)
29
+ x = randint(0, M-1)
30
+
31
+ def test_cf():
32
+ for x in (Fraction(21,11), Fraction(11,21), Fraction(1,3)):
33
+ assert convergents(cf(x))[-1] == x
34
+
35
+ jacobi_data = (
36
+ (1, 1), (0, 1, -1, 0), (0, 1, -1, -1, 1, 0), (0, 1, 1, -1, 1, -1, -1, 0),
37
+ (0, 1, 1, 0, 1, 1, 0, 1, 1, 0), (0, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 0),
38
+ (0, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, 1, 0),
39
+ (0, 1, 1, 0, 1, 0, 0, -1, 1, 0, 0, -1, 0, -1, -1, 0),
40
+ (0, 1, 1, -1, 1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 0) )
41
+
42
+ def test_jacobi_symbol():
43
+ for i in range(len(jacobi_data)):
44
+ n = 2 * i + 1
45
+ if is_prime(n):
46
+ for x in range(n+1):
47
+ assert jacobi_symbol(x, n) == jacobi_data[i][x]
48
+ assert jacobi_symbol(x, n) == legendre_symbol(x, n)
49
+ else:
50
+ for x in range(n+1):
51
+ assert jacobi_symbol(x, n) == jacobi_data[i][x]
52
+
53
+ #https://oeis.org/A000010
54
+ OEIS_A000010 = [
55
+ 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10, 22, 8, 20,
56
+ 12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22,
57
+ 46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20,
58
+ 66, 32, 44 ]
59
+
60
+ def test_euler_phi():
61
+ for n in range(1, len(OEIS_A000010)+1):
62
+ assert euler_phi(n) == OEIS_A000010[n - 1]
63
+
64
+ #https://oeis.org/A002322
65
+ OEIS_A002322 = [
66
+ 1, 1, 2, 2, 4, 2, 6, 2, 6, 4, 10, 2, 12, 6, 4, 4, 16, 6, 18, 4, 6, 10, 22, 2, 20,
67
+ 12, 18, 6, 28, 4, 30, 8, 10, 16, 12, 6, 36, 18, 12, 4, 40, 6, 42, 10, 12, 22, 46,
68
+ 4, 42, 20, 16, 12, 52, 18, 20, 6, 18, 28, 58, 4, 60, 30, 6, 16, 12, 10, 66, 16,
69
+ 22, 12, 70, 6, 72, 36, 20, 18, 30, 12, 78, 4, 54 ]
70
+
71
+ def test_carmichael_lambda():
72
+ for n in range(1, len(OEIS_A002322)+1):
73
+ assert carmichael_lambda(n) == OEIS_A002322[n - 1]
74
+
75
+ #https://oeis.org/A008683
76
+ OEIS_A008683 = [
77
+ 1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1, 0,
78
+ 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, -1, -1, 0, 0, 1, -1, 0, 0,
79
+ 0, 1, 0, -1, 0, 1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 1, -1, -1, 0, 1, -1, -1, 0, -1, 1,
80
+ 0, 0, 1, -1 ]
81
+
82
+ def test_moebius_mu():
83
+ for n in range(1, len(OEIS_A008683)+1):
84
+ assert moebius_mu(n) == OEIS_A008683[n - 1]
85
+
86
+ OEIS_A002997 = [ 561, 1105, 1729, 2465, 2821, 6601, 8911, 10585, 15841, 29341, 41041,
87
+ 46657, 52633, 62745, 63973, 75361, 101101, 115921, 126217, 162401, 172081, 188461,
88
+ 252601, 278545, 294409, 314821, 334153, 340561, 399001, 410041, 449065, 488881,
89
+ 512461, 530881, 552721 ]
90
+
91
+ def test_is_carmichael_number():
92
+ for n in range(OEIS_A002997[4]+2):
93
+ assert is_carmichael_number(n) == (n in OEIS_A002997)
94
+ for n in OEIS_A002997:
95
+ assert is_carmichael_number(n) == True
96
+
@@ -0,0 +1,15 @@
1
+ import pytest
2
+ from kryptools import Poly, Zmod
3
+
4
+
5
+ def test_Poly():
6
+ p = Poly([2, 0, 2])
7
+ q = Poly([1, 2, 3, 4])
8
+ d, m = q.divmod(p)
9
+ assert d * p + m == q
10
+
11
+ Z_5 = Zmod(5)
12
+ p.map(Z_5)
13
+ q.map(Z_5)
14
+ d, m = q.divmod(p)
15
+ assert d * p + m == q
@@ -0,0 +1,104 @@
1
+ import pytest
2
+ from math import isqrt
3
+ from random import randint, seed
4
+ from kryptools import sieve_eratosthenes, prime_pi, next_prime, previous_prime, is_prime, is_safeprime, miller_rabin_test, lucas_test
5
+ seed(0)
6
+
7
+ #https://oeis.org/A000040
8
+ OEIS_A000010 = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
9
+ 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
10
+ 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251,
11
+ 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353,
12
+ 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457,
13
+ 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
14
+ 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673,
15
+ 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
16
+ 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
17
+ 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021,
18
+ 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109,
19
+ 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223 ]
20
+
21
+ def sieve(max: int) -> list:
22
+ is_prime = [True] * (max + 1) # to begin with, all numbers are potentially prime
23
+ is_prime[0 :: 2] = [False] * (max // 2 + 1)
24
+ is_prime[1] = False
25
+ is_prime[2] = True
26
+
27
+ # sieve out the primes starting at 3 in steps of 2 (ignoring even numbers)
28
+ for p in range(3, isqrt(max - 1) + 2, 2):
29
+ if is_prime[p]: # sieve out all multiples; note that numbers p*q with q<p were already sieved out previously
30
+ pp = p * p
31
+ p2 = p + p
32
+ is_prime[pp :: p2] = [False] * ((max - pp) // p2 + 1)
33
+ return is_prime
34
+
35
+ prime_test = sieve(OEIS_A000010[-1])
36
+ assert [ p for p in range(OEIS_A000010[-1]+1) if prime_test[p] ] == OEIS_A000010
37
+
38
+ def test_sieve_eratosthenes():
39
+ for i, p in enumerate(sieve_eratosthenes(OEIS_A000010[-1])):
40
+ assert p == OEIS_A000010[i]
41
+ if i > 0:
42
+ assert previous_prime(p-1) == OEIS_A000010[i-1]
43
+ if i < len(OEIS_A000010) - 1:
44
+ assert next_prime(p+1) == OEIS_A000010[i+1]
45
+
46
+ #https://oeis.org/A000040
47
+ OEIS_A000720 = [ 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,
48
+ 9, 9, 9, 9, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 14, 14,
49
+ 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 18, 18, 18, 18, 19,
50
+ 19, 19, 19, 20, 20, 21, 21, 21, 21, 21, 21 ]
51
+
52
+ def test_prime_pi():
53
+ for n in range(1, len(OEIS_A000720)+1):
54
+ assert prime_pi(n) == OEIS_A000720[n - 1]
55
+
56
+ max_sieve = 25326001
57
+ prime_test = sieve(max_sieve)
58
+
59
+ def test_is_prime():
60
+ for n in range(max_sieve + 1):
61
+ assert is_prime(n) == prime_test[n]
62
+
63
+ OEIS_A005385 = [ 5, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347, 359, 383, 467,
64
+ 479, 503, 563, 587, 719, 839, 863, 887, 983, 1019, 1187, 1283, 1307, 1319, 1367, 1439,
65
+ 1487, 1523, 1619, 1823, 1907, 2027, 2039, 2063, 2099, 2207, 2447, 2459, 2579, 2819,
66
+ 2879, 2903, 2963 ]
67
+
68
+ def test_is_safeprime():
69
+ for n in range(OEIS_A000720[-1]+2):
70
+ assert is_safeprime(n) == (n in OEIS_A005385)
71
+ for n in OEIS_A005385:
72
+ assert is_safeprime(n) == True
73
+
74
+ OEIS_A014233 = [ 2047, 1373653, 25326001, 3215031751, 2152302898747, 3474749660383,
75
+ 341550071728321, 341550071728321, 3825123056546413051, 3825123056546413051,
76
+ 3825123056546413051, 318665857834031151167461, 3317044064679887385961981 ]
77
+
78
+ if OEIS_A014233[2] > max_sieve:
79
+ prime_test = sieve(OEIS_A014233[2])
80
+
81
+ def test_miller_rabin():
82
+ for i, n in enumerate(OEIS_A014233[0:3]):
83
+ bases = OEIS_A000010[:i+1]
84
+ assert miller_rabin_test(n, bases) == True
85
+ if i < 1:
86
+ for m in range(3, n, 2):
87
+ assert miller_rabin_test(m, bases) == prime_test[m]
88
+ else:
89
+ for _ in range(500 * i):
90
+ r = randint(2, n)
91
+ assert miller_rabin_test(m, bases) == prime_test[m]
92
+
93
+ OEIS_A217255 = [ 5459, 5777, 10877, 16109, 18971, 22499, 24569, 25199, 40309, 58519,
94
+ 75077, 97439, 100127, 113573, 115639, 130139, 155819, 158399, 161027, 162133, 176399,
95
+ 176471, 189419, 192509, 197801, 224369, 230691, 231703, 243629, 253259, 268349,
96
+ 288919, 313499, 324899 ]
97
+
98
+ if OEIS_A217255[30] > max_sieve:
99
+ prime_test = sieve(OEIS_A217255[30])
100
+
101
+ def test_lucas():
102
+ for m in range(2, OEIS_A217255[30]+1):
103
+ if not (lucas_test(m) == prime_test[m]):
104
+ assert m in OEIS_A217255
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes