def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reductors=False):
        """
        Return a new system where linear leading variables are
        eliminated if the tail of the polynomial has length at most
        ``maxlength``.

        INPUT:

        - ``maxlength`` - an optional upper bound on the number of
          monomials by which a variable is replaced. If
          ``maxlength==+Infinity`` then no condition is checked.
          (default: +Infinity).

        - ``skip`` - an optional callable to skip eliminations. It
          must accept two parameters and return either ``True`` or
          ``False``. The two parameters are the leading term and the
          tail of a polynomial (default: ``None``).

        - ``return_reductors`` - if ``True`` the list of polynomials
          with linear leading terms which were used for reduction is
          also returned (default: ``False``).

        OUTPUT:

        When ``return_reductors==True``, then a pair of sequences of
        boolean polynomials are returned, along with the promises that:

          1. The union of the two sequences spans the
             same boolean ideal as the argument of the method

          2. The second sequence only contains linear polynomials, and
             it forms a reduced groebner basis (they all have pairwise
             distinct leading variables, and the leading variable of a
             polynomial does not occur anywhere in other polynomials).

          3. The leading variables of the second sequence do not occur
             anywhere in the first sequence (these variables have been
             eliminated).

        When ``return_reductors==False``, only the first sequence is
        returned.

        EXAMPLE::

            sage: B.<a,b,c,d> = BooleanPolynomialRing()
            sage: F = Sequence([c + d + b + 1, a + c + d, a*b + c, b*c*d + c])
            sage: F.eliminate_linear_variables() # everything vanishes
            []
            sage: F.eliminate_linear_variables(maxlength=2)
            [b + c + d + 1, b*c + b*d + c, b*c*d + c]
            sage: F.eliminate_linear_variables(skip=lambda lm,tail: str(lm)=='a')
            [a + c + d, a*c + a*d + a + c, c*d + c]

        The list of reductors can be requested by setting 'return_reductors' to ``True``::

            sage: B.<a,b,c,d> = BooleanPolynomialRing()
            sage: F = Sequence([a + b + d, a + b + c])
            sage: F,R = F.eliminate_linear_variables(return_reductors=True)
            sage: F
            []
            sage: R
            [a + b + d, c + d]

        TESTS:

        The function should really dispose of linear equations (:trac:`13968`)::

            sage: R.<x,y,z> = BooleanPolynomialRing()
            sage: S = Sequence([x+y+z+1, y+z])
            sage: S.eliminate_linear_variables(return_reductors=True)
            ([], [x + 1, y + z])


        The function should take care of linear variables created by previous
        substitution of linear variables ::

            sage: R.<x,y,z> = BooleanPolynomialRing()
            sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y])
            sage: S.eliminate_linear_variables(return_reductors=True)
            ([], [x + y, z + 1])

        .. NOTE::

            This is called "massaging" in [CBJ07]_.

        REFERENCES:

        .. [CBJ07] Gregory V. Bard, and Nicolas T. Courtois, and Chris Jefferson.
           *Efficient Methods for Conversion and Solution of Sparse Systems of Low-Degree
           Multivariate Polynomials over GF(2) via SAT-Solvers*.
           Cryptology ePrint Archive: Report 2007/024. available at
           http://eprint.iacr.org/2007/024
        """
        from sage.rings.polynomial.pbori import BooleanPolynomialRing
        from polybori import gauss_on_polys
        from polybori.ll import eliminate,ll_encode,ll_red_nf_redsb

        R = self.ring()

        if not isinstance(R, BooleanPolynomialRing):
            raise NotImplementedError("Only BooleanPolynomialRing's are supported.")

        F = self
        reductors = []

        if skip is None and maxlength==Infinity:
            # faster solution based on polybori.ll.eliminate
            while True:
                (this_step_reductors, _, higher) = eliminate(F)
                if this_step_reductors == []:
                   break
                reductors.extend( this_step_reductors )
                F = higher
        else:
            # slower, more flexible solution
            if skip is None:
                skip = lambda lm,tail: False

            while True:
                linear = []
                higher = []

                for f in F:
                    if f.degree() == 1 and len(f) <= maxlength + 1:
                        flm = f.lex_lead()
                        if skip(flm, f-flm):
                            higher.append(f)
                            continue
                        linear.append(f)
                    else:
                        higher.append(f)

                if not linear:
                    break

                linear = gauss_on_polys(linear)
                rb = ll_encode(linear)
                reductors.extend(linear)

                F = []
                for f in higher:
                    f = ll_red_nf_redsb(f, rb)
                    if f != 0:
                        F.append(f)

        ret = PolynomialSequence(R, higher)
        if return_reductors:
            reduced_reductors = gauss_on_polys(reductors)
            return ret, PolynomialSequence(R, reduced_reductors)
        else:
            return ret
Exemplo n.º 2
0
    def eliminate_linear_variables(self, maxlength=Infinity, skip=None, return_reductors=False):
        """
        Return a new system where linear leading variables are
        eliminated if the tail of the polynomial has length at most
        ``maxlength``.

        INPUT:

        - ``maxlength`` - an optional upper bound on the number of
          monomials by which a variable is replaced. If
          ``maxlength==+Infinity`` then no condition is checked.
          (default: +Infinity).

        - ``skip`` - an optional callable to skip eliminations. It
          must accept two parameters and return either ``True`` or
          ``False``. The two parameters are the leading term and the
          tail of a polynomial (default: ``None``).

        - ``return_reductors`` - if ``True`` the list of polynomials
          with linear leading terms which were used for reduction is
          also returned (default: ``False``).

        OUTPUT:

        When ``return_reductors==True``, then a pair of sequences of
        boolean polynomials are returned, along with the promises that:

          1. The union of the two sequences spans the
             same boolean ideal as the argument of the method

          2. The second sequence only contains linear polynomials, and
             it forms a reduced groebner basis (they all have pairwise
             distinct leading variables, and the leading variable of a
             polynomial does not occur anywhere in other polynomials).

          3. The leading variables of the second sequence do not occur
             anywhere in the first sequence (these variables have been
             eliminated).

        When ``return_reductors==False``, only the first sequence is
        returned.

        EXAMPLE::

            sage: B.<a,b,c,d> = BooleanPolynomialRing()
            sage: F = Sequence([c + d + b + 1, a + c + d, a*b + c, b*c*d + c])
            sage: F.eliminate_linear_variables() # everything vanishes
            []
            sage: F.eliminate_linear_variables(maxlength=2)
            [b + c + d + 1, b*c + b*d + c, b*c*d + c]
            sage: F.eliminate_linear_variables(skip=lambda lm,tail: str(lm)=='a')
            [a + c + d, a*c + a*d + a + c, c*d + c]

        The list of reductors can be requested by setting 'return_reductors' to ``True``::

            sage: B.<a,b,c,d> = BooleanPolynomialRing()
            sage: F = Sequence([a + b + d, a + b + c])
            sage: F,R = F.eliminate_linear_variables(return_reductors=True)
            sage: F
            []
            sage: R
            [a + b + d, c + d]

        TESTS:

        The function should really dispose of linear equations (:trac:`13968`)::

            sage: R.<x,y,z> = BooleanPolynomialRing()
            sage: S = Sequence([x+y+z+1, y+z])
            sage: S.eliminate_linear_variables(return_reductors=True)
            ([], [x + 1, y + z])


        The function should take care of linear variables created by previous
        substitution of linear variables ::

            sage: R.<x,y,z> = BooleanPolynomialRing()
            sage: S = Sequence([x*y*z+x*y+z*y+x*z, x+y+z+1, x+y])
            sage: S.eliminate_linear_variables(return_reductors=True)
            ([], [x + y, z + 1])

        .. NOTE::

            This is called "massaging" in [CBJ07]_.

        REFERENCES:

        .. [CBJ07] Gregory V. Bard, and Nicolas T. Courtois, and Chris Jefferson.
           *Efficient Methods for Conversion and Solution of Sparse Systems of Low-Degree
           Multivariate Polynomials over GF(2) via SAT-Solvers*.
           Cryptology ePrint Archive: Report 2007/024. available at
           http://eprint.iacr.org/2007/024
        """
        from polybori import gauss_on_polys
        from polybori.ll import eliminate,ll_encode,ll_red_nf_redsb
        from sage.rings.polynomial.pbori import BooleanPolynomialRing

        R = self.ring()

        if not isinstance(R, BooleanPolynomialRing):
            raise NotImplementedError("Only BooleanPolynomialRing's are supported.")

        F = self
        reductors = []

        if skip is None and maxlength==Infinity:
            # faster solution based on polybori.ll.eliminate
            while True:
                (this_step_reductors, _, higher) = eliminate(F)
                if this_step_reductors == []:
                   break
                reductors.extend( this_step_reductors )
                F = higher
        else:
            # slower, more flexible solution
            if skip is None:
                skip = lambda lm,tail: False

            while True:
                linear = []
                higher = []

                for f in F:
                    if f.degree() == 1 and len(f) <= maxlength + 1:
                        flm = f.lex_lead()
                        if skip(flm, f-flm):
                            higher.append(f)
                            continue
                        linear.append(f)
                    else:
                        higher.append(f)

                if not linear:
                    break

                linear = gauss_on_polys(linear)
                rb = ll_encode(linear)
                reductors.extend(linear)

                F = []
                for f in higher:
                    f = ll_red_nf_redsb(f, rb)
                    if f != 0:
                        F.append(f)

        ret = PolynomialSequence(R, higher)
        if return_reductors:
            reduced_reductors = gauss_on_polys(reductors)
            return ret, PolynomialSequence(R, reduced_reductors)
        else:
            return ret
Exemplo n.º 3
0
def llfirstonthefly_pre(I, prot):
    (eliminated, llnf, I) = eliminate(I, on_the_fly=True)
    return (I, eliminated)
Exemplo n.º 4
0
def llfirst_pre(I, prot):
    (eliminated, llnf, I) = eliminate(I, on_the_fly=False, prot=prot)
    return (I, eliminated)
Exemplo n.º 5
0
def llfirstonthefly_pre(I, prot):
    (eliminated, llnf, I) = eliminate(I, on_the_fly=True)
    return (I, eliminated)
Exemplo n.º 6
0
def llfirst_pre(I, prot):
    (eliminated, llnf, I) = eliminate(I, on_the_fly=False, prot=prot)
    return (I, eliminated)