def hensel_step(m, f, g, h, s, t): # step 1 zx = ZX() e = zx.add(f, zx.add_inv(zx.mul(g, h))) e = zx.mod_elem(e, m * m) q = zx.mul(s, e) quot, rem = zx.extended_synthetic_division(q.coefs, h.coefs) q = zx.mod_elem(Polynomial(quot, ZZ()), m * m) r = zx.mod_elem(Polynomial(rem, ZZ()), m * m) g_star = zx.add(g, zx.mul(t, e)) g_star = zx.add(g_star, zx.mul(q, g)) g_star = zx.mod_elem(g_star, m * m) h_star = zx.mod_elem(zx.add(h, r), m * m) # step 2 b = zx.mul(s, g_star) b = zx.add(b, zx.mul(t, h_star)) b = zx.add(b, zx.add_inv(zx.mul_id())) b = zx.mod_elem(b, m * m) c = zx.mul(s, b) quot, rem = zx.extended_synthetic_division(c.coefs, h_star.coefs) c = zx.mod_elem(Polynomial(quot, ZZ()), m * m) d = zx.mod_elem(Polynomial(rem, ZZ()), m * m) s_star = zx.add(s, zx.add_inv(d)) s_star = zx.mod_elem(s_star, m * m) t_star = zx.add(t, zx.add_inv(zx.mul(t, b))) t_star = zx.add(t_star, zx.add_inv(zx.mul(c, g_star))) t_star = zx.mod_elem(t_star, m * m) return g_star, h_star, s_star, t_star
def step_b1(self): # xi = X * 1_F xi = Polynomial([self.field.add_id(), self.field.mul_id()], self.field) # l x l matrix with coefficients in F q_matrix = [] # compute alpha = xi^q through repeated squaring. # we do this by making all calculations on F[X] and then computing the associated elements in E via rem with f. alpha = self.fx.exp(xi, self.field.cardinality()) alpha = self.rep(alpha) print("Tengo el representante") # beta = 1_E, no quotient needed in this case. beta = self.fx.mul_id() for i in range(self._l): # Row_i(Q) = Vec_S(beta) q_matrix.append(self.vec_s(copy.deepcopy(beta.coefs))) # Q[i][i] = Q[i][i] - 1 q_matrix[i][i] = self.field.add( q_matrix[i][i], self.field.add_inv(self.field.mul_id())) # beta = beta * alpha beta = self.rep(self.fx.mul(beta, alpha)) print("Q = ", q_matrix) # compute a basis of the row null space of Q _, x_matrix = self.gaussian_elimination(q_matrix) res = [Polynomial(elem, self.field) for elem in x_matrix] print("base = ", res) return res
def is_irreducible(self, f): x = Polynomial([self.base_field.add_id(), self.base_field.mul_id()], self.base_field) h = self.rem(x, f) for k in range(1, f.degree()//2+1): aux = h # calculate h = h ** card, where card = cardinality of Fq for j in range(1, self.base_field.cardinality()): h = self.mul(h, aux) h = self.rem(h, f) gcd = self.gcd(self.add(h, self.add_inv(x)), f) if self.div(gcd, Polynomial([gcd.get_leading_coef()], self.base_field)) != self.mul_id(): return False return True
def div_mod(self, elem1, elem2): if elem1.degree() < elem2.degree(): return Polynomial([self.base_field.add_id()], self.base_field), elem1 a = list(elem1.coefs) a.reverse() b = list(elem2.coefs) b.reverse() quotient, remainder = self.extended_synthetic_division(a, b) quotient.reverse() remainder.reverse() return Polynomial(quotient, self.base_field), Polynomial(remainder, self.base_field)
def add(self, elem1, elem2): lis = list( map(lambda x1, x2: self.base_field.add(x1, x2), elem1.coefs, elem2.coefs)) lis.extend(elem1.coefs[len(lis):]) lis.extend(elem2.coefs[len(lis):]) return Polynomial(lis, self.base_field)
def mk(self, k): w = self.field.k res = [self.field.add_id()] * (2 ** (w * k - 1) + 1) acc = 1 for i in range(w * k): res[acc] = self.field.mul_id() acc *= 2 return Polynomial(res, self.field)
def obtain_factors(self, T, g_i, b, p): zx = ZX() g = zx.mul_id() for index in T: g = zx.mul(g_i[index], g) g_star = zx.mul_scalar(g, b) g_star_module_p = Polynomial([x % p for x in g_star.coefs], ZX()) return self.symmetric_form(g_star_module_p, p)
def mul(self, elem1, elem2): res = [self.base_field.add_id() ] * (elem1.degree() + elem2.degree() + 1) for in1, e1 in enumerate(elem1.coefs): for in2, e2 in enumerate(elem2.coefs): res[in1 + in2] = self.base_field.add( res[in1 + in2], self.base_field.mul(e1, e2)) return Polynomial(res, self.base_field)
def primitive_part(self, elem): if elem == self.add_id(): return self.add_id() else: # divide all coefficients by common gcd to obtain primitive part return Polynomial( list( map(lambda x: ZZ().div(x, self.mcd_polynomial_coefs(elem)), elem.coefs)), ZZ())
def gcd(self, a, b): c = self.primitive_part(a) d = self.primitive_part(b) if c.degree() < d.degree(): c, d = d, c while d != self.add_id(): lc = c.get_leading_coef() n = c.degree() - d.degree() + 1 aux = Polynomial([lc**n], ZZ()) aux = self.mul(aux, c) r = Polynomial( self.extended_synthetic_division(aux.coefs, d.coefs)[1], ZZ()) c = d d = self.primitive_part(r) gcd_ab = ZZ().gcd(self.mcd_polynomial_coefs(a), self.mcd_polynomial_coefs(b)) return self.mul(Polynomial([gcd_ab], ZZ()), c)
def pth_root(self, f): # f = g(X^p), with g in PolynomialsOverFq g = [] i = 0 # x^4 + x^2 + 1 -> x^2 + x + 1 (p = 2) while i <= f.degree(): g.append(f.coefs[i]) i += self.base_field.p print("g*: ", g) h = [self.base_field.exp(a, self.base_field.p**(self.base_field.k - 1)) for a in g] print("h*: ", h) return Polynomial(h, f.base_ring)
def m1(self): coefs = [] if self.field.p > 2: coefs = [self.field.add_id()] * ( (self.field.cardinality() - 1) // 2 + 1) coefs[(self.field.cardinality() - 1) // 2] = self.field.mul_id() coefs[0] = self.field.add_inv(self.field.add_id()) else: # 1 + X^2 + X^4 + ... coefs = [ self.field.mul_id() if i % 2 == 0 else self.field.add_id() for i in range(2 * self.field.k - 1) ] return Polynomial(coefs, self.field)
def factorize_prime_power(self): # step 1 if self.n == 1: return [self.f] b = self.f.get_leading_coef() B = ceil(sqrt(self.n + 1) * pow(2, self.n) * self.A * b) C = (self.n + 1)**(2 * self.n) * (self.A**(2 * self.n - 1)) gamma = ceil(2 * log2(C)) # step 2 p = ZZ().obtain_random_prime_in_range(3, ceil(2 * gamma * log(gamma))) f_bar = Polynomial(self.f.coefs, Zp(p)) f_bar_der = f_bar.derivative() fx = PolynomialsOverField(Zp(p)) while b % p == 0 or fx.gcd(f_bar, f_bar_der).degree() > 1: p = ZZ().obtain_random_prime_in_range(2, ceil(2 * gamma * log(gamma))) f_bar = Polynomial(self.f.coefs, Zp(p)) f_bar_der = f_bar.derivative() l = ceil(log(2 * B + 1, p)) # step 3 h_i = self.modular_factorization(f_bar, p, fx) # step 4 fact = self.multifactor_hensel_lifting(p, self.f, h_i, l) g_i = [self.symmetric_form(factor, p**l) for factor in fact] # step 5 r = len(g_i) f_star = copy.deepcopy(self.f) T = set(range(r)) s = 1 G = [] # step 6 while 2 * s <= len(T): goto = False combination = list(combinations(T, s)) for subset_tuple in combination: subset = set(subset_tuple) g_star = self.obtain_factors(subset, g_i, b, p**l) h_star = self.obtain_factors(T.difference(subset), g_i, b, p**l) if ZX().onenorm(g_star) * ZX().onenorm(h_star) <= B: T = T.difference(subset) G.append(ZX().primitive_part(g_star)) f_star = ZX().primitive_part(h_star) b = f_star.get_leading_coef() goto = True break if not goto: s += 1 G.append(f_star) return G
def distinct_degree_factorization(self): _L = [] _X = Polynomial([self.field.add_id(), self.field.mul_id()], self.field) h = self.fx.rem(_X, self.f) k = 0 fc = copy.deepcopy(self.f) while fc != self.fx.mul_id(): h = self.fx.rem(self.fx.exp(h, self.field.cardinality()), fc) k += 1 aux = self.fx.add(h, self.fx.add_inv(_X)) g = self.fx.gcd(aux, fc) if g.degree() > 1: _L.append((g, k)) fc = self.fx.div(fc, g) h = self.fx.rem(h, fc) return _L
def factorize_big_prime(self): if self.n == 1: return [self.f] b = self.f.get_leading_coef() B = ceil(sqrt(self.n + 1) * pow(2, self.n) * self.A * b) p = ZZ().obtain_random_prime_in_range(2 * B + 1, 4 * B - 1) f_bar = Polynomial(self.f.coefs, Zp(p)) f_bar_der = f_bar.derivative() fx = PolynomialsOverField(Zp(p)) while fx.gcd(f_bar, f_bar_der).degree() > 1: p = ZZ().obtain_random_prime_in_range(2 * B + 1, 4 * B - 1) f_bar = Polynomial(self.f.coefs, Zp(p)) f_bar_der = f_bar.derivative() g_i = self.modular_factorization(f_bar, p, fx) r = len(g_i) f_star = copy.deepcopy(self.f) T = set(range(r)) s = 1 G = [] while 2 * s <= len(T): goto = False combination = list(combinations(T, s)) for subset_tuple in combination: subset = set(subset_tuple) g_star = self.obtain_factors(subset, g_i, b, p) h_star = self.obtain_factors(T.difference(subset), g_i, b, p) if ZX().onenorm(g_star) * ZX().onenorm(h_star) <= B: T = T.difference(subset) G.append(ZX().primitive_part(g_star)) f_star = ZX().primitive_part(h_star) b = f_star.get_leading_coef() goto = True break if not goto: s += 1 G.append(f_star) return G
def mul_field_scalar(self, elem, f_scalar): return Polynomial( [self.base_field.mul(x, f_scalar) for x in elem.coefs], self.base_field)
i = 0 # x^4 + x^2 + 1 -> x^2 + x + 1 (p = 2) while i <= f.degree(): g.append(f.coefs[i]) i += self.base_field.p print("g*: ", g) h = [self.base_field.exp(a, self.base_field.p**(self.base_field.k - 1)) for a in g] print("h*: ", h) return Polynomial(h, f.base_ring) if __name__ == "__main__": Z2 = Zp(2) Z2X = PolynomialsOverFq(Z2) print(Z2X.is_irreducible(Polynomial([1, 0, 1], Z2))) # false print(Z2X.is_irreducible(Polynomial([1, 1, 1, 1, 1], Z2))) # true print(Z2X.is_irreducible(Polynomial([0, 1, 1, 1, 1], Z2))) # false print(Z2X.is_irreducible(Polynomial([1, 1, 0, 1], Z2))) # true print(Z2X.is_irreducible(Polynomial([1, 0, 1, 1], Z2))) # true print(Z2X.is_irreducible(Polynomial([1, 0, 0, 0, 1, 1], Z2))) # false F16 = FiniteField(2, 4, Polynomial([1, 1, 0, 0, 1], Z2)) F16X = PolynomialsOverFq(F16) One = F16.mul_id() Zero = F16.add_id()
p**l) if ZX().onenorm(g_star) * ZX().onenorm(h_star) <= B: T = T.difference(subset) G.append(ZX().primitive_part(g_star)) f_star = ZX().primitive_part(h_star) b = f_star.get_leading_coef() goto = True break if not goto: s += 1 G.append(f_star) return G if __name__ == "__main__": g = Polynomial([-15, -17, -16, -1, 1], ZZ()) fact = FactorizationZx(g) print(fact.factorize_big_prime()) print(fact.factorize_prime_power()) p_big = Polynomial([-12, -24, -18, -2, 8, 3, 0, 2, 1], ZZ()) # print(FactorizationZx(p_big).factorize_big_prime()) print(FactorizationZx(p_big).factorize_prime_power()) m = 5 f = Polynomial([-1, 0, 0, 0, 1], ZZ()) g = Polynomial([-2, -1, 2, 1], ZZ()) h = Polynomial([-2, 1], ZZ()) s = Polynomial([-2], ZZ()) t = Polynomial([-1, -2, 2], ZZ())
def add_inv(self, elem): lis = list(map(lambda x: self.base_field.add_inv(x), elem.coefs)) return Polynomial(lis, self.base_field)
def mul_id(self): return Polynomial([self.base_field.mul_id()], self.base_field)
def add_id(self): return Polynomial([self.base_field.add_id()], self.base_field)
def symmetric_form(self, pol, p): return Polynomial( [self.symmetrical_representation_number(x, p) for x in pol.coefs], pol.base_ring)
def random_elem(self): elem = [] for i in range(self.k): elem.append(random.randrange(self.p)) return Polynomial(elem, Zp(self.p))
from generic.PolynomialsOverField import PolynomialsOverField from specific.PadicNumber import PadicNumber # Not much to do here, this class is mostly intended for testing purposes. class PolynomialsOverPadics(PolynomialsOverField): def __init__(self, p): self.base_field = PadicNumber(p) if __name__ == "__main__": from generic.Polynomial import Polynomial pp2 = PolynomialsOverPadics(2) p2 = PadicNumber(2) f1 = Polynomial([p2.mul_id(), p2.mul_id()], p2) f2 = Polynomial([p2.mul_id(), p2.mul_id(), p2.mul_id()], p2) f12 = pp2.mul(f1, f2) f11 = pp2.mul(f1, f1) f22 = pp2.mul(f2, f2) print("f1: ", f1) print("f2: ", f2) print("f11: ", f11) print("f12: ", f12) print("f22: ", f22) print("-------------------------------") res1 = pp2.gcd(f11, f12) print("gcd f11, f12: ", res1) print("divided by f1: ", pp2.div(res1, f1)) print("divided by f2: ", pp2.div(res1, f2)) print("-------------------------------") res2 = pp2.gcd(f11, f22)
_H = _Hp return _H def compute(self): step1 = self.distinct_degree_factorization() # print("Step 1 result: ", step1) # for pol in step1: # print(self.fx.obtain_monic_representation(pol[0])) res = [] for elem in step1: # print("elem for: ", elem) sol = self.equal_degree_factorization(elem[0], elem[1]) # print(sol) res.extend(sol) return res if __name__ == "__main__": from generic.FiniteField import FiniteField from specific.Zp import Zp F4 = FiniteField(2, 2, Polynomial([1, 1, 1], Zp(2))) F4One = F4.mul_id() F4Zero = F4.add_id() F4Alpha = Polynomial([0, 1], Zp(2)) F4AlphaPlusOne = Polynomial([1, 1], Zp(2)) f = Polynomial([F4Alpha, F4AlphaPlusOne, F4One], F4) print(".............") print("Compute factorization through Berkelamp's algorithm") print(CantorZassenhaus(f).compute())
elem = [] for i in range(self.k): elem.append(random.randrange(self.p)) return Polynomial(elem, Zp(self.p)) def __eq__(self, other): if isinstance(other, FiniteField): return self.p == other.p and self.k == other.k return NotImplemented if __name__ == "__main__": # test to check if rep works correctly for GF(4) Z2 = Zp(2) F4 = FiniteField(2, 2, Polynomial([1, 1, 1], Z2)) F4One = F4.mul_id() F4Zero = F4.add_id() F4Alpha = Polynomial([0, 1], Z2) F4AlphaPlusOne = Polynomial([1, 1], Z2) # Both of the following elements should have rep [1] print(F4.rep(F4One), F4.rep(F4.add(F4.mul(F4Alpha, F4Alpha), F4Alpha))) # Multiplication table for F4. print("Multiplication table for F4") print(F4.mul(F4One, F4One)) print(F4.mul(F4One, F4Alpha)) print(F4.mul(F4One, F4AlphaPlusOne)) print(F4.mul(F4Alpha, F4Alpha)) print(F4.mul(F4Alpha, F4AlphaPlusOne)) print(F4.mul(F4AlphaPlusOne, F4AlphaPlusOne))
def random_polynomial_h(self, h): res = [] for i in range(h.degree()): res.append(self.field.random_elem()) pol = Polynomial(res, self.field) return self.fx.rem(pol, h)
def add_id(self): return Polynomial([0], self.field)
from generic.FiniteField import FiniteField from specific.ZZ import ZZ from generic.Polynomial import Polynomial from generic.PolynomialsOverField import PolynomialsOverField from generic.Ring import Ring from specific.Zp import Zp # ZZ = ZZ() # print(ZZ.chinese_remainder_theorem([0, 3, 4], [3, 4, 5])) Z2 = Zp(2) Z2X = PolynomialsOverField(Z2) # print(Zp(7).mul_inv(6)) print(Z2X.gcd(Polynomial([1], Z2), Polynomial([1, 1], Z2)).coefs) print(Z2X.add_id() == Polynomial([0], Z2)) print(Z2X.div(Polynomial([0, 0, 1], Z2), Polynomial([1, 1, 1], Z2)).coefs) print(Z2X.rem(Polynomial([0, 0, 1], Z2), Polynomial([1, 1, 1], Z2)).coefs) print( Z2X.chinese_remainder_theorem( [Polynomial([1], Z2), Polynomial([1, 1], Z2)], [Polynomial([1, 1], Z2), Polynomial([1, 1, 1], Z2)]).coefs) # print(ZZ.rem(5, 3)) # print(ZZ.add_id()) # print(ZZ.mul_id()) # print(ZZ.mul(7, -2)) # print(ZZ.gcd(178, 4)) # print(ZZ.gcd(7, -5)) # print(ZZ.extended_gcd(15, 25)) # print(ZZ.extended_gcd(25, 15)) # print(ZZ.extended_gcd(7, 25))
def mul_id(self): return Polynomial([1], self.field)