Example #1
0
def _factor_univariate_polynomial(self, f):
    r"""
    TESTS::

        sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone
        sage: K = GF(2)
        sage: R.<x> = K[]
        sage: L.<x> = K.extension(x^2 + x + 1)
        sage: R.<y> = L[]
        sage: L.<y> = L.extension(y^2 + y + x)
        sage: R.<T> = L[]
        sage: (T^2 + T + x).factor() # indirect doctest
        (T + y) * (T + y + 1)

    """
    from sage.structure.factorization import Factorization

    if f.is_zero():
        raise ValueError("factorization of 0 is not defined")
    elif f.degree() <= 1:
        return Factorization([(f,1)])

    from_absolute_field, to_absolute_field, absolute_field = self.absolute_extension()

    F = f.map_coefficients(lambda c:to_absolute_field(c), absolute_field).factor()
    return Factorization([(g.map_coefficients(lambda c:from_absolute_field(c), self), e) for g,e in F], unit=from_absolute_field(F.unit()))
    def factor(self):
        """
        Return the prime factorization of the graph.

        EXAMPLES::

            sage: KG = KontsevichGraph([(1, 'F', 'L'), (1, 'G', 'R'), \
            ....: (2, 'F', 'L'), (2, 'G', 'R')], ground_vertices=('F','G'))
            sage: KG.factor()
            (Kontsevich graph with 1 vertices on 2 ground vertices)^2

        ALGORITHM::

        Delete ground vertices; the remaining connected components
        correspond to the prime factors.
        """
        floorless = self.copy(immutable=False)
        floorless.delete_vertices(self.ground_vertices())
        factors = []
        for C in floorless.connected_components():
            P = self.subgraph(vertices=C + list(self.ground_vertices()))
            P_KG = KontsevichGraph(P, ground_vertices=self.ground_vertices())
            P_KG.normalize_vertex_labels()
            factors.append(P_KG)
        return Factorization([(f, 1) for f in factors])
Example #3
0
    def factor(self, absprec=None):
        if self == 0:
            raise ValueError, "Factorization of 0 not defined"
        if absprec is None:
            absprec = min([x.precision_absolute() for x in self.list()])
        else:
            absprec = integer.Integer(absprec)
        if absprec <= 0:
            raise ValueError, "absprec must be positive"
        G = self._pari_().factorpadic(self.base_ring().prime(), absprec)
        pols = G[0]
        exps = G[1]
        F = []
        R = self.parent()
        for i in xrange(len(pols)):
            f = R(pols[i], absprec=absprec)
            e = int(exps[i])
            F.append((f, e))

        if R.base_ring().is_field():
            # When the base ring is a field we normalize
            # the irreducible factors so they have leading
            # coefficient 1.
            for i in range(len(F)):
                cur = F[i][0].leading_coefficient()
                if cur != 1:
                    F[i] = (F[i][0].monic(), F[i][1])
            return Factorization(F, self.leading_coefficient())
        else:
            # When the base ring is not a field, we normalize
            # the irreducible factors so that the leading term
            # is a power of p.  We also ensure that the gcd of
            # the coefficients of each term is 1.
            c = self.leading_coefficient().valuation()
            u = self.base_ring()(1)
            for i in range(len(F)):
                upart = F[i][0].leading_coefficient().unit_part()
                lval = F[i][0].leading_coefficient().valuation()
                if upart != 1:
                    F[i] = (F[i][0] // upart, F[i][1])
                    u *= upart**F[i][1]
                c -= lval
            if c != 0:
                F.append((self.parent()(self.base_ring().prime_pow(c)), 1))
            return Factorization(F, u)
Example #4
0
    def slope_factorization(self):
        """
        Return a factorization of ``self`` into a product of factors
        corresponding to each slope in the Newton polygon.

        EXAMPLES::

            sage: K = Qp(5)
            sage: R.<x> = K[]
            sage: K = Qp(5)
            sage: R.<t> = K[]
            sage: f = 5 + 3*t + t^4 + 25*t^10
            sage: f.newton_slopes()
            [1, 0, 0, 0, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3]

            sage: F = f.slope_factorization()
            sage: F.prod() == f
            True
            sage: for (f,_) in F:
            ....:     print(f.newton_slopes())
            [-1/3, -1/3, -1/3, -1/3, -1/3, -1/3]
            [0, 0, 0]
            [1]

        TESTS::

            sage: S.<x> = PowerSeriesRing(GF(5))
            sage: R.<y> = S[]
            sage: p = x^2+y+x*y^2
            sage: p.slope_factorization()
            (x) * ((x + O(x^22))*y + 1 + 4*x^3 + 4*x^6 + 3*x^9 + x^15 + 3*x^18 + O(x^21)) * ((x^-1 + O(x^20))*y + x + x^4 + 2*x^7 + 4*x^13 + 2*x^16 + 2*x^19 + O(x^22))

        AUTHOR:

        - Xavier Caruso (2013-03-20)
        """
        vertices = self.newton_polygon().vertices(copy=False)

        unit = self.leading_coefficient()
        P = ~unit * self

        deg_first = vertices[0][0]
        factors = [ ]
        if deg_first > 0:
            P >>= deg_first
            factors.append((self._parent.gen(), deg_first))
        if len(vertices) > 2:
            for i in range(1, len(vertices)-1):
                deg = vertices[i][0]
                div = P._factor_of_degree(deg-deg_first)
                factors.append((div,1))
                P,_ = P.quo_rem(div)
                deg_first = deg
        if len(vertices) > 1:
            factors.append((P, 1))
        factors.reverse()
        return Factorization(factors, sort=False, unit=unit)
Example #5
0
    def _factor_univariate_polynomial(self, f, proof=True):
        """
        Factor the univariate polynomial f over self.

        EXAMPLES::

        We do a factorization over the function field over the rationals::

            sage: R.<t> = FunctionField(QQ)
            sage: S.<X> = R[]
            sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3)
            sage: f.factor()             # indirect doctest
            (1/t) * (X - t) * (X^2 - 1/t) * (X^2 + 1/t) * (X^2 + t*X + t^2)
            sage: f.factor().prod() == f
            True        

        You must pass in proof=False over finite fields, due to
        Singular's factoring algorithm being incomplete::

            sage: R.<t> = FunctionField(GF(7))
            sage: S.<X> = R[]
            sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3)
            sage: f.factor()
            Traceback (most recent call last):
            ...
            NotImplementedError: proof = True factorization not implemented.  Call factor with proof=False.
            sage: f.factor(proof=False)
            (1/t) * (X + 3*t) * (X + 5*t) * (X + 6*t) * (X^2 + 1/t) * (X^2 + 6/t)
            sage: f.factor(proof=False).prod() == f
            True

        Factoring over a function field over a non-prime finite field::

            sage: k.<a> = GF(9)
            sage: R.<t> = FunctionField(k)
            sage: S.<X> = R[]
            sage: f = (1/t)*(X^3 - a*t^3)
            sage: f.factor(proof=False)
            (1/t) * (X + (a + 2)*t)^3
            sage: f.factor(proof=False).prod() == f
            True
        """
        F, d = self._to_bivariate_polynomial(f)
        fac = F.factor(proof=proof)
        x = f.parent().gen()
        t = f.parent().base_ring().gen()
        phi = F.parent().hom([x, t])
        v = [(phi(P), e) for P, e in fac]
        unit = phi(fac.unit()) / d
        w = []
        for a, e in v:
            c = a.leading_coefficient()
            a = a / c
            unit *= (c**e)
            w.append((a, e))
        from sage.structure.factorization import Factorization
        return Factorization(w, unit=unit)
Example #6
0
def _pari_padic_factorization_to_sage(G, R, leading_coeff):
    """
    Given a PARI factorization matrix `G` representing a factorization
    of some polynomial in the `p`-adic polynomial ring `R`,
    return the corresponding Sage factorization. All factors in `G`
    are assumed to have content 1 (this is how PARI returns its
    factorizations).

    INPUT:

    - ``G`` -- PARI factorization matrix, returned by ``factorpadic``.

    - ``R`` -- polynomial ring to be used as parent ring of the factors

    - ``leading_coeff`` -- leading coefficient of the polynomial which
      was factored. This can belong to any ring which can be coerced
      into ``R.base_ring()``.

    OUTPUT:

    - A Sage :class:`Factorization`.

    """
    B = R.base_ring()
    p = B.prime()
    leading_coeff = B(leading_coeff)

    pols = [R(f, absprec=f.padicprec(p)) for f in G[0]]
    exps = [int(e) for e in G[1]]

    # Determine unit part (which is discarded by PARI)
    if B.is_field():
        # When the base ring is a field, we normalize
        # the irreducible factors so they have leading
        # coefficient 1.
        for i in xrange(len(pols)):
            lc = pols[i].leading_coefficient()
            lc = lc.lift_to_precision()  # Ensure we don't lose precision
            pols[i] *= ~lc
    else:
        # When the base ring is not a field, we normalize
        # the irreducible factors so that the leading term
        # is a power of p.
        c, leading_coeff = leading_coeff.val_unit()
        for i in xrange(len(pols)):
            v, upart = pols[i].leading_coefficient().val_unit()
            upart = upart.lift_to_precision()  # Ensure we don't lose precision
            pols[i] *= ~upart
            c -= exps[i] * v
        if c:
            # Add factor p^c
            pols.append(R(p))
            exps.append(c)

    from sage.structure.factorization import Factorization
    return Factorization(zip(pols, exps), leading_coeff)
    def _factor_univariate_polynomial(self, f, proof=True):
        """
        Factor the univariate polynomial ``f`` over self.

        EXAMPLES::

        We do a factorization over the function field over the rationals::

            sage: R.<t> = FunctionField(QQ)
            sage: S.<X> = R[]
            sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3)
            sage: f.factor()             # indirect doctest
            (1/t) * (X - t) * (X^2 - 1/t) * (X^2 + 1/t) * (X^2 + t*X + t^2)
            sage: f.factor().prod() == f
            True

        We do a factorization over a finite prime field::

            sage: R.<t> = FunctionField(GF(7))
            sage: S.<X> = R[]
            sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3)
            sage: f.factor()
            (1/t) * (X + 3*t) * (X + 5*t) * (X + 6*t) * (X^2 + 1/t) * (X^2 + 6/t)
            sage: f.factor().prod() == f
            True

        Factoring over a function field over a non-prime finite field::

            sage: k.<a> = GF(9)
            sage: R.<t> = FunctionField(k)
            sage: S.<X> = R[]
            sage: f = (1/t)*(X^3 - a*t^3)
            sage: f.factor()
            (1/t) * (X + (a + 2)*t)^3
            sage: f.factor().prod() == f
            True
        """
        F, d = self._to_bivariate_polynomial(f)
        fac = F.factor()
        x = f.parent().gen()
        t = f.parent().base_ring().gen()
        phi = F.parent().hom([x, t])
        v = [(phi(P), e) for P, e in fac]
        unit = phi(fac.unit()) / d
        w = []
        for a, e in v:
            c = a.leading_coefficient()
            a = a / c
            unit *= (c**e)
            w.append((a, e))
        from sage.structure.factorization import Factorization
        return Factorization(w, unit=unit)
    def _acted_upon_(self, scalar, self_on_left=False):
        """
        Return the action of a scalar on ``self``.

        EXAMPLES::

            sage: R.<x,y> = FreeAlgebra(QQ,2)
            sage: f = Factorization([(x,2),(y,3)]); f
            x^2 * y^3
            sage: x * f
            x^3 * y^3
            sage: f * x
            x^2 * y^3 * x
        """
        from sage.structure.factorization import Factorization
        # FIXME: Make factorization work properly in the coercion framework
        # Keep factorization since we want to "coerce" into a factorization
        if isinstance(scalar, Factorization):
            if self_on_left:
                return Factorization([(self, 1)]) * scalar
            return scalar * Factorization([(self, 1)])
        return super(FreeAlgebraElement, self)._acted_upon_(scalar, self_on_left)
Example #9
0
    def _factor_univariate_polynomial(self, f):
        """
        Factor the univariate polynomial ``f``.

        INPUT:

        - ``f`` -- a univariate polynomial defined over the complex numbers

        OUTPUT:

        - A factorization of ``f`` over the complex numbers into a unit and
          monic irreducible factors

        .. NOTE::

            This is a helper method for
            :meth:`sage.rings.polynomial.polynomial_element.Polynomial.factor`.

            This method calls PARI to compute the factorization.

        TESTS::

            sage: k = ComplexField(100)
            sage: R.<x> = k[]
            sage: k._factor_univariate_polynomial( x )
            x
            sage: k._factor_univariate_polynomial( 2*x )
            (2.0000000000000000000000000000) * x
            sage: k._factor_univariate_polynomial( x^2 )
            x^2
            sage: k._factor_univariate_polynomial( x^2 + 3 )
            (x - 1.7320508075688772935274463415*I) * (x + 1.7320508075688772935274463415*I)
            sage: k._factor_univariate_polynomial( x^2 + 1 )
            (x - I) * (x + I)
            sage: k._factor_univariate_polynomial( k(I) * (x^2 + 1) )
            (1.0000000000000000000000000000*I) * (x - I) * (x + I)

        """
        R = f.parent()

        # if the polynomial does not have complex coefficients, PARI will
        # factor it over the reals. To make sure it has complex coefficients we
        # multiply with I.
        I = R.base_ring().gen()
        g = f * I if f.leading_coefficient() != I else f

        F = list(g._pari_with_name().factor())

        from sage.structure.factorization import Factorization
        return Factorization([(R(g).monic(), e) for g, e in zip(*F)],
                             f.leading_coefficient())
Example #10
0
 def factor(self):
     # This will eventually be improved.
     if self == 0:
         raise ValueError, "Factorization of the zero polynomial not defined"
     from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
     from sage.rings.padics.factory import ZpCA
     base = self.base_ring()
     #print self.list()
     m = min([x.precision_absolute() for x in self.list()])
     #print m
     R = ZpCA(base.prime(), prec = m)
     S = PolynomialRing(R, self.parent().variable_name())
     F = S(self).factor()
     return Factorization([(self.parent()(a), b) for (a, b) in F], base(F.unit()))
    def slope_factorization(self):
        """
        Return a factorization of ``self`` into a product of factors
        corresponding to each slope in the Newton polygon.

        EXAMPLES::

            sage: K = Qp(5)
            sage: R.<x> = K[]
            sage: K = Qp(5)
            sage: R.<t> = K[]
            sage: f = 5 + 3*t + t^4 + 25*t^10
            sage: f.newton_slopes()
            [1, 0, 0, 0, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3]

            sage: F = f.slope_factorization()
            sage: F.prod() == f
            True
            sage: for (f,_) in F:
            ....:     print f.newton_slopes()
            [-1/3, -1/3, -1/3, -1/3, -1/3, -1/3]
            [0, 0, 0]
            [1]

        AUTHOR:

        - Xavier Caruso (2013-03-20)
        """
        vertices = self.newton_polygon().vertices(copy=False)

        unit = self.leading_coefficient()
        P = ~unit * self

        deg_first = vertices[0][0]
        factors = [ ]
        if deg_first > 0:
            P >>= deg_first
            factors.append((self._parent.gen(), deg_first))
        if len(vertices) > 2:
            for i in range(1, len(vertices)-1):
                deg = vertices[i][0]
                div = P._factor_of_degree(deg-deg_first)
                factors.append((div,1))
                P,_ = P.quo_rem(div)
                deg_first = deg
        if len(vertices) > 1:
            factors.append((P, 1))
        factors.reverse()
        return Factorization(factors, sort=False, unit=unit)
Example #12
0
    def _factor_univariate_polynomial(self, f):
        """
        Factor the univariate polynomial ``f``.

        INPUT:

        - ``f`` -- a univariate polynomial defined over the rationals

        OUTPUT:

        - A factorization of ``f`` over the rationals into a unit and monic
          irreducible factors

        .. NOTE::

            This is a helper method for
            :meth:`sage.rings.polynomial.polynomial_element.Polynomial.factor`.

            This method calls PARI to compute the factorization.

        TESTS::

            sage: R.<x> = QQ[]
            sage: QQ._factor_univariate_polynomial( x )
            x
            sage: QQ._factor_univariate_polynomial( 2*x )
            (2) * x
            sage: QQ._factor_univariate_polynomial( (x^2 - 1/4)^4 )
            (x - 1/2)^4 * (x + 1/2)^4
            sage: QQ._factor_univariate_polynomial( (2*x + 1) * (3*x^2 - 5)^2 )
            (18) * (x + 1/2) * (x^2 - 5/3)^2
            sage: f = prod((k^2*x^k + k)^(k-1) for k in primes(10))
            sage: QQ._factor_univariate_polynomial(f)
            (1751787911376562500) * (x^2 + 1/2) * (x^3 + 1/3)^2 * (x^5 + 1/5)^4 * (x^7 + 1/7)^6
            sage: QQ._factor_univariate_polynomial( 10*x^5 - 1 )
            (10) * (x^5 - 1/10)
            sage: QQ._factor_univariate_polynomial( 10*x^5 - 10 )
            (10) * (x - 1) * (x^4 + x^3 + x^2 + x + 1)

        """
        G = list(f._pari_with_name().factor())

        # normalize the leading coefficients
        F = [(f.parent()(g).monic(), int(e)) for (g, e) in zip(*G)]

        from sage.structure.factorization import Factorization
        return Factorization(F, f.leading_coefficient())
Example #13
0
        def PolynomialQuotientRing_generic__factor_multivariate_polynomial(
                self, f, proof=True):
            from sage.structure.factorization import Factorization

            if f.is_zero():
                raise ValueError("factorization of 0 not defined")

            from_isomorphic_ring, to_isomorphic_ring, isomorphic_ring = self._isomorphic_ring(
            )
            g = f.map_coefficients(to_isomorphic_ring)
            F = g.factor()
            unit = f.parent(
                from_isomorphic_ring(F.unit().constant_coefficient()))
            return Factorization(
                [(factor.map_coefficients(from_isomorphic_ring), e)
                 for factor, e in F],
                unit=unit)
Example #14
0
    def factored_unit_order(self):
        """
        Returns a list of Factorization objects, each the factorization of the
        order of the units in a `\ZZ / p^n \ZZ` component of this group (using
        the Chinese Remainder Theorem).

        EXAMPLES::

            sage: R = Integers(8*9*25*17*29)
            sage: R.factored_unit_order()
            [2^2, 2 * 3, 2^2 * 5, 2^4, 2^2 * 7]
        """
        ans = []
        from sage.structure.factorization import Factorization
        for p, e in self.factored_order():
            ans.append(Factorization([(p,e-1)]) * factor(p-1, int_=(self.__order < 2**31)))
        return ans
def factor_polynomial_over_function_field(f):
    r"""
    Factor the polynomial f over the function field K.

    INPUT:

    - ``K`` -- a global function field
    - ``f`` -- a nonzero univariate polynomial over ``K``

    OUTPUT: the complete factorization of ``f``

    """
    K = f.base_ring()
    if K is K.rational_function_field():
        return f.factor()

    K0 = K.rational_function_field()
    old_variable_name = f.variable_name()
    if K.variable_name() == f.variable_name() or K0.variable_name() == f.variable_name():
        # replace x with xx to make the variable names distinct
        f = f.change_variable_name(old_variable_name + old_variable_name)
    F, d = to_trivariate_polynomial(K, f)
    R = F.parent()
    G = R(defining_polynomial(K))
    factorization = []
    g = f
    J = R.ideal(F, G)
    for Q, P in J.primary_decomposition_complete():
        prime_factor = prime_factor_from_prime_ideal(K, P)
        if prime_factor.degree() > 0:
            e = 0
            while True:
                q, r = g.quo_rem(prime_factor)
                if r == 0:
                    g = q
                    e += 1
                else:
                    break
            factorization.append((prime_factor, e))
    assert g.degree() == 0, "f did not factor properly"
    from sage.structure.factorization import Factorization
    ret = Factorization(factorization, g[0])
    return ret
Example #16
0
        def factor(self):
            """
            Return a factorization of ``self``.

            Since ``self`` is either a unit or zero, this function is trivial.

            EXAMPLES::

                sage: x = GF(7)(5)
                sage: x.factor()
                5
                sage: RR(0).factor()
                Traceback (most recent call last):
                ...
                ArithmeticError: factorization of 0.000000000000000 is not defined
            """
            if not self:
                raise ArithmeticError("factorization of {!r} is not defined".format(self))
            from sage.structure.factorization import Factorization
            return Factorization([], self)  # No factor; "self" as unit
Example #17
0
    def _factor_univariate_polynomial(self, p, **kwds):
        r"""
        Factorization of univariate polynomials.

        EXAMPLES::

            sage: K = GF(3).algebraic_closure()
            sage: R = PolynomialRing(K, 'T')
            sage: T = R.gen()
            sage: (K.gen(2) * T^2 - 1).factor()
            (z2) * (T + z4^3 + z4^2 + z4) * (T + 2*z4^3 + 2*z4^2 + 2*z4)

            sage: for d in range(10):
            ....:     p = R.random_element(degree=randint(2,8))
            ....:     assert p.factor().prod() == p, "error in the factorization of p={}".format(p)

        """
        from sage.structure.factorization import Factorization
        R = p.parent()
        return Factorization([(R([-root, self.one()]), m) for root, m in p.roots()], unit=p[p.degree()])
    def factor(self):
        """
        Factor the ideal by factoring the corresponding ideal
        in the absolute number field.

        EXAMPLES::

            sage: K.<a, b> = QQ.extension([x^2 + 11, x^2 - 5])
            sage: K.factor(5)
            (Fractional ideal (5, (-1/4*b - 1/4)*a + 1/4*b - 3/4))^2 * (Fractional ideal (5, (-1/4*b - 1/4)*a + 1/4*b - 7/4))^2
            sage: K.ideal(5).factor()
            (Fractional ideal (5, (-1/4*b - 1/4)*a + 1/4*b - 3/4))^2 * (Fractional ideal (5, (-1/4*b - 1/4)*a + 1/4*b - 7/4))^2
            sage: K.ideal(5).prime_factors()
            [Fractional ideal (5, (-1/4*b - 1/4)*a + 1/4*b - 3/4),
             Fractional ideal (5, (-1/4*b - 1/4)*a + 1/4*b - 7/4)]

            sage: PQ.<X> = QQ[]
            sage: F.<a, b> = NumberFieldTower([X^2 - 2, X^2 - 3])
            sage: PF.<Y> = F[]
            sage: K.<c> = F.extension(Y^2 - (1 + a)*(a + b)*a*b)
            sage: I = K.ideal(c)
            sage: P = K.ideal((b*a - b - 1)*c/2 + a - 1)
            sage: Q = K.ideal((b*a - b - 1)*c/2)
            sage: list(I.factor()) == [(P, 2), (Q, 1)]
            True
            sage: I == P^2*Q
            True
            sage: [p.is_prime() for p in [P, Q]]
            [True, True]
        """
        F = self.number_field()
        abs_ideal = self.absolute_ideal()
        to_F = abs_ideal.number_field().structure()[0]
        factor_list = [(F.ideal([to_F(_) for _ in p.gens()]), e)
                       for p, e in abs_ideal.factor()]
        # sorting and simplification will already have been done
        return Factorization(factor_list, sort=False, simplify=False)
Example #19
0
        def _squarefree_decomposition_univariate_polynomial(self, f):
            r"""
            Return the square-free decomposition of ``f`` over this field.

            This is a helper method for
            :meth:`sage.rings.polynomial.squarefree_decomposition`.

            INPUT:

            - ``f`` -- a univariate non-zero polynomial over this field

            ALGORITHM: For rings of characteristic zero, we use the algorithm
            described in [Yun1976]_. Other fields may provide their own
            implementation by overriding this method.

            EXAMPLES::

                sage: x = polygen(QQ)
                sage: p = 37 * (x-1)^3 * (x-2)^3 * (x-1/3)^7 * (x-3/7)
                sage: p.squarefree_decomposition()
                (37*x - 111/7) * (x^2 - 3*x + 2)^3 * (x - 1/3)^7
                sage: p = 37 * (x-2/3)^2
                sage: p.squarefree_decomposition()
                (37) * (x - 2/3)^2
                sage: x = polygen(GF(3))
                sage: x.squarefree_decomposition()
                x
                sage: f = QQbar['x'](1)
                sage: f.squarefree_decomposition()
                1
            """
            from sage.structure.factorization import Factorization
            if f.degree() == 0:
                return Factorization([], unit=f[0])
            if self.characteristic() != 0:
                raise NotImplementedError(
                    "square-free decomposition not implemented for this polynomial."
                )

            factors = []
            cur = f
            f = [f]
            while cur.degree() > 0:
                cur = cur.gcd(cur.derivative())
                f.append(cur)

            g = []
            for i in range(len(f) - 1):
                g.append(f[i] // f[i + 1])

            a = []
            for i in range(len(g) - 1):
                a.append(g[i] // g[i + 1])
            a.append(g[-1])

            unit = f[-1]
            for i in range(len(a)):
                if a[i].degree() > 0:
                    factors.append((a[i], i + 1))
                else:
                    unit = unit * a[i].constant_coefficient()**(i + 1)

            return Factorization(factors, unit=unit, sort=False)
Example #20
0
    def word_problem(self, gens=None):
        r"""
        Solve the word problem.

        This method writes the group element as a product of the
        elements of the list ``gens``, or the standard generators of
        the parent of self if ``gens`` is None.

        INPUT:

        - ``gens`` -- a list/tuple/iterable of elements (or objects
          that can be converted to group elements), or ``None``
          (default). By default, the generators of the parent group
          are used.

        OUTPUT:

        A factorization object that contains information about the
        order of factors and the exponents. A ``ValueError`` is raised
        if the group element cannot be written as a word in ``gens``.

        ALGORITHM:

        Use GAP, which has optimized algorithms for solving the word
        problem (the GAP functions ``EpimorphismFromFreeGroup`` and
        ``PreImagesRepresentative``).

        EXAMPLE::

            sage: G = GL(2,5); G
            General Linear Group of degree 2 over Finite Field of size 5
            sage: G.gens()
            (
            [2 0]  [4 1]
            [0 1], [4 0]
            )
            sage: G(1).word_problem([G.gen(0)])
            1
            sage: type(_)
            <class 'sage.structure.factorization.Factorization'>

            sage: g = G([0,4,1,4])
            sage: g.word_problem()
            ([4 1]
             [4 0])^-1

        Next we construct a more complicated element of the group from the
        generators::

            sage: s,t = G.0, G.1
            sage: a = (s * t * s); b = a.word_problem(); b
            ([2 0]
             [0 1]) *
            ([4 1]
             [4 0]) *
            ([2 0]
             [0 1])
            sage: flatten(b)
            [
            [2 0]     [4 1]     [2 0]
            [0 1], 1, [4 0], 1, [0 1], 1
            ]
            sage: b.prod() == a
            True

        We solve the word problem using some different generators::

            sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0])
            sage: a.word_problem([s,t,u])
            ([2 0]
             [0 1])^-1 *
            ([1 1]
             [0 1])^-1 *
            ([0 4]
             [1 0]) *
            ([2 0]
             [0 1])^-1

        We try some elements that don't actually generate the group::

            sage: a.word_problem([t,u])
            Traceback (most recent call last):
            ...
            ValueError: word problem has no solution

        AUTHORS:

        - David Joyner and William Stein
        - David Loeffler (2010): fixed some bugs
        - Volker Braun (2013): LibGAP
        """
        from sage.libs.gap.libgap import libgap
        G = self.parent()
        if gens:
            gen = lambda i: gens[i]
            H = libgap.Group([G(x).gap() for x in gens])
        else:
            gen = G.gen
            H = G.gap()
        hom = H.EpimorphismFromFreeGroup()
        preimg = hom.PreImagesRepresentative(self.gap())

        if preimg.is_bool():
            assert preimg == libgap.eval('fail')
            raise ValueError('word problem has no solution')

        result = []
        n = preimg.NumberSyllables().sage()
        exponent_syllable = libgap.eval('ExponentSyllable')
        generator_syllable = libgap.eval('GeneratorSyllable')
        for i in range(n):
            exponent = exponent_syllable(preimg, i + 1).sage()
            generator = gen(generator_syllable(preimg, i + 1).sage() - 1)
            result.append((generator, exponent))
        from sage.structure.factorization import Factorization
        result = Factorization(result)
        result._set_cr(True)
        return result
Example #21
0
    def word_problem(self, gens=None):
        r"""
        Write this group element in terms of the elements of the list
        ``gens``, or the standard generators of the parent of self if
        ``gens`` is None.
        
        INPUT:

        - ``gens`` - a list of elements that can be coerced into the parent of
          self, or None.

        ALGORITHM: Use GAP, which has optimized algorithms for solving the
        word problem (the GAP functions EpimorphismFromFreeGroup and
        PreImagesRepresentative).
        
        EXAMPLE::
        
            sage: G = GL(2,5); G
            General Linear Group of degree 2 over Finite Field of size 5
            sage: G.gens()
            [
            [2 0]
            [0 1],
            [4 1]
            [4 0]
            ]
            sage: G(1).word_problem([G.1, G.0])
            1
        
        Next we construct a more complicated element of the group from the
        generators::
        
            sage: s,t = G.0, G.1
            sage: a = (s * t * s); b = a.word_problem(); b
            ([2 0]
            [0 1]) *
            ([4 1]
            [4 0]) *
            ([2 0]
            [0 1])
            sage: b.prod() == a
            True

        We solve the word problem using some different generators::

            sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0])
            sage: a.word_problem([s,t,u])
            ([2 0]
            [0 1])^-1 *
            ([1 1]
            [0 1])^-1 *
            ([0 4]
            [1 0]) *
            ([2 0]
            [0 1])^-1

        We try some elements that don't actually generate the group::

            sage: a.word_problem([t,u])
            Traceback (most recent call last):
            ...
            ValueError: Could not solve word problem

        AUTHORS:

        - David Joyner and William Stein
        - David Loeffler (2010): fixed some bugs
        """
        G = self.parent()
        gg = gap(G)
        if not gens:
            gens = gg.GeneratorsOfGroup()
        else:
            gens = gap([G(x) for x in gens])
        H = gens.Group()
        hom = H.EpimorphismFromFreeGroup()
        in_terms_of_gens = str(hom.PreImagesRepresentative(self))
        if 'identity' in in_terms_of_gens:
            return Factorization([])
        elif in_terms_of_gens == 'fail':
            raise ValueError, "Could not solve word problem"
        v = list(H.GeneratorsOfGroup())
        F = G.field_of_definition()
        w = [G(x._matrix_(F)) for x in v]
        factors = [Factorization([(x,1)]) for x in w]
        d = dict([('x%s'%(i+1),factors[i]) for i in range(len(factors))])

        from sage.misc.sage_eval import sage_eval
        F = sage_eval(str(in_terms_of_gens), d)
        F._set_cr(True)
        return F
Example #22
0
    def montes_factorization(self,
                             G,
                             assume_squarefree=False,
                             required_precision=None):
        """
        Factor ``G`` over the completion of the domain of this valuation.

        INPUT:

        - ``G`` -- a monic polynomial over the domain of this valuation

        - ``assume_squarefree`` -- a boolean (default: ``False``), whether to
          assume ``G`` to be squarefree

        - ``required_precision`` -- a number or infinity (default:
          infinity); if ``infinity``, the returned polynomials are actual factors of
          ``G``, otherwise they are only factors with precision at least
          ``required_precision``.

        ALGORITHM:

            We compute :meth:`mac_lane_approximants` with ``required_precision``.
            The key polynomials approximate factors of ``G``.

        EXAMPLES::

            sage: sys.path.append(os.getcwd()); from mac_lane import * # optional: standalone
            sage: k=Qp(5,4)
            sage: v = pAdicValuation(k)
            sage: R.<x>=k[]
            sage: G = x^2 + 1
            sage: v.montes_factorization(G)
            ((1 + O(5^4))*x + (2 + 5 + 2*5^2 + 5^3 + O(5^4))) * ((1 + O(5^4))*x + (3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4)))

        The computation might not terminate over incomplete fields (in
        particular because the factors can not be represented there)::

            sage: R.<x> = QQ[]
            sage: v = pAdicValuation(QQ, 2)
            sage: v.montes_factorization(x^2 + 1)
            x^2 + 1

            sage: v.montes_factorization(x^2 - 1)
            (x - 1) * (x + 1)

            sage: v.montes_factorization(x^2 - 1, required_precision=5)
            (x + 1) * (x + 31)

        REFERENCES:

        .. [GMN2008] Jordi Guardia, Jesus Montes, Enric Nart (2008). Newton
        polygons of higher order in algebraic number theory. arXiv:0807.2620
        [math.NT]

        """
        if required_precision is None:
            from sage.rings.all import infinity
            required_precision = infinity

        R = G.parent()
        if R.base_ring() is not self.domain():
            raise ValueError(
                "G must be defined over the domain of this valuation")
        if not G.is_monic():
            raise ValueError("G must be monic")
        if not all([self(c) >= 0 for c in G.coefficients()]):
            raise ValueError("G must be integral")

        # W contains approximate factors of G
        W = self.mac_lane_approximants(G,
                                       required_precision=required_precision,
                                       require_maximal_degree=True,
                                       assume_squarefree=assume_squarefree)
        ret = [w.phi() for w in W]

        from sage.structure.factorization import Factorization
        return Factorization([(g, 1) for g in ret], simplify=False)
Example #23
0
    def factor(self):
        r"""
        Return the factorization of this polynomial.

        EXAMPLES::

            sage: R.<t> = PolynomialRing(Qp(3,3,print_mode='terse',print_pos=False))
            sage: pol = t^8 - 1
            sage: for p,e in pol.factor():
            ....:     print("{} {}".format(e, p))
            1 (1 + O(3^3))*t + 1 + O(3^3)
            1 (1 + O(3^3))*t - 1 + O(3^3)
            1 (1 + O(3^3))*t^2 + (5 + O(3^3))*t - 1 + O(3^3)
            1 (1 + O(3^3))*t^2 + (-5 + O(3^3))*t - 1 + O(3^3)
            1 (1 + O(3^3))*t^2 + O(3^3)*t + 1 + O(3^3)
            sage: R.<t> = PolynomialRing(Qp(5,6,print_mode='terse',print_pos=False))
            sage: pol = 100 * (5*t - 1) * (t - 5)
            sage: pol
            (500 + O(5^9))*t^2 + (-2600 + O(5^8))*t + 500 + O(5^9)
            sage: pol.factor()
            (500 + O(5^9)) * ((1 + O(5^5))*t - 1/5 + O(5^5)) * ((1 + O(5^6))*t - 5 + O(5^6))
            sage: pol.factor().value()
            (500 + O(5^8))*t^2 + (-2600 + O(5^8))*t + 500 + O(5^8)

        The same factorization over `\ZZ_p`. In this case, the "unit"
        part is a `p`-adic unit and the power of `p` is considered to be
        a factor::

            sage: R.<t> = PolynomialRing(Zp(5,6,print_mode='terse',print_pos=False))
            sage: pol = 100 * (5*t - 1) * (t - 5)
            sage: pol
            (500 + O(5^9))*t^2 + (-2600 + O(5^8))*t + 500 + O(5^9)
            sage: pol.factor()
            (4 + O(5^6)) * (5 + O(5^7))^2 * ((1 + O(5^6))*t - 5 + O(5^6)) * ((5 + O(5^6))*t - 1 + O(5^6))
            sage: pol.factor().value()
            (500 + O(5^8))*t^2 + (-2600 + O(5^8))*t + 500 + O(5^8)

        In the following example, the discriminant is zero, so the `p`-adic
        factorization is not well defined::

            sage: factor(t^2)
            Traceback (most recent call last):
            ...
            PrecisionError: p-adic factorization not well-defined since the discriminant is zero up to the requestion p-adic precision

        An example of factoring a constant polynomial (see :trac:`26669`)::

            sage: R.<x> = Qp(5)[]
            sage: R(2).factor()
            2 + O(5^20)

        More examples over `\ZZ_p`::

            sage: R.<w> = PolynomialRing(Zp(5, prec=6, type = 'capped-abs', print_mode = 'val-unit'))
            sage: f = w^5-1
            sage: f.factor()
            ((1 + O(5^6))*w + 3124 + O(5^6)) * ((1 + O(5^6))*w^4 + (12501 + O(5^6))*w^3 + (9376 + O(5^6))*w^2 + (6251 + O(5^6))*w + 3126 + O(5^6))

        See :trac:`4038`::

            sage: E = EllipticCurve('37a1')
            sage: K =Qp(7,10)
            sage: EK = E.base_extend(K)
            sage: E = EllipticCurve('37a1')
            sage: K = Qp(7,10)
            sage: EK = E.base_extend(K)
            sage: g = EK.division_polynomial_0(3)
            sage: g.factor()
            (3 + O(7^10)) * ((1 + O(7^10))*x + 1 + 2*7 + 4*7^2 + 2*7^3 + 5*7^4 + 7^5 + 5*7^6 + 3*7^7 + 5*7^8 + 3*7^9 + O(7^10)) * ((1 + O(7^10))*x^3 + (6 + 4*7 + 2*7^2 + 4*7^3 + 7^4 + 5*7^5 + 7^6 + 3*7^7 + 7^8 + 3*7^9 + O(7^10))*x^2 + (6 + 3*7 + 5*7^2 + 2*7^4 + 7^5 + 7^6 + 2*7^8 + 3*7^9 + O(7^10))*x + 2 + 5*7 + 4*7^2 + 2*7^3 + 6*7^4 + 3*7^5 + 7^6 + 4*7^7 + O(7^10))

        TESTS:

        Check that :trac:`13293` is fixed::

            sage: R.<T> = Qp(3)[]
            sage: f = 1926*T^2 + 312*T + 387
            sage: f.factor()
            (3^2 + 2*3^3 + 2*3^4 + 3^5 + 2*3^6 + O(3^22)) * ((1 + O(3^19))*T + 2*3^-1 + 3 + 3^2 + 2*3^5 + 2*3^6 + 2*3^7 + 3^8 + 3^9 + 2*3^11 + 3^15 + 3^17 + O(3^19)) * ((1 + O(3^20))*T + 2*3 + 3^2 + 3^3 + 3^5 + 2*3^6 + 2*3^7 + 3^8 + 3^10 + 3^11 + 2*3^12 + 2*3^14 + 2*3^15 + 2*3^17 + 2*3^18 + O(3^20))

        Check that :trac:`24065` is fixed::

            sage: R = Zp(2, type='fixed-mod', prec=3)
            sage: P.<x> = R[]
            sage: ((1 + 2)*x + (1 + 2)*x^2).factor()
            (1 + 2) * (x + 1) * x
        """
        if self == 0:
            raise ArithmeticError(
                "factorization of {!r} is not defined".format(self))
        elif self.is_constant():
            return Factorization((), self.constant_coefficient())

        # Scale self such that 0 is the lowest valuation
        # amongst the coefficients
        try:
            val = self.valuation(val_of_var=0)
        except TypeError:
            val = min([c.valuation() for c in self])
        self_normal = self / self.base_ring().uniformizer_pow(val)

        absprec = min([x.precision_absolute() for x in self_normal])
        if self_normal.discriminant().valuation() >= absprec:
            raise PrecisionError(
                "p-adic factorization not well-defined since the discriminant is zero up to the requestion p-adic precision"
            )
        G = self_normal.__pari__().factorpadic(self.base_ring().prime(),
                                               absprec)
        return _pari_padic_factorization_to_sage(G, self.parent(),
                                                 self.leading_coefficient())
Example #24
0
    def factor(self, absprec = None):
        r"""
        Returns the factorization of this Polynomial_padic_flat.

        EXAMPLES:
            sage: R.<w> = PolynomialRing(Zp(5, prec=5, type = 'capped-abs', print_mode = 'val-unit'))
            sage: f = w^5-1
            sage: f.factor()
            ((1 + O(5^5))*w + (624 + O(5^5))) * ((1 + O(5^5))*w^4 + (2501 + O(5^5))*w^3 + (1876 + O(5^5))*w^2 + (1251 + O(5^5))*w + (626 + O(5^5)))

        See \#4038:
            sage: E = EllipticCurve('37a1')
            sage: K =Qp(7,10)                 
            sage: EK = E.base_extend(K)
            sage: E = EllipticCurve('37a1')
            sage: K = Qp(7,10)             
            sage: EK = E.base_extend(K)    
            sage: g = EK.division_polynomial_0(3)
            sage: g.factor()
            (3 + O(7^10)) * ((1 + O(7^10))*x + (1 + 2*7 + 4*7^2 + 2*7^3 + 5*7^4 + 7^5 + 5*7^6 + 3*7^7 + 5*7^8 + 3*7^9 + O(7^10))) * ((1 + O(7^10))*x^3 + (6 + 4*7 + 2*7^2 + 4*7^3 + 7^4 + 5*7^5 + 7^6 + 3*7^7 + 7^8 + 3*7^9 + O(7^10))*x^2 + (6 + 3*7 + 5*7^2 + 2*7^4 + 7^5 + 7^6 + 2*7^8 + 3*7^9 + O(7^10))*x + (2 + 5*7 + 4*7^2 + 2*7^3 + 6*7^4 + 3*7^5 + 7^6 + 4*7^7 + O(7^10)))
        """
        if self == 0:
            raise ValueError, "Factorization of 0 not defined"
        if absprec is None:
            absprec = min([x.precision_absolute() for x in self.list()])
        else:
            absprec = Integer(absprec)
        if absprec <= 0:
            raise ValueError, "absprec must be positive"
        G = self._pari_().factorpadic(self.base_ring().prime(), absprec)
        pols = G[0]
        exps = G[1]
        F = []
        R = self.parent()
        for i in xrange(len(pols)):
            f = R(pols[i], absprec = absprec)
            e = int(exps[i])
            F.append((f,e))
            
        #if R.base_ring().is_field():
        #    # When the base ring is a field we normalize
        #    # the irreducible factors so they have leading
        #    # coefficient 1.
        #    for i in range(len(F)):
        #        cur = F[i][0].leading_coefficient()
        #        if cur != 1:
        #            F[i] = (F[i][0].monic(), F[i][1])
        #    return Factorization(F, self.leading_coefficient())
        #else:
        #    # When the base ring is not a field, we normalize
        #    # the irreducible factors so that the leading term
        #    # is a power of p.  We also ensure that the gcd of
        #    # the coefficients of each term is 1.
        c = self.leading_coefficient().valuation()
        u = self.base_ring()(1)
        for i in range(len(F)):
            upart = F[i][0].leading_coefficient().unit_part().lift_to_precision(absprec)
            lval = F[i][0].leading_coefficient().valuation()
            if upart != 1:
                F[i] = (F[i][0] / upart, F[i][1])
                u *= upart ** F[i][1]
            c -= lval * F[i][1]
        if c != 0:
            F.append((self.parent()(self.base_ring().prime_pow(c)), 1))
            u = u.add_big_oh(absprec - c)
        return Factorization(F, u)
Example #25
0
    def _factor_univariate_polynomial(self, f):
        """
        Factor the univariate polynomial ``f``.

        INPUT:

        - ``f`` -- a univariate polynomial defined over self

        OUTPUT:

        - A factorization of ``f`` over self into a unit and monic irreducible
          factors

        .. NOTE::

            This is a helper method for
            :meth:`sage.rings.polynomial.polynomial_element.Polynomial.factor`.

        EXAMPLES::

            sage: UCF = UniversalCyclotomicField()
            sage: x = polygen(UCF)
            sage: p = x^2 +x +1
            sage: p.factor()             # indirect doctest
            (x - E(3)) * (x - E(3)^2)
            sage: p.roots()              # indirect doctest
            [(E(3), 1), (E(3)^2, 1)]

            sage: (p^2).factor()
            (x - E(3))^2 * (x - E(3)^2)^2

            sage: cyclotomic_polynomial(12).change_ring(UCF).factor()
            (x + E(12)^7) * (x - E(12)^11) * (x + E(12)^11) * (x - E(12)^7)

            sage: p = (UCF.zeta(5) + 1) * (x^2 - 2)^2 * (x^2 - 3) * (x - 5)**2 * (x^2 - x + 1)
            sage: p.factor()
            (-E(5)^2 - E(5)^3 - E(5)^4) * (x + E(12)^7 - E(12)^11) * (x + E(3)^2) * (x + E(3)) * (x - E(12)^7 + E(12)^11) * (x - 5)^2 * (x - E(8) + E(8)^3)^2 * (x + E(8) - E(8)^3)^2
            sage: p.factor().value() == p
            True

            sage: (x^3 - 8).factor()
            (x - 2) * (x - 2*E(3)) * (x - 2*E(3)^2)

        In most situations, the factorization will fail with a ``NotImplementedError``::

            sage: (x^3 - 2).factor()
            Traceback (most recent call last):
            ...
            NotImplementedError: no known factorization for this polynomial

        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: x = polygen(UCF)

            sage: p = (x - 2/7) * (x - 3/5)
            sage: sorted(p.roots(multiplicities=False))
            [2/7, 3/5]

            sage: p = UCF.zeta(3) * x - 1 + UCF.zeta(5,2)
            sage: r = p.roots()
            sage: r
            [(-2*E(15) - E(15)^4 - E(15)^7 - E(15)^13, 1)]
            sage: p(r[0][0])
            0
        """
        from sage.structure.factorization import Factorization

        UCF = self
        x = f.parent().gen()

        # make the polynomial monic
        unit = f.leading_coefficient()
        f /= unit

        # trivial degree one case
        if f.degree() == 1:
            return Factorization([(f, 1)], unit)

        # From now on, we restrict to polynomial with rational cofficients. The
        # factorization is provided only in the case it is a product of
        # cyclotomic polynomials and quadratic polynomials. In this situation
        # the roots belong to UCF and the polynomial factorizes as a product of
        # degree one factors.
        if any(ZZ(cf._obj.Conductor()) != 1 for cf in f):
            raise NotImplementedError(
                'no known factorization for this polynomial')
        f = f.change_ring(QQ)

        factors = []
        for p, e in f.factor():
            if p.degree() == 1:
                factors.append((x + p[0], e))

            elif p.degree() == 2:
                c = p[0]
                b = p[1]
                a = p[2]
                D = UCF(b**2 - 4 * a * c).sqrt()
                r1 = (-b - D) / (2 * a)
                r2 = (-b + D) / (2 * a)
                factors.append((x - r1, e))
                factors.append((x - r2, e))

            else:
                m = p.is_cyclotomic(certificate=True)
                if not m:
                    raise NotImplementedError(
                        'no known factorization for this polynomial')
                for i in m.coprime_integers(m):
                    factors.append((x - UCF.zeta(m, i), e))

        return Factorization(factors, unit)
Example #26
0
    def word_problem(self, gens=None):
        r"""
        Solve the word problem.

        This method writes the group element as a product of the
        elements of the list ``gens``, or the standard generators of
        the parent of self if ``gens`` is None.

        INPUT:

        - ``gens`` -- a list/tuple/iterable of elements (or objects
          that can be converted to group elements), or ``None``
          (default). By default, the generators of the parent group
          are used.

        OUTPUT:

        A factorization object that contains information about the
        order of factors and the exponents. A ``ValueError`` is raised
        if the group element cannot be written as a word in ``gens``.

        ALGORITHM:

        Use GAP, which has optimized algorithms for solving the word
        problem (the GAP functions ``EpimorphismFromFreeGroup`` and
        ``PreImagesRepresentative``).

        EXAMPLE::

            sage: G = GL(2,5); G
            General Linear Group of degree 2 over Finite Field of size 5
            sage: G.gens()
            (
            [2 0]  [4 1]
            [0 1], [4 0]
            )
            sage: G(1).word_problem([G.gen(0)])
            1
            sage: type(_)
            <class 'sage.structure.factorization.Factorization'>

            sage: g = G([0,4,1,4])
            sage: g.word_problem()
            ([4 1]
             [4 0])^-1

        Next we construct a more complicated element of the group from the
        generators::

            sage: s,t = G.0, G.1
            sage: a = (s * t * s); b = a.word_problem(); b
            ([2 0]
             [0 1]) *
            ([4 1]
             [4 0]) *
            ([2 0]
             [0 1])
            sage: flatten(b)
            [
            [2 0]     [4 1]     [2 0]
            [0 1], 1, [4 0], 1, [0 1], 1
            ]
            sage: b.prod() == a
            True

        We solve the word problem using some different generators::

            sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0])
            sage: a.word_problem([s,t,u])
            ([2 0]
             [0 1])^-1 *
            ([1 1]
             [0 1])^-1 *
            ([0 4]
             [1 0]) *
            ([2 0]
             [0 1])^-1

        We try some elements that don't actually generate the group::

            sage: a.word_problem([t,u])
            Traceback (most recent call last):
            ...
            ValueError: word problem has no solution

        AUTHORS:

        - David Joyner and William Stein
        - David Loeffler (2010): fixed some bugs
        - Volker Braun (2013): LibGAP
        """
        from sage.libs.gap.libgap import libgap
        G = self.parent()
        if gens:
            gen = lambda i:gens[i]
            H = libgap.Group([G(x).gap() for x in gens])
        else:
            gen = G.gen
            H = G.gap()
        hom = H.EpimorphismFromFreeGroup()
        preimg = hom.PreImagesRepresentative(self.gap())

        if preimg.is_bool():
            assert preimg == libgap.eval('fail')
            raise ValueError('word problem has no solution')

        result = []
        n = preimg.NumberSyllables().sage()
        exponent_syllable  = libgap.eval('ExponentSyllable')
        generator_syllable = libgap.eval('GeneratorSyllable')
        for i in range(n):
            exponent  = exponent_syllable(preimg, i+1).sage()
            generator = gen(generator_syllable(preimg, i+1).sage() - 1)
            result.append( (generator, exponent) )
        from sage.structure.factorization import Factorization
        result = Factorization(result)
        result._set_cr(True)
        return result
Example #27
0
    def montes_factorization(self,
                             G,
                             assume_squarefree=False,
                             required_precision=None):
        """
        Factor ``G`` over the completion of the domain of this valuation.

        INPUT:

        - ``G`` -- a monic polynomial over the domain of this valuation

        - ``assume_squarefree`` -- a boolean (default: ``False``), whether to
          assume ``G`` to be squarefree

        - ``required_precision`` -- a number or infinity (default:
          infinity); if ``infinity``, the returned polynomials are actual factors of
          ``G``, otherwise they are only factors with precision at least
          ``required_precision``.

        ALGORITHM:

        We compute :meth:`mac_lane_approximants` with ``required_precision``.
        The key polynomials approximate factors of ``G``. This can be very
        slow unless ``required_precision`` is set to zero. Single factor
        lifting could improve this significantly.

        EXAMPLES::

            sage: k=Qp(5,4)
            sage: v = k.valuation()
            sage: R.<x>=k[]
            sage: G = x^2 + 1
            sage: v.montes_factorization(G)
            ((1 + O(5^4))*x + 2 + 5 + 2*5^2 + 5^3 + O(5^4)) * ((1 + O(5^4))*x + 3 + 3*5 + 2*5^2 + 3*5^3 + O(5^4))

        The computation might not terminate over incomplete fields (in
        particular because the factors can not be represented there)::

            sage: R.<x> = QQ[]
            sage: v = QQ.valuation(2)
            sage: v.montes_factorization(x^6 - 1)
            (x - 1) * (x + 1) * (x^2 - x + 1) * (x^2 + x + 1)

            sage: v.montes_factorization(x^7 - 1) # not tested, does not terminate

            sage: v.montes_factorization(x^7 - 1, required_precision=5)
            (x - 1) * (x^3 - 5*x^2 - 6*x - 1) * (x^3 + 6*x^2 + 5*x - 1)

        TESTS:

        Some examples that Sebastian Pauli used in a talk at Sage Days 87.

        In this example, ``f`` factors as three factors of degree 50 over an
        unramified extension::

            sage: R.<u> = ZqFM(125)
            sage: S.<x> = R[]
            sage: f = (x^6+2)^25 + 5
            sage: v = R.valuation()
            sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0)
            (x^50 + 2*5*x^45 + 5*x^40 + 5*x^30 + 2*x^25 + 3*5*x^20 + 2*5*x^10 + 2*5*x^5 + 5*x + 3 + 5) * (x^50 + 3*5*x^45 + 5*x^40 + 5*x^30 + (3 + 4*5)*x^25 + 3*5*x^20 + 2*5*x^10 + 3*5*x^5 + 4*5*x + 3 + 5) * (x^50 + 3*5*x^40 + 3*5*x^30 + 4*5*x^20 + 5*x^10 + 3 + 5)

        In this case, ``f`` factors into degrees 1, 2, and 5 over a totally ramified extension::

            sage: R = Zp(5)
            sage: S.<w> = R[]
            sage: R.<w> = R.extension(w^3 + 5)
            sage: S.<x> = R[]
            sage: f = (x^3 + 5)*(x^5 + w) + 625
            sage: v = R.valuation()
            sage: v.montes_factorization(f, assume_squarefree=True, required_precision=0)
            ((1 + O(w^60))*x + 4*w + O(w^61)) * ((1 + O(w^60))*x^2 + (w + O(w^61))*x + w^2 + O(w^62)) * ((1 + O(w^60))*x^5 + w + O(w^61))

        REFERENCES:

        The underlying algorithm is described in [Mac1936II]_ and thoroughly
        analyzed in [GMN2008]_.

        """
        if required_precision is None:
            from sage.rings.all import infinity
            required_precision = infinity

        R = G.parent()
        if R.base_ring() is not self.domain():
            raise ValueError(
                "G must be defined over the domain of this valuation")
        if not G.is_monic():
            raise ValueError("G must be monic")
        if not all([self(c) >= 0 for c in G.coefficients()]):
            raise ValueError("G must be integral")

        # W contains approximate factors of G
        W = self.mac_lane_approximants(G,
                                       required_precision=required_precision,
                                       require_maximal_degree=True,
                                       assume_squarefree=assume_squarefree)
        ret = [w.phi() for w in W]

        from sage.structure.factorization import Factorization
        return Factorization([(g, 1) for g in ret], simplify=False)
Example #28
0
def MacMahonOmega(var,
                  expression,
                  denominator=None,
                  op=operator.ge,
                  Factorization_sort=False,
                  Factorization_simplify=True):
    r"""
    Return `\Omega_{\mathrm{op}}` of ``expression`` with respect to ``var``.

    To be more precise, calculate

    .. MATH::

        \Omega_{\mathrm{op}} \frac{n}{d_1 \dots d_n}

    for the numerator `n` and the factors `d_1`, ..., `d_n` of
    the denominator, all of which are Laurent polynomials in ``var``
    and return a (partial) factorization of the result.

    INPUT:

    - ``var`` -- a variable or a representation string of a variable

    - ``expression`` -- a
      :class:`~sage.structure.factorization.Factorization`
      of Laurent polynomials or, if ``denominator`` is specified,
      a Laurent polynomial interpreted as the numerator of the
      expression

    - ``denominator`` -- a Laurent polynomial or a
      :class:`~sage.structure.factorization.Factorization` (consisting
      of Laurent polynomial factors) or a tuple/list of factors (Laurent
      polynomials)

    - ``op`` -- (default: ``operator.ge``) an operator

      At the moment only ``operator.ge`` is implemented.

    - ``Factorization_sort`` (default: ``False``) and
      ``Factorization_simplify`` (default: ``True``) -- are passed on to
      :class:`sage.structure.factorization.Factorization` when creating
      the result

    OUTPUT:

    A (partial) :class:`~sage.structure.factorization.Factorization`
    of the result whose factors are Laurent polynomials

    .. NOTE::

        The numerator of the result may not be factored.

    REFERENCES:

    - [Mac1915]_

    - [APR2001]_

    EXAMPLES::

        sage: L.<mu, x, y, z, w> = LaurentPolynomialRing(ZZ)

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu])
        1 * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu, 1 - z/mu])
        1 * (-x + 1)^-1 * (-x*y + 1)^-1 * (-x*z + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z/mu])
        (-x*y*z + 1) * (-x + 1)^-1 * (-y + 1)^-1 * (-x*z + 1)^-1 * (-y*z + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu^2])
        1 * (-x + 1)^-1 * (-x^2*y + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^2, 1 - y/mu])
        (x*y + 1) * (-x + 1)^-1 * (-x*y^2 + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z/mu^2])
        (-x^2*y*z - x*y^2*z + x*y*z + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 * (-x^2*z + 1)^-1 * (-y^2*z + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu^3])
        1 * (-x + 1)^-1 * (-x^3*y + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu^4])
        1 * (-x + 1)^-1 * (-x^4*y + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^3, 1 - y/mu])
        (x*y^2 + x*y + 1) * (-x + 1)^-1 * (-x*y^3 + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^4, 1 - y/mu])
        (x*y^3 + x*y^2 + x*y + 1) * (-x + 1)^-1 * (-x*y^4 + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu^2, 1 - y/mu, 1 - z/mu])
        (x*y*z + x*y + x*z + 1) *
        (-x + 1)^-1 * (-x*y^2 + 1)^-1 * (-x*z^2 + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^2, 1 - y*mu, 1 - z/mu])
        (-x*y*z^2 - x*y*z + x*z + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 * (-x*z^2 + 1)^-1 * (-y*z + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z*mu, 1 - w/mu])
        (x*y*z*w^2 + x*y*z*w - x*y*w - x*z*w - y*z*w + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 * (-z + 1)^-1 *
        (-x*w + 1)^-1 * (-y*w + 1)^-1 * (-z*w + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z/mu, 1 - w/mu])
        (x^2*y*z*w + x*y^2*z*w - x*y*z*w - x*y*z - x*y*w + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 *
        (-x*z + 1)^-1 * (-x*w + 1)^-1 * (-y*z + 1)^-1 * (-y*w + 1)^-1

        sage: MacMahonOmega(mu, mu^-2, [1 - x*mu, 1 - y/mu])
        x^2 * (-x + 1)^-1 * (-x*y + 1)^-1
        sage: MacMahonOmega(mu, mu^-1, [1 - x*mu, 1 - y/mu])
        x * (-x + 1)^-1 * (-x*y + 1)^-1
        sage: MacMahonOmega(mu, mu, [1 - x*mu, 1 - y/mu])
        (-x*y + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1
        sage: MacMahonOmega(mu, mu^2, [1 - x*mu, 1 - y/mu])
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

    We demonstrate the different allowed input variants::

        sage: MacMahonOmega(mu,
        ....:     Factorization([(mu, 2), (1 - x*mu, -1), (1 - y/mu, -1)]))
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2,
        ....:     Factorization([(1 - x*mu, 1), (1 - y/mu, 1)]))
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2, [1 - x*mu, 1 - y/mu])
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2, (1 - x*mu)*(1 - y/mu))  # not tested because not fully implemented
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2 / ((1 - x*mu)*(1 - y/mu)))  # not tested because not fully implemented
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

    TESTS::

        sage: MacMahonOmega(mu, 1, [1 - x*mu])
        1 * (-x + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x/mu])
        1
        sage: MacMahonOmega(mu, 0, [1 - x*mu])
        0
        sage: MacMahonOmega(mu, L(1), [])
        1
        sage: MacMahonOmega(mu, L(0), [])
        0
        sage: MacMahonOmega(mu, 2, [])
        2
        sage: MacMahonOmega(mu, 2*mu, [])
        2
        sage: MacMahonOmega(mu, 2/mu, [])
        0

    ::

        sage: MacMahonOmega(mu, Factorization([(1/mu, 1), (1 - x*mu, -1),
        ....:                                  (1 - y/mu, -2)], unit=2))
        2*x * (-x + 1)^-1 * (-x*y + 1)^-2
        sage: MacMahonOmega(mu, Factorization([(mu, -1), (1 - x*mu, -1),
        ....:                                  (1 - y/mu, -2)], unit=2))
        2*x * (-x + 1)^-1 * (-x*y + 1)^-2
        sage: MacMahonOmega(mu, Factorization([(mu, -1), (1 - x, -1)]))
        0
        sage: MacMahonOmega(mu, Factorization([(2, -1)]))
        1 * 2^-1

    ::

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - z, 1 - y/mu])
        1 * (-z + 1)^-1 * (-x + 1)^-1 * (-x*y + 1)^-1

    ::

        sage: MacMahonOmega(mu, 1, [1 - x*mu], op=operator.lt)
        Traceback (most recent call last):
        ...
        NotImplementedError: At the moment, only Omega_ge is implemented.

        sage: MacMahonOmega(mu, 1, Factorization([(1 - x*mu, -1)]))
        Traceback (most recent call last):
        ...
        ValueError: Factorization (-mu*x + 1)^-1 of the denominator
        contains negative exponents.

        sage: MacMahonOmega(2*mu, 1, [1 - x*mu])
        Traceback (most recent call last):
        ...
        ValueError: 2*mu is not a variable.

        sage: MacMahonOmega(mu, 1, Factorization([(0, 2)]))
        Traceback (most recent call last):
        ...
        ZeroDivisionError: Denominator contains a factor 0.

        sage: MacMahonOmega(mu, 1, [2 - x*mu])
        Traceback (most recent call last):
        ...
        NotImplementedError: Factor 2 - x*mu is not normalized.

        sage: MacMahonOmega(mu, 1, [1 - x*mu - mu^2])
        Traceback (most recent call last):
        ...
        NotImplementedError: Cannot handle factor 1 - x*mu - mu^2.

    ::

        sage: L.<mu, x, y, z, w> = LaurentPolynomialRing(QQ)
        sage: MacMahonOmega(mu, 1/mu,
        ....:     Factorization([(1 - x*mu, 1), (1 - y/mu, 2)], unit=2))
        1/2*x * (-x + 1)^-1 * (-x*y + 1)^-2
    """
    from sage.arith.misc import factor
    from sage.misc.misc_c import prod
    from sage.rings.integer_ring import ZZ
    from sage.rings.polynomial.laurent_polynomial_ring \
        import LaurentPolynomialRing, LaurentPolynomialRing_univariate
    from sage.structure.factorization import Factorization

    if op != operator.ge:
        raise NotImplementedError(
            'At the moment, only Omega_ge is implemented.')

    if denominator is None:
        if isinstance(expression, Factorization):
            numerator = expression.unit() * \
                        prod(f**e for f, e in expression if e > 0)
            denominator = tuple(f for f, e in expression if e < 0
                                for _ in range(-e))
        else:
            numerator = expression.numerator()
            denominator = expression.denominator()
    else:
        numerator = expression
    # at this point we have numerator/denominator

    if isinstance(denominator, (list, tuple)):
        factors_denominator = denominator
    else:
        if not isinstance(denominator, Factorization):
            denominator = factor(denominator)
        if not denominator.is_integral():
            raise ValueError(
                'Factorization {} of the denominator '
                'contains negative exponents.'.format(denominator))
        numerator *= ZZ(1) / denominator.unit()
        factors_denominator = tuple(factor for factor, exponent in denominator
                                    for _ in range(exponent))
    # at this point we have numerator/factors_denominator

    P = var.parent()
    if isinstance(P, LaurentPolynomialRing_univariate) and P.gen() == var:
        L = P
        L0 = L.base_ring()
    elif var in P.gens():
        var = repr(var)
        L0 = LaurentPolynomialRing(
            P.base_ring(), tuple(v for v in P.variable_names() if v != var))
        L = LaurentPolynomialRing(L0, var)
        var = L.gen()
    else:
        raise ValueError('{} is not a variable.'.format(var))

    other_factors = []
    to_numerator = []
    decoded_factors = []
    for factor in factors_denominator:
        factor = L(factor)
        D = factor.dict()
        if not D:
            raise ZeroDivisionError('Denominator contains a factor 0.')
        elif len(D) == 1:
            exponent, coefficient = next(iteritems(D))
            if exponent == 0:
                other_factors.append(L0(factor))
            else:
                to_numerator.append(factor)
        elif len(D) == 2:
            if D.get(0, 0) != 1:
                raise NotImplementedError(
                    'Factor {} is not normalized.'.format(factor))
            D.pop(0)
            exponent, coefficient = next(iteritems(D))
            decoded_factors.append((-coefficient, exponent))
        else:
            raise NotImplementedError(
                'Cannot handle factor {}.'.format(factor))
    numerator = L(numerator) / prod(to_numerator)

    result_numerator, result_factors_denominator = \
        _Omega_(numerator.dict(), decoded_factors)
    if result_numerator == 0:
        return Factorization([], unit=result_numerator)

    return Factorization([(result_numerator, 1)] + list(
        (f, -1) for f in other_factors) + list(
            (1 - f, -1) for f in result_factors_denominator),
                         sort=Factorization_sort,
                         simplify=Factorization_simplify)
Example #29
0
def sieve(p, n, r=0, accept=None):
    '''
    Given p, n and r, find the smallest integer m such that there
    exists an element of multiplicative order r in ℤ/m.

    This is equivalent to the condition: for each prime power r'|r,
    there exists a prime power m'|m such that r'|φ(m'). One immediate
    consequence is that r|φ(m).

    The output is a list of pairs of integers (m_i, r_i, o_i), such that

    - m_i is a prime power;
    - the m_i are pairwise coprime and m = ∏ m_i;
    - the r_i are pairwise coprime and r = ∏ r_i;
    - r_i · o_i = φ(m_i).

    The optional parameter `accept` can be passed in order to put
    additional constraints on m. If it is given, it must be a function
    satisfying:

    - it takes six arguments (p, n, r, l, e, s), where p, n, r, l and e
      are integers and s is the factorization of an integer
      (a `Factorization` object);
    - it returns a boolean;
    - let s and t be coprime, accept(p, n, r, l, e, s·t) returns `True` iff
      accept(p, n, r, l, e, s) and accept(p, n, r, l, e, t) also return `True`.

    Then, assuming m_i = l_i^e_i, with l_i prime, the output also
    satisfies

    - accept(p, n, r, l_i, e_i, r_i) returns `True` for any i.

    The obvious use case for `accept` is to ensure that a specific
    element of ℤ/m has order r (or divisible by r), instead of any
    element, e.g. p in the case of Rains' algorithm.

    ## Algorithm

    The algorithm starts by factoring r into prime powers r_i. For
    each r_i, it finds the smallest prime power m_i = l_i^e_i such
    that r_i|φ(m_i) and `accept(p, n, r, l_i, e_i, r_i)` returns `True`
    (if `accept` is provided).

    At this point, the lcm of all the m_i is an acceptable value for
    m, although not necessarily the smallest one. More generally, let

      r_{1,...,s} = r₁ · r₂ ··· r_s

    for some subset of the r_i (up to renumbering), then the lcm of
    m₁, ..., m_s is an acceptable value for r_{1,...,s}, although not
    necessarily the smallest one. Call m_{1,...,s} this optimal value.
    The algorithm goes on by computing the optimal values m_X for
    larger and larger subsets X ⊂ {r_i}, until r is reached.

    This is done by testing all the possible prime powers between the
    largest m_Y already computed for any proper subset Y ⊂ X, and the
    smallest lcm(m_W, m_Z) for any proper partition X = W ∪ Z with
    W ∩ Z = ∅.

    For any given r' = r_{1,...,s}, the algorithm looks for primes of
    the form k·r' + 1. If r_s = l_s^e_s is the largest prime factor of
    r' and if

      r₁ · r₂ ··· r_{s-1} | l_s - 1

    the algorithm also looks for powers of l_s. It is easy to show
    that the algorithm needs consider no other integer.

    ### Example

    Let r = 60 = 4·3·5

    The algorithm starts by computing

      m_{4} = 1·4 + 1 = 5,
      m_{3} = 2·3 + 1 = 7,
      m_{5} = 2·5 + 1 = 11.

    Then it considers all possible pairs of factors. For {4,3}, one
    possible value is 5·7 = 35. 2 divides 3-1, hence the algorithm
    looks for primes and powers of 3 comprised between 7 and 35. It
    finds that 13 is a suitable value, hence

      m_{4,3} = 1·12 + 1 = 13,

    and similarly

      m_{4,5} = 25, m_{3,5} = 31.

    Finally, it considers all three factors. It is useless to test
    prime powers below 31, because 3 and 5 must divide them. There are
    three possible partitions of {4,3,5} into two disjoints sets:

      lcm(m_{4}, m_{3,5}) = 155
      lcm(m_{3}, m_{4,5}) = 175
      lcm(m_{5}, m_{4,3}) = 143

    hence 143 is an acceptable value, and the algorithm needs to test
    no further. The first value tried is 1·60 + 1 = 61, and it turns
    out it is a prime, hence the algorithm returns

      [(61, 60)].

    ### Complexity

    The complexity is obviously polynomial in r. Heuristically, it
    should be sublinear, the dominating step being the factorization
    of r, but it is not so easy to prove it.

    If c is the number of primary factors of r, the main loop is
    executed 2^c times, which is clearly sublinear in r.

    At each iteration, all partitions of the current subset into two
    disjoint subsets must be considered, hence a very crude lower
    bound for this combinatorial step is

      ∑_{i=1}^{c} binom(c,i) (2^{i-1} - 1) < 3^c << e^{o(log r)}

    The most expensive operation of each cycle is the primality
    testing (and, eventually, the `accept` function). Heuristically,
    at each iteration O(log r) primality tests are needed, each with a
    polynomial cost in log r. As noted in `find_root_order`, the best
    bounds under GRH give O(r^{1.4 + ε}) primality tests, instead.

    Whatever the provable complexity is, this algorithm is extremely
    fast in practice, and can handle sizes which are way beyond the
    tractability of the other steps of Rains' algorithm.
    '''
    # Degrees of the the ambient fields.
    ngcd, nlcm = n

    # Actual extension degree within the ambient fields
    if r == 0:
        r = ngcd

    # If accept is not given, always accept
    if accept is None:
        accept = lambda p, n, r, l, e, s: True

    class factorization:
        '''
        This class represents the factorization of an integer,
        carrying one more piece piece of information along each
        primary factor: factors are pairs (p^e, o), with o an
        integer.
        '''
        def __init__(self, f):
            '''
            f must be a dictionary with entries of the form
                l : (e, o)
            '''
            self.factors = f

        def lcm(self, other):
            '''
            Compute the lcm of two factorizations. The auxiliary
            information is multiplied together:

              lcm ( (l^e, s), (l^d, t) ) = (l^max(e,d), s·t)
            '''
            lcm = self.factors.copy()
            for (l, (e, s)) in other.factors.iteritems():
                try:
                    E, S = lcm[l]
                    lcm[l] = (max(E, e), S * s)
                except KeyError:
                    lcm[l] = (e, s)
            return factorization(lcm)

        @cached_method
        def expand(self):
            'Return the integer represented by this factorization'
            return prod(map(lambda (l, (e, _)): l**e, self.factors.items()))

        def __str__(self):
            return ' * '.join('%d^%d<--%d' % (l, e, s)
                              for (l, (e, s)) in self.factors.iteritems())

        def __repr__(self):
            return 'factorization(%s)' % repr(self.factors)

    # Represent the factorization of r as a set of primary factors
    fact = Set(list(r.factor()))
    # This dictionary holds the values r_X for each subset of `fact`
    optima = {}

    # Main loop, execute for each subset `S` ⊂ `fact`
    # It assumes subsets are enumerated by growing size
    for S in fact.subsets():
        # ignore the empty set
        if S.is_empty():
            continue

        # A Factorization object corresponding to S
        Sfact = Factorization(S.list())

        # find `L` the greatest prime in `S`
        L, E = max(S, key=lambda (l, e): l)
        # the product of the remaining prime powers
        c = prod(l**e for l, e in S if l != L)
        # a boolean, True only if it is worth testing powers of `L` in
        # the sequel
        powers = (L - 1) % c == 0
        # the product of all the factors of `S`
        s = c * L**E

        # For singletons, we don't have an upper bound
        if S.cardinality() == 1:
            start = 1
            end = None
        # For larger subsets, we compute the minimum and maximum value
        # to test
        else:
            parts = SetPartitions(S, 2)
            # Start from the largest m_X already computed for any
            # strict subset of `S`
            start = max(optima[T].expand() for T in S.subsets()
                        if (not T.is_empty()) and (T != S))
            # Stop at the smallest lcm of any 2-partition of `S`
            end = min((optima[T0].lcm(optima[T1]) for T0, T1 in parts),
                      key=lambda x: x.expand())

        # We only consider primes of the form
        #    k·s + 1
        # and powers of `L` of the form
        #    k·s + L^E
        # if `powers` is true.
        # We determine the starting `k`
        k = start // s or 1
        while True:
            m = k * s + 1
            # Once the upper bound, is reached, it is used with no
            # further test
            if end is not None and m >= end.expand():
                optima[S] = end
                break
            # Test primes
            elif m.is_prime() and accept(p, n, r, m, 1, Sfact):
                optima[S] = factorization({m: (1, s)})
                break
            # Test powers of `L`
            # notice the correction for L = 2 on the second line
            d = k * c + 1
            if (powers and d.is_power_of(L) and (L != 2 or E == 1 or k > 1)
                    and accept(p, n, r, L, E + d.valuation(L), Sfact)):
                optima[S] = factorization({L: (E + d.valuation(L), s)})
                break

            k += 1

    # the last computed m_X is the optimum for r
    return [(l**e, s, (l - 1) * l**(e - 1) // s)
            for (l, (e, s)) in optima[fact].factors.iteritems()]