kryptools 1.8.2__tar.gz → 1.8.3__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-1.8.2 → kryptools-1.8.3}/PKG-INFO +1 -1
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/Zmod.py +6 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/code.py +1 -1
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/poly.py +201 -62
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/PKG-INFO +1 -1
- {kryptools-1.8.2 → kryptools-1.8.3}/pyproject.toml +1 -1
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_poly.py +4 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/LICENSE +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/README.md +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/GF2.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/__init__.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/aes.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/blockcipher.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/conway_polynomials.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/des.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_bsgs.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_ic.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_qs.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_rho.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/ec.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_dix.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_ecm.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_fmt.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_pm1.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_qs.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_rho.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/intfuncs.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/keccak.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/la.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/lat.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/nt.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/primes.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/sha1.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/SOURCES.txt +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/dependency_links.txt +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/top_level.txt +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/setup.cfg +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_GF2.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_Zmod.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_aes.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_code.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_des.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_dlog.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_ec.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor_fmt.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor_pm1.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor_qs.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_intfuncs.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_keccak.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_la.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_lat.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_nt.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_primes.py +0 -0
- {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_sha1.py +0 -0
|
@@ -91,6 +91,12 @@ class Zmod:
|
|
|
91
91
|
self.group_order *= p**k
|
|
92
92
|
return self.group_order
|
|
93
93
|
|
|
94
|
+
def factor_n(self) -> bool:
|
|
95
|
+
"Returns the factorization of n and stores it."
|
|
96
|
+
if not self.n_factors:
|
|
97
|
+
self.n_factors = factorint(self.n)
|
|
98
|
+
return self.n_factors
|
|
99
|
+
|
|
94
100
|
def is_cyclic(self) -> bool:
|
|
95
101
|
"Test if the group Z_n^* is cyclic."
|
|
96
102
|
n = self.n
|
|
@@ -414,7 +414,7 @@ class CyclicCode():
|
|
|
414
414
|
>>> cc = CyclicCode(n, g)
|
|
415
415
|
|
|
416
416
|
To encode a list of letters (the length must be equal to the dimension k of the code):
|
|
417
|
-
>>> cc.encode([0,1,0])
|
|
417
|
+
>>> cc.encode([0, 1, 0])
|
|
418
418
|
[0, 1, 1, 0]
|
|
419
419
|
|
|
420
420
|
Decoding is only implemented for code words:
|
|
@@ -6,7 +6,9 @@ from math import prod, comb
|
|
|
6
6
|
from numbers import Number
|
|
7
7
|
from random import randint
|
|
8
8
|
from itertools import combinations
|
|
9
|
-
|
|
9
|
+
from copy import copy
|
|
10
|
+
from .Zmod import Zmod
|
|
11
|
+
from .nt import crt
|
|
10
12
|
|
|
11
13
|
class Poly:
|
|
12
14
|
"""
|
|
@@ -24,17 +26,51 @@ class Poly:
|
|
|
24
26
|
print_x = "x" # variable for printing
|
|
25
27
|
print_pow = "^" # you can change this to "**" if you want it python style
|
|
26
28
|
|
|
27
|
-
def __init__(self, coeff: list, ring=None, modulus: list = None):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
def __init__(self, coeff: list, ring=None, modulus: list = None, cyclic: int = 0, check: bool = True):
|
|
30
|
+
if not check:
|
|
31
|
+
self.coeff = coeff
|
|
32
|
+
self.modulus = modulus
|
|
33
|
+
self.cyclic = cyclic
|
|
34
|
+
return
|
|
35
|
+
if ring:
|
|
36
|
+
self.coeff = list(map(ring, coeff))
|
|
37
|
+
else:
|
|
38
|
+
self.coeff = list(coeff)
|
|
39
|
+
if len(self.coeff) < 1:
|
|
40
|
+
raise ValueError("The coefficient list must not be empty.")
|
|
41
|
+
self.strip()
|
|
42
|
+
self.modulus = None
|
|
43
|
+
if isinstance(cyclic, int):
|
|
44
|
+
if cyclic > 0:
|
|
45
|
+
modulus = [-1] + [0] * (cyclic - 1) + [1] # x^n - 1
|
|
46
|
+
elif cyclic < 0:
|
|
47
|
+
modulus = [1] + [0] * (-cyclic - 1) + [1] # x^n + 1
|
|
48
|
+
else:
|
|
49
|
+
cyclic = 0
|
|
50
|
+
self.cyclic = cyclic
|
|
51
|
+
if modulus is None:
|
|
52
|
+
return
|
|
34
53
|
if ring:
|
|
35
|
-
self.map(ring)
|
|
36
|
-
|
|
37
|
-
self.
|
|
54
|
+
self.modulus = list(map(ring, modulus))
|
|
55
|
+
else:
|
|
56
|
+
self.modulus = list(modulus)
|
|
57
|
+
if len(self.modulus) < 2:
|
|
58
|
+
raise ValueError("The modulus must have degree at least one.")
|
|
59
|
+
zero = self.coeff[0] * 0
|
|
60
|
+
one = zero**0
|
|
61
|
+
if self.modulus[-1] != one:
|
|
62
|
+
try:
|
|
63
|
+
tmp = one / self.modulus[-1]
|
|
64
|
+
except Exception as exc:
|
|
65
|
+
raise ValueError("Failed to make the modulus monic.") from exc
|
|
66
|
+
for i in range(len(self.modulus)): # pylint: disable=C0200
|
|
67
|
+
self.modulus[i] *= tmp
|
|
68
|
+
if all( c == zero for c in self.modulus[1:-1]):
|
|
69
|
+
if self.modulus[0] == one:
|
|
70
|
+
self.cyclic = 1 - len(self.modulus)
|
|
71
|
+
elif self.modulus[0]**2 == one:
|
|
72
|
+
self.cyclic = len(self.modulus) - 1
|
|
73
|
+
self.mod()
|
|
38
74
|
|
|
39
75
|
def __call__(self, x):
|
|
40
76
|
result = 0 * self.coeff[0]
|
|
@@ -148,6 +184,13 @@ class Poly:
|
|
|
148
184
|
def __bool__(self):
|
|
149
185
|
return bool(self.degree()) or bool(self.coeff[0])
|
|
150
186
|
|
|
187
|
+
def strip(self) -> None:
|
|
188
|
+
"Strip leading zeros."
|
|
189
|
+
for i in range(len(self.coeff) - 1, 0, -1):
|
|
190
|
+
if self.coeff[i]:
|
|
191
|
+
break
|
|
192
|
+
self.coeff.pop(i)
|
|
193
|
+
|
|
151
194
|
def degree(self) -> int:
|
|
152
195
|
"Return the degree."
|
|
153
196
|
return len(self.coeff) - 1
|
|
@@ -156,13 +199,30 @@ class Poly:
|
|
|
156
199
|
"Return the number of nonzero coefficients."
|
|
157
200
|
return sum(map(bool,self.coeff))
|
|
158
201
|
|
|
159
|
-
def map(self, func):
|
|
202
|
+
def map(self, func, modulus = True):
|
|
160
203
|
"Apply a given function to all coefficients in place."
|
|
161
204
|
self.coeff = list(map(func, self.coeff))
|
|
162
|
-
|
|
163
|
-
|
|
205
|
+
self.strip()
|
|
206
|
+
if modulus and self.modulus is not None:
|
|
207
|
+
self.modulus = list(map(func, self.modulus))
|
|
208
|
+
if self.modulus[-1] != self.coeff[0]**0:
|
|
209
|
+
raise ValueError("The modulus is no longer monic.")
|
|
210
|
+
if self.cyclic:
|
|
211
|
+
zero = self.modulus[0] * 0
|
|
212
|
+
self.cyclic = 0
|
|
213
|
+
if abs(self.cyclic) > 1 and self.modulus[1] != zero:
|
|
214
|
+
return
|
|
215
|
+
one = zero**0
|
|
216
|
+
if self.modulus[0] == one:
|
|
217
|
+
self.cyclic = 1 - len(self.modulus)
|
|
218
|
+
elif self.modulus[0]**2 == one:
|
|
219
|
+
self.cyclic = len(self.modulus) - 1
|
|
220
|
+
|
|
221
|
+
def applyfunc(self, func, modulus = True) -> "Poly":
|
|
164
222
|
"Apply a function to all coefficients."
|
|
165
|
-
|
|
223
|
+
if modulus and self.modulus is not None:
|
|
224
|
+
return self.__class__(list(map(func, self.coeff)), modulus = list(map(func, self.modulus)))
|
|
225
|
+
return self.__class__(list(map(func, self.coeff)), modulus = self.modulus, cyclic = self.cyclic)
|
|
166
226
|
|
|
167
227
|
def _check_type(self, other):
|
|
168
228
|
return isinstance(other, int) or (isinstance(other, Number) and isinstance(self.coeff[0], Number)) or type(other) == type(self.coeff[0]) # pylint: disable=C0123
|
|
@@ -201,21 +261,27 @@ class Poly:
|
|
|
201
261
|
if self._check_type(other):
|
|
202
262
|
tmp = self.coeff[:]
|
|
203
263
|
tmp[0] += other
|
|
204
|
-
return self.__class__(tmp, modulus=self.modulus)
|
|
264
|
+
return self.__class__(tmp, modulus = self.modulus, check = False)
|
|
205
265
|
return NotImplemented
|
|
206
266
|
modulus = self.modulus
|
|
207
|
-
|
|
267
|
+
cyclic = self.cyclic
|
|
268
|
+
if modulus is None and other.modulus is not None:
|
|
208
269
|
modulus = other.modulus
|
|
270
|
+
cyclic = other.cyclic
|
|
209
271
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
210
272
|
if ls < lo:
|
|
211
|
-
|
|
212
|
-
|
|
273
|
+
ret = [s + o for s, o in zip(self.coeff, other.coeff)] + other.coeff[ls:]
|
|
274
|
+
else:
|
|
275
|
+
ret = [s + o for s, o in zip(self.coeff, other.coeff)] + self.coeff[lo:]
|
|
276
|
+
ret = self.__class__(ret, modulus = modulus, cyclic = cyclic, check = False)
|
|
277
|
+
ret.strip()
|
|
278
|
+
return ret
|
|
213
279
|
|
|
214
280
|
def __radd__(self, other: "Poly") -> "Poly":
|
|
215
281
|
return self + other
|
|
216
282
|
|
|
217
283
|
def __neg__(self) -> "Poly":
|
|
218
|
-
return
|
|
284
|
+
return self.__class__([-s for s in self.coeff], modulus = self.modulus, cyclic = self.cyclic, check = False)
|
|
219
285
|
|
|
220
286
|
def __pos__(self) -> "Poly":
|
|
221
287
|
return self
|
|
@@ -229,23 +295,50 @@ class Poly:
|
|
|
229
295
|
def __mul__(self, other: "Poly") -> "Poly":
|
|
230
296
|
if not isinstance(other, self.__class__):
|
|
231
297
|
if self._check_type(other):
|
|
232
|
-
|
|
298
|
+
if not other:
|
|
299
|
+
return self.__class__([other], modulus = self.modulus, cyclic = self.cyclic, check = False)
|
|
300
|
+
return self.__class__([other * s for s in self.coeff], modulus = self.modulus, cyclic = self.cyclic, check = False)
|
|
233
301
|
return NotImplemented
|
|
234
302
|
zero = 0 * self.coeff[0]
|
|
235
303
|
ls, lo = len(self.coeff), len(other.coeff)
|
|
304
|
+
if self.cyclic or other.cyclic:
|
|
305
|
+
if self.cyclic:
|
|
306
|
+
cyclic = self.cyclic
|
|
307
|
+
modulus = self.modulus
|
|
308
|
+
else:
|
|
309
|
+
cyclic = other.cyclic
|
|
310
|
+
modulus = other.modulus
|
|
311
|
+
n = abs(cyclic)
|
|
312
|
+
if ls < lo:
|
|
313
|
+
otherc = other.coeff + [zero] * (n - lo)
|
|
314
|
+
selfc = self.coeff
|
|
315
|
+
else:
|
|
316
|
+
otherc = self.coeff + [zero] * (n - ls)
|
|
317
|
+
selfc = other.coeff
|
|
318
|
+
ls = lo
|
|
319
|
+
if cyclic > 0:
|
|
320
|
+
coeff = [ sum( (selfc[j] * otherc[(k-j) % n] for j in range(ls)), start = zero) for k in range(n)]
|
|
321
|
+
else:
|
|
322
|
+
coeff = [ sum( (selfc[j] * otherc[(k-j) % n] for j in range(min(k,ls))), start = zero) -
|
|
323
|
+
sum( (selfc[j] * otherc[(k-j) % n] for j in range(k,ls)), start = zero) for k in range(n)]
|
|
324
|
+
ret = self.__class__(coeff, modulus = modulus, cyclic = cyclic, check = False)
|
|
325
|
+
ret.strip()
|
|
326
|
+
return ret
|
|
236
327
|
coeff = [sum((self.coeff[j] * other.coeff[k - j] for j in range(max(0, k - lo + 1), min(ls, k + 1))), start=zero)
|
|
237
328
|
for k in range(ls + lo - 1)]
|
|
238
329
|
modulus = self.modulus
|
|
239
330
|
if not modulus and other.modulus:
|
|
240
331
|
modulus = other.modulus
|
|
241
|
-
|
|
332
|
+
ret = self.__class__(coeff, modulus = modulus, check = False)
|
|
333
|
+
ret.mod()
|
|
334
|
+
return ret
|
|
242
335
|
|
|
243
336
|
def __rmul__(self, other) -> "Poly":
|
|
244
337
|
return self * other
|
|
245
338
|
|
|
246
339
|
def __truediv__(self, other) -> "Poly":
|
|
247
340
|
if self._check_type(other):
|
|
248
|
-
return self.__class__([s / other for s in self.coeff], modulus=self.modulus)
|
|
341
|
+
return self.__class__([s / other for s in self.coeff], modulus = self.modulus, cyclic = self.cyclic, check = False)
|
|
249
342
|
if isinstance(other, self.__class__):
|
|
250
343
|
if not other.modulus:
|
|
251
344
|
raise NotImplementedError(
|
|
@@ -263,7 +356,7 @@ class Poly:
|
|
|
263
356
|
if not isinstance(j, int):
|
|
264
357
|
return NotImplemented
|
|
265
358
|
one = self.coeff[0]**0
|
|
266
|
-
res = self.__class__([one], modulus=self.modulus)
|
|
359
|
+
res = self.__class__([one], modulus = self.modulus, cyclic = self.cyclic, check = False)
|
|
267
360
|
if j < 0:
|
|
268
361
|
if not self.modulus:
|
|
269
362
|
raise NotImplementedError(
|
|
@@ -285,8 +378,7 @@ class Poly:
|
|
|
285
378
|
"List of bits of all coefficients."
|
|
286
379
|
ring = self.coeff[0].__class__
|
|
287
380
|
if not (hasattr(ring, 'bits') and callable(ring.bits)):
|
|
288
|
-
raise NotImplementedError(
|
|
289
|
-
"Coefficients cannot be converted to bits.")
|
|
381
|
+
raise NotImplementedError("Coefficients cannot be converted to bits.")
|
|
290
382
|
out = []
|
|
291
383
|
for c in reversed(self.coeff):
|
|
292
384
|
out += c.bits()
|
|
@@ -310,23 +402,26 @@ class Poly:
|
|
|
310
402
|
"Polynom division with remainder."
|
|
311
403
|
zero, one, ring = self._guess_ring()
|
|
312
404
|
if isinstance(other, list):
|
|
313
|
-
other = self.__class__(other, ring
|
|
405
|
+
other = self.__class__(other, ring)
|
|
314
406
|
elif not isinstance(other, self.__class__):
|
|
315
407
|
raise NotImplementedError(f"Cannot divide {self} and {other}.")
|
|
316
408
|
if not other:
|
|
317
409
|
raise ValueError(f"{other} must be nonzero.")
|
|
318
410
|
sd, od = self.degree(), other.degree()
|
|
319
411
|
if sd < od:
|
|
320
|
-
return self.__class__([zero]), self
|
|
412
|
+
return self.__class__([zero], check = False), self
|
|
321
413
|
div = [zero] * (sd - od + 1)
|
|
322
414
|
lco = other.coeff[-1]
|
|
323
415
|
if lco != one:
|
|
324
|
-
|
|
416
|
+
try:
|
|
417
|
+
tmp = one / lco
|
|
418
|
+
except Exception as exc:
|
|
419
|
+
raise ValueError("The leading coefficient of the divisor must be invertible.") from exc
|
|
325
420
|
oth = [c * tmp for c in other.coeff]
|
|
326
421
|
rem = [c * tmp for c in self.coeff]
|
|
327
422
|
else:
|
|
328
|
-
oth = other.coeff
|
|
329
|
-
rem = [c * one for c in self.coeff]
|
|
423
|
+
oth = other.coeff
|
|
424
|
+
rem = [c * one for c in self.coeff] # "* 1" is here to make sure we get a copy
|
|
330
425
|
for i in range(sd - od + 1):
|
|
331
426
|
tmp = rem[sd - i] * one # "* 1" is here to make sure we get a copy
|
|
332
427
|
div[sd - od - i] = tmp
|
|
@@ -334,26 +429,41 @@ class Poly:
|
|
|
334
429
|
rem[sd - i - j] -= tmp * oth[od - j]
|
|
335
430
|
if lco != one:
|
|
336
431
|
rem = [c * lco for c in rem]
|
|
337
|
-
return self.__class__(div
|
|
432
|
+
return self.__class__(div), self.__class__(rem)
|
|
338
433
|
|
|
339
|
-
def mod(self, other: "Poly") -> None:
|
|
434
|
+
def mod(self, other: "Poly" = None) -> None:
|
|
340
435
|
"Reduce with respect to a given polynomial."
|
|
341
436
|
one, ring = self._guess_ring()[1:]
|
|
342
|
-
if
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
437
|
+
if other is None:
|
|
438
|
+
if self.modulus is None or len(self.coeff) < len(self.modulus):
|
|
439
|
+
return
|
|
440
|
+
if self.cyclic:
|
|
441
|
+
n = abs(self.cyclic)
|
|
442
|
+
zero = self.coeff[0] * 0
|
|
443
|
+
if self.cyclic > 0:
|
|
444
|
+
self.coeff = [ sum(self.coeff[i::n], start = zero) for i in range(n)]
|
|
445
|
+
else:
|
|
446
|
+
even = [ sum(self.coeff[i::2*n], start = zero) for i in range(n)]
|
|
447
|
+
odd = [ sum(self.coeff[i+n::2*n], start = zero) for i in range(n)]
|
|
448
|
+
self.coeff = [ c1 - c2 for c1, c2 in zip(even, odd)]
|
|
449
|
+
self.strip()
|
|
450
|
+
return
|
|
451
|
+
oth = self.modulus
|
|
355
452
|
else:
|
|
453
|
+
if isinstance(other, list):
|
|
454
|
+
other = self.__class__(other, ring=ring)
|
|
455
|
+
elif not isinstance(other, self.__class__):
|
|
456
|
+
raise NotImplementedError(f"Cannot divide {self} and {other}.")
|
|
457
|
+
if not other:
|
|
458
|
+
raise NotImplementedError(f"{other} must be nonzero.")
|
|
356
459
|
oth = other.coeff
|
|
460
|
+
if len(self.coeff) < len(oth):
|
|
461
|
+
return
|
|
462
|
+
lco = oth[-1]
|
|
463
|
+
if lco != one:
|
|
464
|
+
tmp = one / lco
|
|
465
|
+
oth = [c * tmp for c in oth]
|
|
466
|
+
sd, od = len(self.coeff) - 1, len(oth) - 1
|
|
357
467
|
for i in range(sd - od + 1):
|
|
358
468
|
tmp = self.coeff[sd - i] * one # "* 1" is here to make sure we get a copy
|
|
359
469
|
for j in range(od + 1):
|
|
@@ -373,11 +483,37 @@ class Poly:
|
|
|
373
483
|
elif not isinstance(other, list):
|
|
374
484
|
raise NotImplementedError(
|
|
375
485
|
f"{other} must be a list of coefficients or a polynomial.")
|
|
376
|
-
other =
|
|
486
|
+
other = self.__class__(other, ring=ring)
|
|
377
487
|
if not other:
|
|
378
488
|
raise NotImplementedError(f"{other} must be nonzero.")
|
|
489
|
+
if ring is not None and hasattr(ring, 'n') and not ring.is_field(): # Zmod; no field
|
|
490
|
+
inverse = [] # inverse in Z_{p^k} for all primefactors of n
|
|
491
|
+
primefactors = [] # primefactors of n
|
|
492
|
+
for p, k in ring.factor_n().items():
|
|
493
|
+
primefactors.append(p**k)
|
|
494
|
+
self_p = copy(self)
|
|
495
|
+
Z_p = Zmod(p)
|
|
496
|
+
Z_p.isfield = True
|
|
497
|
+
self_p.map(Z_p)
|
|
498
|
+
self_p_inv = self_p.inv()
|
|
499
|
+
if k > 1:
|
|
500
|
+
self_p = copy(self)
|
|
501
|
+
self_p.map(Zmod(p**k))
|
|
502
|
+
self_p_inv.map(Zmod(p**k))
|
|
503
|
+
l = 1
|
|
504
|
+
while k > l:
|
|
505
|
+
self_p_inv = self_p_inv * (2 - self_p * self_p_inv)
|
|
506
|
+
l *=2
|
|
507
|
+
inverse.append(self_p_inv)
|
|
508
|
+
|
|
509
|
+
coeff = []
|
|
510
|
+
for i in range(other.degree()):
|
|
511
|
+
coeff.append(crt([int(inv[i]) for inv in inverse], primefactors))
|
|
512
|
+
ret = self.__class__(coeff, ring=ring)
|
|
513
|
+
ret.modulus = self.modulus
|
|
514
|
+
return ret
|
|
379
515
|
r0, r1 = other, self
|
|
380
|
-
y0, y1 = self.__class__([zero]), self.__class__([one])
|
|
516
|
+
y0, y1 = self.__class__([zero], check = False), self.__class__([one], check = False)
|
|
381
517
|
while r1:
|
|
382
518
|
q, r = r0.divmod(r1)
|
|
383
519
|
r0, r1 = r1, r
|
|
@@ -396,7 +532,7 @@ class Poly:
|
|
|
396
532
|
_, one, _ = self._guess_ring()
|
|
397
533
|
if not self:
|
|
398
534
|
if not other:
|
|
399
|
-
return
|
|
535
|
+
return self.__class__([self.coeff[0]], check = False)
|
|
400
536
|
tmp = other / other.coeff[-1]
|
|
401
537
|
tmp.modulus = None
|
|
402
538
|
return tmp
|
|
@@ -419,13 +555,13 @@ class Poly:
|
|
|
419
555
|
raise NotImplementedError(f"Cannot perform egcd: {other} must be a polynomial.")
|
|
420
556
|
if not self:
|
|
421
557
|
if not other:
|
|
422
|
-
return
|
|
558
|
+
return self.__class__([zero], check = False), self.__class__([zero], check = False), self.__class__([zero], check = False)
|
|
423
559
|
if other.coeff[-1] == one:
|
|
424
|
-
return other,
|
|
425
|
-
return other / other.coeff[-1],
|
|
560
|
+
return other, self.__class__([zero], check = False), self.__class__([one], check = False)
|
|
561
|
+
return other / other.coeff[-1], __class__([zero], check = False), self.__class__([one / other.coeff[-1]], check = False)
|
|
426
562
|
r0, r1 = other, self
|
|
427
|
-
x0, x1 = self.__class__([one]), self.__class__([zero])
|
|
428
|
-
y0, y1 =
|
|
563
|
+
x0, x1 = self.__class__([one], check = False), self.__class__([zero], check = False)
|
|
564
|
+
y0, y1 = x1, x0
|
|
429
565
|
while r1:
|
|
430
566
|
q, r = r0.divmod(r1)
|
|
431
567
|
r0, r1 = r1, r
|
|
@@ -443,11 +579,15 @@ class Poly:
|
|
|
443
579
|
tmp = 0 * self.coeff[0]
|
|
444
580
|
else:
|
|
445
581
|
tmp = [j * self.coeff[j] for j in range(1, l)]
|
|
446
|
-
|
|
582
|
+
ret = self.__class__(tmp, modulus = self.modulus, check = False)
|
|
583
|
+
ret.strip()
|
|
584
|
+
return ret
|
|
447
585
|
|
|
448
586
|
def reciprocal(self) -> "Poly":
|
|
449
587
|
"Returns the reciprocal (reversed) polynomial."
|
|
450
|
-
|
|
588
|
+
ret = self.__class__(reversed(self.coeff), modulus = self.modulus, check = False)
|
|
589
|
+
ret.strip()
|
|
590
|
+
return ret
|
|
451
591
|
|
|
452
592
|
def square_free_factors(self) -> dict:
|
|
453
593
|
"Determine the square free factors of a polynomial over a Galois field."
|
|
@@ -481,7 +621,7 @@ class Poly:
|
|
|
481
621
|
root[d] = c.coeff[d * p]**pr
|
|
482
622
|
d -= 1
|
|
483
623
|
# assert Poly(root)**p == c, root
|
|
484
|
-
for fac, mult in
|
|
624
|
+
for fac, mult in self.__class__(root).square_free_factors().items():
|
|
485
625
|
factors[fac] = p * mult
|
|
486
626
|
return factors
|
|
487
627
|
|
|
@@ -497,7 +637,7 @@ class Poly:
|
|
|
497
637
|
raise ValueError("The polynomial must be non-constant.")
|
|
498
638
|
factors = {}
|
|
499
639
|
w = self
|
|
500
|
-
x =
|
|
640
|
+
x = self.__class__([zero, one], modulus = self.coeff, check = False) # x
|
|
501
641
|
b = x
|
|
502
642
|
k = 1
|
|
503
643
|
while k <= w.degree()//2:
|
|
@@ -543,8 +683,7 @@ class Poly:
|
|
|
543
683
|
|
|
544
684
|
factors = [self]
|
|
545
685
|
while len(factors) < r:
|
|
546
|
-
g =
|
|
547
|
-
for _ in range(d+1)], ring=field, modulus=self)
|
|
686
|
+
g = self.__class__([field(randint(0, q-1)) for _ in range(d+1)], modulus = self.coeff)
|
|
548
687
|
if p == 2: # Gathen-Shoup
|
|
549
688
|
h = g
|
|
550
689
|
for _ in range(1, k):
|
|
@@ -574,10 +713,10 @@ class Poly:
|
|
|
574
713
|
"Factors a polynomials over a Galois field."
|
|
575
714
|
if self.degree() < 1:
|
|
576
715
|
return {self: 1}
|
|
577
|
-
c =
|
|
716
|
+
c = self.coeff[-1]
|
|
578
717
|
one = c**0
|
|
579
718
|
if c != one:
|
|
580
|
-
factors = {c: 1}
|
|
719
|
+
factors = {self.__class__([c], check = False): 1}
|
|
581
720
|
else:
|
|
582
721
|
factors = {}
|
|
583
722
|
for fac1, m in self.square_free_factors().items():
|
|
@@ -60,6 +60,10 @@ def test_Poly():
|
|
|
60
60
|
assert (p - q)(x) == p(x) - q(x)
|
|
61
61
|
assert (p * q)(x) == p(x) * q(x)
|
|
62
62
|
|
|
63
|
+
ring = Zmod(24)
|
|
64
|
+
p = Poly([1, 3, 2, 1], ring = ring, modulus = [-1,0,0,0,0,1])
|
|
65
|
+
assert p.inv() * p == Poly([ring(1)])
|
|
66
|
+
|
|
63
67
|
def test_rabin():
|
|
64
68
|
for gf,t in [ [Zmod(7), 4], [GF2(4), 3]]:
|
|
65
69
|
order = len(list(gf))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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
|