def croot(self): """ Computes the cth root of the element, where c is the characteristic of the ring """ if self == self.ring.zero: return self # Assumes generator is primitive # Multiplicative ring order if self.ring.char() == self.ring.order(): return self Z = externals.Z o = self.ring.order() - 1 p = self.ring.char() g = self.ring.build([0, 1]) log = externals.discrete_log(g, self) assuming(log is not None, "The chosen field generator is not primitive") # Find an integer x such that x*p = log (mod o) gcd, __, inv = externals.eea(Z.build(o), Z.build(p)) if gcd != Z.one: assuming( gcd.is_unit(), f"Could not find c-th root of {self} in {type(self)}. Is the field well constructed?" ) inv = inv // gcd x = inv * Z.build(log) % Z.build(o) return g**(x.val)
def __init__(self, val): while hasattr(val, "val"): val = val.val assuming(hasattr(val, "__int__"), f"Can't build an integer from {val}") self.val = int(val)
def rabin_test(pol): """ Rabin's test of irreducibility for polynomials with coefficients in a finite field """ PR = pol.ring # polinomial ring R = PR.coefRing # coefficient ring (field) assuming(R.is_finite()) p = R.order() n = pol.deg() quotRing = GetRingQuotient(PR,(PR*pol)) x = quotRing.build([R.zero,R.one]) if (x**(p**n)-x) != quotRing.zero: return False factors = set(prime_factors(n).keys()) for f in factors: ni = n//f if not gcd(pol, (x**(p**ni)-x).val).is_unit(): return False return True
def FiniteField(p, pol=None, var=None): if pol is None: return GetQuotient(Z, p * Z) else: Pols = GetPolynomials(Z / (p * Z), var) assuming( Pols(pol).is_prime(), f"{Pols(pol)} is not irreducible on {Pols}") if not Pols(pol).is_primitive_field(): print( f"Warning: building FF{p**(len(pol)-1)} with a non-primitive polynomial" ) return GetQuotient(Pols, Pols * Pols(pol))
def __init__(self, ring, generators): assuming(len(generators) > 0, "At least one generator is needed") assuming(ring.is_euclidean(), "Ring must be Euclidean") gen = [ring(g) for g in generators] # We can calculate the generator as the gcd of all the given elements if len(generators) > 1: self.generator = externals.gcd(*gen) else: self.generator = gen[0] super().__init__(ring, [self.generator])
def __init__(self, num, den=1): while hasattr(num, "val"): num = num.val if hasattr(num, "__len__") and len(num) == 2: num, den = num assuming( hasattr(num, "__int__") and hasattr(den, "__int__") and int(den) != 0, f"Can't build a rational from {num}, {den}") num, den = int(num), int(den) g = int_gcd(num, den) self.num = num // g self.den = den // g self.val = (self.num, self.den)
def hensel_lifting(f, g, h, s, t): """ Input: polynomials in Z/(qZ)[x], q = p^r where f = gh, sg + th = 1 Returns f', g', h', s' and t' in Z/(q^2)[x] lifted from g,h,s,t respectively which satisfy the same constraints """ # Implements algorithm 2.4.6 R = f.ring.coefRing q = R.ideal.generator newR = R.baseRing / (R.baseRing * (q**2)) RX = newR[f.ring.var] # Move to the higher ring fp = RX(f) gp = RX(g) hp = RX(h) sp = RX(s) tp = RX(t) # Calculate lifted polynomials nabla = fp - gp * hp newg = gp * (RX.one + (sp * nabla // hp)) + tp * nabla newh = hp + (sp * nabla % hp) delta = sp * newg + tp * newh - RX.one news = sp - (sp * delta % newh) newt = (RX.one - delta) * tp - newg * (sp * delta // newh) # Check output meets requirements assuming(fp == newg * newh, "Hensel lifting failed") assuming(RX.one == news * newg + newt * newh, "Hensel lifting failed") return fp, newg, newh, news, newt
def inverse(self): # This does not work assuming(self.is_unit(), "Can't invert non-unit") return type(self)(self.coefs[0].inverse())
def inverse(self): assuming(self.is_unit(), "Can't invert non-unit") return self
def zx_factorization(f): """ """ # Implements Algorithm 2.4.3 def norm(f): return sqrt(sum([int(a)**2 for a in f.coefs])) L = f.coefs[f.deg()] prim = primes() next(prim) p = next(prim) while True: RX = (Z / (p * Z))["x"] f_ = RX.build([a.val for a in f.coefs]) if L.val % p != 0 and f_.der() != RX.zero and gcd(f_, f_.der()).is_unit(): break p = next(prim) factors = berlekamp_cantor_zassenhaus(f_) nfac = len(factors) N = 1 nor = norm(f) while p**N < (2**(f.deg() + 1)) * nor: N += 1 n_lifts = int(ceil(log2(N))) for i in range(n_lifts): # Hensel lifting from p**k to p**2k RX = f_.ring RX.setRepr("reduced") newfactors = [] nh = None nf = None for j in range(nfac - 1): g = factors[j] h = reduce(RX.Element.__mul__, factors[j + 1:], RX.one) prod = g * h gc, s, t = eea(g, h) if gc != gc.normal(): s /= gc t /= gc assuming(gc.is_unit(), "Factorization failed") nf, ng, nh, ns, nt = hensel_lifting(prod, g, h, s, t) newfactors.append(ng) newfactors.append(nh) f_ = type(nf)(f_) factors = newfactors return recover_factors(f, factors)
def inverse(self): assuming(self.is_unit(), "Can't invert non-unit") return type(self)(self.val[0].inverse())
def inverse(self): assuming(self.is_unit(), "Can't invert non-unit") return next(u for u in type(self).units() if u * self == type(self).one)
def __truediv__(self, other): assuming(other.is_unit(), "Can't divide by non-unit") return self * other.inverse()