kryptools 0.4__tar.gz → 0.6__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 (28) hide show
  1. {kryptools-0.4 → kryptools-0.6}/PKG-INFO +2 -2
  2. {kryptools-0.4 → kryptools-0.6}/kryptools/Zmod.py +29 -11
  3. {kryptools-0.4 → kryptools-0.6}/kryptools/__init__.py +1 -1
  4. {kryptools-0.4 → kryptools-0.6}/kryptools/dlp.py +5 -4
  5. {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_qs.py +3 -3
  6. {kryptools-0.4 → kryptools-0.6}/kryptools/ec.py +1 -1
  7. {kryptools-0.4 → kryptools-0.6}/kryptools/factor_ecm.py +1 -1
  8. {kryptools-0.4 → kryptools-0.6}/kryptools/la.py +26 -6
  9. {kryptools-0.4 → kryptools-0.6}/kryptools/lat.py +13 -4
  10. {kryptools-0.4 → kryptools-0.6}/kryptools/nt.py +3 -2
  11. {kryptools-0.4 → kryptools-0.6}/kryptools/poly.py +51 -26
  12. {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/PKG-INFO +2 -2
  13. {kryptools-0.4 → kryptools-0.6}/pyproject.toml +2 -2
  14. {kryptools-0.4 → kryptools-0.6}/LICENSE +0 -0
  15. {kryptools-0.4 → kryptools-0.6}/README.md +0 -0
  16. {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_bsgs.py +0 -0
  17. {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_ic.py +0 -0
  18. {kryptools-0.4 → kryptools-0.6}/kryptools/dlp_rho.py +0 -0
  19. {kryptools-0.4 → kryptools-0.6}/kryptools/factor.py +0 -0
  20. {kryptools-0.4 → kryptools-0.6}/kryptools/factor_dix.py +0 -0
  21. {kryptools-0.4 → kryptools-0.6}/kryptools/factor_fmt.py +0 -0
  22. {kryptools-0.4 → kryptools-0.6}/kryptools/factor_pm1.py +0 -0
  23. {kryptools-0.4 → kryptools-0.6}/kryptools/factor_qs.py +0 -0
  24. {kryptools-0.4 → kryptools-0.6}/kryptools/primes.py +0 -0
  25. {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/SOURCES.txt +0 -0
  26. {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/dependency_links.txt +0 -0
  27. {kryptools-0.4 → kryptools-0.6}/kryptools.egg-info/top_level.txt +0 -0
  28. {kryptools-0.4 → kryptools-0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.4
3
+ Version: 0.6
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.8
12
+ Requires-Python: >=3.9
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
 
@@ -90,48 +90,66 @@ class ZmodPoint:
90
90
  return hash(self.x)
91
91
 
92
92
  def __add__(self, other: "ZmodPoint") -> "ZmodPoint":
93
- if isinstance(other, self.__class__) and self.ring == other.ring:
93
+ if isinstance(other, self.__class__):
94
+ if self.ring != other.ring:
95
+ raise NotImplementedError("Cannot add elements from different rings.")
94
96
  return self.__class__(self.x + other.x, self.ring)
95
97
  if isinstance(other, int):
96
98
  return self.__class__(self.x + other, self.ring)
97
99
  return NotImplemented
98
100
 
99
101
  def __radd__(self, scalar: int) -> "ZmodPoint":
100
- return self.__class__(scalar + self.x, self.ring)
102
+ if isinstance(scalar, int):
103
+ return self.__class__(scalar + self.x, self.ring)
104
+ return NotImplemented
101
105
 
102
106
  def __neg__(self) -> "ZmodPoint":
103
107
  return self.__class__(-self.x, self.ring)
104
108
 
105
109
  def __sub__(self, other: "ZmodPoint") -> "ZmodPoint":
106
- if isinstance(other, self.__class__) and self.ring == other.ring:
110
+ if isinstance(other, self.__class__):
111
+ if self.ring != other.ring:
112
+ raise NotImplementedError("Cannot subtract elements from different rings.")
107
113
  return self.__class__(self.x - other.x, self.ring)
108
114
  if isinstance(other, int):
109
115
  return self.__class__(self.x - other, self.ring)
110
116
  return NotImplemented
111
117
 
112
118
  def __rsub__(self, scalar: int) -> "ZmodPoint":
113
- return self.__class__(scalar - self.x, self.ring)
119
+ if isinstance(scalar, int):
120
+ return self.__class__(scalar - self.x, self.ring)
121
+ return NotImplemented
114
122
 
115
123
  def __mul__(self, other: "ZmodPoint") -> "ZmodPoint":
116
- if isinstance(other, self.__class__) and self.ring == other.ring:
124
+ if isinstance(other, self.__class__):
125
+ if self.ring != other.ring:
126
+ raise NotImplementedError("Cannot multiply elements from different rings.")
117
127
  return self.__class__(self.x * other.x, self.ring)
118
128
  if isinstance(other, int):
119
129
  return self.__class__(self.x * other, self.ring)
120
130
  return NotImplemented
121
131
 
122
132
  def __rmul__(self, scalar: int) -> "ZmodPoint":
123
- return self.__class__(scalar * self.x, self.ring)
133
+ if isinstance(scalar, int):
134
+ return self.__class__(scalar * self.x, self.ring)
135
+ return NotImplemented
124
136
 
125
137
  def __truediv__(self, other: "ZmodPoint") -> "ZmodPoint":
126
- if not isinstance(other, self.__class__) or self.ring != other.ring:
127
- return NotImplemented
128
- return self.__class__(self.x * pow(other.x, -1, self.ring.n), self.ring)
138
+ if isinstance(other, self.__class__):
139
+ if self.ring != other.ring:
140
+ raise NotImplementedError("Cannot divide elements from different rings.")
141
+ return self.__class__(self.x * pow(other.x, -1, self.ring.n), self.ring)
142
+ return NotImplemented
129
143
 
130
144
  def __rtruediv__(self, scalar: int) -> "ZmodPoint":
131
- return self.__class__(scalar * pow(self.x, -1, self.ring.n), self.ring)
145
+ if isinstance(scalar, int):
146
+ return self.__class__(scalar * pow(self.x, -1, self.ring.n), self.ring)
147
+ return NotImplemented
132
148
 
133
149
  def __pow__(self, scalar: int) -> "ZmodPoint":
134
- return self.__class__(pow(self.x, scalar, self.ring.n), self.ring)
150
+ if isinstance(scalar, int):
151
+ return self.__class__(pow(self.x, scalar, self.ring.n), self.ring)
152
+ return NotImplemented
135
153
 
136
154
  def sharp(self):
137
155
  "Returns a symmetric (w.r.t. 0) representative."
@@ -8,6 +8,6 @@ from .factor import factorint
8
8
  from .dlp import dlog
9
9
  from .ec import EC_Weierstrass
10
10
  from .la import Matrix, zeros, eye
11
- from .lat import gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round_cvp, babai_plane_cvp, lagrange_lr, lll, random_unimodular_matrix
11
+ from .lat import gram_det, hadamard_ratio, hermite_nf, gram_schmidt, babai_round_cvp, babai_round_bnd, babai_plane_cvp, babai_plane_bnd, lagrange_lr, lll, random_unimodular_matrix
12
12
  from .poly import Poly
13
13
  from .Zmod import Zmod
@@ -11,7 +11,7 @@ from .dlp_qs import dlog_qs
11
11
  from .factor import factorint
12
12
 
13
13
 
14
- def dlog_naive(a: int, b: int, n: int, m: int = None) -> int:
14
+ def dlog_naive(a: int, b: int, n: int, m: int = None) -> int|None:
15
15
  """Compute the discrete log_a(b) in Z_n of an element a of order m by exhaustive search."""
16
16
  a %= n
17
17
  b %= n
@@ -43,14 +43,14 @@ def _dlog_ph(a: int, b: int, n: int, q: int, k: int) -> int:
43
43
  for j in range(2, k + 1):
44
44
  aj = pow(a, q ** (k - j), n)
45
45
  bj = pow(b, q ** (k - j), n)
46
- yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q)
46
+ yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q) # pylint: disable=E1130
47
47
  if yj is None:
48
48
  return None
49
49
  xj = xj + q ** (j - 1) * yj % q**j
50
50
  return xj
51
51
 
52
52
 
53
- def dlog(a: int, b: int, n: int, m: int = None) -> int:
53
+ def dlog(a: int, b: int, n: int, m: int|None = None) -> int:
54
54
  """Compute the discrete log_a(b) in Z_n of an element a of order m using Pohlig-Hellman reduction."""
55
55
  a %= n
56
56
  b %= n
@@ -60,7 +60,8 @@ def dlog(a: int, b: int, n: int, m: int = None) -> int:
60
60
  mf = factorint(m)
61
61
  else:
62
62
  m, mf = order(a, n, True)
63
- assert pow(b, m, n) == 1, "DLP not solvable."
63
+ if pow(b, m, n) != 1:
64
+ raise ValueError("DLP not solvable.")
64
65
  # We first use Pohlig-Hellman to split m into powers of prime factors
65
66
  mm = []
66
67
  ll = []
@@ -227,7 +227,7 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
227
227
  if r == 0:
228
228
  roots = [ -(inv2 * aa) % p ] # one root
229
229
  else:
230
- roots = [ (inv2 * (r - aa)) % p, (inv2 * (-r - aa)) % p ] # two roots
230
+ roots = [ (inv2 * (r - aa)) % p, (inv2 * (-r - aa)) % p ] # two roots pylint: disable=E1130
231
231
  for r in roots:
232
232
  x = r # start value for x
233
233
  while x < max_j:
@@ -255,10 +255,10 @@ def dlog_qs(a: int, b: int, n: int, m: int, pollard: bool = True, sieve_factor:
255
255
  relation = find_relation(include_b)
256
256
  res = process_relation(relation)
257
257
  if res:
258
- return(res)
258
+ return res
259
259
 
260
260
  #
261
- # find the B-smooth numbers and add them to the system
261
+ # find the B-smooth numbers and add them to the system
262
262
  #
263
263
 
264
264
  for s in range(sieve_bound):
@@ -320,7 +320,7 @@ class ECPoint:
320
320
  def __neg__(self) -> "ECPoint":
321
321
  if self.x is None or not self.y:
322
322
  return self
323
- return ECPoint(self.x, -self.y, self.curve)
323
+ return ECPoint(self.x, -self.y, self.curve) # pylint: disable=E1130
324
324
 
325
325
  def order(self) -> int:
326
326
  """Compute the order of an element."""
@@ -84,7 +84,7 @@ def _ecm_parameters(B1: int, B2: int = None, D: int = None, primes: tuple = None
84
84
  return D, stage_one, stage_two_deltas
85
85
 
86
86
 
87
- def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple = None):
87
+ def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple|None = None):
88
88
  "Factors a number n using Lentsta's ECM method."
89
89
 
90
90
  if ecm_parameters:
@@ -3,6 +3,8 @@ Linear algebra
3
3
  """
4
4
 
5
5
  from math import inf, sqrt, prod
6
+ from numbers import Number
7
+ from fractions import Fraction
6
8
 
7
9
  class Matrix:
8
10
  """
@@ -146,15 +148,21 @@ class Matrix:
146
148
  for j in range(other.cols):
147
149
  for k in range(other.rows):
148
150
  result.matrix[i][j] += self.matrix[i][k] * other.matrix[k][j]
151
+ if self.rows == 1 and other.cols == 1:
152
+ return result.matrix[0][0]
149
153
  return result
150
154
 
151
155
  def __add__(self, other) -> "Matrix":
152
- if isinstance(other, Matrix) and other.cols == self.cols and other.rows == self.rows:
156
+ if isinstance(other, Matrix):
157
+ if other.cols != self.cols or other.rows != self.rows:
158
+ raise NotImplementedError("Matrix dimensions do not match!")
153
159
  return Matrix([ [ x1 + y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
154
160
  return NotImplemented
155
161
 
156
162
  def __sub__(self, other) -> "Matrix":
157
- if isinstance(other, Matrix) and other.cols == self.cols and other.rows == self.rows:
163
+ if isinstance(other, Matrix):
164
+ if other.cols != self.cols or other.rows != self.rows:
165
+ raise NotImplementedError("Matrix dimensions do not match!")
158
166
  return Matrix([ [ x1 - y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
159
167
  return NotImplemented
160
168
 
@@ -164,12 +172,14 @@ class Matrix:
164
172
  def __mul__(self, other) -> "Matrix":
165
173
  if isinstance(other, Matrix):
166
174
  return self.multiply(other)
167
- return Matrix([ [item * other for item in row] for row in self.matrix ])
175
+ if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
176
+ return Matrix([ [item * other for item in row] for row in self.matrix ])
177
+ return NotImplemented
168
178
 
169
179
  def __rmul__(self, other) -> "Matrix":
170
- if isinstance(other, Matrix):
171
- return self.multiply(other)
172
- return Matrix([ [item * other for item in row] for row in self.matrix ])
180
+ if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
181
+ return Matrix([ [item * other for item in row] for row in self.matrix ])
182
+ return NotImplemented
173
183
 
174
184
  def rref(self) -> "Matrix":
175
185
  "Compute the reduced echelon form of a matrix M."
@@ -238,6 +248,16 @@ class Matrix:
238
248
  raise ValueError("Matrix is not invertible!")
239
249
  return MM[:,n:]
240
250
 
251
+ def is_unimodular(self) -> bool:
252
+ "Test if the matrix is unimodular."
253
+ if self.rows != self.rows:
254
+ return False
255
+ def is_integer(i):
256
+ if isinstance(i, int) or (isinstance(i, Fraction) and i.denominator == 1):
257
+ return True
258
+ return False
259
+ return all([is_integer(i) for i in self]) and self.det()**2 == 1
260
+
241
261
  def zeros(self, m: int = None, n: int = None):
242
262
  "Returns a zero matrix of the same dimension"
243
263
  if not m and not n:
@@ -2,7 +2,7 @@
2
2
  Lattice tools
3
3
  """
4
4
 
5
- from math import prod
5
+ from math import prod, floor
6
6
  from fractions import Fraction
7
7
  from random import choice, sample
8
8
  from .la import Matrix, zeros
@@ -73,19 +73,28 @@ def hadamard_ratio(M: Matrix) -> float:
73
73
  return (gram_det(M) / prod([M[:, i].norm() for i in range(m)])) ** (1 / m)
74
74
 
75
75
  def babai_round_cvp(x: Matrix, U: Matrix) -> Matrix:
76
- "Babai's rounding algorithm for solving the CVP."
76
+ "Babai's rounding algorithm for approximately solving the CVP."
77
77
  s = U.inv() * x
78
78
  k = s.applyfunc(round)
79
79
  return U * k
80
80
 
81
+ def babai_round_bnd(U: Matrix) -> float:
82
+ "Bound for Babai's rounding algorithm for solving the CVP."
83
+ return floor(1 / (2 * max([ U.inv()[i,:].norm(1) for i in range(U.rows)])))
84
+
81
85
  def babai_plane_cvp(x: Matrix, U: Matrix) -> Matrix:
82
- "Babai's closest plane algorithm for solving the CVP."
86
+ "Babai's closest plane algorithm for approximately solving the CVP."
83
87
  Us = gram_schmidt(U)[0]
84
88
  y = x
85
89
  for k in range(U.cols - 1, -1, -1):
86
90
  y = y - round(y.dot(Us[:, k]) / norm2(Us[:, k])) * U[:, k]
87
91
  return (x - y).applyfunc(round)
88
92
 
93
+ def babai_plane_bnd(U: Matrix, p = 2) -> float:
94
+ "Bound for Babai's closest plane algorithm for solving the CVP."
95
+ Us = gram_schmidt(U)[0]
96
+ return float(0.5 * min([Us[:, i].norm(p) for i in range(Us.rows)]))
97
+
89
98
  def lagrange_lr(V: Matrix) -> Matrix:
90
99
  "Lagrange lattice reduction."
91
100
  assert (V.rows, V.cols) == (2, 2)
@@ -99,7 +108,7 @@ def lagrange_lr(V: Matrix) -> Matrix:
99
108
  return Matrix([list(v1), list(v3)]).transpose()
100
109
 
101
110
  def lll(V: Matrix, delta: float = 0.75, sort: bool = True) -> Matrix:
102
- "lll algorithm for lattice reduction"
111
+ "LLL algorithm for lattice reduction."
103
112
 
104
113
  assert 0 < delta <= 1, f"LLL reqires 0 < delta={delta} <= 1"
105
114
  j = 1
@@ -104,6 +104,7 @@ def crt(a: list[int], m: list[int]) -> int:
104
104
 
105
105
 
106
106
  def fraction_repr(self):
107
+ "Representation of a fraction."
107
108
  if self.denominator == 1:
108
109
  return str(self.numerator)
109
110
  return str(self.numerator) + "/" + str(self.denominator)
@@ -228,7 +229,7 @@ def order(a: int, n: int, factor=False) -> int:
228
229
  """Compute the order of `a` in the group Z_n^*."""
229
230
  a %= n
230
231
  assert a != 0 and gcd(a, n) == 1, f"{a} and {n} are not coprime!"
231
- factors = dict() # We compute euler_phi(n) and its factorization in one pass
232
+ factors = {} # We compute euler_phi(n) and its factorization in one pass
232
233
  for p, k in factorint(n).items(): # first factorize n
233
234
  for pm, km in factorint(p - 1).items(): # factor p-1 and add the factors
234
235
  if pm in factors:
@@ -255,7 +256,7 @@ def order(a: int, n: int, factor=False) -> int:
255
256
  else:
256
257
  break
257
258
  if factor and i < k:
258
- factors_order[p] = k - i
259
+ factors_order[p] = k - i # pylint: disable=E0606
259
260
  if factor:
260
261
  return order_a, factors_order
261
262
  return order_a
@@ -2,6 +2,8 @@
2
2
  Polynomials
3
3
  """
4
4
 
5
+ from numbers import Number
6
+
5
7
  class Poly:
6
8
  """
7
9
  Represents a polynomial as a list of coefficients.
@@ -92,14 +94,16 @@ class Poly:
92
94
  "Apply a given function to all coefficients."
93
95
  self.coeff = list(map(func, self.coeff))
94
96
 
97
+ def _check_type(self, other):
98
+ return isinstance(other, int) or (isinstance(other, Number) and isinstance(self.coeff[0], Number)) or type(other) == type(self.coeff[0])
99
+
95
100
  def __add__(self, other: "Poly") -> "Poly":
96
101
  if not isinstance(other, self.__class__):
97
- try:
102
+ if self._check_type(other):
98
103
  tmp = self.coeff[:]
99
104
  tmp[0] += other
100
105
  return self.__class__(tmp, modulus=self.modulus)
101
- except:
102
- return NotImplemented
106
+ return NotImplemented
103
107
  ls, lo = len(self.coeff), len(other.coeff)
104
108
  if ls < lo:
105
109
  scoeff = self.coeff + (lo - ls) * [0]
@@ -116,12 +120,10 @@ class Poly:
116
120
 
117
121
  def __radd__(self, other: "Poly") -> "Poly":
118
122
  if not isinstance(other, self.__class__):
119
- try:
123
+ if self._check_type(other):
120
124
  tmp = self.coeff[:]
121
125
  tmp[0] += other
122
126
  return self.__class__(tmp, modulus=self.modulus)
123
- except:
124
- pass
125
127
  return NotImplemented
126
128
 
127
129
  def __neg__(self) -> "Poly":
@@ -129,12 +131,11 @@ class Poly:
129
131
 
130
132
  def __sub__(self, other: "Poly") -> "Poly":
131
133
  if not isinstance(other, self.__class__):
132
- try:
134
+ if self._check_type(other):
133
135
  tmp = self.coeff[:]
134
136
  tmp[0] -= other
135
137
  return self.__class__(tmp, modulus=self.modulus)
136
- except:
137
- return NotImplemented
138
+ return NotImplemented
138
139
  ls, lo = len(self.coeff), len(other.coeff)
139
140
  if ls < lo:
140
141
  scoeff = self.coeff + (lo - ls) * [0]
@@ -147,24 +148,21 @@ class Poly:
147
148
  modulus = self.modulus
148
149
  if not modulus and other.modulus:
149
150
  modulus = other.modulus
150
- return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus=modulus)
151
+ return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus = modulus)
151
152
 
152
153
  def __rsub__(self, other: "Poly") -> "Poly":
153
154
  if not isinstance(other, self.__class__):
154
- try:
155
+ if self._check_type(other):
155
156
  tmp = self.coeff[:]
156
157
  tmp[0] -= other
157
158
  return self.__class__(tmp, modulus=self.modulus)
158
- except:
159
- pass
160
159
  return NotImplemented
161
160
 
162
161
  def __mul__(self, other: "Poly") -> "Poly":
163
162
  if not isinstance(other, self.__class__):
164
- try:
165
- return Poly([other * s for s in self.coeff])
166
- except:
167
- return NotImplemented
163
+ if self._check_type(other):
164
+ return Poly([other * s for s in self.coeff], modulus = self.modulus)
165
+ return NotImplemented
168
166
  ls, lo = len(self.coeff), len(other.coeff)
169
167
  coeff = [0] * (ls + lo - 1)
170
168
  for k in range(ls + lo - 1):
@@ -177,23 +175,50 @@ class Poly:
177
175
  modulus = self.modulus
178
176
  if not modulus and other.modulus:
179
177
  modulus = other.modulus
180
- return self.__class__(coeff, modulus=modulus)
178
+ return self.__class__(coeff, modulus = modulus)
179
+
180
+ def __rmul__(self, other) -> "Poly":
181
+ if self._check_type(other):
182
+ return self.__class__([other * s for s in self.coeff], modulus=self.modulus)
183
+ return NotImplemented
181
184
 
182
- def __rmul__(self, other: int) -> "Poly":
183
- return self.__class__([other * s for s in self.coeff], modulus=self.modulus)
185
+ def __truediv__(self, other) -> "Poly":
186
+ if self._check_type(other):
187
+ return self.__class__([s / other for s in self.coeff], modulus=self.modulus)
188
+ if isinstance(other, self.__class__):
189
+ if not other.modulus:
190
+ raise NotImplementedError("Cannot invert polynomials without modulus.")
191
+ return self * other.inv()
192
+ return NotImplemented
193
+
194
+ def __rtruediv__(self, other) -> "Poly":
195
+ if not self.modulus:
196
+ raise NotImplementedError("Cannot invert polynomials without modulus.")
197
+ return other * self.inv()
184
198
 
185
199
  def __pow__(self, i: int) -> "Poly":
186
- res = self.__class__([1], modulus=self.modulus)
200
+ if not isinstance(i, int):
201
+ return NotImplemented
202
+ zero = 0 * self.coeff[0]
203
+ one = zero + 1
204
+ res = self.__class__([one], modulus=self.modulus)
187
205
  if i < 0:
188
206
  if not self.modulus:
189
- raise NotImplementedError("Cannot divide.")
207
+ raise NotImplementedError("Cannot divide polynomials without modulus.")
190
208
  tmp = self.inv()
209
+ i *= -1
191
210
  else:
192
211
  tmp = self
193
212
  for _ in range(i):
194
213
  res *= tmp
195
214
  return res
196
215
 
216
+ def __floordiv__(self, other: "Poly") -> "Poly":
217
+ return self.divmod(other)[0]
218
+
219
+ def __mod__(self, other: "Poly") -> "Poly":
220
+ return self.divmod(other)[1]
221
+
197
222
  def divmod(self, other: "Poly") -> ("Poly", "Poly"):
198
223
  "Polynom division with remainder."
199
224
  if isinstance(other, list):
@@ -261,10 +286,10 @@ class Poly:
261
286
  raise NotImplementedError(f"Cannot invert {self} modulo {other}.")
262
287
  if not other:
263
288
  raise NotImplementedError(f"{other} must be nonzero.")
289
+ zero = 0 * self.coeff[0]
290
+ one = zero +1
264
291
  r0, r1 = other, self
265
- y0, y1 = self.__class__([0], modulus=self.modulus), self.__class__(
266
- [1], modulus=self.modulus
267
- )
292
+ y0, y1 = self.__class__([zero], modulus=self.modulus), self.__class__([one], modulus=self.modulus)
268
293
  while r1:
269
294
  q, r = r0.divmod(r1)
270
295
  r0, r1 = r1, r
@@ -272,6 +297,6 @@ class Poly:
272
297
  if r0.degree() != 0:
273
298
  raise ValueError(f"{self} is not invertible mod {other}.")
274
299
  tmp = 1 / r0[0]
275
- for i in range(y0.degree()):
300
+ for i in range(len(y0)):
276
301
  y0.coeff[i] *= tmp
277
302
  return y0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kryptools
3
- Version: 0.4
3
+ Version: 0.6
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.8
12
+ Requires-Python: >=3.9
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
 
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "kryptools"
3
- version = "0.4"
3
+ version = "0.6"
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.8"
9
+ requires-python = ">=3.9"
10
10
  classifiers = [
11
11
  "Programming Language :: Python :: 3",
12
12
  "License :: OSI Approved :: MIT License",
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