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.
Files changed (57) hide show
  1. {kryptools-1.8.2 → kryptools-1.8.3}/PKG-INFO +1 -1
  2. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/Zmod.py +6 -0
  3. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/code.py +1 -1
  4. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/poly.py +201 -62
  5. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/PKG-INFO +1 -1
  6. {kryptools-1.8.2 → kryptools-1.8.3}/pyproject.toml +1 -1
  7. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_poly.py +4 -0
  8. {kryptools-1.8.2 → kryptools-1.8.3}/LICENSE +0 -0
  9. {kryptools-1.8.2 → kryptools-1.8.3}/README.md +0 -0
  10. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/GF2.py +0 -0
  11. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/__init__.py +0 -0
  12. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/aes.py +0 -0
  13. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/blockcipher.py +0 -0
  14. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/conway_polynomials.py +0 -0
  15. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/des.py +0 -0
  16. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp.py +0 -0
  17. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_bsgs.py +0 -0
  18. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_ic.py +0 -0
  19. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_qs.py +0 -0
  20. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/dlp_rho.py +0 -0
  21. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/ec.py +0 -0
  22. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor.py +0 -0
  23. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_dix.py +0 -0
  24. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_ecm.py +0 -0
  25. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_fmt.py +0 -0
  26. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_pm1.py +0 -0
  27. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_qs.py +0 -0
  28. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/factor_rho.py +0 -0
  29. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/intfuncs.py +0 -0
  30. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/keccak.py +0 -0
  31. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/la.py +0 -0
  32. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/lat.py +0 -0
  33. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/nt.py +0 -0
  34. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/primes.py +0 -0
  35. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools/sha1.py +0 -0
  36. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/SOURCES.txt +0 -0
  37. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/dependency_links.txt +0 -0
  38. {kryptools-1.8.2 → kryptools-1.8.3}/kryptools.egg-info/top_level.txt +0 -0
  39. {kryptools-1.8.2 → kryptools-1.8.3}/setup.cfg +0 -0
  40. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_GF2.py +0 -0
  41. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_Zmod.py +0 -0
  42. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_aes.py +0 -0
  43. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_code.py +0 -0
  44. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_des.py +0 -0
  45. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_dlog.py +0 -0
  46. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_ec.py +0 -0
  47. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor.py +0 -0
  48. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor_fmt.py +0 -0
  49. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor_pm1.py +0 -0
  50. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_factor_qs.py +0 -0
  51. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_intfuncs.py +0 -0
  52. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_keccak.py +0 -0
  53. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_la.py +0 -0
  54. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_lat.py +0 -0
  55. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_nt.py +0 -0
  56. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_primes.py +0 -0
  57. {kryptools-1.8.2 → kryptools-1.8.3}/tests/test_sha1.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.8.2
3
+ Version: 1.8.3
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  License: MIT
@@ -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
- self.coeff = list(coeff)
29
- for i in range(len(self.coeff) - 1, 0, -1): # strip leading zeros
30
- if self.coeff[i]:
31
- break
32
- self.coeff.pop(i)
33
- self.modulus = modulus
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
- if modulus:
37
- self.mod(modulus)
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
- def applyfunc(self, func) -> "Poly":
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
- return self.__class__(list(map(func, self.coeff)), modulus=self.modulus)
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
- if not modulus and other.modulus:
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
- return self.__class__([s + o for s, o in zip(self.coeff, other.coeff)] + other.coeff[ls:], modulus=modulus)
212
- return self.__class__([s + o for s, o in zip(self.coeff, other.coeff)] + self.coeff[lo:], modulus=modulus)
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 Poly([-s for s in self.coeff], modulus=self.modulus)
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
- return Poly([other * s for s in self.coeff], modulus=self.modulus)
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
- return self.__class__(coeff, modulus=modulus)
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=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
- tmp = one / lco
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 # "* 1" is here to make sure we get a copy
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, modulus=self.modulus), self.__class__(rem, modulus=self.modulus)
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 isinstance(other, list):
343
- other = self.__class__(other, ring=ring)
344
- elif not isinstance(other, self.__class__):
345
- raise NotImplementedError(f"Cannot divide {self} and {other}.")
346
- if not other:
347
- raise NotImplementedError(f"{other} must be nonzero.")
348
- sd, od = self.degree(), other.degree()
349
- if sd < od:
350
- return self
351
- lco = other.coeff[-1]
352
- if lco != one:
353
- tmp = one / lco
354
- oth = [c * tmp for c in other.coeff]
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 = Poly(other, ring=ring)
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 Poly([self.coeff[0]])
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 Poly([zero]), Poly([zero]), Poly([zero])
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, Poly([zero]), one
425
- return other / other.coeff[-1], Poly([zero]), one / 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 = self.__class__([zero]), self.__class__([one])
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
- return self.__class__(tmp, modulus=self.modulus)
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
- return self.__class__(reversed(self.coeff), modulus=self.modulus)
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 Poly(root).square_free_factors().items():
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 = Poly([zero, one], modulus=self.coeff) # 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 = Poly([randint(0, q-1)
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 = Poly([self.coeff[-1]])
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():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kryptools
3
- Version: 1.8.2
3
+ Version: 1.8.3
4
4
  Summary: Implemenation of same basic algorithms used in cryptography.
5
5
  Author-email: Gerald Teschl <gerald.teschl@univie.ac.at>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "1.8.2"
3
+ version = "1.8.3"
4
4
  authors = [
5
5
  { name="Gerald Teschl", email="gerald.teschl@univie.ac.at" },
6
6
  ]
@@ -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