kryptools 0.9.7__tar.gz → 0.9.9__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.7 → kryptools-0.9.9}/PKG-INFO +3 -3
- {kryptools-0.9.7 → kryptools-0.9.9}/README.md +1 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/Zmod.py +9 -11
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/__init__.py +6 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/ec.py +5 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor.py +1 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor_ecm.py +0 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor_fmt.py +1 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor_pm1.py +1 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor_rho.py +0 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/la.py +31 -16
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/nt.py +3 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/poly.py +44 -63
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/primes.py +3 -3
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools.egg-info/PKG-INFO +3 -3
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools.egg-info/SOURCES.txt +10 -1
- {kryptools-0.9.7 → kryptools-0.9.9}/pyproject.toml +2 -2
- kryptools-0.9.9/tests/test_Zmod.py +51 -0
- kryptools-0.9.9/tests/test_dlog.py +18 -0
- kryptools-0.9.9/tests/test_ec.py +25 -0
- kryptools-0.9.9/tests/test_factor.py +18 -0
- kryptools-0.9.9/tests/test_la.py +15 -0
- kryptools-0.9.9/tests/test_lat.py +9 -0
- kryptools-0.9.9/tests/test_nt.py +38 -0
- kryptools-0.9.9/tests/test_poly.py +15 -0
- kryptools-0.9.9/tests/test_primes.py +27 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/LICENSE +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/dlp.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/dlp_ic.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/dlp_qs.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/dlp_rho.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor_dix.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/factor_qs.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools/lat.py +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-0.9.7 → kryptools-0.9.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.9
|
|
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
|
|
@@ -9,7 +9,7 @@ Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ The tools contained are:
|
|
|
29
29
|
* number theory: sqrt modulo primes, Jacobi/Legendre symbol, Chinese Remainder Theorem, continued fractions, etc.
|
|
30
30
|
* primes: Sieve of Erathostenes, primality tests, generation of random safe/strong primes
|
|
31
31
|
* solvers for discrete logarithms (naive, Pollard rho, Shanks baby step/giant step, index calculus, quadratic sieve)
|
|
32
|
-
* integer factorization (Fermat, Pollard p-1, Lenstra's ECM, Dixon, basic quadratic sieve)
|
|
32
|
+
* integer factorization (Fermat, Pollard p-1, Pollard rho, Lenstra's ECM, Dixon, basic quadratic sieve)
|
|
33
33
|
* elliptic curves (Weierstrass form), group operations, order, discrete logarithms
|
|
34
34
|
* linear algebra: Hermite normal form, Gram-Schmidt
|
|
35
35
|
* lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
|
|
@@ -14,7 +14,7 @@ The tools contained are:
|
|
|
14
14
|
* number theory: sqrt modulo primes, Jacobi/Legendre symbol, Chinese Remainder Theorem, continued fractions, etc.
|
|
15
15
|
* primes: Sieve of Erathostenes, primality tests, generation of random safe/strong primes
|
|
16
16
|
* solvers for discrete logarithms (naive, Pollard rho, Shanks baby step/giant step, index calculus, quadratic sieve)
|
|
17
|
-
* integer factorization (Fermat, Pollard p-1, Lenstra's ECM, Dixon, basic quadratic sieve)
|
|
17
|
+
* integer factorization (Fermat, Pollard p-1, Pollard rho, Lenstra's ECM, Dixon, basic quadratic sieve)
|
|
18
18
|
* elliptic curves (Weierstrass form), group operations, order, discrete logarithms
|
|
19
19
|
* linear algebra: Hermite normal form, Gram-Schmidt
|
|
20
20
|
* lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
|
|
@@ -30,7 +30,11 @@ class Zmod:
|
|
|
30
30
|
self.group_order = 0
|
|
31
31
|
self.factors = {} # factoring of the group order
|
|
32
32
|
|
|
33
|
-
def __call__(self, x: int):
|
|
33
|
+
def __call__(self, x: int|list|tuple):
|
|
34
|
+
if isinstance(x, list):
|
|
35
|
+
return [ZmodPoint(xx, self) for xx in x]
|
|
36
|
+
elif isinstance(x, tuple):
|
|
37
|
+
return (ZmodPoint(xx, self) for xx in x)
|
|
34
38
|
return ZmodPoint(x, self)
|
|
35
39
|
|
|
36
40
|
def __eq__(self, other):
|
|
@@ -76,7 +80,7 @@ class Zmod:
|
|
|
76
80
|
|
|
77
81
|
def star(self) -> int:
|
|
78
82
|
"""Return a list of all elements of the group Z_n^*."""
|
|
79
|
-
elements = [ 1 ]
|
|
83
|
+
elements = [ self(1) ]
|
|
80
84
|
for a in range(2, self.n):
|
|
81
85
|
if gcd(a, self.n) > 1:
|
|
82
86
|
continue
|
|
@@ -119,9 +123,7 @@ class ZmodPoint:
|
|
|
119
123
|
return NotImplemented
|
|
120
124
|
|
|
121
125
|
def __radd__(self, scalar: int) -> "ZmodPoint":
|
|
122
|
-
|
|
123
|
-
return self.__class__(scalar + self.x, self.ring)
|
|
124
|
-
return NotImplemented
|
|
126
|
+
return self + scalar
|
|
125
127
|
|
|
126
128
|
def __neg__(self) -> "ZmodPoint":
|
|
127
129
|
return self.__class__(-self.x, self.ring)
|
|
@@ -139,9 +141,7 @@ class ZmodPoint:
|
|
|
139
141
|
return NotImplemented
|
|
140
142
|
|
|
141
143
|
def __rsub__(self, scalar: int) -> "ZmodPoint":
|
|
142
|
-
|
|
143
|
-
return self.__class__(scalar - self.x, self.ring)
|
|
144
|
-
return NotImplemented
|
|
144
|
+
return (- self) + scalar
|
|
145
145
|
|
|
146
146
|
def __mul__(self, other: "ZmodPoint") -> "ZmodPoint":
|
|
147
147
|
if isinstance(other, self.__class__):
|
|
@@ -153,9 +153,7 @@ class ZmodPoint:
|
|
|
153
153
|
return NotImplemented
|
|
154
154
|
|
|
155
155
|
def __rmul__(self, scalar: int) -> "ZmodPoint":
|
|
156
|
-
|
|
157
|
-
return self.__class__(scalar * self.x, self.ring)
|
|
158
|
-
return NotImplemented
|
|
156
|
+
return self * scalar
|
|
159
157
|
|
|
160
158
|
def __truediv__(self, other: "ZmodPoint") -> "ZmodPoint":
|
|
161
159
|
if isinstance(other, self.__class__):
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
Implemenation of same basic algorithms used in cryptography.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
__author__ = "Gerald Teschl"
|
|
6
|
+
__copyright__ = "Copyright 2024, Gerald Teschl"
|
|
7
|
+
__license__ = "MIT License"
|
|
8
|
+
__version__ = "0.9.9"
|
|
9
|
+
__email__ = "Gerald.Teschl@univie.ac.at"
|
|
10
|
+
|
|
5
11
|
from .nt import egcd, cf, convergents, legendre_symbol, jacobi_symbol, sqrt_mod, euler_phi, carmichael_lambda, moebius_mu, order, crt
|
|
6
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
|
|
7
13
|
from .factor import factorint, divisors
|
|
@@ -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
|
|
275
|
+
if self not in curve:
|
|
272
276
|
raise ValueError("Point not on curve!")
|
|
273
277
|
|
|
274
278
|
def __repr__(self):
|
|
@@ -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
|
|
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
|
|
@@ -23,6 +23,9 @@ class Matrix:
|
|
|
23
23
|
self.matrix = matrix
|
|
24
24
|
self.cols = len(matrix[0])
|
|
25
25
|
self.rows = len(matrix)
|
|
26
|
+
for i in range(1, self.rows):
|
|
27
|
+
if len(matrix[i]) != self.cols:
|
|
28
|
+
raise ValueError("All matrix rows must have equal length!")
|
|
26
29
|
if ring:
|
|
27
30
|
self.map(ring)
|
|
28
31
|
|
|
@@ -62,11 +65,11 @@ class Matrix:
|
|
|
62
65
|
cols = i
|
|
63
66
|
else:
|
|
64
67
|
cols = range(self.cols)[j]
|
|
65
|
-
return
|
|
68
|
+
return self.__class__([[self.matrix[i][j] for j in cols] for i in rows])
|
|
66
69
|
if isinstance(item, int):
|
|
67
70
|
i, j = divmod(item, self.cols)
|
|
68
71
|
return self.matrix[i][j]
|
|
69
|
-
return
|
|
72
|
+
return self.__class__([self.matrix[k // self.cols][k % self.cols] for k in range(self.cols * self.rows)[item]])
|
|
70
73
|
|
|
71
74
|
def __setitem__(self, item, value):
|
|
72
75
|
if isinstance(item, tuple):
|
|
@@ -89,15 +92,24 @@ class Matrix:
|
|
|
89
92
|
for i, ii in zip(cols,range(len(cols))):
|
|
90
93
|
for j, jj in zip(rows,range(len(rows))):
|
|
91
94
|
self.matrix[j][i] = value[jj,ii]
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
elif isinstance(item, int):
|
|
96
|
+
i, j = divmod(item, self.cols)
|
|
97
|
+
self.matrix[i][j] = value
|
|
98
|
+
else:
|
|
99
|
+
for k in range(self.cols * self.rows)[item]:
|
|
100
|
+
i, j = divmod(k, self.cols)
|
|
101
|
+
self.matrix[i][j] = value[k]
|
|
95
102
|
|
|
96
103
|
def __eq__(self, other):
|
|
97
104
|
if not isinstance(other, self.__class__):
|
|
98
105
|
return False
|
|
99
|
-
|
|
106
|
+
if self.rows != other.rows or self.cols != other.cols:
|
|
107
|
+
return False
|
|
108
|
+
return any([ self[i] == other[i] for i in range(self.rows * self.cols) ])
|
|
100
109
|
|
|
110
|
+
def __bool__(self):
|
|
111
|
+
return any([ bool(self[i]) for i in range(self.rows * self.cols) ])
|
|
112
|
+
|
|
101
113
|
def map(self, func):
|
|
102
114
|
"Apply a function to all elements in place."
|
|
103
115
|
for row in self.matrix:
|
|
@@ -156,14 +168,14 @@ class Matrix:
|
|
|
156
168
|
if isinstance(other, Matrix):
|
|
157
169
|
if other.cols != self.cols or other.rows != self.rows:
|
|
158
170
|
raise NotImplementedError("Matrix dimensions do not match!")
|
|
159
|
-
return
|
|
171
|
+
return self.__class__([ [ x1 + y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
|
|
160
172
|
return NotImplemented
|
|
161
173
|
|
|
162
174
|
def __sub__(self, other) -> "Matrix":
|
|
163
175
|
if isinstance(other, Matrix):
|
|
164
176
|
if other.cols != self.cols or other.rows != self.rows:
|
|
165
177
|
raise NotImplementedError("Matrix dimensions do not match!")
|
|
166
|
-
return
|
|
178
|
+
return self.__class__([ [ x1 - y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
|
|
167
179
|
return NotImplemented
|
|
168
180
|
|
|
169
181
|
def __neg__(self) -> "Matrix":
|
|
@@ -175,13 +187,13 @@ class Matrix:
|
|
|
175
187
|
def __mul__(self, other) -> "Matrix":
|
|
176
188
|
if isinstance(other, Matrix):
|
|
177
189
|
return self.multiply(other)
|
|
178
|
-
if isinstance(other, Number) or type(other)
|
|
179
|
-
return
|
|
190
|
+
if isinstance(other, Number) or type(other) is type(self.matrix[0][0]):
|
|
191
|
+
return self.__class__([ [item * other for item in row] for row in self.matrix ])
|
|
180
192
|
return NotImplemented
|
|
181
193
|
|
|
182
194
|
def __rmul__(self, other) -> "Matrix":
|
|
183
|
-
if isinstance(other, Number) or type(other)
|
|
184
|
-
return
|
|
195
|
+
if isinstance(other, Number) or type(other) is type(self.matrix[0][0]):
|
|
196
|
+
return self.__class__([ [item * other for item in row] for row in self.matrix ])
|
|
185
197
|
return NotImplemented
|
|
186
198
|
|
|
187
199
|
def rref(self) -> "Matrix":
|
|
@@ -242,7 +254,7 @@ class Matrix:
|
|
|
242
254
|
if self.rows != self.cols:
|
|
243
255
|
raise ValueError("Matrix must be square!")
|
|
244
256
|
n = self.cols
|
|
245
|
-
MM =
|
|
257
|
+
MM = self.__class__([[0 for _ in range(2*n)] for _ in range(n)])
|
|
246
258
|
for i in range(n):
|
|
247
259
|
MM[i,n+i] = 1
|
|
248
260
|
MM[:,0:n] = self
|
|
@@ -271,7 +283,7 @@ class Matrix:
|
|
|
271
283
|
zero = 0 * self[0]
|
|
272
284
|
except:
|
|
273
285
|
zero = 0
|
|
274
|
-
return
|
|
286
|
+
return self.__class__([[ zero for j in range(n) ] for i in range(m) ])
|
|
275
287
|
|
|
276
288
|
def eye(self, m: int = None, n: int = None):
|
|
277
289
|
"Returns an identity matrix of the same dimension."
|
|
@@ -287,8 +299,11 @@ class Matrix:
|
|
|
287
299
|
zero = 0 * self[0]
|
|
288
300
|
except:
|
|
289
301
|
zero = 0
|
|
290
|
-
|
|
291
|
-
|
|
302
|
+
try:
|
|
303
|
+
one = zero**0
|
|
304
|
+
except:
|
|
305
|
+
one = 1
|
|
306
|
+
return self.__class__([[ delta(i, j) for j in range(n) ] for i in range(m) ])
|
|
292
307
|
|
|
293
308
|
|
|
294
309
|
def zeros(m: int, n: int = None, zero = 0) -> "Matrix":
|
|
@@ -143,6 +143,8 @@ def carmichael_lambda(n: int) -> int:
|
|
|
143
143
|
"""Carmichael's lambda function of `n`."""
|
|
144
144
|
if not isinstance(n, int) or n < 1:
|
|
145
145
|
raise ValueError(f"{n} is not a positive integer")
|
|
146
|
+
if n == 1:
|
|
147
|
+
return 1
|
|
146
148
|
k = factorint(n)
|
|
147
149
|
lam_all = [] # values corresponding to the prime factors
|
|
148
150
|
for p in k:
|
|
@@ -224,7 +226,7 @@ def crt(a: list[int], m: list[int], coprime = True) -> int:
|
|
|
224
226
|
if g == 1: # moduli are coprime, nothing to be done
|
|
225
227
|
continue
|
|
226
228
|
if (a[i] - a[j]) % g:
|
|
227
|
-
|
|
229
|
+
raise ValueError("Congruences not solvable!")
|
|
228
230
|
for p, k in factorint(g).items():
|
|
229
231
|
p = p**k # remove this factor from one of the equations
|
|
230
232
|
if gcd(m[i] // p, p) == 1:
|
|
@@ -55,8 +55,9 @@ class Poly:
|
|
|
55
55
|
return "x"
|
|
56
56
|
return "x^" + str(i)
|
|
57
57
|
|
|
58
|
-
if
|
|
58
|
+
if not self:
|
|
59
59
|
return str(self.coeff[0])
|
|
60
|
+
one = self.coeff[0]**0
|
|
60
61
|
plus = ""
|
|
61
62
|
tmp = ""
|
|
62
63
|
coef_range = range(len(self.coeff))
|
|
@@ -66,12 +67,12 @@ class Poly:
|
|
|
66
67
|
s = self.coeff[i]
|
|
67
68
|
if not s:
|
|
68
69
|
continue
|
|
69
|
-
if not s -
|
|
70
|
+
if not s - one and i != 0:
|
|
70
71
|
tmp += plus + prx(i)
|
|
71
72
|
plus = " + "
|
|
72
73
|
continue
|
|
73
74
|
try:
|
|
74
|
-
if s
|
|
75
|
+
if not s + one and i != 0:
|
|
75
76
|
if plus:
|
|
76
77
|
plus = " "
|
|
77
78
|
tmp += plus + "- " + prx(i)
|
|
@@ -96,7 +97,7 @@ class Poly:
|
|
|
96
97
|
def __eq__(self, other):
|
|
97
98
|
if not isinstance(other, self.__class__):
|
|
98
99
|
return False
|
|
99
|
-
return self
|
|
100
|
+
return not bool(self - other)
|
|
100
101
|
|
|
101
102
|
def __bool__(self):
|
|
102
103
|
return bool(self.degree()) or bool(self.coeff[0])
|
|
@@ -116,7 +117,18 @@ class Poly:
|
|
|
116
117
|
def _check_type(self, other):
|
|
117
118
|
return isinstance(other, int) or (isinstance(other, Number) and isinstance(self.coeff[0], Number)) or type(other) == type(self.coeff[0])
|
|
118
119
|
|
|
120
|
+
def _guess_ring(self):
|
|
121
|
+
zero = 0 * self.coeff[0]
|
|
122
|
+
try:
|
|
123
|
+
ring = type(zero)
|
|
124
|
+
one = ring(1)
|
|
125
|
+
except:
|
|
126
|
+
ring = None
|
|
127
|
+
one = zero**0 # zero + 1
|
|
128
|
+
return zero, one, ring
|
|
129
|
+
|
|
119
130
|
def __add__(self, other: "Poly") -> "Poly":
|
|
131
|
+
zero = 0 * self.coeff[0]
|
|
120
132
|
if not isinstance(other, self.__class__):
|
|
121
133
|
if self._check_type(other):
|
|
122
134
|
tmp = self.coeff[:]
|
|
@@ -125,11 +137,11 @@ class Poly:
|
|
|
125
137
|
return NotImplemented
|
|
126
138
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
127
139
|
if ls < lo:
|
|
128
|
-
scoeff = self.coeff + (lo - ls) * [
|
|
140
|
+
scoeff = self.coeff + (lo - ls) * [zero]
|
|
129
141
|
else:
|
|
130
142
|
scoeff = self.coeff
|
|
131
143
|
if ls > lo:
|
|
132
|
-
ocoeff = other.coeff + (ls - lo) * [
|
|
144
|
+
ocoeff = other.coeff + (ls - lo) * [zero]
|
|
133
145
|
else:
|
|
134
146
|
ocoeff = other.coeff
|
|
135
147
|
modulus = self.modulus
|
|
@@ -138,12 +150,7 @@ class Poly:
|
|
|
138
150
|
return self.__class__([s + o for s, o in zip(scoeff, ocoeff)], modulus=modulus)
|
|
139
151
|
|
|
140
152
|
def __radd__(self, other: "Poly") -> "Poly":
|
|
141
|
-
|
|
142
|
-
if self._check_type(other):
|
|
143
|
-
tmp = self.coeff[:]
|
|
144
|
-
tmp[0] += other
|
|
145
|
-
return self.__class__(tmp, modulus=self.modulus)
|
|
146
|
-
return NotImplemented
|
|
153
|
+
return self + other
|
|
147
154
|
|
|
148
155
|
def __neg__(self) -> "Poly":
|
|
149
156
|
return Poly([-s for s in self.coeff], modulus=self.modulus)
|
|
@@ -152,57 +159,33 @@ class Poly:
|
|
|
152
159
|
return self
|
|
153
160
|
|
|
154
161
|
def __sub__(self, other: "Poly") -> "Poly":
|
|
155
|
-
|
|
156
|
-
if self._check_type(other):
|
|
157
|
-
tmp = self.coeff[:]
|
|
158
|
-
tmp[0] -= other
|
|
159
|
-
return self.__class__(tmp, modulus=self.modulus)
|
|
160
|
-
return NotImplemented
|
|
161
|
-
ls, lo = len(self.coeff), len(other.coeff)
|
|
162
|
-
if ls < lo:
|
|
163
|
-
scoeff = self.coeff + (lo - ls) * [0]
|
|
164
|
-
else:
|
|
165
|
-
scoeff = self.coeff
|
|
166
|
-
if ls > lo:
|
|
167
|
-
ocoeff = other.coeff + (ls - lo) * [0]
|
|
168
|
-
else:
|
|
169
|
-
ocoeff = other.coeff
|
|
170
|
-
modulus = self.modulus
|
|
171
|
-
if not modulus and other.modulus:
|
|
172
|
-
modulus = other.modulus
|
|
173
|
-
return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus = modulus)
|
|
162
|
+
return self + (- other)
|
|
174
163
|
|
|
175
164
|
def __rsub__(self, other: "Poly") -> "Poly":
|
|
176
|
-
|
|
177
|
-
if self._check_type(other):
|
|
178
|
-
tmp = self.coeff[:]
|
|
179
|
-
tmp[0] -= other
|
|
180
|
-
return self.__class__(tmp, modulus=self.modulus)
|
|
181
|
-
return NotImplemented
|
|
165
|
+
return (- self) + other
|
|
182
166
|
|
|
183
167
|
def __mul__(self, other: "Poly") -> "Poly":
|
|
184
168
|
if not isinstance(other, self.__class__):
|
|
185
169
|
if self._check_type(other):
|
|
186
170
|
return Poly([other * s for s in self.coeff], modulus = self.modulus)
|
|
187
171
|
return NotImplemented
|
|
172
|
+
zero = 0 * self.coeff[0]
|
|
188
173
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
189
|
-
coeff = [
|
|
174
|
+
coeff = [None] * (ls + lo - 1)
|
|
190
175
|
for k in range(ls + lo - 1):
|
|
191
176
|
coeff[k] = sum(
|
|
192
177
|
[
|
|
193
178
|
self.coeff[j] * other.coeff[k - j]
|
|
194
179
|
for j in range(max(0, k - lo + 1), min(ls, k + 1))
|
|
195
180
|
]
|
|
196
|
-
)
|
|
181
|
+
, start = zero)
|
|
197
182
|
modulus = self.modulus
|
|
198
183
|
if not modulus and other.modulus:
|
|
199
184
|
modulus = other.modulus
|
|
200
185
|
return self.__class__(coeff, modulus = modulus)
|
|
201
186
|
|
|
202
187
|
def __rmul__(self, other) -> "Poly":
|
|
203
|
-
|
|
204
|
-
return self.__class__([other * s for s in self.coeff], modulus=self.modulus)
|
|
205
|
-
return NotImplemented
|
|
188
|
+
return self * other
|
|
206
189
|
|
|
207
190
|
def __truediv__(self, other) -> "Poly":
|
|
208
191
|
if self._check_type(other):
|
|
@@ -221,8 +204,7 @@ class Poly:
|
|
|
221
204
|
def __pow__(self, j: int) -> "Poly":
|
|
222
205
|
if not isinstance(j, int):
|
|
223
206
|
return NotImplemented
|
|
224
|
-
|
|
225
|
-
one = zero + 1
|
|
207
|
+
one = self.coeff[0]**0
|
|
226
208
|
res = self.__class__([one], modulus=self.modulus)
|
|
227
209
|
if j < 0:
|
|
228
210
|
if not self.modulus:
|
|
@@ -256,39 +238,39 @@ class Poly:
|
|
|
256
238
|
|
|
257
239
|
def divmod(self, other: "Poly") -> ("Poly", "Poly"):
|
|
258
240
|
"Polynom division with remainder."
|
|
241
|
+
zero, one, ring = self._guess_ring()
|
|
259
242
|
if isinstance(other, list):
|
|
260
|
-
other = self.__class__(other)
|
|
243
|
+
other = self.__class__(other , ring = ring)
|
|
261
244
|
elif not isinstance(other, self.__class__):
|
|
262
245
|
raise NotImplementedError(f"Cannot divide {self} and {other}.")
|
|
263
246
|
if not other:
|
|
264
247
|
raise ValueError(f"{other} must be nonzero.")
|
|
265
248
|
sd, od = self.degree(), other.degree()
|
|
266
249
|
if sd < od:
|
|
267
|
-
return self.__class__([
|
|
268
|
-
div = [
|
|
250
|
+
return self.__class__([zero]), self
|
|
251
|
+
div = [zero] * (sd - od + 1)
|
|
269
252
|
lco = other.coeff[-1]
|
|
270
|
-
if bool(lco -
|
|
271
|
-
tmp =
|
|
253
|
+
if bool(lco - one):
|
|
254
|
+
tmp = one / lco
|
|
272
255
|
oth = [c * tmp for c in other.coeff]
|
|
273
256
|
rem = [c * tmp for c in self.coeff]
|
|
274
257
|
else:
|
|
275
258
|
oth = other.coeff
|
|
276
|
-
rem = [c for c in self.coeff]
|
|
259
|
+
rem = [c * one for c in self.coeff] # "* 1" is here to make sure we get a copy
|
|
277
260
|
for i in range(sd - od + 1):
|
|
278
|
-
tmp = rem[sd - i]
|
|
261
|
+
tmp = rem[sd - i] * one # "* 1" is here to make sure we get a copy
|
|
279
262
|
div[sd - od - i] = tmp
|
|
280
263
|
for j in range(od + 1):
|
|
281
264
|
rem[sd - i - j] -= tmp * oth[od - j]
|
|
282
|
-
if bool(lco -
|
|
265
|
+
if bool(lco - one):
|
|
283
266
|
rem = [c * lco for c in rem]
|
|
284
|
-
return self.__class__(div, modulus=self.modulus), self.__class__(
|
|
285
|
-
rem, modulus=self.modulus
|
|
286
|
-
)
|
|
267
|
+
return self.__class__(div, modulus=self.modulus), self.__class__(rem, modulus=self.modulus)
|
|
287
268
|
|
|
288
269
|
def mod(self, other: "Poly") -> None:
|
|
289
270
|
"Reduce with respect to a given polynomial."
|
|
271
|
+
one, ring = self._guess_ring()[1:]
|
|
290
272
|
if isinstance(other, list):
|
|
291
|
-
other = self.__class__(other)
|
|
273
|
+
other = self.__class__(other , ring = ring)
|
|
292
274
|
elif not isinstance(other, self.__class__):
|
|
293
275
|
raise NotImplementedError(f"Cannot divide {self} and {other}.")
|
|
294
276
|
if not other:
|
|
@@ -297,13 +279,13 @@ class Poly:
|
|
|
297
279
|
if sd < od:
|
|
298
280
|
return self
|
|
299
281
|
lco = other.coeff[-1]
|
|
300
|
-
if bool(lco -
|
|
301
|
-
tmp =
|
|
282
|
+
if bool(lco - one):
|
|
283
|
+
tmp = one / lco
|
|
302
284
|
oth = [c * tmp for c in other.coeff]
|
|
303
285
|
else:
|
|
304
286
|
oth = other.coeff
|
|
305
287
|
for i in range(sd - od + 1):
|
|
306
|
-
tmp = self.coeff[sd - i]
|
|
288
|
+
tmp = self.coeff[sd - i] * one # "* 1" is here to make sure we get a copy
|
|
307
289
|
for j in range(od + 1):
|
|
308
290
|
self.coeff[sd - i - j] -= tmp * oth[od - j]
|
|
309
291
|
for i in range(len(self.coeff) - 1, 0, -1):
|
|
@@ -315,14 +297,13 @@ class Poly:
|
|
|
315
297
|
"Inverse modulo a given polynomial."
|
|
316
298
|
if not other:
|
|
317
299
|
other = self.modulus
|
|
300
|
+
zero, one, ring = self._guess_ring()
|
|
318
301
|
if isinstance(other, list):
|
|
319
|
-
other = self.__class__(other)
|
|
302
|
+
other = self.__class__(other , ring = ring)
|
|
320
303
|
elif not isinstance(other, self.__class__):
|
|
321
304
|
raise NotImplementedError(f"Cannot invert {self} modulo {other}.")
|
|
322
305
|
if not other:
|
|
323
306
|
raise NotImplementedError(f"{other} must be nonzero.")
|
|
324
|
-
zero = 0 * self.coeff[0]
|
|
325
|
-
one = zero +1
|
|
326
307
|
r0, r1 = other, self
|
|
327
308
|
y0, y1 = self.__class__([zero], modulus=self.modulus), self.__class__([one], modulus=self.modulus)
|
|
328
309
|
while r1:
|
|
@@ -331,7 +312,7 @@ class Poly:
|
|
|
331
312
|
y0, y1 = y1, y0 - q * y1
|
|
332
313
|
if r0.degree() != 0:
|
|
333
314
|
raise ValueError(f"{self} is not invertible mod {other}.")
|
|
334
|
-
tmp =
|
|
315
|
+
tmp = one / r0[0]
|
|
335
316
|
for i in range(len(y0)):
|
|
336
317
|
y0.coeff[i] *= tmp
|
|
337
318
|
return y0
|
|
@@ -72,7 +72,7 @@ def miller_rabin_test(n: int, bases: list[int] | int) -> bool:
|
|
|
72
72
|
if a == 0:
|
|
73
73
|
continue
|
|
74
74
|
b = pow(a, m, n)
|
|
75
|
-
if b
|
|
75
|
+
if b in (1, n - 1):
|
|
76
76
|
continue
|
|
77
77
|
for _ in range(1, k):
|
|
78
78
|
b = pow(b, 2, n)
|
|
@@ -154,7 +154,7 @@ def lucas_test(n: int) -> bool:
|
|
|
154
154
|
return n == 2
|
|
155
155
|
if perfect_square(n) is not None: # the search for D will not succeed in this case
|
|
156
156
|
return False
|
|
157
|
-
|
|
157
|
+
|
|
158
158
|
# write n = k 2^s - 1
|
|
159
159
|
k = (n + 1) // 2
|
|
160
160
|
s = 1
|
|
@@ -163,7 +163,7 @@ def lucas_test(n: int) -> bool:
|
|
|
163
163
|
s += 1
|
|
164
164
|
|
|
165
165
|
# Selfridge method for choosing D
|
|
166
|
-
|
|
166
|
+
|
|
167
167
|
D = 5
|
|
168
168
|
while True:
|
|
169
169
|
g = gcd(abs(D), n)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kryptools
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.9
|
|
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
|
|
@@ -9,7 +9,7 @@ Project-URL: Docs, https://github.com/teschlg/kryptools/tree/main/doc
|
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ The tools contained are:
|
|
|
29
29
|
* number theory: sqrt modulo primes, Jacobi/Legendre symbol, Chinese Remainder Theorem, continued fractions, etc.
|
|
30
30
|
* primes: Sieve of Erathostenes, primality tests, generation of random safe/strong primes
|
|
31
31
|
* solvers for discrete logarithms (naive, Pollard rho, Shanks baby step/giant step, index calculus, quadratic sieve)
|
|
32
|
-
* integer factorization (Fermat, Pollard p-1, Lenstra's ECM, Dixon, basic quadratic sieve)
|
|
32
|
+
* integer factorization (Fermat, Pollard p-1, Pollard rho, Lenstra's ECM, Dixon, basic quadratic sieve)
|
|
33
33
|
* elliptic curves (Weierstrass form), group operations, order, discrete logarithms
|
|
34
34
|
* linear algebra: Hermite normal form, Gram-Schmidt
|
|
35
35
|
* lattices: Hadamard ratio, Babai rounding/nearest plane algorithm, lattice reduction (Lenstra-Lenstra-Lovaz)
|
|
@@ -24,4 +24,13 @@ kryptools/primes.py
|
|
|
24
24
|
kryptools.egg-info/PKG-INFO
|
|
25
25
|
kryptools.egg-info/SOURCES.txt
|
|
26
26
|
kryptools.egg-info/dependency_links.txt
|
|
27
|
-
kryptools.egg-info/top_level.txt
|
|
27
|
+
kryptools.egg-info/top_level.txt
|
|
28
|
+
tests/test_Zmod.py
|
|
29
|
+
tests/test_dlog.py
|
|
30
|
+
tests/test_ec.py
|
|
31
|
+
tests/test_factor.py
|
|
32
|
+
tests/test_la.py
|
|
33
|
+
tests/test_lat.py
|
|
34
|
+
tests/test_nt.py
|
|
35
|
+
tests/test_poly.py
|
|
36
|
+
tests/test_primes.py
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kryptools"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.9"
|
|
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
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Programming Language :: Python :: 3",
|
|
12
12
|
"License :: OSI Approved :: MIT License",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from math import gcd
|
|
3
|
+
from kryptools import Zmod
|
|
4
|
+
|
|
5
|
+
def test_Zmod():
|
|
6
|
+
for n in [5, 6]:
|
|
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
|
+
Z_5 = Zmod(5)
|
|
32
|
+
assert Z_5(3).order() == 4
|
|
33
|
+
assert Z_5(1).is_generator() == False
|
|
34
|
+
assert Z_5(3).is_generator() == True
|
|
35
|
+
assert Z_5.generator(all = True) == Z_5([2, 3])
|
|
36
|
+
assert Z_5.star() == Z_5([1, 2, 3, 4])
|
|
37
|
+
assert [Z_5(i).sharp() for i in range(Z_5.n)] == [0, 1, 2, -2, -1]
|
|
38
|
+
assert [abs(Z_5(i)) for i in range(Z_5.n)] == [0, 1, 2, 2, 1]
|
|
39
|
+
assert str(Z_5(3)) == "3"
|
|
40
|
+
Z_5.short = False
|
|
41
|
+
assert str(Z_5(6)) == "1 (mod 5)"
|
|
42
|
+
|
|
43
|
+
Z_6 = Zmod(6)
|
|
44
|
+
assert Z_6(5).order() == 2
|
|
45
|
+
with pytest.raises(ValueError):
|
|
46
|
+
Z_6(3).order()
|
|
47
|
+
assert Z_6(1).is_generator() == False
|
|
48
|
+
assert Z_6(5).is_generator() == True
|
|
49
|
+
assert Z_6.generator(all = True) == Z_6([5])
|
|
50
|
+
assert Z_6.star() == Z_6([1, 5])
|
|
51
|
+
assert str(Z_6(3)) == "3"
|
|
@@ -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,25 @@
|
|
|
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
|
+
R = O
|
|
18
|
+
for i in range(5):
|
|
19
|
+
assert i * P == R
|
|
20
|
+
R += P
|
|
21
|
+
assert P + Q == Q + P
|
|
22
|
+
assert Q.order() * Q == O
|
|
23
|
+
k = randint(1, ec.order())
|
|
24
|
+
R = k * Q
|
|
25
|
+
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,15 @@
|
|
|
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
|
+
M.map(Fraction)
|
|
9
|
+
Mi = M.inv()
|
|
10
|
+
assert M * Mi == M.eye()
|
|
11
|
+
|
|
12
|
+
Z_11 = Zmod(11)
|
|
13
|
+
M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 12]], ring=Z_11)
|
|
14
|
+
Mi = M.inv()
|
|
15
|
+
assert M * Mi == M.eye()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from kryptools import carmichael_lambda, euler_phi, moebius_mu
|
|
3
|
+
|
|
4
|
+
#https://oeis.org/A000010
|
|
5
|
+
OEIS_A000010 = [
|
|
6
|
+
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,
|
|
7
|
+
12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22,
|
|
8
|
+
46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20,
|
|
9
|
+
66, 32, 44 ]
|
|
10
|
+
|
|
11
|
+
def test_euler_phi():
|
|
12
|
+
for n in range(1, len(OEIS_A000010)+1):
|
|
13
|
+
assert euler_phi(n) == OEIS_A000010[n - 1]
|
|
14
|
+
|
|
15
|
+
#https://oeis.org/A002322
|
|
16
|
+
OEIS_A002322 = [
|
|
17
|
+
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,
|
|
18
|
+
12, 18, 6, 28, 4, 30, 8, 10, 16, 12, 6, 36, 18, 12, 4, 40, 6, 42, 10, 12, 22, 46,
|
|
19
|
+
4, 42, 20, 16, 12, 52, 18, 20, 6, 18, 28, 58, 4, 60, 30, 6, 16, 12, 10, 66, 16,
|
|
20
|
+
22, 12, 70, 6, 72, 36, 20, 18, 30, 12, 78, 4, 54 ]
|
|
21
|
+
|
|
22
|
+
def test_carmichael_lambda():
|
|
23
|
+
for n in range(1, len(OEIS_A002322)+1):
|
|
24
|
+
assert carmichael_lambda(n) == OEIS_A002322[n - 1]
|
|
25
|
+
|
|
26
|
+
#https://oeis.org/A008683
|
|
27
|
+
OEIS_A008683 = [
|
|
28
|
+
1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1, 0,
|
|
29
|
+
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,
|
|
30
|
+
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,
|
|
31
|
+
0, 0, 1, -1 ]
|
|
32
|
+
|
|
33
|
+
def test_moebius_mu():
|
|
34
|
+
for n in range(1, len(OEIS_A008683)+1):
|
|
35
|
+
assert moebius_mu(n) == OEIS_A008683[n - 1]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
@@ -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,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from kryptools import sieve_eratosthenes, prime_pi, next_prime, previous_prime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
#https://oeis.org/A000040
|
|
6
|
+
OEIS_A000010 = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
|
|
7
|
+
71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163,
|
|
8
|
+
167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
|
|
9
|
+
269, 271 ]
|
|
10
|
+
|
|
11
|
+
def test_sieve_eratosthenes():
|
|
12
|
+
for i, p in enumerate(sieve_eratosthenes(OEIS_A000010[-1])):
|
|
13
|
+
assert p == OEIS_A000010[i]
|
|
14
|
+
if i > 0:
|
|
15
|
+
assert previous_prime(p-1) == OEIS_A000010[i-1]
|
|
16
|
+
if i < len(OEIS_A000010) - 1:
|
|
17
|
+
assert next_prime(p+1) == OEIS_A000010[i+1]
|
|
18
|
+
|
|
19
|
+
#https://oeis.org/A000040
|
|
20
|
+
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,
|
|
21
|
+
9, 9, 9, 9, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 14, 14,
|
|
22
|
+
15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 18, 18, 18, 18, 19,
|
|
23
|
+
19, 19, 19, 20, 20, 21, 21, 21, 21, 21, 21 ]
|
|
24
|
+
|
|
25
|
+
def test_prime_pi():
|
|
26
|
+
for n in range(1, len(OEIS_A000720)+1):
|
|
27
|
+
assert prime_pi(n) == OEIS_A000720[n - 1]
|
|
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
|