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 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 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
Exemple #4
0
    def div_mod(self, dividendo, divisor):
        if len(dividendo) == 2:
            a, b, c1 = self.obtain_padic_representation(dividendo)
        else:
            a, b, c1 = dividendo
        if len(divisor) == 2:
            c, d, c2 = self.obtain_padic_representation(divisor)
        else:
            c, d, c2 = divisor

        # |sigma|p < |tau|p
        if c2 < c1:
            return divisor, (0, 1, 0)

        # Otherwise
        s = (a * d) % self.p**(c2 - c1 + 1)
        t = (b * c) % self.p**(c2 - c1 + 1)

        _, t_inv, _ = ZZ().extended_gcd(t, self.p**(c2 - c1 + 1))
        quotient = self.symmetric_representation(
            (s * t_inv) % self.p**(c2 - c1 + 1), self.p**(c2 - c1 + 1))

        # nu = sigma - q*tau
        remainder = self.add(
            self.obtain_fraction_representation(dividendo),
            self.add_inv(
                self.mul(self.obtain_fraction_representation(divisor),
                         (quotient, self.p**(c2 - c1)))))

        # In case input was in fraction form, output is given in the same format
        if len(dividendo) == 2:
            return (quotient, self.p**(c2 - c1)), remainder
        return (quotient, 1,
                c1 - c2), self.obtain_padic_representation(remainder)
 def extended_synthetic_division(dividend, divisor):
     dividend.reverse()
     divisor.reverse()
     out = list(dividend)  # Copy the dividend
     normalizer = divisor[0]
     for i in range(len(dividend) - len(divisor) + 1):
         out[i] = ZZ().div(out[i], normalizer)
         # for general polynomial division (when polynomials are non-monic),
         # we need to normalize by dividing the coefficient with the divisor's first coefficient
         coef = out[i]
         if coef != ZZ().add_id():  # useless to multiply if coef is 0
             for j in range(1, len(divisor)):
                 # in synthetic division, we always skip the first coefficient of the divisor,
                 # because it is only used to normalize the dividend coefficients
                 aux = ZZ().add_inv(ZZ().mul(divisor[j], coef))
                 out[i + j] = ZZ().add(out[i + j], aux)
     separator = len(dividend) - len(divisor) + 1
     quotient = out[:separator]
     quotient.reverse()
     remainder = out[separator:]
     remainder.reverse()
     dividend.reverse()
     divisor.reverse()
     if len(quotient) == 0:
         quotient = [ZZ().add_id()]
     if len(remainder) == 0:
         remainder = [ZZ().add_id()]
     return quotient, remainder  # return quotient, remainder.
    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 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
Exemple #8
0
    def pollard(self):
        ai, bi, xi = 0, 0, self.finite_field.mul_id()
        a2i, b2i, x2i = 0, 0, self.finite_field.mul_id()

        while True:
            xi, ai, bi = self._f(xi), self._h(xi, ai), self._g(xi, bi)
            x2i, a2i, b2i = self._f(self._f(x2i)), self._h(self._f(x2i), self._h(x2i, a2i)), \
                            self._g(self._f(x2i), self._g(x2i, b2i))

            if xi == x2i:
                r = bi - b2i
                if r == 0:
                    return None
                x = (ZZ().extended_gcd(r, self.p)[1] * (a2i - ai)) % self.p
                return x
    def multifactor_hensel_lifting(self, p, f, fr, l):
        zx = ZX()
        r = len(fr)
        # step 1
        if r == 1:
            # extended gcd in ZZ
            if f.get_leading_coef() == 1:
                return [f]

            pl = p**l
            _, w, _ = ZZ().extended_gcd(f.get_leading_coef(), pl)
            return [zx.mod_elem(zx.mul_scalar(f, w), pl)]

        # step 2
        k = floor(r / 2)
        d = ceil(log2(l))

        # step 3
        g = reduce(zx.mul, fr[0:k])
        g = zx.mul_scalar(g, f.get_leading_coef())
        g = zx.mod_elem(g, p)
        h = reduce(zx.mul, fr[k:r])
        h = zx.mod_elem(h, p)

        # step 4: ZZ/<p> is always a field (namely Zp), since p is prime
        zpx = PolynomialsOverField(Zp(p))
        coef, s, t = zpx.extended_gcd(g, h)
        s = zpx.mul_scalar(s, Zp(p).mul_inv(coef.coefs[0]))
        t = zpx.mul_scalar(t, Zp(p).mul_inv(coef.coefs[0]))

        # step 5
        m = p
        for j in range(d):
            # step 6
            g, h, s, t = self.hensel_step(m, f, g, h, s, t)
            m **= 2

        _k1 = self.multifactor_hensel_lifting(p, g, fr[0:k], l)
        _k2 = self.multifactor_hensel_lifting(p, h, fr[k:r], l)
        _k1.extend(_k2)
        return _k1
 def mcd_polynomial_coefs(elem):
     # gcd not defined for zero values: remove them all before proceeding
     without_zeros = list(filter(lambda x: x != 0, elem.coefs))
     # calculate gcd of all non-zero values in list
     return ZZ().multiple_gcd(without_zeros)
 def mod_elem(f, m):
     return Polynomial([elem % m for elem in f.coefs], ZZ())
Exemple #12
0
 def simplify(elem):
     a, b = elem
     if a == 0:
         return 0, 1
     gcd = ZZ().gcd(a, b)
     return a // gcd, b // gcd
 def add_inv(self, elem):
     lis = list(map(lambda x: ZZ().add_inv(x), elem.coefs))
     return Polynomial(lis, ZZ())
 def add(self, elem1, elem2):
     lis = list(
         map(lambda x1, x2: ZZ().add(x1, x2), elem1.coefs, elem2.coefs))
     lis.extend(elem1.coefs[len(lis):])
     lis.extend(elem2.coefs[len(lis):])
     return Polynomial(lis, ZZ())
 def mul(self, elem1, elem2):
     res = [0] * (elem1.degree() + elem2.degree() + 1)
     for in1, e1 in enumerate(elem1.coefs):
         for in2, e2 in enumerate(elem2.coefs):
             res[in1 + in2] += ZZ().mul(e1, e2)
     return Polynomial(res, ZZ())
 def mul_id(self):
     return Polynomial([1], ZZ())
Exemple #17
0
            elif self.monomial_less_grlex(max_m, elem):
                max_m = elem
        return max_m

    def leading_coef(self):
        if len(self.coefs) == 0:
            return self.base_field.add_id()
        return self.coefs[self.md]

    def leading_monomial(self):
        return self.md

    def leading_term(self):
        return MultivariablePolynomial({self.md: self.leading_coef()}, self.base_field)

    def __repr__(self):
        return str(self.coefs)

    def __eq__(self, other):
        return self.coefs == other.coefs


if __name__ == "__main__":
    from specific.ZZ import ZZ
    dic = {(1, 1, 2): 4, (3, 0, 0): 4, (0, 4, 0): -5, (1, 2, 1): 7}
    p = MultivariablePolynomial(dic, ZZ())
    print("Multidegree:", p.md)
    print("L.coef:", p.leading_coef())
    print("L.monomial:", p.leading_monomial())
    print("L.term:", p.leading_term())
Exemple #18
0
                        _s.append(r)
            if not _s:
                return _g
            else:
                _g.extend(_s)

    # check whether f is in <f1, ..., fs>. Theorem 21.28, page 605, MCA
    def in_ideal(self, f, fs):
        _g = self.groebner_basis(fs)
        return self.div(f, fs)[1] == MultivariablePolynomial({}, self.field)


if __name__ == "__main__":
    from specific.ZZ import ZZ
    dic1 = {(1, 1, 2): 4, (3, 0, 0): 4, (0, 4, 0): -5, (1, 2, 1): 7}
    p1 = MultivariablePolynomial(dic1, ZZ())
    dic2 = {(1, 1, 2): 4, (3, 0, 0): 4, (0, 5, 0): -3, (1, 2, 1): -7}
    p2 = MultivariablePolynomial(dic2, ZZ())
    print("p1 = ", p1)
    print("p2 = ", p2)
    print("p1 + p2 = ", MultivariablePolynomialsOverField(ZZ()).add(p1, p2))
    print("p1 * p2 = ", MultivariablePolynomialsOverField(ZZ()).mul(p1, p2))

    dic3 = {(2, 4, 6, 8): 25}
    p3 = MultivariablePolynomial(dic3, ZZ())
    dic4 = {(2, 2, 3, 2): 5}
    p4 = MultivariablePolynomial(dic4, ZZ())
    print("p3 = ", p3)
    print("p4 = ", p4)
    print("p3 / p4 = ",
          MultivariablePolynomialsOverField(ZZ()).div_term(p3, p4))
 def add_id(self):
     return Polynomial([0], ZZ())
Exemple #20
0
from specific.ZZ import ZZ

ZZ = ZZ()
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))
                                             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 mul_inv(self, elem):
     _, d, _ = ZZ().extended_gcd(elem, self.p)
     return d % self.p
    def maxnorm(f):
        return reduce(lambda x, y: max(abs(x), abs(y)), f.coefs)

    @staticmethod
    def onenorm(f):
        return reduce(lambda x, y: abs(x) + abs(y), f.coefs)

    # compute f mod m, where m is an element of ZZ
    @staticmethod
    def mod_elem(f, m):
        return Polynomial([elem % m for elem in f.coefs], ZZ())


if __name__ == "__main__":
    ZX = ZX()
    _a = Polynomial([1, 2, 1], ZZ())
    _b = Polynomial([1, 0, 2], ZZ())
    _c = Polynomial([1, 2, 2, 1], ZZ())
    _d = Polynomial([27, 9, 18, 0, 9], ZZ())
    _e = Polynomial([3, 6, 3], ZZ())
    _f = Polynomial([3, 6, 6, 3], ZZ())
    _g = Polynomial([1, 0, 1], ZZ())
    _h = Polynomial([5, 3], ZZ())
    _i = Polynomial([1, 1], ZZ())
    _j = Polynomial([1, 2], ZZ())
    _k = Polynomial([-5, 3, -7, -2], ZZ())
    # print(ZX.extended_synthetic_division(_c.coefs, _a.coefs))
    # print(ZX.mcd_polynomial_coefs(_d))
    # print(ZX.primitive_part(_d).coefs)
    print(ZX.gcd(_c, _a).coefs)
    print(ZX.gcd(_a, _c).coefs)