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
Ejemplo n.º 2
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):]