Skip to content

Commit

Permalink
Various Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
teschlg committed Jul 18, 2024
1 parent de93c5b commit 3a6190b
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 168 deletions.
324 changes: 206 additions & 118 deletions doc/kryptools.ipynb

Large diffs are not rendered by default.

40 changes: 29 additions & 11 deletions kryptools/Zmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,48 +90,66 @@ def __hash__(self):
return hash(self.x)

def __add__(self, other: "ZmodPoint") -> "ZmodPoint":
if isinstance(other, self.__class__) and self.ring == other.ring:
if isinstance(other, self.__class__):
if self.ring != other.ring:
raise NotImplementedError("Cannot add elements from different rings.")
return self.__class__(self.x + other.x, self.ring)
if isinstance(other, int):
return self.__class__(self.x + other, self.ring)
return NotImplemented

def __radd__(self, scalar: int) -> "ZmodPoint":
return self.__class__(scalar + self.x, self.ring)
if isinstance(scalar, int):
return self.__class__(scalar + self.x, self.ring)
return NotImplemented

def __neg__(self) -> "ZmodPoint":
return self.__class__(-self.x, self.ring)

def __sub__(self, other: "ZmodPoint") -> "ZmodPoint":
if isinstance(other, self.__class__) and self.ring == other.ring:
if isinstance(other, self.__class__):
if self.ring != other.ring:
raise NotImplementedError("Cannot subtract elements from different rings.")
return self.__class__(self.x - other.x, self.ring)
if isinstance(other, int):
return self.__class__(self.x - other, self.ring)
return NotImplemented

def __rsub__(self, scalar: int) -> "ZmodPoint":
return self.__class__(scalar - self.x, self.ring)
if isinstance(scalar, int):
return self.__class__(scalar - self.x, self.ring)
return NotImplemented

def __mul__(self, other: "ZmodPoint") -> "ZmodPoint":
if isinstance(other, self.__class__) and self.ring == other.ring:
if isinstance(other, self.__class__):
if self.ring != other.ring:
raise NotImplementedError("Cannot multiply elements from different rings.")
return self.__class__(self.x * other.x, self.ring)
if isinstance(other, int):
return self.__class__(self.x * other, self.ring)
return NotImplemented

def __rmul__(self, scalar: int) -> "ZmodPoint":
return self.__class__(scalar * self.x, self.ring)
if isinstance(scalar, int):
return self.__class__(scalar * self.x, self.ring)
return NotImplemented

def __truediv__(self, other: "ZmodPoint") -> "ZmodPoint":
if not isinstance(other, self.__class__) or self.ring != other.ring:
return NotImplemented
return self.__class__(self.x * pow(other.x, -1, self.ring.n), self.ring)
if isinstance(other, self.__class__):
if self.ring != other.ring:
raise NotImplementedError("Cannot divide elements from different rings.")
return self.__class__(self.x * pow(other.x, -1, self.ring.n), self.ring)
return NotImplemented

def __rtruediv__(self, scalar: int) -> "ZmodPoint":
return self.__class__(scalar * pow(self.x, -1, self.ring.n), self.ring)
if isinstance(scalar, int):
return self.__class__(scalar * pow(self.x, -1, self.ring.n), self.ring)
return NotImplemented

def __pow__(self, scalar: int) -> "ZmodPoint":
return self.__class__(pow(self.x, scalar, self.ring.n), self.ring)
if isinstance(scalar, int):
return self.__class__(pow(self.x, scalar, self.ring.n), self.ring)
return NotImplemented

def sharp(self):
"Returns a symmetric (w.r.t. 0) representative."
Expand Down
9 changes: 5 additions & 4 deletions kryptools/dlp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .factor import factorint


def dlog_naive(a: int, b: int, n: int, m: int = None) -> int:
def dlog_naive(a: int, b: int, n: int, m: int = None) -> int|None:
"""Compute the discrete log_a(b) in Z_n of an element a of order m by exhaustive search."""
a %= n
b %= n
Expand Down Expand Up @@ -43,14 +43,14 @@ def _dlog_ph(a: int, b: int, n: int, q: int, k: int) -> int:
for j in range(2, k + 1):
aj = pow(a, q ** (k - j), n)
bj = pow(b, q ** (k - j), n)
yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q)
yj = _dlog_switch(a1, bj * pow(aj, -xj, n) % n, n, q) # pylint: disable=E1130
if yj is None:
return None
xj = xj + q ** (j - 1) * yj % q**j
return xj


def dlog(a: int, b: int, n: int, m: int = None) -> int:
def dlog(a: int, b: int, n: int, m: int|None = None) -> int:
"""Compute the discrete log_a(b) in Z_n of an element a of order m using Pohlig-Hellman reduction."""
a %= n
b %= n
Expand All @@ -60,7 +60,8 @@ def dlog(a: int, b: int, n: int, m: int = None) -> int:
mf = factorint(m)
else:
m, mf = order(a, n, True)
assert pow(b, m, n) == 1, "DLP not solvable."
if pow(b, m, n) != 1:
raise ValueError("DLP not solvable.")
# We first use Pohlig-Hellman to split m into powers of prime factors
mm = []
ll = []
Expand Down
6 changes: 3 additions & 3 deletions kryptools/dlp_qs.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def process_relation(relation: list) -> None or int:
if r == 0:
roots = [ -(inv2 * aa) % p ] # one root
else:
roots = [ (inv2 * (r - aa)) % p, (inv2 * (-r - aa)) % p ] # two roots
roots = [ (inv2 * (r - aa)) % p, (inv2 * (-r - aa)) % p ] # two roots pylint: disable=E1130
for r in roots:
x = r # start value for x
while x < max_j:
Expand Down Expand Up @@ -255,10 +255,10 @@ def process_relation(relation: list) -> None or int:
relation = find_relation(include_b)
res = process_relation(relation)
if res:
return(res)
return res

#
# find the B-smooth numbers and add them to the system
# find the B-smooth numbers and add them to the system
#

for s in range(sieve_bound):
Expand Down
2 changes: 1 addition & 1 deletion kryptools/ec.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def __rmul__(self, scalar: int) -> "ECPoint":
def __neg__(self) -> "ECPoint":
if self.x is None or not self.y:
return self
return ECPoint(self.x, -self.y, self.curve)
return ECPoint(self.x, -self.y, self.curve) # pylint: disable=E1130

def order(self) -> int:
"""Compute the order of an element."""
Expand Down
2 changes: 1 addition & 1 deletion kryptools/factor_ecm.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _ecm_parameters(B1: int, B2: int = None, D: int = None, primes: tuple = None
return D, stage_one, stage_two_deltas


def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple = None):
def factor_ecm(n: int, B1: int = 11000, B2: int = 1900000, curves: int = 74, ecm_parameters: tuple|None = None):
"Factors a number n using Lentsta's ECM method."

if ecm_parameters:
Expand Down
20 changes: 14 additions & 6 deletions kryptools/la.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"""

from math import inf, sqrt, prod
from numbers import Number


class Matrix:
"""
Expand Down Expand Up @@ -149,12 +151,16 @@ def multiply(self, other) -> "Matrix":
return result

def __add__(self, other) -> "Matrix":
if isinstance(other, Matrix) and other.cols == self.cols and other.rows == self.rows:
if isinstance(other, Matrix):
if other.cols != self.cols or other.rows != self.rows:
raise NotImplementedError("Matrix dimensions do not match!")
return Matrix([ [ x1 + y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
return NotImplemented

def __sub__(self, other) -> "Matrix":
if isinstance(other, Matrix) and other.cols == self.cols and other.rows == self.rows:
if isinstance(other, Matrix):
if other.cols != self.cols or other.rows != self.rows:
raise NotImplementedError("Matrix dimensions do not match!")
return Matrix([ [ x1 - y1 for x1, y1 in zip(x,y)] for x, y in zip(self.matrix, other.matrix)])
return NotImplemented

Expand All @@ -164,12 +170,14 @@ def __neg__(self) -> "Matrix":
def __mul__(self, other) -> "Matrix":
if isinstance(other, Matrix):
return self.multiply(other)
return Matrix([ [item * other for item in row] for row in self.matrix ])
if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
return Matrix([ [item * other for item in row] for row in self.matrix ])
return NotImplemented

def __rmul__(self, other) -> "Matrix":
if isinstance(other, Matrix):
return self.multiply(other)
return Matrix([ [item * other for item in row] for row in self.matrix ])
if isinstance(other, Number) or type(other) == type(self.matrix[0][0]):
return Matrix([ [item * other for item in row] for row in self.matrix ])
return NotImplemented

def rref(self) -> "Matrix":
"Compute the reduced echelon form of a matrix M."
Expand Down
5 changes: 3 additions & 2 deletions kryptools/nt.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def crt(a: list[int], m: list[int]) -> int:


def fraction_repr(self):
"Representation of a fraction."
if self.denominator == 1:
return str(self.numerator)
return str(self.numerator) + "/" + str(self.denominator)
Expand Down Expand Up @@ -228,7 +229,7 @@ def order(a: int, n: int, factor=False) -> int:
"""Compute the order of `a` in the group Z_n^*."""
a %= n
assert a != 0 and gcd(a, n) == 1, f"{a} and {n} are not coprime!"
factors = dict() # We compute euler_phi(n) and its factorization in one pass
factors = {} # We compute euler_phi(n) and its factorization in one pass
for p, k in factorint(n).items(): # first factorize n
for pm, km in factorint(p - 1).items(): # factor p-1 and add the factors
if pm in factors:
Expand All @@ -255,7 +256,7 @@ def order(a: int, n: int, factor=False) -> int:
else:
break
if factor and i < k:
factors_order[p] = k - i
factors_order[p] = k - i # pylint: disable=E0606
if factor:
return order_a, factors_order
return order_a
52 changes: 30 additions & 22 deletions kryptools/poly.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Polynomials
"""

from numbers import Number

class Poly:
"""
Represents a polynomial as a list of coefficients.
Expand Down Expand Up @@ -94,12 +96,11 @@ def map(self, func):

def __add__(self, other: "Poly") -> "Poly":
if not isinstance(other, self.__class__):
try:
tmp = self.coeff[:]
tmp = self.coeff[:]
if isinstance(other, int) or type(other) == type(tmp[0]):
tmp[0] += other
return self.__class__(tmp, modulus=self.modulus)
except:
return NotImplemented
return NotImplemented
ls, lo = len(self.coeff), len(other.coeff)
if ls < lo:
scoeff = self.coeff + (lo - ls) * [0]
Expand All @@ -116,25 +117,22 @@ def __add__(self, other: "Poly") -> "Poly":

def __radd__(self, other: "Poly") -> "Poly":
if not isinstance(other, self.__class__):
try:
if isinstance(other, Number) or type(other) == type(self.coeff[0]):
tmp = self.coeff[:]
tmp[0] += other
return self.__class__(tmp, modulus=self.modulus)
except:
pass
return NotImplemented

def __neg__(self) -> "Poly":
return Poly([-s for s in self.coeff], modulus=self.modulus)

def __sub__(self, other: "Poly") -> "Poly":
if not isinstance(other, self.__class__):
try:
if isinstance(other, int) or type(other) == type(self.coeff[0]):
tmp = self.coeff[:]
tmp[0] -= other
return self.__class__(tmp, modulus=self.modulus)
except:
return NotImplemented
return NotImplemented
ls, lo = len(self.coeff), len(other.coeff)
if ls < lo:
scoeff = self.coeff + (lo - ls) * [0]
Expand All @@ -147,24 +145,21 @@ def __sub__(self, other: "Poly") -> "Poly":
modulus = self.modulus
if not modulus and other.modulus:
modulus = other.modulus
return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus=modulus)
return self.__class__([s - o for s, o in zip(scoeff, ocoeff)], modulus = modulus)

def __rsub__(self, other: "Poly") -> "Poly":
if not isinstance(other, self.__class__):
try:
if isinstance(other, int) or type(other) == type(self.coeff[0]):
tmp = self.coeff[:]
tmp[0] -= other
return self.__class__(tmp, modulus=self.modulus)
except:
pass
return NotImplemented

def __mul__(self, other: "Poly") -> "Poly":
if not isinstance(other, self.__class__):
try:
return Poly([other * s for s in self.coeff])
except:
return NotImplemented
if isinstance(other, int) or type(other) == type(self.coeff[0]):
return Poly([other * s for s in self.coeff], modulus = self.modulus)
return NotImplemented
ls, lo = len(self.coeff), len(other.coeff)
coeff = [0] * (ls + lo - 1)
for k in range(ls + lo - 1):
Expand All @@ -177,23 +172,36 @@ def __mul__(self, other: "Poly") -> "Poly":
modulus = self.modulus
if not modulus and other.modulus:
modulus = other.modulus
return self.__class__(coeff, modulus=modulus)
return self.__class__(coeff, modulus = modulus)

def __rmul__(self, other: int) -> "Poly":
return self.__class__([other * s for s in self.coeff], modulus=self.modulus)
if isinstance(other, int) or type(other) == type(self.coeff[0]):
return self.__class__([other * s for s in self.coeff], modulus=self.modulus)
return NotImplemented

def __pow__(self, i: int) -> "Poly":
res = self.__class__([1], modulus=self.modulus)
if not isinstance(i, int):
return NotImplemented
zero = 0 * self.coeff[0]
one = zero + 1
res = self.__class__([one], modulus=self.modulus)
if i < 0:
if not self.modulus:
raise NotImplementedError("Cannot divide.")
raise NotImplementedError("Cannot divide polynomials without modulus.")
tmp = self.inv()
i *= -1
else:
tmp = self
for _ in range(i):
res *= tmp
return res

def __floordiv__(self, other: "Poly") -> "Poly":
return self.divmod(other)[0]

def __mod__(self, other: "Poly") -> "Poly":
return self.divmod(other)[1]

def divmod(self, other: "Poly") -> ("Poly", "Poly"):
"Polynom division with remainder."
if isinstance(other, list):
Expand Down

0 comments on commit 3a6190b

Please sign in to comment.