Пример #1
0
    def newton_polygon(self):
        r"""
        Returns a list of vertices of the Newton polygon of this polynomial.

        .. NOTE::

            If some coefficients have not enough precision an error is raised.

        EXAMPLES::

            sage: K = Qp(5)
            sage: R.<t> = K[]
            sage: f = 5 + 3*t + t^4 + 25*t^10
            sage: f.newton_polygon()
            Finite Newton polygon with 4 vertices: (0, 1), (1, 0), (4, 0), (10, 2)

            sage: g = f + K(0,0)*t^4; g
            (5^2 + O(5^22))*t^10 + O(5^0)*t^4 + (3 + O(5^20))*t + 5 + O(5^21)
            sage: g.newton_polygon()
            Traceback (most recent call last):
            ...
            PrecisionError: The coefficient of t^4 has not enough precision

        TESTS:

        Check that :trac:`22936` is fixed::

            sage: S.<x> = PowerSeriesRing(GF(5))
            sage: R.<y> = S[]
            sage: p = x^2+y+x*y^2
            sage: p.newton_polygon()
            Finite Newton polygon with 3 vertices: (0, 2), (1, 0), (2, 1)

        AUTHOR:

        - Xavier Caruso (2013-03-20)
        """
        d = self.degree()
        from sage.geometry.newton_polygon import NewtonPolygon
        polygon = NewtonPolygon([(x, self[x].valuation()) for x in range(d+1)])
        polygon_prec = NewtonPolygon([ (x, self[x].precision_absolute()) for x in range(d+1) ])
        vertices = polygon.vertices(copy=False)
        vertices_prec = polygon_prec.vertices(copy=False)
        if len(vertices_prec) > 0:
            if vertices[0][0] > vertices_prec[0][0]:
                raise PrecisionError("first term with non-infinite valuation must have determined valuation")
            elif vertices[-1][0] < vertices_prec[-1][0]:
                raise PrecisionError("last term with non-infinite valuation must have determined valuation")
            else:
                for (x, y) in vertices:
                    if polygon_prec(x) <= y:
                         raise PrecisionError("The coefficient of %s^%s has not enough precision" % (self.parent().variable_name(), x))
        return polygon
Пример #2
0
    def __invert__(self):
        r"""
        Return the multiplicative inverse of this element.

        NOTE::

        The result of division always lives in the fraction field,
        even if the element to be inverted is a unit.

        EXAMPLES::

            sage: R = ZpLC(19)
            sage: x = R(-5/2, 5); x
            7 + 9*19 + 9*19^2 + 9*19^3 + 9*19^4 + O(19^5)

            sage: y = ~x    # indirect doctest
            sage: y
            11 + 7*19 + 11*19^2 + 7*19^3 + 11*19^4 + O(19^5)
            sage: y == -2/5
            True

        TESTS::

            sage: a = R.random_element()
            sage: a * ~a == 1
            True
        """
        if self.is_zero():
            raise PrecisionError("cannot invert something indistinguishable from zero")
        x_self = self._value
        x = self._parent._approx_one / x_self
        # dx = -(1/self^2)*dself
        dx = [  [self, self._parent._approx_minusone/(x_self*x_self)] ]
        return self.__class__(self._parent.fraction_field(), x, dx=dx, check=False)
Пример #3
0
    def expansion(self, n=None, lift_mode='simple', start_val=None):
        r"""
        Return a list giving the `p`-adic expansion of this element.
        If this is a field element, start at
        `p^{\mbox{valuation}}`, if a ring element at `p^0`.

        INPUT:

        - ``n`` -- an integer or ``None`` (default ``None``); if given, 
          return the corresponding entry in the expansion.

        - ``lift_mode`` -- a string (default: ``simple``); currently
          only ``simple`` is implemented.

        - ``start_val`` -- an integer or ``None`` (default: ``None``);
          start at this valuation rather than the default (`0` or the 
          valuation of this element).

        EXAMPLES::

            sage: R = ZpLC(5, 10)
            sage: x = R(123456789); x
            4 + 2*5 + 5^2 + 4*5^3 + 5^5 + 5^6 + 5^8 + 3*5^9 + O(5^10)
            sage: x.expansion()
            [4, 2, 1, 4, 0, 1, 1, 0, 1, 3]

            sage: x.expansion(3)
            4

            sage: x.expansion(start_val=5)
            [1, 1, 0, 1, 3]

        If any, trailing zeros are included in the expansion::

            sage: y = R(1234); y
            4 + 5 + 4*5^2 + 4*5^3 + 5^4 + O(5^10)
            sage: y.expansion()
            [4, 1, 4, 4, 1, 0, 0, 0, 0, 0]
        """
        if lift_mode != 'simple':
            raise NotImplementedError("Other modes than 'simple' are not implemented yet")
        prec = self.precision_absolute()
        val = self.valuation()
        expansion = self._value.list(prec)
        if n is not None:
            if n < val:
                return ZZ(0)
            try:
                return expansion[n-val]
            except KeyError:
                raise PrecisionError("The digit in position %s is not determined" % n)
        if start_val is None:
            if self._parent.is_field():
                start_val = val
            else:
                start_val = 0
        if start_val > val:
            return expansion[start_val-val:]
        else:
            return (val-start_val)*[ZZ(0)] + expansion
Пример #4
0
    def _basic_integral(self, a, j):
        r"""
        Return `\int_{a+pZ_p} (z-{a})^j d\Phi(0-infty)`.

        See formula in section 9.2 of [PS2011]_

        INPUT:

        - ``a`` -- integer in range(p)
        - ``j`` -- integer in range(self.symbol().precision_relative())

        EXAMPLES::

            sage: from sage.modular.pollack_stevens.padic_lseries import pAdicLseries
            sage: E = EllipticCurve('11a3')
            sage: L = E.padic_lseries(5, implementation="pollackstevens", precision=4) #long time
            sage: L._basic_integral(1,2) # long time
            2*5^2 + 5^3 + O(5^4)
        """
        symb = self.symbol()
        M = symb.precision_relative()
        if j > M:
            raise PrecisionError("Too many moments requested")
        p = self.prime()
        ap = symb.Tq_eigenvalue(p)
        D = self._quadratic_twist
        ap = ap * kronecker(D, p)
        K = pAdicField(p, M)
        symb_twisted = symb.evaluate_twisted(a, D)
        return sum(
            binomial(j, r) * ((a - ZZ(K.teichmuller(a)))**(j - r)) *
            (p**r) * symb_twisted.moment(r) for r in range(j + 1)) / ap
Пример #5
0
    def __call__(self, x):
        r"""
        Evaluate this character at an element of `\ZZ_p^\times`.

        EXAMPLES:

        Exact answers are returned when this is possible::

            sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, QQ).0)
            sage: kappa(1)
            1
            sage: kappa(0)
            0
            sage: kappa(12)
            -106993205379072
            sage: kappa(-1)
            -1
            sage: kappa(13 + 4*29 + 11*29^2 + O(29^3))
            9 + 21*29 + 27*29^2 + O(29^3)

        When the character chi is defined over a p-adic field, the results returned are inexact::

            sage: kappa = pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0^14)
            sage: kappa(1)
            1 + O(29^20)
            sage: kappa(0)
            0
            sage: kappa(12)
            17 + 11*29 + 7*29^2 + 4*29^3 + 5*29^4 + 2*29^5 + 13*29^6 + 3*29^7 + 18*29^8 + 21*29^9 + 28*29^10 + 28*29^11 + 28*29^12 + 28*29^13 + 28*29^14 + 28*29^15 + 28*29^16 + 28*29^17 + 28*29^18 + 28*29^19 + O(29^20)
            sage: kappa(12) == -106993205379072
            True
            sage: kappa(-1) == -1
            True
            sage: kappa(13 + 4*29 + 11*29^2 + O(29^3))
            9 + 21*29 + 27*29^2 + O(29^3)
        """
        if isinstance(x, pAdicGenericElement):
            if x.parent().prime() != self._p:
                raise TypeError("x must be an integer or a %s-adic integer" %
                                self._p)
            if self._p**(x.precision_absolute()) < self._chi.conductor():
                raise PrecisionError("Precision too low")
            xint = x.lift()
        else:
            xint = x
        if (xint % self._p == 0):
            return 0
        return self._chi(xint) * x**self._k
Пример #6
0
    def _div_(self, other):
        r"""
        Return the quotient of this element and ``other``.

        NOTE::

        The result of division always lives in the fraction field,
        even if the element to be inverted is a unit.

        EXAMPLES::

            sage: R = ZpLC(19)
            sage: a = R(-1, 5); a
            18 + 18*19 + 18*19^2 + 18*19^3 + 18*19^4 + O(19^5)
            sage: b = R(-5/2, 5); b
            7 + 9*19 + 9*19^2 + 9*19^3 + 9*19^4 + O(19^5)
 
            sage: c = a / b   # indirect doctest
            sage: c
            8 + 11*19 + 7*19^2 + 11*19^3 + 7*19^4 + O(19^5)
            sage: c.parent()
            19-adic Field with lattice-cap precision

            sage: a / (19*b)
            8*19^-1 + 11 + 7*19 + 11*19^2 + 7*19^3 + O(19^4)

        If the division is indistinguishable from zero, an error is raised::

            sage: c = a / (2*b + 5)
            Traceback (most recent call last):
            ...
            PrecisionError: cannot divide by something indistinguishable from zero
        """
        if other.is_zero():
            raise PrecisionError(
                "cannot divide by something indistinguishable from zero")
        x_self = self._value
        x_other = other._value
        x = x_self / x_other
        # dx = (1/other)*dself - (self/other^2)*dother
        dx = [[self, self._parent._approx_one / x_other],
              [other, -x_self / (x_other * x_other)]]
        return self.__class__(self._parent.fraction_field(),
                              x,
                              dx=dx,
                              check=False)
Пример #7
0
    def valuation(self, secure=False):
        r"""
        Return the valuation of this element.

        INPUT:

        - ``secure`` -- a boolean (default: ``False``); when ``True``,
          an error is raised if the precision on the element is not
          enough to determine for sure its valuation; otherwise the
          absolute precision (which is the smallest possible valuation)
          is returned

        EXAMPLES::

            sage: R = ZpLC(2)
            sage: x = R(12, 10); x
            2^2 + 2^3 + O(2^10)
            sage: x.valuation()
            2

            sage: y = x - x; y
            O(2^40)
            sage: y.valuation()
            40
            sage: y.valuation(secure=True)
            Traceback (most recent call last):
            ...
            PrecisionError: Not enough precision
        """
        p = self._parent.prime()
        val = self._value.valuation()
        prec = self.precision_absolute()
        if val < prec:
            return val
        elif secure:
            raise PrecisionError("Not enough precision")
        else:
            return prec
Пример #8
0
def upper_bound_tate(cp, frob_matrix, precision, over_Qp=False, pedantic=True):
    """
    Return a upper bound for Tate classes over characteristic 0
    TODO: improove documentation
    """
    p = cp.list()[-1].prime_factors()[0]
    # it would be nice to use QpLF
    OK = ZpCA(p, prec=precision)

    # adjust precision
    frob_matrix = matrix(OK, frob_matrix)

    # get the p-adic eigenvalues
    _, _, cyc_factorization = rank_fieldextension(cp)

    # a bit hacky
    val = [
        min(elt.valuation() for elt in col) for col in frob_matrix.columns()
    ]
    projection_cols = frob_matrix.ncols() - val.index(0)
    assert set(val[-projection_cols:]) == {0}
    # P1 = | zero matrix |
    #      | Identity    |
    P1 = zero_matrix(frob_matrix.ncols() - projection_cols,
                     projection_cols).stack(identity_matrix(projection_cols))
    # computing a kernel, either via smith form or howell form
    # involves some kind of gauss elimination,
    # and thus having the columns with lowest valuation first improves
    # the numerical stability of the algorithms
    P1.reverse_rows_and_columns()
    frob_matrix.reverse_rows_and_columns()

    @cached_function
    def frob_power(k):
        if k == 0:
            return identity_matrix(frob_matrix.ncols())
        elif k == 1:
            return frob_matrix
        else:
            return frob_matrix * frob_power(k - 1)

    factor_i = []
    dim_Ti = []
    obsi = []
    dim_Li = []
    for cyc_fac, cyc_exp in cyc_factorization:
        factor_i.append((cyc_fac, cyc_exp))
        Ti = matrix(0, frob_matrix.ncols())
        obsij = []
        dim_Tij = []
        dim_Lij = []
        for fac, exp in tate_factor_Zp(cyc_fac):
            # the rows of Tij are a basis for Tij
            # the 'computed' argument avoids echelonizing the kernel basis
            # which might induce some precision loss on the projection
            #Tij = fac(frob_matrix).right_kernel_matrix(basis='computed')

            # computing the right kernel with smith form
            # howell form or strong echelon could also be good options
            Tij = padic_right_kernel_matrix(fac(frob_matrix))
            if Tij.nrows() != fac.degree() * exp * cyc_exp:
                raise PrecisionError(
                    "Number of eigenvectors (%d) doesn't match the number of eigenvalues (%d), increasing  precision should solve this"
                    % (Tij.nrows(), fac.degree() * exp * cyc_exp))
            if over_Qp:
                dim_Tij.append(Tij.nrows())
                obs_map = Tij * P1
                rank_obs_ij = obs_map.rank()
                obsij.append(rank_obs_ij)
                Lijmatrix = matrix(Tij.base_ring(), Tij.nrows(), 0)
                for ell in range(fac.degree()):
                    Lijmatrix = Lijmatrix.augment(
                        Tij * frob_power(ell).transpose() * P1)
                # Lij = right_kernel(K) subspace of Tij that is invariant under Frob and unobstructed
                Krank = Lijmatrix.rank()
                dim_Lij.append(Tij.nrows() - Krank)
                if dim_Lij[-1] % fac.degree() != 0:
                    old_dim = dim_Li[-1]
                    deg = fac.degree()
                    new_dim = dim_Li[-1] = deg * (old_dim // deg)
                    if pedantic:
                        warnings.warn(
                            "rounding dimension of Li from %d to %d for factor = %s"
                            % (old_dim, new_dim, fac))

            Ti = Ti.stack(Tij)

        if over_Qp:
            dim_Ti.append(dim_Tij)
            obsi.append(obsij)
            dim_Li.append(dim_Lij)
        else:
            obs_map = Ti * P1
            if Ti.nrows() != cyc_fac.degree() * cyc_exp:
                raise PrecisionError(
                    "Number of eigenvectors (%d) doesn't match the number of eigenvalues (%d), increasing  precision should solve this"
                    % (Tij.nrows(), cyc_fac.degree() * cyc_exp))
            dim_Ti.append(Ti.nrows())
            rank_obs_i = padic_rank(obs_map)
            obsi.append(Ti.nrows() - rank_obs_i)
            Limatrix = matrix(Ti.base_ring(), Ti.nrows(), 0)
            for ell in range(0, cyc_fac.degree()):
                Limatrix = Limatrix.augment(Ti * frob_power(ell).transpose() *
                                            P1)
            #print(Limatrix.smith_form(exact=False, integral=True, transformation=False).diagonal())
            # Li = right_kernel(K) subspace of Tij that is invariant under Frob and unobstructed
            Krank = padic_rank(Limatrix)
            dim_Li.append(Ti.nrows() - Krank)
            if dim_Li[-1] % cyc_fac.degree() != 0:
                old_dim = dim_Li[-1]
                deg = cyc_fac.degree()
                new_dim = dim_Li[-1] = deg * (old_dim // deg)
                if pedantic:
                    warnings.warn(
                        "rounding dimension of Li from %d to %d for cyc_factor = %s"
                        % (old_dim, new_dim, cyc_fac))
    return factor_i, dim_Ti, obsi, dim_Li,
Пример #9
0
    def residue(self, absprec=1, field=None, check_prec=True):
        r"""
        Reduces this element modulo `p^{\mathrm{absprec}}`.

        INPUT:

        - ``absprec`` -- a non-negative integer (default: ``1``)

        - ``field`` -- boolean (default ``None``).  Whether to return an element of GF(p) or Zmod(p).

        - ``check_prec`` -- boolean (default ``True``).  Whether to raise an error if this
          element has insufficient precision to determine the reduction.

        OUTPUT:

        This element reduced modulo `p^\mathrm{absprec}` as an element of
        `\ZZ/p^\mathrm{absprec}\ZZ`

        EXAMPLES::

            sage: R = ZpLC(7,4)
            sage: a = R(8)
            sage: a.residue(1)
            1

        TESTS::

            sage: R = ZpLC(7,4)
            sage: a = R(8)
            sage: a.residue(0)
            0
            sage: a.residue(-1)
            Traceback (most recent call last):
            ...
            ValueError: cannot reduce modulo a negative power of p.
            sage: a.residue(5)
            Traceback (most recent call last):
            ...
            PrecisionError: not enough precision known in order to compute residue.
            sage: a.residue(5, check_prec=False)
            8

            sage: a.residue(field=True).parent()
            Finite Field of size 7
        """
        if not isinstance(absprec, Integer):
            absprec = Integer(absprec)
        if check_prec and absprec > self.precision_absolute():
            raise PrecisionError(
                "not enough precision known in order to compute residue.")
        elif absprec < 0:
            raise ValueError("cannot reduce modulo a negative power of p.")
        if self.valuation() < 0:
            raise ValueError(
                "element must have non-negative valuation in order to compute residue."
            )
        if field is None:
            field = (absprec == 1)
        elif field and absprec != 1:
            raise ValueError("field keyword may only be set at precision 1")
        p = self._parent.prime()
        if field:
            from sage.rings.finite_rings.finite_field_constructor import GF
            ring = GF(p)
        else:
            from sage.rings.finite_rings.integer_mod_ring import Integers
            ring = Integers(p**absprec)
        return ring(self.value())
Пример #10
0
    def factor(self):
        """
        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 + (0 + 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

        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)))
        """
        if self == 0:
            raise ArithmeticError("factorization of 0 not defined")
        # 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())
Пример #11
0
    def _roots(self, secure, minval, hint):
        """
        Return the roots of this polynomial whose valuation is
        at least ``minval``.

        This is a helper method for :meth:`roots`.
        It is not meant to be called directly.

        INPUT:

        - ``secure`` -- a boolean; whether we raise an error or
          not in case of multiple roots

        - ``minval`` -- an integer

        - ``hint`` -- a list or ``None``; if given, it must be the
          list of roots of the residual polynomial of slope ``minval``

        OUTPUT:

        A list of pairs ``(root, multiplicity)``

        TESTS::

            sage: R = Zp(2)
            sage: S.<x> = R[]
            sage: P = (x-1) * (x-2) * (x-4) * (x-8) * (x-16)
            sage: Q = P^2
            sage: Q.roots(algorithm="sage")  # indirect doctest
            [(2^4 + O(2^14), 2),
             (2^3 + O(2^13), 2),
             (2^2 + O(2^12), 2),
             (2 + O(2^11), 2),
             (1 + O(2^10), 2)]

        """
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        K = self.base_ring()
        Pk = PolynomialRing(K.residue_field(), names='xbar')
        x = self.parent().gen()

        # Trivial cases
        if self.degree() == 0:
            return [ ]
        if self.degree() == 1:
            return [ (-self[0]/self[1], 1) ]

        # We consider the case where zero is a (possibly multiple) root
        i = 0
        while self[i] == 0:
            i += 1
        if secure and i > 1:
            raise PrecisionError("not enough precision to determine the number of roots")
        if i == 0:
            roots = [ ]
            P = self
        else:
            vali = self[i].valuation()
            prec = min((self[j].precision_absolute()-vali) / (i-j) for j in range(i))
            if prec is not Infinity:
                prec = prec.ceil()
            roots = [ (K(0,prec), i) ]
            P = self // self[:i+1]  # we do not shift because we need to track precision here

        # We use Newton polygon and slope factorisation to find roots
        vertices = P.newton_polygon().vertices(copy=False)
        deg = 0
        for i in range(1, len(vertices)):
            deg_left, val_left = vertices[i-1]
            deg_right, val_right = vertices[i]
            slope = (val_right - val_left) / (deg_left - deg_right)
            if slope not in ZZ or slope < minval:
                continue
            if hint is not None and slope == minval:
                rootsbar = hint
                if not rootsbar: continue
            if i < len(vertices) - 1:
                F = P._factor_of_degree(deg_right - deg)
                P = P // F
            else:
                F = P
            if deg < deg_left:
                G = F._factor_of_degree(deg_left - deg)
                F //= G
            deg = deg_right
            val = F[0].valuation()
            if hint is None or slope != minval:
                Fbar = Pk([ F[j] >> (val - j*slope) for j in range(F.degree()+1) ])
                rootsbar = [ r for (r, _) in Fbar.roots() ]
                if not rootsbar: continue
            rbar = rootsbar.pop()
            shift = K(rbar).lift_to_precision() << slope  # probably we should choose a better lift
            roots += [(r+shift, m) for (r, m) in F(x+shift)._roots(secure, slope, [r-rbar for r in rootsbar])]  # recursive call
        return roots