class FiniteField(Field):

    def __init__(self, p, k, f):
        self.p, self.k, self.f = p, k, f
        self.field = PolynomialsOverField(Zp(p))

    def cardinality(self):
        return self.p ** self.k

    def div_mod(self, elem1, elem2):

        e1 = copy.deepcopy(elem1)
        e2 = copy.deepcopy(elem2)

        # make both polynomials of degree lower than f's
        e1 = self.rep(e1)
        e2 = self.rep(e2)

        # make sure that elem1 has a higher degree than elem2's.
        # note that adding f is essentially no different from adding 0 in this field.
        # e1 = self.field.add(self.f, e1)

        # result = self.field.div_mod(e1, e2)
        result = self.mul(e1, self.mul_inv(e2))
        # make sure result is returned in proper representation.
        return self.rep(result), self.add_id()

    def add_id(self):
        return Polynomial([0], self.field)

    def mul_id(self):
        return Polynomial([1], self.field)

    def add_inv(self, elem):
        return self.rep(self.field.add_inv(elem))

    def mul_inv(self, elem):
        res = self.field.extended_gcd(elem, self.f)
        return res[1]

    def add(self, elem1, elem2):
        return self.rep(self.field.add(elem1, elem2))

    def mul(self, elem1, elem2):
        return self.rep(self.field.mul(elem1, elem2))

    # elem's equivalence class
    def rep(self, elem):
        return self.field.div_mod(elem, self.f)[1]

    def random_elem(self):
        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
    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 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 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
Ejemplo n.º 5
0
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 __init__(self, f):
     # field = Fq, f = f, fx = F[X]
     self.field = f.base_ring
     self.f = f
     self.fx = PolynomialsOverField(f.base_ring)
     self._l = f.degree()
class CantorZassenhaus:

    def __init__(self, f):
        # field = Fq, f = f, fx = F[X]
        self.field = f.base_ring
        self.f = f
        self.fx = PolynomialsOverField(f.base_ring)
        self._l = f.degree()

    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

    # generate a random polynomial in F[X]/(h)
    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 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)

    # f is a monic polynomial of degree l and k the number of times
    def equal_degree_factorization(self, pol, k):
        mk = self.mk(k)
        fc = copy.deepcopy(pol)
        r = pol.degree() // k
        # print("r: ", r)
        _H = [fc]
        # print("_H: ", _H)
        while len(_H) < r:
            _Hp = []
            for elem in _H:
                alpha = self.random_polynomial_h(elem)
                eval_mk = self.fx.evaluate_polynomial(mk, alpha)
                eval_mk = self.fx.rem(eval_mk, fc)
                d = self.fx.gcd(eval_mk, elem)
                if d.degree() == 0 or d == elem:
                    _Hp.append(elem)
                else:
                    # print("d: ", d)
                    # print("elem: ", elem)
                    _Hp.append(d)
                    # print("Div :", self.fx.div(elem, d))
                    _Hp.append(self.fx.div(elem, d))
            _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
 def __init__(self, p, k, f):
     self.p, self.k, self.f = p, k, f
     self.field = PolynomialsOverField(Zp(p))
Ejemplo n.º 9
0
class Berlekamp:
    def __init__(self, f):
        # field = Fq, f = f, fx = F[X]
        self.field = f.base_ring
        self.f = f
        self.fx = PolynomialsOverField(f.base_ring)
        self._l = f.degree()

    def compute(self):
        base = self.step_b1()
        return self.step_b2(base)

    # elem is an element in F[X]
    def rep(self, elem):
        return self.fx.rem(elem, self.f)

    # Compute a Berlekamp basis for E := F[X]/(f), where f is a polynomial over F
    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 step_b2(self, base):
        h_list = [self.f]
        base_list = [self.rep(elem) for elem in base]
        m1_computed = self.m1()
        h_p = []

        while len(h_list) < len(base_list):
            g_aux = [
                self.fx.mul_field_scalar(gi, self.field.random_elem())
                for gi in base_list
            ]
            # g = c1 * g1 + ... + cr * gr (in F[X])
            g = reduce(self.fx.add, g_aux)
            h_p = []
            for h in h_list:
                # beta = [g]_h (in F[X]/(h))
                beta = self.fx.rem(g, h)
                # compute rep(M1(beta))
                eval_m1_beta = self.rep(
                    self.fx.evaluate_polynomial(m1_computed, beta))
                d = self.fx.gcd(eval_m1_beta, h)
                if d == self.fx.mul_id() or d == h or d.degree() < 1:
                    h_p.append(h)
                else:
                    h_p.append(d)
                    h_p.append(self.fx.div(h, d))
            h_list = h_p
        return h_p

    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)

    # coordinates of beta in standard basis S = {1, X, X^2,..., X^l}
    def vec_s(self, beta):
        res = [self.field.add_id()] * self._l
        res[:len(beta)] = beta
        return res

    # returns (gaussian elimination of matrix, basis for the row null space of matrix)
    def gaussian_elimination(self, _matrix):
        q_matrix = copy.deepcopy(_matrix)
        f_one = self.field.mul_id()
        f_zero = self.field.add_id()

        # identity matrix
        x_matrix = [[
            copy.deepcopy(f_one) if row == col else copy.deepcopy(f_zero)
            for col in range(len(q_matrix[0]))
        ] for row in range(len(q_matrix))]

        r = -1
        for j in range(len(q_matrix[0])):
            print("Q matrix: ", q_matrix)
            print("X matrix: ", x_matrix)
            _l = -1
            i = r
            while _l == -1 and i < len(q_matrix) - 1:
                i += 1
                if q_matrix[i][j] != f_zero:
                    _l = i
            if _l != -1:
                r += 1

                # swap rows r and l of X
                aux = x_matrix[r]
                x_matrix[r] = x_matrix[_l]
                x_matrix[_l] = aux
                # swap rows r and l of Q
                aux = q_matrix[r]
                q_matrix[r] = q_matrix[_l]
                q_matrix[_l] = aux

                # B(r, j)^(-1)
                row_divisor = self.field.mul_inv(q_matrix[r][j])

                # Row_r(X) = B(r, j)^(-1) * Row_r(X)
                x_matrix[r] = [
                    self.field.mul(row_divisor, x) for x in x_matrix[r]
                ]
                # Row_r(Q) = B(r, j)^(-1) * Row_r(Q)
                q_matrix[r] = [
                    self.field.mul(row_divisor, x) for x in q_matrix[r]
                ]

                for _i in range(len(q_matrix)):
                    if _i != r:
                        # Row_i(X) = Row_i(X) - B(i, j) * Row_r(X)
                        aux = [
                            self.field.add_inv(
                                self.field.mul(q_matrix[_i][j], x))
                            for x in x_matrix[r]
                        ]
                        x_matrix[_i] = [
                            self.field.add(x, y)
                            for x, y in zip(x_matrix[_i], aux)
                        ]
                        # Row_i(B) = Row_i(B) - B(i, j) * Row_r(B)
                        aux = [
                            self.field.add_inv(
                                self.field.mul(q_matrix[_i][j], x))
                            for x in q_matrix[r]
                        ]
                        q_matrix[_i] = [
                            self.field.add(x, y)
                            for x, y in zip(q_matrix[_i], aux)
                        ]
        r += 1
        return q_matrix, x_matrix[-(len(q_matrix) - r):]
Ejemplo n.º 10
0
    # f = Polynomial([F4Alpha, F4AlphaPlusOne, F4One], F4)
    # print(".............")
    # print(Berlekamp(f).step_b1())
    #
    # print(".............")
    # print("Compute factorization through Berkelamp's algorithm")
    # print(Berlekamp(f).step_b2([Polynomial([F4One], F4), Polynomial([F4Zero, F4One], F4)]))
    # print(Berlekamp(f).compute())
    # factors = Berlekamp(f).compute()
    # for factor in factors:
    #     print("Factor", PolynomialsOverField(F4).obtain_monic_representation(factor))
    #
    # Z7 = Zp(7)
    # g = Polynomial([6,11,6,1], Z7)
    # factors = Berlekamp(g).compute()
    # for factor in factors:
    #     print("Factor", PolynomialsOverField(Z7).obtain_monic_representation(factor))

    # Z6473 = Zp(1201)
    # g = Polynomial([-40, -62, -21, 2,1], Z6473)
    # factors = Berlekamp(g).compute()
    # for factor in factors:
    #     print("Factor", PolynomialsOverField(Z6473).obtain_monic_representation(factor))

    Z3 = Zp(3)
    g = Polynomial([1, 1, 2, 3, 1], Z3)
    factors = Berlekamp(g).compute()
    for factor in factors:
        print("Factor",
              PolynomialsOverField(Z3).obtain_monic_representation(factor))