def square_free_decomposition(poly): assert type(poly) == ZPoly print("!") if len(poly) == 1: print(poly) return poly d = poly.derivative() if d != ZPoly([0],poly.M): g = zpoly_gcd(poly,d) print(g) if g == ZPoly([1],poly.M): print("!!!") print(poly) return poly else: print(poly//g) return square_free_decomposition(g) else: pwr = 1 while True: for g in all_monic_zpolys(poly.M): p = g**(poly.M**pwr) if p == poly and g.derivative() != ZPoly([0],poly.M): return square_free_decomposition(g) if len(p) > len(poly): pwr += 1 break
def all_irreducible_mod2(): """Sieve of Eratosthenes for polynomials in Z/2Z""" out = [ZPoly([1,1,1],2)] yield ZPoly([0,1],2) yield ZPoly([1,1],2) all_p = all_zpolys(2) for i in range(7): next(all_p) while True: poly = next(all_p) if poly(0) == poly(1) == 1: for i in out: if len(i) > len(poly) // 2: out.append(poly) yield poly break r = poly%i if r == ZPoly([0],2): out.append(poly) yield poly break
def zpoly_egcd(a, b): """Extended Euclidean Algorithm for ZPolys""" M = a.M if a == ZPoly([0],M): return (b, ZPoly([0],M), ZPoly([1],M)) else: g, y, x = zpoly_egcd(b % a, a) return (g, x - (b // a) * y, y)
def zpoly_lagrange_interpolation(X,Y,M): """Use lagrange polynomial to interpolate points of a zpoly""" final = ZPoly([0],M) for x,y in zip(X,Y): out = ZPoly([y],M) for m in X: if m != x: d = mod_inv(x-m,M) P = ZPoly([-m,1],M) out *= P*d final += out return final
def all_monic_zpolys(M): """Generator for all monic polynomials over M""" yield ZPoly([1],M) co = [i for i in range(M)] l = 1 while True: P = product(co,repeat=l) for p in P: coefs = [i for i in reversed((1,)+p)] yield ZPoly(coefs,M) l += 1
def all_zpolys(M): """Generator for all polynomials over M""" co = [i for i in range(M)] for c in co: yield ZPoly([c],M) l = 1 while True: P = product(co,repeat=l) for p in P: for c in co[1:]: coefs = [i for i in reversed((c,)+p)] yield ZPoly(coefs,M) l += 1
def finite_field_example(P): S = FF2(ZPoly([0, 1], P.M), P) out = FF2(ZPoly([1], P.M), P) L = [FF2(P, P)] elems = (P.M)**(len(P) - 1) for i in range(1, elems): if out in L: raise Exception("Not a finite field") L.append(out) out = (out * S) # for i in L: # print(i) return L
def ff_inv(a, R): """Modular Multiplicative Inverse""" a = a % R g, x, _ = zpoly_egcd(a, R) if g != ZPoly([1], a.M): raise ValueError( f"Multiplicative inverse of {a.full_name} mod {R.full_name} does not exist" ) else: return x % R
def __pow__(self, other): if type(other) != int: raise TypeError("Power of ZPolyProd must be an integer") if other < 0: raise TypeError("Power of ZPolyProd must be non-negative") if other == 0: return ZPolyProd(ZPoly([1], self.M), self.M) if other == 1: return self else: out = self for i in range(other - 1): out *= self return out
def make_shamir_secret(secret,k,n,M): """Take a number and produce k ordered pairs such that n can be used to reconstruct the number""" if secret > M: raise ValueError("secret cannot be less than M or information will be lost") if k > n: raise ValueError("parts needed to reconstruct cannot be greater than total points created") if len(prime_factorization(M)) != 1: raise ValueError("Order for finite field must be a prime") co = [secret] + [randint(0,M-1) for i in range(k-1)] P = ZPoly( co, M ) pts = [] for i in range(n): r = randint(0,M-1) pts.append( (r,P(r)) ) return pts
def __add__(self, other): A = self.terms.copy() if type(other) == int: return self + ZPoly([other], self.M) if type(other) == ZPoly: if other.M == self.M: A.update([other]) else: raise ValueError("Values of M do not match") elif type(other) == ZPolySum: A.update(other.terms) else: return NotImplemented return ZPolySum(A, self.M)
U = RFunc(["1/8"], ["1/3", "5/4"]) rf_minitests = [ (R, "(x + 1) / (3x + 2)"), (S, "(12x + 28) / (x^2 - 3x + 2)"), #simplify (T, "x^2 + 5x + 3"), # denominator one (U, "1/8 / (5/4x + 1/3)"), (U.primitive_part, "12 / (120x + 32)") ] ########### ## ZPoly ## ########### M = 29 P = ZPoly([50, 0, -8, 121, 9], M) Q = ZPoly([25, 29, 8], M) PP = ZPoly([50, 0, -8, 121, 9]) QQ = ZPoly([25, 29, 1]) z_minitests = [ (P, "9x^4 + 5x^3 + 21x^2 + 21"), (Q, "8x^2 + 25"), (P + Q, "9x^4 + 5x^3 + 17"), (P * Q, "14x^6 + 11x^5 + 16x^4 + 9x^3 + 26x^2 + 3"), (Q**0, "1"), (Q**1, "8x^2 + 25"), (Q**2, "6x^4 + 23x^2 + 16"), (P // Q, "12x^2 + 26x + 5"), (P % Q, "17x + 12"), (P // Q * Q + P % Q, str(P)),
def cast_to_poly(self): """Add everything together as a ZPoly""" out = ZPoly([0], self.M) for val, mul in self.terms.items(): out += mul * val return out
pts = make_shamir_secret(secret,min_parts,total_parts,F) print(f"We will use Shamir's method to break up the secret number {secret} into {total_parts} pieces such any {min_parts} pieces can be used to get the secret.") print(f"\nTo do this we create a random polynomial of degree {min_parts} that has constant term {secret} over a finite field and choose {total_parts} points on it.") print(f"\nUsing a finite field of order {F} we get the points:") for i in pts: print(i) print(f"\nPicking three of those we can reconstruct the answer:") rpts = sample(pts,min_parts) print(get_shamir_secret(rpts,F)) print("\n\nGCD of ZPolys") P = ZPoly( [1,2,3], 5) * ZPoly( [2,4], 5) Q = ZPoly( [1,2,3], 5) print(P) print(Q) print(zpoly_gcd(P,Q)) print("\n\nSquare-Free Decomposition") P = ZPoly([1,0,2,2,0,1,1,0,2,2,0,1],3) print(P) print(square_free_decomposition(P)) print("\nAll monic polynomials over Z/3Z with degree less than 3") for poly in all_monic_zpolys(3): if len(poly) > 3:
def cast_to_poly(self): """Multiply everything together as a ZPoly""" out = ZPoly([1], self.M) for val, pwr in self.terms.items(): out *= val**pwr return out
# Remove the modulo terms out = re.sub(" \(mod \d*\)", "", out) # Remove $ signs out = out.replace("$", "") if self.M: return f"${out}$ [mod {self.M}]" else: return f"${out}$" # Things that are like attributes can be access as properties pretty_name = property(_pretty_name) full_name = property(_full_name) if __name__ == '__main__': P = ZPoly([1, 2, 3], 19) Q = ZPoly([2, 4], 19) C = ZPolyProd([P, Q], 19) print(P) print(Q) print(C) print(C * Q) print(C * C) print(C**3) print(C * 2) D = 2 * C * Q print(D) print(D**0) print(D.pretty_name) print(D.full_name)
f"Multiplicative inverse of {a.full_name} mod {R.full_name} does not exist" ) else: return x % R def ff_div(a, b, R): if b == R or str(b) == "0": raise ZeroDivisionError return (a * ff_inv(b, R)) % R if __name__ == '__main__': import random P = ZPoly([1, 1, 1, 0, 0, 0, 0, 1, 1], 2) L = finite_field_example(P) print( f"\n\nFinite Field of Characteristic 2 with {len(L)} elements\nReducing Polynomial: {P}" ) print("\n\nAddition and Subtraction and identical") for i in range(3): a, b = random.sample(L, 2) print(f"{a.bitstring()} + {b.bitstring()} = {(a+b).bitstring()}") print(f"{a.bitstring()} - {b.bitstring()} = {(a-b).bitstring()}") print() print("\n\nExamples of Multiplication over the Finite Field") for i in range(3):