示例#1
0
文件: kplt.py 项目: joelypoley/kplt
def element_of_norm(M, O, bound=100):
    """Finds an element of B with norm M.

    This corresponds to Step 3 of the algorithm in the notes.

    Args:
        M: A sage integer.
        O: A maximal order in a quaternion algebra.
        bound: The values 0 <= y, z <= bound will be tried. If no solution
            is found then the function returns None.

    Returns:
        gamma in B such that gamma.reduced_norm() == M or None if there is no
        solution in the box [0, bound]**2.
    """
    B = O.quaternion_algebra()
    a, b = B.invariants()
    i, j, k = B.gens()
    q, p = -Integer(a), -Integer(b)
    for y in range(bound + 1):
        for z in range(bound + 1):
            r = M - p * (y**2 + q * z**2)

            if not is_prime(r):  # Can replace with easily factorizable.
                continue

            # The norm equation is N(x + iy) = x^2 + qy^2.
            sol = solve_norm_equation(q, r)
            if sol is not None:
                t, x = sol
                gamma = t + x * i + y * j + z * k
                assert Integer((gamma).reduced_norm()) == M
                return gamma

    return None
示例#2
0
文件: kplt.py 项目: joelypoley/kplt
def solve_ideal_equation(gamma, I, D, N, O):
    """Find mu_0 in Rj such that (O* gamma / NO)[mu_0] = I / NO.

    Args:
        gamma: An element of O.
        I: A left O-ideal.
        D: The index [O : R + Rj].
        N: The norm of I. Must be prime.
        O: An order in a rational quaternion algebra containing 1, i, j, k.

    Returns:
        mu_0 in Rj such that 0 != gamma * mu_0 in I.
    """
    assert N == I.norm()
    assert is_prime(N)

    # d = D*c + N*_
    d, c, _ = xgcd(D, N)
    assert d == 1

    a, b = [Integer(x) for x in O.quaternion_algebra().invariants()]

    F = GF(N)
    # The suffix _ff means that this element is over a finite field, not the
    # rationals.
    B_ff = QuaternionAlgebra(F, a, b)
    i_ff, j_ff, k_ff = B_ff.gens()

    # phi is homomorphism O -> (a, b | F) with kernel NO.
    def phi(alpha):
        # alpha + NO = alpha_prime + NO.
        alpha_prime = D * c * alpha
        t, x, y, z = [
            Integer(coeff) for coeff in alpha_prime.coefficient_tuple()
        ]
        return t + x * i_ff + y * j_ff + z * k_ff

    gamma_ff = phi(gamma)
    gamma_ff_mat = gamma_ff.matrix(action="left")
    I_basis_ff = [phi(alpha).coefficient_tuple() for alpha in I.basis()]
    # Only use 2nd and 3rd rows of gamma_ff_mat so solution is in Rj.
    lin_system = matrix(F, [gamma_ff_mat[2], gamma_ff_mat[3]] + I_basis_ff)
    sol = lin_system.left_kernel().basis()[0]
    y, z = sol[0], sol[1]
    mu_ff = y * j_ff + z * k_ff
    assert vector(F, (gamma_ff * mu_ff).coefficient_tuple()) in span(
        I_basis_ff, F)

    B = O.quaternion_algebra()
    i, j, k = B.gens()
    mu_0 = sum(
        Integer(coeff) * elem
        for coeff, elem in zip(mu_ff.coefficient_tuple(), [1, i, j, k]))

    assert 0 != mu_0
    assert gamma * mu_0 in I

    return mu_0
示例#3
0
文件: kplt.py 项目: joelypoley/kplt
def ell_power_equiv(J, O, ell, print_progress=False):
    """Solve ell isogeny problem.

    This function only works in quaternion algebras with prime discriminant
    p = 3 mod 4.

    Args:
        J: A left O-ideal.
        O: An order in a quaternion algebra.
        ell: A prime.
        print_progress: True if you want to print progress.

    Returns:
        A pair (J, delta) where J = I*delta, delta is in the quaternion algebra
        and J is a nonfractional ideal in the same class as I that has ell
        power norm.
    """
    B = O.quaternion_algebra()
    if not is_prime(B.discriminant()) or not mod(B.discriminant(), 4) == 3:
        raise NotImplementedError("The quaternion algebra must have prime"
                                  " discrimint p = 3 mod 4.")

    # When B has discriminant p = 3 mod 4 the call B.maximal_order() always
    # returns the first maximal order in the cases environment in Lemma 2 of
    # the paper. I probably shouldn't rely on this.
    O_special = B.maximal_order()
    I = connecting_ideal(O_special, O)
    K = I * J
    I_1, gamma_1 = special_ell_power_equiv(I, O_special, ell)
    I_2, gamma_2 = special_ell_power_equiv(K, O_special, ell)
    gamma = gamma_1.conjugate() * gamma_2 * I.norm()
    J_2 = J.scale(gamma)
    assert J_2.left_order() == O
    assert Integer(J_2.norm()).prime_factors() == [ell]
    assert [x in O for x in J_2.basis()]
    return J_2, gamma
示例#4
0
文件: kplt.py 项目: joelypoley/kplt
def strong_approximation(mu_0, N, O, ell):
    """Find mu in O with nrd(mu) = ell^e and mu = lambda * mu_0 mod NO

    Args:
        mu_0: An element of Rj.
        N: A prime.
        O: An order contaning 1, i, j, k.
        ell: A prime.

    Returns:
        mu in O with nrd(mu) = ell^e and mu = lambda * mu_0 mod NO.
    """
    ell = Integer(ell)
    N = Integer(N)
    B = O.quaternion_algebra()
    p = B.discriminant()
    i, j, k = B.gens()
    t_0, x_0, y_0, z_0 = mu_0.coefficient_tuple()
    assert t_0 == x_0 == 0
    beta_0 = y_0 + z_0 * i
    # TODO: gracefully handle the case where
    # ~mod(p * Integer(beta_0.reduced_norm()), N) does not exist.
    e = 2 * ceil(log(N**4 * (p + 1) / 2, ell)) + (0 if (
        ~mod(p * Integer(beta_0.reduced_norm()), N)).is_square() else 1)
    e_max = e + 2 * (2 * ceil(log(p, ell)) + 2)

    # First we solve for lambda.
    lamb = Integer(
        (ell**e * ~mod(p * Integer(beta_0.reduced_norm()), N)).sqrt())
    assert mod(ell**e, N) == mod(lamb**2 * Integer(mu_0.reduced_norm()), N)

    mu = None
    count = 0
    count_max = 5 * e
    while e < e_max:
        # Then we solve for beta_1.
        lhs = Integer(
            (ell**e - p * lamb**2 * Integer(beta_0.reduced_norm())) / N)
        y_1, z_1 = solve_linear_congruence(2 * Integer(y_0) * p * lamb,
                                           p * lamb * 2 * z_0, lhs, N)
        y_1 = center_around(y_1, -2 * lamb * y_0, N)
        z_1 = center_around(z_1, -2 * lamb * y_0, N)
        beta_1 = Integer(y_1) + Integer(z_1) * i
        assert mod(lhs, N) == mod(
            p * lamb * (beta_0 * beta_1.conjugate()).reduced_trace(), N)

        # Now we calculate r.
        r = Integer(
            (ell**e - p * (lamb * beta_0 + N * beta_1).reduced_norm()) / N**2)

        # In the paper they say that r can be the product of a prime and a
        # smooth square. For simplicity I will just wait for r prime.
        if not is_prime(r):
            count += 1
            if count > count_max:
                print("Increasing", e, e_max)
                e += 2
                count_max = 0

            continue

        sol = solve_norm_equation(1, r)
        if sol is not None:
            t_1, x_1 = sol
            alpha_1 = t_1 + x_1 * i
            mu_1 = alpha_1 + beta_1 * j
            mu = lamb * mu_0 + N * mu_1
            assert mu - lamb * mu_0 in O.left_ideal(O.basis()).scale(N)
            assert mu.reduced_norm() == ell**e
            return mu

        # This is sort of a random heuristic. Can do better.
        count += 1
        if count > count_max:
            e += 2
            count_max = 0

    return None
示例#5
0
文件: kplt.py 项目: joelypoley/kplt
def prime_norm_representative(I, O, D, ell):
    """
    Given an order O and a left O-ideal I return another
    left O-ideal J in the same class, but with prime norm.

    This corresponds to Step 1 in the notes. So given an ideal I it returns
    an ideal in the same class but with reduced norm N where N != ell is
    a large prime coprime to both D and p, and ell is a quadratic
    nonresidue module N.


    Args:
        I: A left O-ideal.
        O: An order in a quaternion algebra.
        D: An integer.
        ell: A prime.
    Returns:
        A pair (J, gamma) where J = I * gamma is a left O-ideal in the same
        class with prime norm N. N will be coprime to both D and p, and ell
        will be a nonquadratic residue module N.
    """
    # TODO: Change so O is not an argument.
    if not is_minkowski_basis(I.basis()):
        print("Warning: The ideal I does not have a minkowski basis"
              " precomputed and Sage can not do it for you.")

    nrd_I = I.norm()
    B = I.quaternion_algebra()
    p = B.discriminant()
    alpha = B(0)
    normalized_norm = Integer(alpha.reduced_norm() / nrd_I)
    # Choose random elements in I until one is found with norm N*nrd(I) where N
    # is prime.
    m_power = 3
    m = Integer(2)**m_power  # TODO: Change this to a proper bound.
    count = 0
    while (not is_prime(normalized_norm) or normalized_norm.divides(D)
           or normalized_norm == ell or normalized_norm == p
           or mod(ell, normalized_norm).is_square()):
        # Make a new random element.
        alpha = random_combination(I.basis(), bound=m)
        normalized_norm = Integer(alpha.reduced_norm() / nrd_I)

        # Increase the box we search in if we've been trying for too long. Note
        # this was just a random heuristic I came up with, it's not in the
        # paper.
        count += 1
        if count > 4 * m_power:
            m_power += 1
            m = Integer(2)**m_power
            count = 0

    # We now have an element alpha with norm N*nrd(I) where N is prime. The
    # ideal J = I*gamma has prime norm where gamma = conjugate(alpha) / nrd(I).
    gamma = alpha.conjugate() / nrd_I
    J = I.scale(gamma)

    assert is_prime(Integer(J.norm()))
    assert not mod(ell, Integer(J.norm())).is_square()
    assert gcd(Integer(J.norm()), D) == 1
    return J, gamma
示例#6
0
    def hilbert_symbol_negative_at_S(self, S, b, check=True):
        r"""
        Returns an integer that has a negative Hilbert symbol with respect
        to a given rational number and a given set of primes (or places).

        The function is algorithm 3.4.1 in [Kir2016]_. It finds an integer `a`
        that has negative Hilbert symbol with respect to a given rational number
        exactly at a given set of primes (or places).

        INPUT:

        - ``S`` -- a list of rational primes, the infinite place as real
          embedding of `\QQ` or as -1
        - ``b`` -- a non-zero rational number which is a non-square locally
          at every prime in ``S``.
        - ``check`` -- ``bool`` (default:``True``) perform additional checks on
          input and confirm the output.

        OUTPUT:

        - An integer `a` that has negative Hilbert symbol `(a,b)_p` for
          every place `p` in `S` and no other place.

        EXAMPLES::

            sage: QQ.hilbert_symbol_negative_at_S([-1,5,3,2,7,11,13,23], -10/7)
            -9867
            sage: QQ.hilbert_symbol_negative_at_S([3, 5, QQ.places()[0], 11], -15)
            -33
            sage: QQ.hilbert_symbol_negative_at_S([3, 5], 2)
            15

        TESTS::

            sage: QQ.hilbert_symbol_negative_at_S(5/2, -2)
            Traceback (most recent call last):
            ...
            TypeError: first argument must be a list or integer

        ::

            sage: QQ.hilbert_symbol_negative_at_S([1, 3], 0)
            Traceback (most recent call last):
            ...
            ValueError: second argument must be nonzero

        ::

            sage: QQ.hilbert_symbol_negative_at_S([-1, 3, 5], 2)
            Traceback (most recent call last):
            ...
            ValueError: list should be of even cardinality

        ::

            sage: QQ.hilbert_symbol_negative_at_S([1, 3], 2)
            Traceback (most recent call last):
            ...
            ValueError: all entries in list must be prime or -1 for
            infinite place

        ::

            sage: QQ.hilbert_symbol_negative_at_S([5, 7], 2)
            Traceback (most recent call last):
            ...
            ValueError: second argument must be a nonsquare with
            respect to every finite prime in the list

        ::

            sage: QQ.hilbert_symbol_negative_at_S([1, 3], sqrt(2))
            Traceback (most recent call last):
            ...
            TypeError: second argument must be a rational number

        ::

            sage: QQ.hilbert_symbol_negative_at_S([-1, 3], 2)
            Traceback (most recent call last):
            ...
            ValueError: if the infinite place is in the list, the second
            argument must be negative

        AUTHORS:

        - Simon Brandhorst, Juanita Duque, Anna Haensch, Manami Roy, Sandi Rudzinski (10-24-2017)

        """
        from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF
        from sage.rings.padics.factory import Qp
        from sage.modules.free_module import VectorSpace
        from sage.matrix.constructor import matrix
        from sage.sets.primes import Primes
        from sage.arith.misc import hilbert_symbol, is_prime

        # input checks
        if not type(S) is list:
            raise TypeError("first argument must be a list or integer")
        # -1 is used for the infinite place
        infty = -1
        for i in range(len(S)):
            if S[i] == self.places()[0]:
                S[i] = -1
        if not b in self:
            raise TypeError("second argument must be a rational number")
        b = self(b)
        if b == 0:
            raise ValueError("second argument must be nonzero")
        if len(S) % 2:
            raise ValueError("list should be of even cardinality")
        for p in S:
            if p != infty:
                if check and not is_prime(p):
                    raise ValueError("all entries in list must be prime"
                                     " or -1 for infinite place")
                R = Qp(p)
                if R(b).is_square():
                    raise ValueError(
                        "second argument must be a nonsquare with"
                        " respect to every finite prime in the list")
            elif b > 0:
                raise ValueError("if the infinite place is in the list, "
                                 "the second argument must be negative")
        # L is the list of primes that we need to consider, b must have
        # nonzero valuation for each prime in L, this is the set S'
        # in Kirschmer's algorithm
        L = []
        L = [p[0] for p in b.factor() if p[0] not in S]
        # We must also consider 2 to be in L
        if 2 not in L and 2 not in S:
            L.append(2)
        # This adds the infinite place to L
        if b < 0 and infty not in S:
            L.append(infty)

        P = S + L
        # This constructs the vector v in the algorithm. This is the vector
        # that we are searching for. It represents the case when the Hilbert
        # symbol is negative for all primes in S and positive
        # at all primes in S'
        V = VectorSpace(GF(2), len(P))
        v = V([1] * len(S) + [0] * len(L))

        # Compute the map phi of Hilbert symbols at all the primes
        # in S and S'
        # For technical reasons, a Hilbert symbol of -1 is
        # respresented as 1 and a Hilbert symbol of 1
        # is represented as 0
        def phi(x):
            v = [(1 - hilbert_symbol(x, b, p)) // 2 for p in P]
            return V(v)

        M = matrix(GF(2), [phi(p) for p in P + [-1]])
        # We search through all the primes
        for q in Primes():
            # Only look at this prime if it is not in our list
            if q in P:
                continue

            # The algorithm terminates when the vector v is in the
            # subspace of V generated by the image of the phi map
            # on the set of generators
            w = phi(q)
            W = M.stack(matrix(w))
            if v in W.row_space():
                break
        Pq = P + [-1] + [q]
        l = W.solve_left(v)
        a = self.prod([Pq[i]**ZZ(l[i]) for i in range(l.degree())])
        if check:
            assert phi(a) == v, "oops"
        return a
示例#7
0
    def euler_factor(self, t, p, cache_p=False):
        """
        Return the Euler factor of the motive `H_t` at prime `p`.

        INPUT:

        - `t` -- rational number, not 0 or 1

        - `p` -- prime number of good reduction

        OUTPUT:

        a polynomial

        See [Benasque2009]_ for explicit examples of Euler factors.

        For odd weight, the sign of the functional equation is +1. For even
        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_.

        EXAMPLES::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
            sage: H.euler_factor(-1, 5)
            15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1

            sage: H = Hyp(gamma_list=[-6,-1,4,3])
            sage: H.weight(), H.degree()
            (1, 2)
            sage: t = 189/125
            sage: [H.euler_factor(1/t,p) for p in [11,13,17,19,23,29]]
            [11*T^2 + 4*T + 1,
            13*T^2 + 1,
            17*T^2 + 1,
            19*T^2 + 1,
            23*T^2 + 8*T + 1,
            29*T^2 + 2*T + 1]

            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
            sage: H.weight(), H.degree()
            (2, 3)
            sage: [H.euler_factor(1/4,p) for p in [5,7,11,13,17,19]]
            [125*T^3 + 20*T^2 + 4*T + 1,
             343*T^3 - 42*T^2 - 6*T + 1,
             -1331*T^3 - 22*T^2 + 2*T + 1,
             -2197*T^3 - 156*T^2 + 12*T + 1,
             4913*T^3 + 323*T^2 + 19*T + 1,
             6859*T^3 - 57*T^2 - 3*T + 1]

            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
            sage: H.weight(), H.degree()
            (2, 4)
            sage: t = -5
            sage: [H.euler_factor(1/t,p) for p in [11,13,17,19,23,29]]
            [-14641*T^4 - 1210*T^3 + 10*T + 1,
             -28561*T^4 - 2704*T^3 + 16*T + 1,
             -83521*T^4 - 4046*T^3 + 14*T + 1,
             130321*T^4 + 14440*T^3 + 969*T^2 + 40*T + 1,
             279841*T^4 - 25392*T^3 + 1242*T^2 - 48*T + 1,
             707281*T^4 - 7569*T^3 + 696*T^2 - 9*T + 1]

        This is an example of higher degree::

            sage: H = Hyp(cyclotomic=([11], [7, 12]))
            sage: H.euler_factor(2, 13)
            371293*T^10 - 85683*T^9 + 26364*T^8 + 1352*T^7 - 65*T^6 + 394*T^5 - 5*T^4 + 8*T^3 + 12*T^2 - 3*T + 1
            sage: H.euler_factor(2, 19) # long time
            2476099*T^10 - 651605*T^9 + 233206*T^8 - 77254*T^7 + 20349*T^6 - 4611*T^5 + 1071*T^4 - 214*T^3 + 34*T^2 - 5*T + 1

        TESTS::

             sage: H1 = Hyp(alpha_beta=([1,1,1],[1/2,1/2,1/2]))
             sage: H2 = H1.swap_alpha_beta()
             sage: H1.euler_factor(-1, 3)
             27*T^3 + 3*T^2 + T + 1
             sage: H2.euler_factor(-1, 3)
             27*T^3 + 3*T^2 + T + 1
             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3],[1/2,1/5,2/5,3/5,4/5]))
             sage: H.euler_factor(5,7)
             16807*T^5 - 686*T^4 - 105*T^3 - 15*T^2 - 2*T + 1

        Check for precision downsampling::

            sage: H = Hyp(cyclotomic=[[3],[4]])
            sage: H.euler_factor(2, 11, cache_p=True)
            11*T^2 - 3*T + 1
            sage: H = Hyp(cyclotomic=[[12],[1,2,6]])
            sage: H.euler_factor(2, 11, cache_p=True)
            -T^4 + T^3 - T + 1

        REFERENCES:

        - [Roberts2015]_
        - [Watkins]_
        """
        if t not in QQ or t in [0, 1]:
            raise ValueError('wrong t')
        alpha = self._alpha
        if 0 in alpha:
            return self._swap.euler_factor(~t, p)

        if not is_prime(p):
            raise ValueError('p not prime')
        if not all(x.denominator() % p for x in self._alpha + self._beta):
            raise NotImplementedError('p is wild')
        if (t.valuation(p) or (t - 1).valuation(p) > 0):
            raise NotImplementedError('p is tame')
        # now p is good
        d = self.degree()
        bound = d // 2
        traces = [self.padic_H_value(p, i + 1, t, cache_p=cache_p)
                  for i in range(bound)]

        w = self.weight()
        sign = self.sign(t, p)
        return characteristic_polynomial_from_traces(traces, d, p, w, sign)
    def euler_factor(self, t, p, degree=0):
        """
        Return the Euler factor of the motive `H_t` at prime `p`.

        INPUT:

        - `t` -- rational number, not 0 or 1

        - `p` -- prime number of good reduction

        - ``degree`` -- optional integer (default 0)

        OUTPUT:

        a polynomial

        See [Benasque2009]_ for explicit examples of Euler factors.

        For odd weight, the sign of the functional equation is +1. For even
        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_.

        EXAMPLES::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
            sage: H.euler_factor(-1, 5)
            15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1

            sage: H = Hyp(gamma_list=[-6,-1,4,3])
            sage: H.weight(), H.degree()
            (1, 2)
            sage: t = 189/125
            sage: [H.euler_factor(1/t,p) for p in [11,13,17,19,23,29]]
            [11*T^2 + 4*T + 1,
            13*T^2 + 1,
            17*T^2 + 1,
            19*T^2 + 1,
            23*T^2 + 8*T + 1,
            29*T^2 + 2*T + 1]

            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
            sage: H.weight(), H.degree()
            (2, 3)
            sage: [H.euler_factor(1/4,p) for p in [5,7,11,13,17,19]]
            [125*T^3 + 20*T^2 + 4*T + 1,
             343*T^3 - 42*T^2 - 6*T + 1,
             -1331*T^3 - 22*T^2 + 2*T + 1,
             -2197*T^3 - 156*T^2 + 12*T + 1,
             4913*T^3 + 323*T^2 + 19*T + 1,
             6859*T^3 - 57*T^2 - 3*T + 1]

            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
            sage: H.weight(), H.degree()
            (2, 4)
            sage: t = -5
            sage: [H.euler_factor(1/t,p) for p in [11,13,17,19,23,29]]
            [-14641*T^4 - 1210*T^3 + 10*T + 1,
             -28561*T^4 - 2704*T^3 + 16*T + 1,
             -83521*T^4 - 4046*T^3 + 14*T + 1,
             130321*T^4 + 14440*T^3 + 969*T^2 + 40*T + 1,
             279841*T^4 - 25392*T^3 + 1242*T^2 - 48*T + 1,
             707281*T^4 - 7569*T^3 + 696*T^2 - 9*T + 1]

        TESTS::

             sage: H1 = Hyp(alpha_beta=([1,1,1],[1/2,1/2,1/2]))
             sage: H2 = H1.swap_alpha_beta()
             sage: H1.euler_factor(-1, 3)
             27*T^3 + 3*T^2 + T + 1
             sage: H2.euler_factor(-1, 3)
             27*T^3 + 3*T^2 + T + 1
             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3],[1/2,1/5,2/5,3/5,4/5]))
             sage: H.euler_factor(5,7)
             16807*T^5 - 686*T^4 - 105*T^3 - 15*T^2 - 2*T + 1

        REFERENCES:

        - [Roberts2015]_
        - [Watkins]_
        """
        alpha = self._alpha
        if 0 in alpha:
            H = self.swap_alpha_beta()
            return(H.euler_factor(~t, p, degree))

        if t not in QQ or t in [0, 1]:
            raise ValueError('wrong t')
        if not is_prime(p):
            raise ValueError('p not prime')
        if not all(x.denominator() % p for x in self._alpha + self._beta):
            raise NotImplementedError('p is wild')
        if (t.valuation(p) or (t - 1).valuation(p) > 0):
            raise NotImplementedError('p is tame')
        # now p is good
        if degree == 0:
            d = self.degree()
        bound = d // 2
        traces = [self.padic_H_value(p, i + 1, t) for i in range(bound)]

        w = self.weight()

        if w % 2:  # sign is always +1 for odd weight
            sign = 1
        elif d % 2:
            sign = -kronecker_symbol((1 - t) * self._sign_param, p)
        else:
            sign = kronecker_symbol(t * (t - 1) * self._sign_param, p)

        return characteristic_polynomial_from_traces(traces, d, p, w, sign)
    def euler_factor(self, t, p, degree=0):
        """
        Return the Euler factor of the motive `H_t` at prime `p`.

        INPUT:

        - `t` -- rational number, not 0 or 1

        - `p` -- prime number of good reduction

        - ``degree`` -- optional integer (default 0)

        OUTPUT:

        a polynomial

        See [Benasque2009]_ for explicit examples of Euler factors.

        For odd weight, the sign of the functional equation is +1. For even
        weight, the sign is computed by a recipe found in 11.1 of [Watkins]_.

        EXAMPLES::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
            sage: H.euler_factor(-1, 5)
            15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1

            sage: H = Hyp(gamma_list=[-6,-1,4,3])
            sage: H.weight(), H.degree()
            (1, 2)
            sage: t = 189/125
            sage: [H.euler_factor(1/t,p) for p in [11,13,17,19,23,29]]
            [11*T^2 + 4*T + 1,
            13*T^2 + 1,
            17*T^2 + 1,
            19*T^2 + 1,
            23*T^2 + 8*T + 1,
            29*T^2 + 2*T + 1]

            sage: H = Hyp(cyclotomic=([6,2],[1,1,1]))
            sage: H.weight(), H.degree()
            (2, 3)
            sage: [H.euler_factor(1/4,p) for p in [5,7,11,13,17,19]]
            [125*T^3 + 20*T^2 + 4*T + 1,
             343*T^3 - 42*T^2 - 6*T + 1,
             -1331*T^3 - 22*T^2 + 2*T + 1,
             -2197*T^3 - 156*T^2 + 12*T + 1,
             4913*T^3 + 323*T^2 + 19*T + 1,
             6859*T^3 - 57*T^2 - 3*T + 1]

            sage: H = Hyp(alpha_beta=([1/12,5/12,7/12,11/12],[0,1/2,1/2,1/2]))
            sage: H.weight(), H.degree()
            (2, 4)
            sage: t = -5
            sage: [H.euler_factor(1/t,p) for p in [11,13,17,19,23,29]]
            [-14641*T^4 - 1210*T^3 + 10*T + 1,
             -28561*T^4 - 2704*T^3 + 16*T + 1,
             -83521*T^4 - 4046*T^3 + 14*T + 1,
             130321*T^4 + 14440*T^3 + 969*T^2 + 40*T + 1,
             279841*T^4 - 25392*T^3 + 1242*T^2 - 48*T + 1,
             707281*T^4 - 7569*T^3 + 696*T^2 - 9*T + 1]

        TESTS::

             sage: H1 = Hyp(alpha_beta=([1,1,1],[1/2,1/2,1/2]))
             sage: H2 = H1.swap_alpha_beta()
             sage: H1.euler_factor(-1, 3)
             27*T^3 + 3*T^2 + T + 1
             sage: H2.euler_factor(-1, 3)
             27*T^3 + 3*T^2 + T + 1
             sage: H = Hyp(alpha_beta=([0,0,0,1/3,2/3],[1/2,1/5,2/5,3/5,4/5]))
             sage: H.euler_factor(5,7)
             16807*T^5 - 686*T^4 - 105*T^3 - 15*T^2 - 2*T + 1

        REFERENCES:

        - [Roberts2015]_
        - [Watkins]_
        """
        alpha = self._alpha
        if 0 in alpha:
            H = self.swap_alpha_beta()
            return (H.euler_factor(~t, p, degree))

        if t not in QQ or t in [0, 1]:
            raise ValueError('wrong t')
        if not is_prime(p):
            raise ValueError('p not prime')
        if not all(x.denominator() % p for x in self._alpha + self._beta):
            raise NotImplementedError('p is wild')
        if (t.valuation(p) or (t - 1).valuation(p) > 0):
            raise NotImplementedError('p is tame')
        # now p is good
        if degree == 0:
            d = self.degree()
        bound = d // 2
        traces = [self.padic_H_value(p, i + 1, t) for i in range(bound)]

        w = self.weight()

        if w % 2:  # sign is always +1 for odd weight
            sign = 1
        elif d % 2:
            sign = -kronecker_symbol((1 - t) * self._sign_param, p)
        else:
            sign = kronecker_symbol(t * (t - 1) * self._sign_param, p)

        return characteristic_polynomial_from_traces(traces, d, p, w, sign)