r"""
Isogenies of small prime degree

Functions for the computation of isogenies of small primes degree. First: `l` =
2, 3, 5, 7, or 13, where the modular curve `X_0(l)` has genus 0.  Second: `l` =
11, 17, 19, 23, 29, 31, 41, 47, 59, or 71, where `X_0^+(l)` has genus 0 and
`X_0(l)` is elliptic or hyperelliptic.  Also: `l` = 11, 17, 19, 37, 43, 67 or
163 over `\QQ` (the sporadic cases with only finitely many `j`-invariants
each).  All the above only require factorization of a polynomial of degree
`l+1`.  Finally, a generic function which works for arbitrary odd primes `l`
(including the characteristic), but requires factorization of the `l`-division
polynomial, of degree `(l^2-1)/2`.

AUTHORS:

- John Cremona and Jenny Cooley: 2009-07..11: the genus 0 cases the sporadic
  cases over `\QQ`.

- Kimi Tsukazaki and John Cremona: 2013-07: The 10 (hyper)-elliptic cases and
  the generic algorithm.  See [KT2013]_.
"""

# ****************************************************************************
#       Copyright (C) 2012-2013 John Cremona, Jenny Cooley, Kimi Tsukazaki
#
#  Distributed under the terms of the GNU General Public License (GPL)
#  as published by the Free Software Foundation; either version 2 of
#  the License, or (at your option) any later version.
#                  https://www.gnu.org/licenses/
# ****************************************************************************

from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.polynomial.polynomial_ring import polygen
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.misc.misc_c import prod
from sage.schemes.elliptic_curves.constructor import EllipticCurve

from sage.misc.cachefunc import cached_function

##########################################################################
# The following section is all about computing l-isogenies, where l is
# a prime.  The genus 0 cases `l` = 2, 3, 5, 7 and 13 are
# implemented over any field of characteristic not 2, 3 or `l`; over
# `\QQ` the "sporadic" cases `l` = 11, 17, 19, 37, 43, 67 or 163 with
# only finitely many `j`-invariants each. are also implemented.
##########################################################################


@cached_function
def Fricke_polynomial(l):
    r"""
    Fricke polynomial for ``l`` in `2,3,5,7,13`.

    For these primes (and these only) the modular curve `X_0(l)` has
    genus zero, and its field is generated by a single modular
    function called the Fricke module (or Hauptmodul), `t`.  There is
    a classical choice of such a generator `t` in each case, and the
    `j`-function is a rational function of `t` of degree `l+1` of the
    form `P(t)/t` where `P` is a polynomial of degree `l+1`.  Up to
    scaling, `t` is determined by the condition that the ramification
    points above `j=\infty` are `t=0` (with ramification degree `1`)
    and `t=\infty` (with degree `l`).  The ramification above `j=0`
    and `j=1728` may be seen in the factorizations of `j(t)` and
    `k(t)` where `k=j-1728`.

    OUTPUT:

    The polynomial `P(t)` as an element of `\ZZ[t]`.

    TESTS::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import Fricke_polynomial
        sage: Fricke_polynomial(2)
        t^3 + 48*t^2 + 768*t + 4096
        sage: Fricke_polynomial(3)
        t^4 + 36*t^3 + 270*t^2 + 756*t + 729
        sage: Fricke_polynomial(5)
        t^6 + 30*t^5 + 315*t^4 + 1300*t^3 + 1575*t^2 + 750*t + 125
        sage: Fricke_polynomial(7)
        t^8 + 28*t^7 + 322*t^6 + 1904*t^5 + 5915*t^4 + 8624*t^3 + 4018*t^2 + 748*t + 49
        sage: Fricke_polynomial(13)
        t^14 + 26*t^13 + 325*t^12 + 2548*t^11 + 13832*t^10 + 54340*t^9 + 157118*t^8
        + 333580*t^7 + 509366*t^6 + 534820*t^5 + 354536*t^4 + 124852*t^3
        + 15145*t^2 + 746*t + 13
    """
    t = polygen(ZZ, 't')
    if l == 2:
        return (t+16)**3
    if l == 3:
        return (t+3)**3*(t+27)
    if l == 5:
        return (t**2+10*t+5)**3
    if l == 7:
        return (t**2+5*t+1)**3 * (t**2+13*t+49)
    if l == 13:
        return (t**2+5*t+13)*(t**4+7*t**3+20*t**2+19*t+1)**3
    raise ValueError(f"{l} is not a genus 0 prime")


@cached_function
def Fricke_module(l):
    r"""
    Fricke module for ``l`` in `2,3,5,7,13`.

    For these primes (and these only) the modular curve `X_0(l)` has
    genus zero, and its field is generated by a single modular
    function called the Fricke module (or Hauptmodul), `t`.  There is
    a classical choice of such a generator `t` in each case, and the
    `j`-function is a rational function of `t` of degree `l+1` of the
    form `P(t)/t` where `P` is a polynomial of degree `l+1`.  Up to
    scaling, `t` is determined by the condition that the ramification
    points above `j=\infty` are `t=0` (with ramification degree `1`)
    and `t=\infty` (with degree `l`).  The ramification above `j=0`
    and `j=1728` may be seen in the factorizations of `j(t)` and
    `k(t)` where `k=j-1728`.

    OUTPUT:

    The rational function `P(t)/t`.

    TESTS::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import Fricke_module
        sage: Fricke_module(2)
        (t^3 + 48*t^2 + 768*t + 4096)/t
        sage: Fricke_module(3)
        (t^4 + 36*t^3 + 270*t^2 + 756*t + 729)/t
        sage: Fricke_module(5)
        (t^6 + 30*t^5 + 315*t^4 + 1300*t^3 + 1575*t^2 + 750*t + 125)/t
        sage: Fricke_module(7)
        (t^8 + 28*t^7 + 322*t^6 + 1904*t^5 + 5915*t^4 + 8624*t^3 + 4018*t^2 + 748*t + 49)/t
        sage: Fricke_module(13)
        (t^14 + 26*t^13 + 325*t^12 + 2548*t^11 + 13832*t^10
         + 54340*t^9 + 157118*t^8 + 333580*t^7 + 509366*t^6
         + 534820*t^5 + 354536*t^4 + 124852*t^3 + 15145*t^2 + 746*t + 13)/t
    """
    t = polygen(QQ, 't')
    return Fricke_polynomial(l) / t


@cached_function
def Psi(l, use_stored=True):
    r"""
    Generic kernel polynomial for genus zero primes.

    For each of the primes `l` for which `X_0(l)` has genus zero
    (namely `l=2,3,5,7,13`), we may define an elliptic curve `E_t`
    over `\QQ(t)`, with coefficients in `\ZZ[t]`, which has good
    reduction except at `t=0` and `t=\infty` (which lie above
    `j=\infty`) and at certain other values of `t` above `j=0` when
    `l=3` (one value) or `l\equiv1\pmod{3}` (two values) and above
    `j=1728` when `l=2` (one value) or `l\equiv1 \pmod{4}` (two
    values).  (These exceptional values correspond to endomorphisms of
    `E_t` of degree `l`.)  The `l`-division polynomial of `E_t` has a
    unique factor of degree `(l-1)/2` (or 1 when `l=2`), with
    coefficients in `\ZZ[t]`, which we call the Generic Kernel
    Polynomial for `l`.  These are used, by specialising `t`, in the
    function :meth:`isogenies_prime_degree_genus_0`, which also has to
    take into account the twisting factor between `E_t` for a specific
    value of `t` and the short Weierstrass form of an elliptic curve
    with `j`-invariant `j(t)`.  This enables the computation of the
    kernel polynomials of isogenies without having to compute and
    factor division polynomials.

    All of this data is quickly computed from the Fricke modules, except
    that for `l=13` the factorization of the Generic Division Polynomial
    takes a long time, so the value have been precomputed and cached; by
    default the cached values are used, but the code here will recompute
    them when ``use_stored`` is ``False``, as in the doctests.

    INPUT:

    - ``l`` -- either 2, 3, 5, 7, or 13

    - ``use_stored``-- boolean (default: ``True``); if ``True``, use
      precomputed values, otherwise compute them on the fly

    .. NOTE::

        This computation takes a negligible time for `l=2,3,5,7`
        but more than 100s for `l=13`.  The reason
        for allowing dynamic computation here instead of just using
        precomputed values is for testing.

    TESTS::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import Fricke_module, Psi
        sage: assert Psi(2, use_stored=True) == Psi(2, use_stored=False)
        sage: assert Psi(3, use_stored=True) == Psi(3, use_stored=False)
        sage: assert Psi(5, use_stored=True) == Psi(5, use_stored=False)
        sage: assert Psi(7, use_stored=True) == Psi(7, use_stored=False)
        sage: assert Psi(13, use_stored=True) == Psi(13, use_stored=False)  # long time (2s)
    """
    if l not in [2, 3, 5, 7, 13]:
        raise ValueError(f"{l} is not a genus 0 prime")

    R, (X, t) = PolynomialRing(ZZ, ['X','t']).objgens()

    if use_stored:
        if l == 2:
            return X + t + 64
        if l == 3:
            return X + t + 27
        if l == 5:
            return X**2 + 2*X*(t**2 + 22*t + 125) + (t**2 + 22*t + 89) * (t**2 + 22*t + 125)
        if l == 7:
            return (X**3 + 3*(t**2 + 13*t + 49)*X**2
                    + 3*(t**2 + 13*t + 33)*(t**2 + 13*t + 49)*X
                    + (t**2 + 13*t + 49)*(t**4 + 26*t**3 + 219*t**2 + 778*t + 881))
        if l == 13:
            return (t**24 + 66*t**23 + 2091*t**22 + 6*X*t**20 + 42582*t**21 + 330*X*t**19 + 627603*t**20 + 8700*X*t**18 + 7134744*t**19 + 15*X**2*t**16 + 146886*X*t**17 + 65042724*t**18 + 660*X**2*t**15 + 1784532*X*t**16 + 487778988*t**17 + 13890*X**2*t**14 + 16594230*X*t**15 + 3061861065*t**16 + 20*X**3*t**12 + 186024*X**2*t**13 + 122552328*X*t**14 + 16280123754*t**15 + 660*X**3*t**11 + 1774887*X**2*t**12 + 735836862*X*t**13 + 73911331425*t**14 + 10380*X**3*t**10 + 12787272*X**2*t**11 + 3646188342*X*t**12 + 287938949178*t**13 + 15*X**4*t**8 + 102576*X**3*t**9 + 71909658*X**2*t**10 + 15047141292*X*t**11 + 964903805434*t**12 + 330*X**4*t**7 + 707604*X**3*t**8 + 321704316*X**2*t**9 + 51955096824*X*t**10 + 2781843718722*t**11 + 3435*X**4*t**6 + 3582876*X**3*t**7 + 1155971196*X**2*t**8 + 150205315932*X*t**9 + 6885805359741*t**10 + 6*X**5*t**4 + 21714*X**4*t**5 + 13632168*X**3*t**6 + 3343499244*X**2*t**7 + 362526695094*X*t**8 + 14569390179114*t**9 + 66*X**5*t**3 + 90660*X**4*t**4 + 39215388*X**3*t**5 + 7747596090*X**2*t**6 + 725403501318*X*t**7 + 26165223178293*t**8 + 336*X**5*t**2 + 255090*X**4*t**3 + 84525732*X**3*t**4 + 14206132008*X**2*t**5 + 1189398495432*X*t**6 + 39474479008356*t**7 + X**6 + 858*X**5*t + 472143*X**4*t**2 + 132886992*X**3*t**3 + 20157510639*X**2*t**4 + 1569568001646*X*t**5 + 49303015587132*t**6 + 1014*X**5 + 525954*X**4*t + 144222780*X**3*t**2 + 21320908440*X**2*t**3 + 1622460290100*X*t**4 + 49941619724976*t**5 + 272259*X**4 + 96482100*X**3*t + 15765293778*X**2*t**2 + 1260038295438*X*t**3 + 39836631701295*t**4 + 29641924*X**3 + 7210949460*X**2*t + 686651250012*X*t**2 + 23947528862166*t**3 + 1506392823*X**2 + 231462513906*X*t + 10114876838391*t**2 + 35655266790*X + 2644809206442*t + 317295487717)
# The coefficients for l=13 are:
# X**6: 1
# X**5: (6) * (t**2 + 5*t + 13) * (t**2 + 6*t + 13)
# X**4: (3) * (t**2 + 5*t + 13) * (t**2 + 6*t + 13) * (5*t**4 + 55*t**3 + 260*t**2 + 583*t + 537)
# X**3: (4) * (t**2 + 5*t + 13) * (t**2 + 6*t + 13)**2 * (5*t**6 + 80*t**5 + 560*t**4 + 2214*t**3 + 5128*t**2 + 6568*t + 3373)
# X**2: (3) * (t**2 + 5*t + 13)**2 * (t**2 + 6*t + 13)**2 * (5*t**8 + 110*t**7 + 1045*t**6 + 5798*t**5 + 20508*t**4 + 47134*t**3 + 67685*t**2 + 54406*t + 17581)
# X**1: (6) * (t**2 + 5*t + 13)**2 * (t**2 + 6*t + 13)**3 * (t**10 + 27*t**9 + 316*t**8 + 2225*t**7 + 10463*t**6 + 34232*t**5 + 78299*t**4 + 122305*t**3 + 122892*t**2 + 69427*t + 16005)
# X**0: (t**2 + 5*t + 13)**2 * (t**2 + 6*t + 13)**3 * (t**14 + 38*t**13 + 649*t**12 + 6844*t**11 + 50216*t**10 + 271612*t**9 + 1115174*t**8 + 3520132*t**7 + 8549270*t**6 + 15812476*t**5 + 21764840*t**4 + 21384124*t**3 + 13952929*t**2 + 5282630*t + 854569)

    # Here the generic kernel polynomials are actually calculated:
    j = Fricke_module(l)
    k = j - 1728
    f = prod([p for p,e in j.factor() if e == 3]
           + [p for p,e in k.factor() if e == 2])
    A4 = -3*t**2*j*k // f**2
    A6 = -2*t**3*j*k**2 // f**3
    E = EllipticCurve([A4, A6])
    assert E.j_invariant() == j
    return E.division_polynomial(l, X).factor()[0][0]


def isogenies_prime_degree_genus_0(E, l=None, minimal_models=True):
    """
    Return list of ``l``-isogenies with domain ``E``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``l`` -- either ``None`` or 2, 3, 5, 7, or 13

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) When ``l`` is None a list of all isogenies of degree 2, 3,
    5, 7 and 13, otherwise a list of isogenies of the given degree.

    .. NOTE::

        This function would normally be invoked indirectly via
        ``E.isogenies_prime_degree(l)``, which automatically calls the
        appropriate function.

    ALGORITHM:

    Cremona and Watkins [CW2005]_. See also [KT2013]_, Chapter 4.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree_genus_0
        sage: E = EllipticCurve([0,12])
        sage: isogenies_prime_degree_genus_0(E, 5)
        []

        sage: E = EllipticCurve('1450c1')
        sage: isogenies_prime_degree_genus_0(E)
        [Isogeny of degree 3
          from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + 300*x - 1000
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 5950*x - 182250
               over Rational Field]

        sage: E = EllipticCurve('50a1')
        sage: isogenies_prime_degree_genus_0(E)
        [Isogeny of degree 3
          from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 - 126*x - 552 over Rational Field,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 - 76*x + 298 over Rational Field]
    """
    if l not in [2, 3, 5, 7, 13, None]:
        raise ValueError(f"{l} is not a genus 0 prime")
    F = E.base_ring()
    j = E.j_invariant()
    if F.characteristic() in [2, 3, l]:
        raise NotImplementedError("2, 3, 5, 7 and 13-isogenies are not yet implemented in characteristic 2 and 3, and when the characteristic is the same as the degree of the isogeny")
    if l == 2:
        return isogenies_2(E, minimal_models=minimal_models)
    if l == 3:
        return isogenies_3(E, minimal_models=minimal_models)
    if not j:
        if l == 5:
            return isogenies_5_0(E, minimal_models=minimal_models)
        if l == 7:
            return isogenies_7_0(E, minimal_models=minimal_models)
        if l == 13:
            return isogenies_13_0(E, minimal_models=minimal_models)
    if j == 1728:
        if l == 5:
            return isogenies_5_1728(E, minimal_models=minimal_models)
        if l == 7:
            return isogenies_7_1728(E, minimal_models=minimal_models)
        if l == 13:
            return isogenies_13_1728(E, minimal_models=minimal_models)

    if l is None:
        return [isog for ell in [2, 3, 5, 7, 13]
                     for isog in isogenies_prime_degree_genus_0(E, ell, minimal_models=minimal_models)]

    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None

    R, t = PolynomialRing(F, 't').objgen()
    f = R(Fricke_polynomial(l))
    t_list = sorted((f-j*t).roots(multiplicities=False))
    # The generic kernel polynomial applies to a standard curve
    # E_t with the correct j-invariant; we must compute the
    # appropriate twisting factor to scale X by:
    c4, c6 = E.c_invariants()
    T = c4/(3*c6)
    jt = Fricke_module(l)
    kt = jt - 1728
    psi = Psi(l)
    X = t
    f = R(prod([p for p,e in jt.factor() if e == 3]
             + [p for p,e in kt.factor() if e == 2]))

    E1 = EllipticCurve([-27*c4, -54*c6])
    kernels = [R(psi(X * T * (j - 1728) * t0 / f(t0), t0)).monic() for t0 in t_list]
    isogs = [E1.isogeny(ker, model=model) for ker in kernels]

    w = E.isomorphism_to(E1)
    isogs = [isog * w for isog in isogs]
    return isogs


# The following code computes data to be used in
# isogenies_sporadic_Q. Over Q there are only finitely many
# j-invariants of curves with l-isogenies where l is not equal to 2,
# 3, 5, 7 or 13. In these cases l is equal to 11, 17, 19, 37, 43, 67
# or 163. We refer to these l as "sporadic".

# sporadic_j is a dictionary holding for each possible sporadic
# j-invariant, the unique l such that an l-isogeny exists.
sporadic_j = {
    QQ(-121)                : 11,
    QQ(-32768)              : 11,
    QQ(-24729001)           : 11,
    QQ(-297756989)/2        : 17,
    QQ(-882216989)/131072   : 17,
    QQ(-884736)             : 19,
    QQ(-9317)               : 37,
    QQ(-162677523113838677) : 37,
    QQ(-884736000)          : 43,
    QQ(-147197952000)       : 67,
    QQ(-262537412640768000) : 163
    }


@cached_function
def _sporadic_Q_data(j):
    r"""
    Return technical data used in computing sporadic isogenies over `\QQ`.

    INPUT:

    - ``j`` -- the `j`-invariant of a sporadic curve, i.e. one of the
      keys of ``sporadic_j``

    OUTPUT:

    ``([a4,a6], coeffs)`` where ``[a4,a6]`` are the coefficients of a
    short Weierstrass equation of an elliptic curve E with j(E)=``j``,
    and ``coeffs`` is a list of coefficients of a polynomial defining
    the kernel of an l-isogeny from E.  In all but one case this
    polynomial is monic with integer coefficients.  In one case
    (`\ell=37`, `j=-162677523113838677`) the constant coefficient has
    denominator 37.

    Whenever we have a curve of j-invariant ``j``, we can compute the
    corresponding l-isogeny by just scaling ``coeffs`` by the right
    twisting factor and using the result as a kernel-polynomial.

    ALGORITHM:

    For small l it works fine to factor the l-division polynomial, but
    this takes a long time for the larger l and is a very bad idea for
    l=163; hence we use floating point arithmetic with a precision
    which is known to work.  This idea was suggested by Samir Siksek.

    TESTS::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import sporadic_j, _sporadic_Q_data
        sage: [_sporadic_Q_data(j) for j in sorted(sporadic_j) if j != -262537412640768000]
        [([-269675595, -1704553285050],
          [-31653754873248632711650187487655160190139073510876609346911928661154296875/37,
           -1469048260972089939455942042937882262144594798448952781325533511718750,
           -1171741935131505774747142644126089902595908234671576131857702734375,
           -574934780393177024547076427530739751753985644656221274606250000,
           -193516922725803688001809624711400287605136013195315374687500,
           -47085563820928456130325308223963045033502182349693125000,
           -8472233937388712980597845725196873697064639957437500,
           -1124815211213953261752081095348112305023653750000,
           -105684015609077608033913080859605951322531250,
           -5911406027236569746089675554748135312500,
           22343907270397352965399097794968750,
           43602171843758666292581116410000,
           5054350766002463251474186500,
           350135768194635636171000,
           16633063574896677300,
           549939627039600,
           12182993865,
           163170,
           1]),
         ([-117920, 15585808],
          [426552448394636714720553816389274308035895411389805883034985546818882031845376,
           -55876556222880738651382959148329502876096075327084935039031884373558741172224,
           3393295715290183821010552313572221545212247684503012173117764703828786020352,
           -125729166452196578653551230178028570067747190427221869867485520072257044480,
           3121342502030777257351089270834971957072933779704445667351054593298530304,
           -52544031605544530265465344472543470442324636919759253720520768014516224,
           532110915869155495738137756847596184665209453108323879594125221167104,
           -399031158106622651277981701966309467713625045637309782055519780864,
           -101914346170769215732007802723651742508893380955930030421292613632,
           2296526155500449624398016447877283594461904009374321659789443072,
           -31950871094301541469458501953701002806003991982768349794795520,
           329792235011603804948028315065667439678526339671142107709440,
           -2655636715955021784085217734679612378726442691190553837568,
           16825164648840434987220620681420687654501026066872664064,
           -81705027839007003131400500185224450729843244954288128,
           273656504606483403474090105104132405333665144373248,
           -320807702482945680116212224172370503903312084992,
           -3166683390779345463318656135338172047199043584,
           27871349428383710305216046431806697565585408,
           -132774697798318602604125735604528772808704,
           436096215568182871014215818309741314048,
           -964687143341252402362763535357837312,
           942144169187362941776488535425024,
           2794850106281773765892648206336,
           -17236916236678037389276086272,
           50979778712911923486851072,
           -105035658611718440992768,
           161833913559276412928,
           -188675698610077696,
           163929317513984,
           -102098677888,
           42387952,
           -10184,
           1]),
         ([-13760, 621264],
          [-1961864562041980324821547425314935668736,
           784270445793223959453256359333693751296,
           -120528107728500223255333768387027271680,
           10335626145581464192664472924270362624,
           -568426570575654606865505142156820480,
           21261993723422650574629752537088000,
           -544630471727787626557612832587776,
           8870521306520473088172555763712,
           -54993059067301585878494740480,
           -1434261324709904840432549888,
           50978938193065926383894528,
           -845761855773797582372864,
           8627493611216601088000,
           -48299605284169187328,
           -32782260293713920,
           3415534989828096,
           -34580115625984,
           199359712512,
           -730488128,
           1658080,
           -2064,
           1]),
         ([-3940515, 3010787550],
          [-6458213126940667330314375,
           34699336325466068070000,
           -72461450055340471500,
           68342601718080000,
           -15140380554450,
           -25802960400,
           23981220,
           -8160,
           1]),
         ([-38907, -2953962], [-20349931239, -424530315, -134838, 53658, 429, 1]),
         ([-608, 5776],
          [-34162868224,
           -8540717056,
           6405537792,
           -1123778560,
           84283392,
           -2033152,
           -92416,
           6992,
           -152,
           1]),
         ([-9504, 365904], [1294672896, -92835072, 1463616, 7920, -264, 1]),
         ([-10395, 444150],
          [-38324677699334121599624973029296875,
           -17868327793500376961572310472656250,
           2569568362004197901139023084765625,
           -95128267987528547588017818750000,
           -822168183291347061312510937500,
           134395594560592096297190625000,
           -2881389756919344324888937500,
           -2503855007083401977250000,
           922779077075655997443750,
           -11503912310262102937500,
           -18237870962450291250,
           1457822151548910000,
           -10087015556047500,
           -13677678063000,
           490243338900,
           -2461460400,
           5198445,
           -4410,
           1]),
         ([-856035, -341748450],
          [103687510635057329105625,
           961598491955315190000,
           1054634146768300500,
           -6553122389064000,
           -14554350284850,
           -2046589200,
           13185540,
           8160,
           1]),
         ([-3267, -280962], [1480352841, -56169531, -2829222, 10890, 429, 1])]

    See :issue:`22328`.  This used to fail on l=37,
    j=-162677523113838677 for which the kernel polynomial is not
    integral::

        sage: R = PolynomialRing(QQ, 'x')
        sage: for j in sporadic_j:
        ....:     ell = sporadic_j[j]
        ....:     if ell==163: continue # takes 40s
        ....:     E = EllipticCurve(j=j).short_weierstrass_model()
        ....:     f = R(_sporadic_Q_data(j)[1])
        ....:     g = E.division_polynomial(ell)
        ....:     assert g % f == 0
    """
    from sage.rings.real_mpfr import RealField
    ell = sporadic_j[j]
    E = EllipticCurve(j=j).short_weierstrass_model()
    a4a6 = [E.a4(), E.a6()]
    L = E.period_lattice()
    pr = 100
    if ell == 163:
        pr = 1000
    elif ell > 30:
        pr = 300
    w1, w2 = L.basis(prec=pr)
    X = polygen(RealField(pr), 'X')
    w = w1  # real period
    if j in [-121, -24729001, -162677523113838677, QQ(-882216989)/131072]:
        w = 2*w2 - w1  # imaginary period
    kerpol = prod(X - L.elliptic_exponential(n*w/ell)[0] for n in range(1, (ell+1)//2))
    if j == -162677523113838677:
        kerpolcoeffs = [(37 * c.real()).round() / 37 for c in kerpol]
    else:
        kerpolcoeffs = [c.real().round() for c in kerpol]
    return a4a6, kerpolcoeffs


def isogenies_sporadic_Q(E, l=None, minimal_models=True):
    r"""
    Return a list of sporadic l-isogenies from E (l = 11, 17, 19, 37,
    43, 67 or 163). Only for elliptic curves over `\QQ`.

    INPUT:

    - ``E`` -- an elliptic curve defined over `\QQ`

    - ``l`` -- either ``None`` or a prime number

    OUTPUT:

    (list) If ``l`` is None, a list of all isogenies with domain ``E``
    and of degree 11, 17, 19, 37, 43, 67 or 163; otherwise a list of
    isogenies of the given degree.

    .. NOTE::

        This function would normally be invoked indirectly via
        ``E.isogenies_prime_degree(l)``, which automatically calls the appropriate
        function.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_sporadic_Q
        sage: E = EllipticCurve('121a1')
        sage: isogenies_sporadic_Q(E, 11)
        [Isogeny of degree 11
          from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 30*x - 76
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 305*x + 7888
               over Rational Field]
        sage: isogenies_sporadic_Q(E, 13)
        []
        sage: isogenies_sporadic_Q(E, 17)
        []
        sage: isogenies_sporadic_Q(E)
        [Isogeny of degree 11
          from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 30*x - 76
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 305*x + 7888
               over Rational Field]

        sage: E = EllipticCurve([1, 1, 0, -660, -7600])
        sage: isogenies_sporadic_Q(E, 17)
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 660*x - 7600
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 878710*x + 316677750
               over Rational Field]
        sage: isogenies_sporadic_Q(E)
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 660*x - 7600
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 878710*x + 316677750
               over Rational Field]
        sage: isogenies_sporadic_Q(E, 11)
        []

        sage: E = EllipticCurve([0, 0, 1, -1862, -30956])
        sage: isogenies_sporadic_Q(E, 11)
        []
        sage: isogenies_sporadic_Q(E, 19)
        [Isogeny of degree 19
          from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956
               over Rational Field
            to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489
               over Rational Field]
        sage: isogenies_sporadic_Q(E)
        [Isogeny of degree 19
          from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956
               over Rational Field
            to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489
               over Rational Field]

        sage: E = EllipticCurve([0, -1, 0, -6288, 211072])
        sage: E.conductor()
        19600
        sage: isogenies_sporadic_Q(E, 37)
        [Isogeny of degree 37
          from Elliptic Curve defined by y^2 = x^3 - x^2 - 6288*x + 211072
               over Rational Field
            to Elliptic Curve defined by y^2 = x^3 - x^2 - 163137088*x - 801950801728
               over Rational Field]

        sage: E = EllipticCurve([1, 1, 0, -25178045, 48616918750])
        sage: E.conductor()
        148225
        sage: isogenies_sporadic_Q(E, 37)
        [Isogeny of degree 37
          from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 25178045*x + 48616918750
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 970*x - 13075
               over Rational Field]

        sage: E = EllipticCurve([-3440, 77658])
        sage: E.conductor()
        118336
        sage: isogenies_sporadic_Q(E, 43)
        [Isogeny of degree 43
          from Elliptic Curve defined by y^2 = x^3 - 3440*x + 77658
               over Rational Field
            to Elliptic Curve defined by y^2 = x^3 - 6360560*x - 6174354606
               over Rational Field]

        sage: E = EllipticCurve([-29480, -1948226])
        sage: E.conductor()
        287296
        sage: isogenies_sporadic_Q(E, 67)
        [Isogeny of degree 67
         from Elliptic Curve defined by y^2 = x^3 - 29480*x - 1948226
              over Rational Field
           to Elliptic Curve defined by y^2 = x^3 - 132335720*x + 585954296438
              over Rational Field]

        sage: E = EllipticCurve([-34790720, -78984748304])
        sage: E.conductor()
        425104
        sage: isogenies_sporadic_Q(E, 163)
        [Isogeny of degree 163
          from Elliptic Curve defined by y^2 = x^3 - 34790720*x - 78984748304
               over Rational Field
            to Elliptic Curve defined by y^2 = x^3 - 924354639680*x + 342062961763303088
               over Rational Field]
    """
    j = E.j_invariant()
    j = QQ(j)
    if j not in sporadic_j or (l is not None and sporadic_j[j] != l):
        return []

    F = E.base_field()
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None

    data = _sporadic_Q_data(j)
    Ew = E.short_weierstrass_model()
    c4, c6 = Ew.c_invariants()
    (a4, a6), f = data
    d = (c6*a4) / (18*c4*a6)  # twisting factor
    R = PolynomialRing(F, 'x')
    n = len(f)
    ker = R([d**(n-i-1) * f[i] for i in range(n)])
    isog = Ew.isogeny(ker, degree=l, model=model, check=False)
    w = E.isomorphism_to(Ew)
    isog = isog * w
    return [isog]


def isogenies_2(E, minimal_models=True):
    r"""
    Return a list of all 2-isogenies with domain ``E``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 2-isogenies with domain ``E``.  In general these are
    normalised, but over `\QQ` and other number fields, the codomain
    is a minimal model where possible.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_2
        sage: E = EllipticCurve('14a1'); E
        Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field
        sage: [phi.codomain().ainvs() for phi in isogenies_2(E)]
        [(1, 0, 1, -36, -70)]

        sage: E = EllipticCurve([1,2,3,4,5]); E
        Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field
        sage: [phi.codomain().ainvs() for phi in isogenies_2(E)]
        []
        sage: E = EllipticCurve(QQbar, [9,8]); E                                        # needs sage.rings.number_field
        Elliptic Curve defined by y^2 = x^3 + 9*x + 8 over Algebraic Field
        sage: isogenies_2(E)                    # not implemented                       # needs sage.rings.number_field
    """
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(E.base_field(), NumberField) else None

    f2 = E.division_polynomial(2)
    x2 = sorted(f2.roots(multiplicities=False))
    x = f2.parent().gen()
    ff = [x-x2i for x2i in x2]
    isogs = [E.isogeny(f, model=model) for f in ff]
    return isogs


def isogenies_3(E, minimal_models=True):
    r"""
    Return a list of all 3-isogenies with domain ``E``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 3-isogenies with domain ``E``.  In general these are
    normalised, but over `\QQ` or a number field, the codomain is a
    global minimal model where possible.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_3
        sage: E = EllipticCurve(GF(17), [1,1])
        sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
        [(0, 0, 0, 9, 7), (0, 0, 0, 0, 1)]

        sage: E = EllipticCurve(GF(17^2,'a'), [1,1])                                    # needs sage.rings.finite_rings
        sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]                        # needs sage.rings.finite_rings
        [(0, 0, 0, 9, 7), (0, 0, 0, 0, 1), (0, 0, 0, 5*a + 1, a + 13), (0, 0, 0, 12*a + 6, 16*a + 14)]

        sage: E = EllipticCurve('19a1')
        sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
        [(0, 1, 1, 1, 0), (0, 1, 1, -769, -8470)]

        sage: E = EllipticCurve([1,1])
        sage: [phi.codomain().ainvs() for phi in isogenies_3(E)]
        []
    """
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(E.base_field(), NumberField) else None

    f3 = E.division_polynomial(3)
    x3 = sorted(f3.roots(multiplicities=False))
    x = f3.parent().gen()
    ff = [x - x3i for x3i in x3]
    isogs = [E.isogeny(f, model=model) for f in ff]
    return isogs

# 6 special cases: `l` = 5, 7, 13 and `j` = 0, 1728.


def isogenies_5_0(E, minimal_models=True):
    r"""
    Return a list of all the 5-isogenies with domain ``E`` when the
    j-invariant is 0.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 0

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 5-isogenies with codomain E.  In general these are
    normalised, but over `\QQ` or a number field, the codomain is a
    global minimal model where possible.

    .. NOTE::

        This implementation requires that the characteristic is not 2,
        3 or 5.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(5)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_5_0
        sage: E = EllipticCurve([0,12])
        sage: isogenies_5_0(E)
        []

        sage: E = EllipticCurve(GF(13^2,'a'), [0,-3])                                   # needs sage.rings.finite_rings
        sage: isogenies_5_0(E)                                                          # needs sage.rings.finite_rings
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + (4*a+6)*x + (2*a+10)
               over Finite Field in a of size 13^2,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + (12*a+5)*x + (2*a+10)
               over Finite Field in a of size 13^2,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + (10*a+2)*x + (2*a+10)
               over Finite Field in a of size 13^2,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + (3*a+12)*x + (11*a+12)
               over Finite Field in a of size 13^2,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + (a+4)*x + (11*a+12)
               over Finite Field in a of size 13^2,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + (9*a+10)*x + (11*a+12)
               over Finite Field in a of size 13^2]

        sage: x = polygen(QQ, 'x')
        sage: K.<a> = NumberField(x**6 - 320*x**3 - 320)                                # needs sage.rings.number_field
        sage: E = EllipticCurve(K, [0,0,1,0,0])                                         # needs sage.rings.number_field
        sage: isogenies_5_0(E)                                                          # needs sage.rings.number_field
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 + y = x^3
               over Number Field in a with defining polynomial x^6 - 320*x^3 - 320
            to Elliptic Curve defined by
               y^2 + y = x^3 + (643/8*a^5-15779/48*a^4-32939/24*a^3-71989/2*a^2+214321/6*a-112115/3)*x
                          + (2901961/96*a^5+4045805/48*a^4+12594215/18*a^3-30029635/6*a^2+15341626/3*a-38944312/9)
               over Number Field in a with defining polynomial x^6 - 320*x^3 - 320,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 + y = x^3
               over Number Field in a with defining polynomial x^6 - 320*x^3 - 320
            to Elliptic Curve defined by
               y^2 + y = x^3 + (-1109/8*a^5-53873/48*a^4-180281/24*a^3-14491/2*a^2+35899/6*a-43745/3)*x
                          + (-17790679/96*a^5-60439571/48*a^4-77680504/9*a^3+1286245/6*a^2-4961854/3*a-73854632/9)
               over Number Field in a with defining polynomial x^6 - 320*x^3 - 320]
    """
    F = E.base_field()
    if E.j_invariant() != 0:
        raise ValueError("j-invariant must be 0")
    if F.characteristic() in [2, 3, 5]:
        raise NotImplementedError("not implemented in characteristic 2, 3 or 5")
    if not F(5).is_square():
        return []
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None

    Ew = E.short_weierstrass_model()
    b = Ew.a6()
    x = polygen(F)
    betas = sorted((x**6-160*b*x**3-80*b**2).roots(multiplicities=False))
    if not betas:
        return []

    gammas = [(beta**2 * (beta**3-140*b)) / (120*b) for beta in betas]

    kernels = [x**2 + beta*x + gamma for beta, gamma in zip(betas, gammas)]
    isogs = [Ew.isogeny(ker, model=model) for ker in kernels]

    w = E.isomorphism_to(Ew)
    isogs = [isog * w for isog in isogs]
    return isogs


def isogenies_5_1728(E, minimal_models=True):
    r"""
    Return a list of 5-isogenies with domain ``E`` when the j-invariant is
    1728.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 1728

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 5-isogenies with codomain E.  In general these are
    normalised; but if `-1` is a square then there are two
    endomorphisms of degree `5`, for which the codomain is the same as
    the domain curve; and over `\QQ` or a number field, the codomain
    is a global minimal model where possible.

    .. NOTE::

        This implementation requires that the characteristic is not 2,
        3 or 5.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(5)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_5_1728
        sage: E = EllipticCurve([7,0])
        sage: isogenies_5_1728(E)
        []

        sage: E = EllipticCurve(GF(13), [11,0])
        sage: isogenies_5_1728(E)
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13
            to Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13
            to Elliptic Curve defined by y^2 = x^3 + 11*x over Finite Field of size 13]

    An example of endomorphisms of degree 5::

        sage: # needs sage.rings.number_field
        sage: K.<i> = QuadraticField(-1)
        sage: E = EllipticCurve(K, [0,0,0,1,0])
        sage: isogenies_5_1728(E)
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I
            to Elliptic Curve defined by y^2 = x^3 + x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I
            to Elliptic Curve defined by y^2 = x^3 + x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I]
        sage: _[0].rational_maps()
        (((4/25*i + 3/25)*x^5
          + (4/5*i - 2/5)*x^3 - x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)),
         ((11/125*i + 2/125)*x^6*y + (-23/125*i + 64/125)*x^4*y
          + (141/125*i + 162/125)*x^2*y
          + (3/25*i - 4/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4
                                + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125)))

    An example of 5-isogenies over a number field::

        sage: # needs sage.rings.number_field
        sage: x = polygen(QQ, 'x')
        sage: K.<a> = NumberField(x**4 + 20*x**2 - 80)
        sage: K(5).is_square()  # necessary but not sufficient!
        True
        sage: E = EllipticCurve(K, [0,0,0,1,0])
        sage: isogenies_5_1728(E)
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + x
               over Number Field in a with defining polynomial x^4 + 20*x^2 - 80
            to Elliptic Curve defined by y^2 = x^3 + (-753/4*a^2-4399)*x + (2779*a^3+65072*a)
               over Number Field in a with defining polynomial x^4 + 20*x^2 - 80,
         Isogeny of degree 5
          from Elliptic Curve defined by y^2 = x^3 + x
               over Number Field in a with defining polynomial x^4 + 20*x^2 - 80
            to Elliptic Curve defined by y^2 = x^3 + (-753/4*a^2-4399)*x + (-2779*a^3-65072*a)
               over Number Field in a with defining polynomial x^4 + 20*x^2 - 80]

    See :issue:`19840`::

        sage: # needs sage.rings.number_field
        sage: K.<a> = NumberField(x^4 - 5*x^2 + 5)
        sage: E = EllipticCurve([a^2 + a + 1, a^3 + a^2 + a + 1, a^2 + a,
        ....:                    17*a^3 + 34*a^2 - 16*a - 37,
        ....:                    54*a^3 + 105*a^2 - 66*a - 135])
        sage: len(E.isogenies_prime_degree(5))
        2
        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_5_1728
        sage: [phi.codomain().j_invariant() for phi in isogenies_5_1728(E)]
        [19691491018752*a^2 - 27212977933632, 19691491018752*a^2 - 27212977933632]
    """
    F = E.base_field()
    if E.j_invariant() != 1728:
        raise ValueError("j-invariant must be 1728")
    if F.characteristic() in [2, 3, 5]:
        raise NotImplementedError("not implemented in characteristic 2, 3 or 5")
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None

    # quick test for a negative answer (from Fricke module)
    square5 = F(5).is_square()
    square1 = F(-1).is_square()
    if not square5 and not square1:
        return []

    Ew = E.short_weierstrass_model()
    a = Ew.a4()
    x = polygen(F)

    isogs = []

    # Type 1: if -1 is a square we have 2 endomorphisms
    if square1:
        i = F(-1).sqrt()
        kernels = [x**2 + a / (1+2*i), x**2 + a / (1-2*i)]
        isogs.extend(Ew.isogeny(ker, codomain=E) for ker in kernels)

    # Type 2: if 5 is a square we have up to 4 (non-endomorphism) isogenies
    if square5:
        betas = sorted((x**4 + 20*a*x**2 - 80*a**2).roots(multiplicities=False))
        gammas = [(beta**2 - 2*a) / 6 for beta in betas]
        kernels = [x**2 + beta*x + gamma for beta, gamma in zip(betas, gammas)]
        isogs.extend(Ew.isogeny(ker, model=model) for ker in kernels)

    w = E.isomorphism_to(Ew)
    isogs = [isog * w for isog in isogs]
    return isogs


def isogenies_7_0(E, minimal_models=True):
    r"""
    Return list of all 7-isogenies from E when the j-invariant is 0.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 0

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 7-isogenies with codomain E.  In general these are
    normalised; but if `-3` is a square then there are two
    endomorphisms of degree `7`, for which the codomain is the same as
    the domain; and over `\QQ` or a number field, the codomain is a
    global minimal model where possible.

    .. NOTE::

        This implementation requires that the characteristic is not 2,
        3 or 7.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(7)``.

    EXAMPLES:

    First some examples of endomorphisms::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_7_0
        sage: K.<r> = QuadraticField(-3)                                                # needs sage.rings.number_field
        sage: E = EllipticCurve(K, [0,1])                                               # needs sage.rings.number_field
        sage: isogenies_7_0(E)                                                          # needs sage.rings.number_field
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
            to Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I,
         Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
            to Elliptic Curve defined by y^2 = x^3 + 1 over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I]

        sage: E = EllipticCurve(GF(13^2,'a'), [0,-3])                                   # needs sage.rings.finite_rings
        sage: isogenies_7_0(E)                                                          # needs sage.rings.finite_rings
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2,
         Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2
            to Elliptic Curve defined by y^2 = x^3 + 10 over Finite Field in a of size 13^2]

    Now some examples of 7-isogenies which are not endomorphisms::

        sage: K = GF(101)
        sage: E = EllipticCurve(K, [0,1])
        sage: isogenies_7_0(E)
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 101
          to Elliptic Curve defined by y^2 = x^3 + 83*x + 26 over Finite Field of size 101,
         Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 101
            to Elliptic Curve defined by y^2 = x^3 + 55*x + 100 over Finite Field of size 101]

    Examples over a number field::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_7_0
        sage: E = EllipticCurve('27a1').change_ring(QuadraticField(-3,'r'))             # needs sage.rings.number_field
        sage: isogenies_7_0(E)                                                          # needs sage.rings.number_field
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
            to Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I,
         Isogeny of degree 7
          from Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
            to Elliptic Curve defined by y^2 + y = x^3 + (-7) over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I]

        sage: # needs sage.rings.number_field
        sage: x = polygen(QQ, 'x')
        sage: K.<a> = NumberField(x^6 + 1512*x^3 - 21168)
        sage: E = EllipticCurve(K, [0,1])
        sage: isogs = isogenies_7_0(E)
        sage: [phi.codomain().a_invariants() for phi in isogs]
        [(0,
          0,
          0,
          -415/98*a^5 - 675/14*a^4 + 2255/7*a^3 - 74700/7*a^2 - 25110*a - 66420,
          -141163/56*a^5 + 1443453/112*a^4 - 374275/2*a^3
                         - 3500211/2*a^2 - 17871975/4*a - 7710065),
         (0,
          0,
          0,
          -24485/392*a^5 - 1080/7*a^4 - 2255/7*a^3 - 1340865/14*a^2 - 230040*a - 553500,
          1753037/56*a^5 + 8345733/112*a^4 + 374275/2*a^3
                         + 95377029/2*a^2 + 458385345/4*a + 275241835)]
        sage: [phi.codomain().j_invariant() for phi in isogs]
        [158428486656000/7*a^3 - 313976217600000,
        -158428486656000/7*a^3 - 34534529335296000]
    """
    if E.j_invariant() != 0:
        raise ValueError("j-invariant must be 0")
    F = E.base_field()
    if F.characteristic() in [2, 3, 7]:
        raise NotImplementedError("not implemented when the characteristic of the base field is 2, 3 or 7")
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None
    x = polygen(F)

    Ew = E.short_weierstrass_model()
    b = Ew.a6()

    # there will be 2 endomorphisms if -3 is a square:
    ts = sorted(F(-3).sqrt(all=True, extend=False))
    kernels = [7*x - (2 + 6*t) for t in ts]
    kernels = [ker(x**3/b).monic() for ker in kernels]
    isogs = [Ew.isogeny(ker, codomain=E) for ker in kernels]

    # we may have up to 6 other isogenies:
    ts = sorted(F(21).sqrt(all=True, extend=False))
    for t0 in ts:
        s3 = b / (28 + 6*t0)
        ss = sorted((x**3 - s3).roots(multiplicities=False))
        ker = x**3 - 2*t0*x**2 - 4*t0*x + 4*t0 + 28
        kernels = [ker(x/s).monic() for s in ss]
        isogs.extend(Ew.isogeny(ker, model=model) for ker in kernels)

    w = E.isomorphism_to(Ew)
    isogs = [isog * w for isog in isogs]
    return isogs


def isogenies_7_1728(E, minimal_models=True):
    r"""
    Return list of all 7-isogenies from E when the j-invariant is 1728.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 1728

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 7-isogenies with codomain E.  In general these are
    normalised; but over `\QQ` or a number field, the codomain is a
    global minimal model where possible.

    .. NOTE::

        This implementation requires that the characteristic is not 2,
        3, or 7.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(7)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_7_1728
        sage: E = EllipticCurve(GF(47), [1, 0])
        sage: isogenies_7_1728(E)
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 47
            to Elliptic Curve defined by y^2 = x^3 + 26 over Finite Field of size 47,
         Isogeny of degree 7
          from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 47
            to Elliptic Curve defined by y^2 = x^3 + 21 over Finite Field of size 47]

    An example in characteristic 53 (for which an earlier implementation did not work)::

        sage: # needs sage.rings.finite_rings
        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_7_1728
        sage: E = EllipticCurve(GF(53), [1, 0])
        sage: isogenies_7_1728(E)
        []
        sage: E = EllipticCurve(GF(53^2,'a'), [1, 0])
        sage: [iso.codomain().ainvs() for iso in isogenies_7_1728(E)]
        [(0, 0, 0, 36, 19*a + 15), (0, 0, 0, 36, 34*a + 38), (0, 0, 0, 33, 39*a + 28),
         (0, 0, 0, 33, 14*a + 25), (0, 0, 0, 19, 45*a + 16), (0, 0, 0, 19, 8*a + 37),
         (0, 0, 0, 3, 45*a + 16), (0, 0, 0, 3, 8*a + 37)]

    ::

        sage: # needs sage.rings.number_field
        sage: x = polygen(QQ, 'x')
        sage: K.<a> = NumberField(x^8 + 84*x^6 - 1890*x^4 + 644*x^2 - 567)
        sage: E = EllipticCurve(K, [1, 0])
        sage: isogs = isogenies_7_1728(E)
        sage: [phi.codomain().j_invariant() for phi in isogs]
        [-526110256146528/53*a^6 + 183649373229024*a^4
                                 - 3333881559996576/53*a^2 + 2910267397643616/53,
         -526110256146528/53*a^6 + 183649373229024*a^4
                                 - 3333881559996576/53*a^2 + 2910267397643616/53]
        sage: E1 = isogs[0].codomain()
        sage: E2 = isogs[1].codomain()
        sage: E1.is_isomorphic(E2)
        False
        sage: E1.is_quadratic_twist(E2)
        -1
    """
    if E.j_invariant() != 1728:
        raise ValueError("j-invariant must be 1728 (in base field)")
    F = E.base_field()
    if F.characteristic() in [2, 3, 7]:
        raise NotImplementedError("not implemented when the characteristic of the base field is 2, 3 or 7")
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None
    x = polygen(F)

    ts = sorted((Fricke_module(7) - 1728).numerator().roots(F, multiplicities=False))
    if not ts:
        return []

    Ew = E.short_weierstrass_model()
    a = Ew.a4()

    isogs = []
    for t0 in ts:
        s2 = a/t0
        ss = sorted(s2.sqrt(all=True, extend=False))
        ker = 9*x**3 + (-3*t0**3 - 36*t0**2 - 123*t0)*x**2 + (-8*t0**3 - 101*t0**2 - 346*t0 + 35)*x - 7*t0**3 - 88*t0**2 - 296*t0 + 28
        kernels = [ker(x/s).monic() for s in ss]
        isogs.extend(Ew.isogeny(ker, model=model) for ker in kernels)

    w = E.isomorphism_to(Ew)
    isogs = [isog * w for isog in isogs]
    return isogs


def isogenies_13_0(E, minimal_models=True):
    """
    Return list of all 13-isogenies from E when the j-invariant is 0.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 0

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 13-isogenies with codomain E.  In general these are
    normalised; but if `-3` is a square then there are two
    endomorphisms of degree `13`, for which the codomain is the same
    as the domain.

    .. NOTE::

        This implementation requires that the characteristic is not 2,
        3 or 13.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(13)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_13_0

    Endomorphisms of degree 13 will exist when -3 is a square::

        sage: # needs sage.rings.number_field
        sage: K.<r> = QuadraticField(-3)
        sage: E = EllipticCurve(K, [0, r]); E
        Elliptic Curve defined by y^2 = x^3 + r over Number Field in r
         with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
        sage: isogenies_13_0(E)
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + r over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
            to Elliptic Curve defined by y^2 = x^3 + r over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I,
         Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + r over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I
            to Elliptic Curve defined by y^2 = x^3 + r over Number Field in r
               with defining polynomial x^2 + 3 with r = 1.732050807568878?*I]
        sage: isogenies_13_0(E)[0].rational_maps()
        (((7/338*r + 23/338)*x^13 + (-164/13*r - 420/13)*x^10
          + (720/13*r + 3168/13)*x^7 + (3840/13*r - 576/13)*x^4
          + (4608/13*r + 2304/13)*x)/(x^12 + (4*r + 36)*x^9 + (1080/13*r + 3816/13)*x^6
                                      + (2112/13*r - 5184/13)*x^3 + (-17280/169*r - 1152/169)),
         ((18/2197*r + 35/2197)*x^18*y + (23142/2197*r + 35478/2197)*x^15*y
          + (-1127520/2197*r - 1559664/2197)*x^12*y + (-87744/2197*r + 5992704/2197)*x^9*y
          + (-6625152/2197*r - 9085824/2197)*x^6*y + (-28919808/2197*r - 2239488/2197)*x^3*y
          + (-1990656/2197*r - 3870720/2197)*y)/(x^18 + (6*r + 54)*x^15
                + (3024/13*r + 11808/13)*x^12 + (31296/13*r + 51840/13)*x^9
                + (487296/169*r - 2070144/169)*x^6 + (-940032/169*r + 248832/169)*x^3
                + (1990656/2197*r + 3870720/2197)))

    An example of endomorphisms over a finite field::

        sage: # needs sage.rings.finite_rings
        sage: K = GF(19^2,'a')
        sage: E = EllipticCurve(j=K(0)); E
        Elliptic Curve defined by y^2 = x^3 + 1
         over Finite Field in a of size 19^2
        sage: isogenies_13_0(E)
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2
            to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2,
         Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2
            to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 19^2]
        sage: isogenies_13_0(E)[0].rational_maps()
        ((6*x^13 - 6*x^10 - 3*x^7 + 6*x^4 + x)/(x^12 - 5*x^9 - 9*x^6 - 7*x^3 + 5),
         (-8*x^18*y - 9*x^15*y + 9*x^12*y - 5*x^9*y
          + 5*x^6*y - 7*x^3*y + 7*y)/(x^18 + 2*x^15 + 3*x^12 - x^9 + 8*x^6 - 9*x^3 + 7))

    A previous implementation did not work in some characteristics::

        sage: K = GF(29)
        sage: E = EllipticCurve(j=K(0))
        sage: isogenies_13_0(E)
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 29
            to Elliptic Curve defined by y^2 = x^3 + 26*x + 12 over Finite Field of size 29,
         Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 29
            to Elliptic Curve defined by y^2 = x^3 + 16*x + 28 over Finite Field of size 29]

    ::

        sage: K = GF(101)
        sage: E = EllipticCurve(j=K(0)); E.ainvs()
        (0, 0, 0, 0, 1)
        sage: [phi.codomain().ainvs() for phi in isogenies_13_0(E)]
        [(0, 0, 0, 64, 36), (0, 0, 0, 42, 66)]

    ::

        sage: x = polygen(QQ)
        sage: f = x^12 + 78624*x^9 - 130308048*x^6 + 2270840832*x^3 - 54500179968
        sage: K.<a> = NumberField(f)                                                    # needs sage.rings.number_field
        sage: E = EllipticCurve(j=K(0)); E.ainvs()                                      # needs sage.rings.number_field
        (0, 0, 0, 0, 1)
        sage: len([phi.codomain().ainvs()       # long time                             # needs sage.rings.number_field
        ....:      for phi in isogenies_13_0(E)])
        2
    """
    if E.j_invariant() != 0:
        raise ValueError("j-invariant must be 0")
    F = E.base_field()
    if F.characteristic() in [2, 3, 13]:
        raise NotImplementedError("not implemented when the characteristic of the base field is 2, 3 or 13")
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None
    x = polygen(F)

    Ew = E.short_weierstrass_model()
    b = Ew.a6()

    # there will be 2 endomorphisms if -3 is a square:
    ts = sorted(F(-3).sqrt(all=True, extend=False))
    kernels = [13*x**2 + (78*t + 26)*x + 24*t + 40 for t in ts]
    kernels = [ker(x**3/b).monic() for ker in kernels]
    isogs = [Ew.isogeny(ker, codomain=E) for ker in kernels]

    # we may have up to 12 other isogenies:
    ts = sorted((x**4 + 7*x**3 + 20*x**2 + 19*x + 1).roots(multiplicities=False))
    for t0 in ts:
        s3 = b / (6*t0**3 + 32*t0**2 + 68*t0 + 4)
        ss = sorted((x**3-s3).roots(multiplicities=False))
        ker = (x**6 + (20*t0**3 + 106*t0**2 + 218*t0 + 4)*x**5
            + (-826*t0**3 - 4424*t0**2 - 9244*t0 - 494)*x**4
            + (13514*t0**3 + 72416*t0**2 + 151416*t0 + 8238)*x**3
            + (-101948*t0**3 - 546304*t0**2 - 1142288*t0 - 62116)*x**2
            + (354472*t0**3 + 1899488*t0**2 + 3971680*t0 + 215960)*x
            - 459424*t0**3 - 2461888*t0**2 - 5147648*t0 - 279904)
        kernels = [ker(x/s).monic() for s in ss]
        isogs.extend(Ew.isogeny(ker, model=model) for ker in kernels)

    w = E.isomorphism_to(Ew)
    isogs = [isog * w for isog in isogs]
    return isogs


def isogenies_13_1728(E, minimal_models=True):
    r"""
    Return list of all 13-isogenies from E when the j-invariant is 1728.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 1728

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) 13-isogenies with codomain E.  In general these are
    normalised; but if `-1` is a square then there are two
    endomorphisms of degree `13`, for which the codomain is the same
    as the domain; and over `\QQ` or a number field, the codomain is a
    global minimal model where possible.

    .. NOTE::

        This implementation requires that the characteristic is not
        2, 3 or 13.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(13)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_13_1728

        sage: K.<i> = QuadraticField(-1)                                                # needs sage.rings.number_field
        sage: E = EllipticCurve([0,0,0,i,0]); E.ainvs()                                 # needs sage.rings.number_field
        (0, 0, 0, i, 0)
        sage: isogenies_13_1728(E)                                                      # needs sage.rings.number_field
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I
            to Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I,
         Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I
            to Elliptic Curve defined by y^2 = x^3 + i*x over Number Field in i
               with defining polynomial x^2 + 1 with i = 1*I]

    ::

        sage: K = GF(83)
        sage: E = EllipticCurve(K, [0,0,0,5,0]); E.ainvs()
        (0, 0, 0, 5, 0)
        sage: isogenies_13_1728(E)
        []
        sage: K = GF(89)
        sage: E = EllipticCurve(K, [0,0,0,5,0]); E.ainvs()
        (0, 0, 0, 5, 0)
        sage: isogenies_13_1728(E)
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89
            to Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89,
         Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89
            to Elliptic Curve defined by y^2 = x^3 + 5*x over Finite Field of size 89]

    ::

        sage: K = GF(23)
        sage: E = EllipticCurve(K, [1,0])
        sage: isogenies_13_1728(E)
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 23
            to Elliptic Curve defined by y^2 = x^3 + 16 over Finite Field of size 23,
         Isogeny of degree 13
          from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 23
            to Elliptic Curve defined by y^2 = x^3 + 7 over Finite Field of size 23]

    ::

        sage: x = polygen(QQ)
        sage: f = (x^12 + 1092*x^10 - 432432*x^8 + 6641024*x^6
        ....:      - 282896640*x^4 - 149879808*x^2 - 349360128)
        sage: K.<a> = NumberField(f)                                                    # needs sage.rings.number_field
        sage: E = EllipticCurve(K, [1,0])                                               # needs sage.rings.number_field
        sage: [phi.codomain().ainvs()           # long time                             # needs sage.rings.number_field
        ....:  for phi in isogenies_13_1728(E)]
        [(0,
          0,
          0,
          -4225010072113/3063768069807341568*a^10 - 24841071989413/15957125363579904*a^8
            + 11179537789374271/21276167151439872*a^6 - 407474562289492049/47871376090739712*a^4
            + 1608052769560747/4522994717568*a^2 + 7786720245212809/36937790193472,
          -363594277511/574456513088876544*a^11 - 7213386922793/2991961005671232*a^9
            - 2810970361185589/1329760446964992*a^7 + 281503836888046601/8975883017013696*a^5
            - 1287313166530075/848061509544*a^3 + 9768837984886039/6925835661276*a),
         (0,
          0,
          0,
          -4225010072113/3063768069807341568*a^10 - 24841071989413/15957125363579904*a^8
            + 11179537789374271/21276167151439872*a^6 - 407474562289492049/47871376090739712*a^4
            + 1608052769560747/4522994717568*a^2 + 7786720245212809/36937790193472,
          363594277511/574456513088876544*a^11 + 7213386922793/2991961005671232*a^9
            + 2810970361185589/1329760446964992*a^7 - 281503836888046601/8975883017013696*a^5
            + 1287313166530075/848061509544*a^3 - 9768837984886039/6925835661276*a)]
    """
    if E.j_invariant() != 1728:
        raise ValueError("j-invariant must be 1728")
    F = E.base_field()
    if F.characteristic() in [2, 3, 13]:
        raise NotImplementedError("not implemented when the characteristic of the base field is 2, 3 or 13")
    from sage.rings.number_field.number_field_base import NumberField
    model = "minimal" if minimal_models and isinstance(F, NumberField) else None
    x = polygen(F)

    Ew = E.short_weierstrass_model()
    a = Ew.a4()

    # we will have two endomorphisms if -1 is a square:
    ts = sorted(F(-1).sqrt(all=True, extend=False))
    kernels = [13*x**3 + (-26*i - 13)*x**2 + (-52*i - 13)*x - 2*i - 3 for i in ts]
    kernels = [ker(x**2/a).monic() for ker in kernels]
    isogs = [Ew.isogeny(ker, codomain=E) for ker in kernels]

    # we may have up to 12 other isogenies:
    ts = sorted((x**6 + 10*x**5 + 46*x**4 + 108*x**3 + 122*x**2 + 38*x - 1).roots(multiplicities=False))
    for t0 in ts:
        s2 = a / (66*t0**5 + 630*t0**4 + 2750*t0**3 + 5882*t0**2 + 5414*t0 + 162)
        ss = sorted(s2.sqrt(all=True, extend=False))
        ker = (x**6 + (-66*t0**5 - 630*t0**4 - 2750*t0**3 - 5882*t0**2
              - 5414*t0 - 162)*x**5 + (-21722*t0**5 - 205718*t0**4 -
              890146*t0**3 - 1873338*t0**2 - 1652478*t0 + 61610)*x**4
              + (-3391376*t0**5 - 32162416*t0**4 - 139397232*t0**3 -
              294310576*t0**2 - 261885968*t0 + 6105552)*x**3 +
              (-241695080*t0**5 - 2291695976*t0**4 - 9930313256*t0**3
              - 20956609720*t0**2 - 18625380856*t0 + 469971320)*x**2 +
              (-8085170432*t0**5 - 76663232384*t0**4 -
              332202985024*t0**3 - 701103233152*t0**2 -
              623190845440*t0 + 15598973056)*x - 101980510208*t0**5 -
              966973468160*t0**4 - 4190156868352*t0**3 -
              8843158270336*t0**2 - 7860368751232*t0 + 196854655936)
        kernels = [ker(x/s).monic() for s in ss]
        isogs.extend(Ew.isogeny(ker, model=model) for ker in kernels)

    w = E.isomorphism_to(Ew)
    isogs = [isog * w for isog in isogs]
    return isogs

# List of primes l for which X_0(l) is (hyper)elliptic and X_0^+(l) has genus 0


hyperelliptic_primes = [11, 17, 19, 23, 29, 31, 41, 47, 59, 71]


@cached_function
def _hyperelliptic_isogeny_data(l):
    r"""
    Helper function for elliptic curve isogenies.

    INPUT:

    - ``l`` -- a prime in [11, 17, 19, 23, 29, 31, 41, 47, 59, 71]

    OUTPUT: a dict holding a collection of precomputed data needed for
    computing `l`-isogenies

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import _hyperelliptic_isogeny_data
        sage: HID = _hyperelliptic_isogeny_data(11)
        sage: HID['A2']
        55*u - 33
        sage: HID['A4']
        -183*u^2 + 738*u - 180*v - 135
        sage: HID['A6']
        1330*u^3 - 11466*u^2 + 1332*u*v + 2646*u - 1836*v + 1890
        sage: HID['alpha']
        u^11 - 55*u^10 + 1188*u^9 - 12716*u^8 + 69630*u^7 - 177408*u^6 + 133056*u^5 + 132066*u^4 - 187407*u^3 + 40095*u^2 + 24300*u - 6750
        sage: HID['beta']
        u^9 - 47*u^8 + 843*u^7 - 7187*u^6 + 29313*u^5 - 48573*u^4 + 10665*u^3 + 27135*u^2 - 12150*u
        sage: HID['hyper_poly']
        u^4 - 16*u^3 + 2*u^2 + 12*u - 7

        sage: _hyperelliptic_isogeny_data(37)
        Traceback (most recent call last):
        ...
        ValueError: 37 must be one of [11, 17, 19, 23, 29, 31, 41, 47, 59, 71]
    """
    if l not in hyperelliptic_primes:
        raise ValueError(f"{l} must be one of {hyperelliptic_primes}")
    data = {}
    Zu = PolynomialRing(ZZ, 'u')
    Zuv = PolynomialRing(ZZ, ['u','v'])
    Zxuv, (x, u, v) = PolynomialRing(ZZ, ['x','u','v']).objgens()
    if l == 11:
        data['hyper_poly'] = Zu([-7, 12, 2, -16, 1])
        data['A2'] = Zu([-33, 55])
        data['A4'] = Zuv(Zu([-135, 738, -183])+v*Zu([-180]))
        data['A6'] = Zuv(Zu([1890, 2646, -11466, 1330]) + v*Zu([-1836, 1332]))
        data['alpha'] = Zu([-6750, 24300, 40095, -187407, 132066, 133056, -177408, 69630, -12716, 1188, -55, 1])
        data['beta'] = Zu([0, -12150, 27135, 10665, -48573, 29313, -7187, 843, -47, 1])
        #beta factors as (u - 15) * (u - 6) * (u - 3) * (u - 1) * u * (u**2 - 12*u - 9) * (u**2 - 10*u + 5)
        return data
    if l == 17:
        data['hyper_poly'] = Zu([-8, 4, -3, -10, 1])
        data['A2'] = Zu([68, -204, 136])
        data['A4'] = Zuv(Zu([60, 720, -2595, 2250, -435]) + v*Zu([-360, 792, -432]))
        data['A6'] = Zuv(Zu([-8512, 22608, -5064, -57528, 87288, -43704, 4912] ) + v*Zu( [2520, -15372, 28098, -20160, 4914]))
        data['alpha'] = Zu([16000, -67200, 2720, 557600, -1392232, 1073992, 1104830, -3131026, 2450210, 73746, -1454945, 1110355, -424065, 95659, -13243, 1105, -51, 1])
        data['beta'] = Zu([0, 22400, -105920, 146208, 111616, -593800, 680948, -102282, -457950, 468035, -219274, 58549, -9374, 889, -46, 1])
        #beta factors as (u - 10) * (u - 5) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 10*u + 7) * (u**2 - 6*u - 4) * (u**2 - 4*u + 2) * (u**3 - 9*u**2 + 8*u - 4)
        data['endo'] = 17*x**8 + 17*(-4*u + 4)*v*x**6 + 17*(4*u + 6)*v**2*x**4 + 17*(4*u + 4)*v**3*x**2 + (-4*u + 1)*v**4
        data['endo_u'] = 1
        return data
    if l == 19:
        data['hyper_poly'] = Zu([-8, 20, -8, -8, 1])
        data['A2'] = Zu([-114, 57, 171])
        data['A4'] = Zuv(Zu([-1020, 444, 2733, 726, -543]) + v*Zu([-180, -720, -540]))
        data['A6'] = Zuv(Zu([-10080, 21816, 54324, -37386, -86742, -20070, 6858]) + v*Zu([-2968, -13748, -11284, 6356, 6860]))
        data['alpha'] = Zu([16000, -22400, -337440, 475456, 1562104, -1988616, -3025294, 3245960, 2833014, -2420087, -1140950, 932406, 129580, -180443, 21090, 11153, -4066, 570, -38, 1])
        data['beta'] = Zu([0, 33600, -8160, -292400, 23472, 791244, 39282, -847909, -47024, 392654, -24046, -82469, 19162, 4833, -2652, 446, -34, 1])
        #beta factors as (u - 7) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 8*u - 4) * (u**2 - 6*u - 15) * (u**2 - 5*u - 5) * (u**2 - 5*u + 2) * (u**2 - 2*u - 4) * (u**2 + u - 1)
        data['endo'] = 19*x**9 + 19*(-12*u - 24)*v*x**6 + 19*(-24*u - 24)*v**2*x**3 + (96*u - 224)*v**3
        data['endo_u'] = -1
        return data
    if l == 23:
        data['hyper_poly'] = Zu([-7, 10, -11, 2, 2, -8, 1])
        data['A2'] = Zu([69, -230, 253])
        data['A4'] = Zuv(Zu([405, 180, -930, 2820, -795]) + v*Zu([360, -792]))
        data['A6'] = Zuv(Zu([-15498, 34020, -36918, -8120, 51114, -72492, 12166]) + v*Zu([-1080, 7704, -24840, 12168]))
        data['alpha'] = Zu([-6750, 48600, -83835, -170775, 1115109, -2492280, 2732814, -116403, -4877702, 8362616, -6612454, 302266, 5423124, -6447728, 3209696, 336674, -1470068, 953856, -336927, 74221, -10465, 920, -46, 1])
        data['beta'] = Zu( [0, 12150, -72495, 168588, -144045, -254034, 930982, -1256170, 604358, 693650, -1563176, 1271974, -225188, -444070, 421050, -184350, 47754, -7696, 759, -42, 1])
        #beta factors as (u - 5) * (u - 3) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 8*u + 3) * (u**2 - 6*u - 9) * (u**3 - 7*u**2 + 3*u - 5) * (u**3 - 7*u**2 + 7*u - 3) * (u**4 - 4*u**3 - 1)
        return data
    if l == 29:
        data['hyper_poly'] = Zu([-7, 8, 8, 2, -12, -4, 1])
        data['A2'] = Zu([-174, -232, 348, 406])
        data['A4'] = Zuv(Zu([-1215, -3096, 132, 7614, 6504, -360, -1263] ) + v*Zu( [180, -720, -2160, -1260]))
        data['A6'] = Zuv(Zu([-18900, -63504, 24696, 285068, 285264, -185136, -506268, -275520, 504, 24388] ) + v*Zu( [4482, -2448, -59868, -94968, -18144, 48276, 24390]))
        data['alpha'] = Zu([-6750, -12150, 281880, 570024, -1754181, -5229135, 2357613, 19103721, 9708910, -31795426, -38397537, 19207947, 54103270, 9216142, -37142939, -18871083, 14041394, 10954634, -3592085, -3427365, 853818, 622398, -189399, -53679, 26680, -580, -1421, 319, -29, 1])
        data['beta'] = Zu([0, -24300, -57510, 257850, 839187, -373185, -3602119, -2371192, 5865017, 8434433, -2363779, -10263744, -2746015, 5976011, 3151075, -2093854, -1356433, 569525, 299477, -129484, -28279, 19043, -895, -1076, 273, -27, 1])
        #beta factors as (u - 3) * (u - 1) * u * (u + 1) * (u + 2) * (u**2 - 6*u + 2) * (u**2 - 5*u - 5) * (u**2 - 5*u + 3) * (u**2 - 3*u - 9) * (u**2 - u - 3) * (u**2 - u - 1) * (u**2 + u - 1) * (u**3 - 4*u**2 - 6*u - 5) * (u**4 - 2*u**3 - 5*u**2 - 4*u - 1)
        data['endo'] = 29*x**14 + 29*(-14*u + 3)*v*x**12 + 29*(-20*u + 73)*v**2*x**10 + 29*(-58*u + 115)*v**3*x**8 + 29*(-56*u + 59)*v**4*x**6 + 29*(30*u + 1)*v**5*x**4 + 29*(12*u - 5)*v**6*x**2 + (2*u + 5)*v**7
        data['endo_u'] = -1
        return data
    if l == 31:
        data['hyper_poly'] = Zu([-3, -14, -11, 18, 6, -8, 1])
        data['A2'] = Zu([558, 837, -1488, 465])
        data['A4'] = Zuv(Zu([-4140, -12468, 15189, 16956, -27054, 11184, -1443]) + v*Zu([2160, -7560, 6120, -1440]))
        data['A6'] = Zuv(Zu([71280, 592056, -108324, -2609730, 2373048, 1282266, -2793204, 1530882, -356976, 29790]) + v*Zu([-81312, 181664, 294728, -868392, 701400, -238840, 29792]))
        data['alpha'] = Zu([108000, 475200, -7053120, -27353408, 90884374, 303670296, -665806437, -1361301729, 3259359840, 2249261823, -9368721606, 2279583264, 13054272515, -12759480061, -4169029296, 14390047139, -7803693550, -2988803682, 6239473912, -3296588360, 134066754, 908915598, -685615437, 294482733, -87483178, 18983315, -3052818, 361336, -30659, 1767, -62, 1])
        data['beta'] = Zu([0, 712800, 1216080, -18430560, -15262464, 168899202, -12931221, -720077416, 624871714, 1239052988, -2259335558, 68648452, 2679085427, -2318039014, -229246628, 1710545918, -1243026758, 211524870, 296674626, -291810274, 145889932, -48916468, 11793961, -2085662, 269348, -24778, 1540, -58, 1])
        #beta factors as (u - 3) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 8*u + 11) * (u**2 - 7*u + 2) * (u**2 - 5*u - 2) * (u**2 - 5*u + 5) * (u**2 - 4*u - 4) * (u**2 - 4*u - 1) * (u**2 - 2*u - 1) * (u**2 - u - 1) * (u**3 - 9*u**2 + 21*u - 15) * (u**4 - 8*u**3 + 8*u**2 + 12*u - 9)
        data['endo'] = 31*x**15 + 31*(-66*u + 86)*v*x**12 + 31*(168*u + 280)*v**2*x**9 + 31*(576*u + 1792)*v**3*x**6 + 31*(384*u + 896)*v**4*x**3 + (-3072*u - 2048)*v**5
        data['endo_u'] = 2
        return data
    if l == 41:
        data['hyper_poly'] = Zu([-8, -20, -15, 8, 20, 10, -8, -4, 1])
        data['A2'] = Zu([328, 656, -656, -1148, 820])
        data['A4'] = Zuv(Zu([-1380, -4008, 1701, 10872, 6144, -18378, -2160, 9732, -2523]) + v*Zu([720, -1440, -2160, 5400, -2520]))
        data['A6'] = Zuv(Zu([4480, 155616, 16080, -550720, -343968, 832680, 938632, -621648, -1468608, 953920, 427632, -413016, 68920]) + v*Zu([-14616, 6804, 96390, -2016, -324324, 184464, 260568, -276192, 68922]))
        data['alpha'] = Zu([16000, 67200, -465760, -2966432, -1742664, 20985112, 46140990, -31732934, -217030548, -147139488, 436080674, 745775322, -271341362, -1542677562, -605560447, 1832223375, 1772593672, -1270633050, -2400692229, 343522723, 2179745361, 282422801, -1503727029, -421357697, 879637411, 261059095, -462271351, -61715127, 193718727, -24135265, -49355103, 20512341, 3613289, -4706595, 1099661, 163057, -162483, 46617, -7544, 738, -41, 1])
        data['beta'] = Zu([0, 44800, 167040, -447040, -2734272, -1104272, 13488360, 21067652, -24681704, -83929974, -8986886, 169059382, 127641266, -196479899, -283039783, 124573790, 366614063, -12946368, -332987597, -58867672, 241909907, 60568430, -155045647, -17919564, 79114945, -12025938, -24060781, 11190142, 1979597, -2931764, 750233, 110144, -122263, 37484, -6439, 666, -39, 1])
        #beta factors as (u - 5) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 5*u + 5) * (u**2 - 3*u - 7) * (u**2 - 2*u - 4) * (u**2 - 2*u - 1) * (u**2 - u - 1) * (u**2 - 2) * (u**2 + u - 1) * (u**3 - 3*u**2 - 5*u - 2) * (u**3 - 2*u**2 - 2*u - 1) * (u**4 - 6*u**3 + 5*u**2 + 2*u - 1) * (u**4 - 5*u**3 + u**2 + 4) * (u**4 - 4*u**3 + 2)
        data['endo'] = 41*x**20 + 41*(-12*u - 22)*v*x**18 + 41*(-252*u - 247)*v**2*x**16 + 41*(-176*u - 424)*v**3*x**14 + 41*(464*u - 254)*v**4*x**12 + 41*(1688*u - 868)*v**5*x**10 + 41*(1720*u - 1190)*v**6*x**8 + 41*(528*u - 232)*v**7*x**6 + 41*(16*u + 29)*v**8*x**4 + 41*(20*u + 10)*v**9*x**2 + (4*u + 5)*v**10
        data['endo_u'] = 1
        return data
    if l == 47:
        data['hyper_poly'] = Zu([-11, 28, -38, 30, -13, -16, 19, -24, 11, -6, 1])
        data['A2'] = Zu([376, -1504, 2209, -1598, 1081])
        data['A4'] = Zuv(Zu([2400, -4080, -1440, 18000, -26355, 34740, -22050, 12900, -3315]) + v*Zu([1152, -3384, 3672, -3312]))
        data['A6'] = Zuv(Zu([-119504, 606336, -1505280, 2109392, -1509360, -515808, 2920702, -4614012, 4334322, -3260312, 1571442, -622428, 103822]) + v*Zu([2016, 48384, -235872, 438984, -627480, 503496, -311976, 103824]))
        data['alpha'] = Zu([-65536, 688128, -2502656, -96256, 38598656, -187217920, 508021120, -845669120, 552981696, 1469334304, -5945275904, 11705275552, -14673798654, 9100068184, 8421580132, -34288012648, 56657584158, -60426283952, 36612252089, 9942017442, -60791892299, 93046207239, -92028642340, 59196883097, -10454018992, -33364599371, 57280402355, -57873890484, 41879296232, -20241250112, 2065827049, 8435506655, -11611941072, 10182603298, -7040645261, 4071881378, -2013138357, 856757031, -313468474, 97893151, -25770006, 5617769, -990431, 136864, -14194, 1034, -47, 1])
        data['beta'] = Zu([0, 114688, -1114112, 4854784, -11205632, 7426048, 42663936, -182555136, 394092544, -508851472, 213245648, 743315936, -2203729384, 3409478688, -3280008936, 1139839970, 2576264698, -6272528962, 8005203155, -6671665088, 2744569094, 1996771588, -5520074039, 6637395180, -5455622885, 3028415830, -601645255, -1012737914, 1632999370, -1525982346, 1093778952, -644352392, 319489974, -134176208, 47566499, -14083902, 3424200, -667810, 101271, -11438, 901, -44, 1])
        #beta factors as (u - 4) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 5*u + 2) * (u**2 - 2*u - 1) * (u**3 - 5*u**2 + 5*u - 7) * (u**3 - 4*u**2 + 3*u - 4) * (u**3 - 4*u**2 + 3*u - 1) * (u**3 - 3*u**2 + 2*u - 4) * (u**3 - 2*u**2 + 2*u - 2) * (u**3 + u + 1) * (u**4 - 4*u**3 - 2*u**2 - 4) * (u**5 - 5*u**4 + 5*u**3 - 11*u**2 + 6*u - 4) * (u**6 - 4*u**5 + 2*u**4 - 4*u**3 - u**2 + 4*u - 2)
        return data
    if l == 59:
        data['hyper_poly'] = Zu([-8, -4, 20, -24, -3, 40, -62, 40, 3, -28, 22, -8, 1])
        data['A2'] = Zu([590, -1475, -295, 4130, -4425, 1711])
        data['A4'] = Zuv(Zu([-2460, 8844, -3843, -20718, 57153, -50418, -12600, 72762, -69339, 30978, -5223]) + v*Zu([900, 360, -7560, 10800, -5220]))
        data['A6'] = Zuv(Zu([25760, -373560, 568020, 1147870, -4634370, 5318070, 1631996, -14270202, 21535998, -14119408, -2820102, 14275410, -13535292, 6790074, -1847898, 205378]) + v*Zu([-23688, 27972, 183708, -696024, 721980, 453600, -1925028, 2039184, -1027404, 205380]))
        data['alpha'] = Zu([16000, -67200, -783520, 5573376, -5127336, -60792184, 241324042, -170978932, -1262437160, 4310971231, -3953349811, -10887235780, 41679530185, -51342089572, -33068562195, 230682514316, -372641172307, 121615007703, 682044179678, -1549365239197, 1373184591667, 614906882627, -3566756201696, 4920423266916, -2342393877496, -3589340274442, 8772457933356, -8488557160148, 1742977715620, 7131088674129, -11643540780203, 8512399456274, -315658868113, -6917286294515, 8713332734648, -5190227733987, -54249978263, 3397583328372, -3658171840037, 1987950394792, -179519591637, -748989116551, 800595050760, -459184355769, 134398080099, 28871590941, -64236756338, 46651654354, -23352309386, 9059054346, -2830320860, 721829600, -150487052, 25475079, -3452149, 365800, -29205, 1652, -59, 1])
        data['beta'] = Zu([0, -56000, 320800, 391440, -7693120, 21125500, 11515130, -204780145, 486681785, -102547033, -2147060784, 5552726794, -4419031758, -9431888681, 33728080307, -42367773552, -2994127157, 105330637610, -188172973931, 127559513693, 123083802224, -421097252069, 490425751691, -161944881372, -408669953969, 799965143719, -668167261718, 69589638764, 563644022562, -787681290965, 505670881115, 2900924856, -364669742737, 407962360532, -223582547975, 9985786664, 102435489491, -105519055992, 58212400117, -14331637533, -6742538722, 10205452686, -6853903214, 3244679736, -1188153136, 347102566, -81626216, 15409226, -2307408, 268126, -23322, 1429, -55, 1])
        #beta factors as (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 4*u - 1) * (u**2 - 3*u - 5) * (u**2 - 3*u - 2) * (u**2 - 3*u + 1) * (u**2 - u - 1) * (u**3 - 6*u**2 + 10*u - 7) * (u**3 - 5*u**2 + 7*u - 5) * (u**3 - 3*u**2 + 2*u - 1) * (u**3 - u**2 + 1) * (u**4 - 5*u**3 + 4*u**2 - 1) * (u**4 - 4*u**3 + 3*u**2 + 2*u - 4) * (u**4 - 3*u**3 - u - 1) * (u**4 - u**3 + 2*u - 1) * (u**5 - 6*u**4 + 10*u**3 - 11*u**2 + 8*u - 4) * (u**6 - 5*u**5 + 5*u**4 - 5*u**2 + 5*u - 5)
        return data
    if l == 71:
        data['hyper_poly'] = Zu([-7, 6, -27, 40, -58, 66, -66, 40, 15, -48, 66, -66, 37, -10, 1])
        data['A2'] = Zu([213, -1420, 4260, -4970, 9940, -9088, 2485])
        data['A4'] = Zuv(Zu([2565, -10008, 18024, -26532, 23208, 7584, -104418, 189432, -251736, 275148, -182232, 60144, -7563]) + v*Zu([720, -4320, 7560, -20160, 23040, -7560]))
        data['A6'] = Zuv(Zu([-69930, 382536, -1898568, 5206124, -11813256, 23115792, -35705670, 44318064, -41531952, 20674360, 23881872, -77986944, 114989770, -124612152, 103122936, -59431204, 21485688, -4294416, 357910]) + v*Zu([18576, -53856, 57672, 161856, -961920, 3199176, -5706288, 8032896, -9352584, 6786720, -2505888, 357912]))
        data['alpha'] = Zu([-6750, 97200, -603855, 2263977, -4854483, -2486349, 75190491, -399596520, 1441975423, -4089818964, 9450153463, -17516526653, 23635982289, -11859874932, -53385529273, 230566737711, -585283867605, 1136695427037, -1753961304140, 2020891913264, -1147488305875, -1930304898882, 8102336330029, -17218530732347, 27006964902986, -32365758791872, 25902000374138, -468390635342, -46332664858222, 107139839089502, -162234735929274, 182582147217312, -140033523896938, 22513210292184, 152367877270246, -334009986053250, 451855980915164, -443144048889720, 284518400252142, -11142427766850, -289840331821002, 512373447321402, -576967281819172, 466024421705696, -230395084854230, -36287337331916, 241209603962570, -330646545417814, 304702155703516, -205131886553392, 87504290135653, 5131997859077, -54867900326127, 66216047255551, -54817285755105, 36239054778472, -20052219750661, 9464634765852, -3841191816845, 1343947848527, -405138280373, 104923131180, -23228729413, 4364552115, -689157169, 90223321, -9613968, 812240, -52327, 2414, -71, 1])
        data['beta'] = Zu([0, 12150, -163215, 1115640, -5311143, 18820224, -50700172, 99823812, -102454041, -183909134, 1354660714, -4462311942, 10695310224, -20015395554, 28262441676, -23240987282, -17879387475, 124501604946, -315187724212, 564766450688, -765154573538, 705985549104, -115433273216, -1206098873334, 3175185881748, -5228317292044, 6292310032120, -5077451367560, 719644756530, 6451571564682, -14460150103020, 19999710623352, -19681838601268, 11819712227412, 2180981559572, -17790742756618, 29025463386612, -31179247603548, 23207078145510, -8345354986332, -7468523752270, 18486966963350, -21719818051100, 17831212433536, -10100011266030, 2336962513536, 2906983627184, -4989755986066, 4711466210012, -3361479243242, 1952316811463, -948555371584, 389878900245, -136099552242, 40341734984, -10121407164, 2136756509, -376218102, 54551634, -6399080, 591884, -41538, 2078, -66, 1])
        #beta factors as (u - 3) * (u - 2) * (u - 1) * u * (u + 1) * (u**2 - 5*u + 5) * (u**2 - 3*u + 1) * (u**2 - 2*u - 1) * (u**2 - u - 1) * (u**3 - 5*u**2 + 5*u - 3) * (u**3 - 4*u**2 - 1) * (u**3 - 2*u**2 - 1) * (u**4 - 6*u**3 + 7*u**2 + 6*u - 9) * (u**4 - 5*u**3 + 4*u**2 + u + 3) * (u**4 - 5*u**3 + 6*u**2 - 3*u + 5) * (u**4 - 4*u**3 + u**2 - 4*u + 1) * (u**4 - 4*u**3 + 2*u**2 - u + 1) * (u**4 - 2*u**3 - 3*u**2 - 2*u - 1) * (u**4 - 2*u**3 + u - 1) * (u**6 - 5*u**5 + 8*u**4 - 7*u**3 + 6*u**2 - 3*u + 1) * (u**8 - 6*u**7 + 9*u**6 - 2*u**5 + 2*u**3 - 9*u**2 + 2*u - 1)
        return data


@cached_function
def Psi2(l):
    """
    Return the generic kernel polynomial for hyperelliptic `l`-isogenies.

    INPUT:

    - ``l`` -- either 11, 17, 19, 23, 29, 31, 41, 47, 59, or 71

    OUTPUT: the generic `l`-kernel polynomial

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import Psi2
        sage: Psi2(11)
        x^5 - 55*x^4*u + 994*x^3*u^2 - 8774*x^2*u^3 + 41453*x*u^4 - 928945/11*u^5
        + 33*x^4 + 276*x^3*u - 7794*x^2*u^2 + 4452*x*u^3 + 1319331/11*u^4 + 216*x^3*v
        - 4536*x^2*u*v + 31752*x*u^2*v - 842616/11*u^3*v + 162*x^3 + 38718*x^2*u
        - 610578*x*u^2 + 33434694/11*u^3 - 4536*x^2*v + 73872*x*u*v - 2745576/11*u^2*v
        - 16470*x^2 + 580068*x*u - 67821354/11*u^2 - 185976*x*v + 14143896/11*u*v
        + 7533*x - 20437029/11*u - 12389112/11*v + 19964151/11
        sage: p = Psi2(71)                        # long time
        sage: (x,u,v) = p.variables()             # long time
        sage: p.coefficient({x: 0, u: 210, v: 0}) # long time
        -2209380711722505179506258739515288584116147237393815266468076436521/71
        sage: p.coefficient({x: 0, u: 0, v: 0})   # long time
        -14790739586438315394567393301990769678157425619440464678252277649/71

    TESTS::

        sage: Psi2(13)
        Traceback (most recent call last):
        ...
        ValueError: 13 must be one of [11, 17, 19, 23, 29, 31, 41, 47, 59, 71]
    """
    data = _hyperelliptic_isogeny_data(l)

    R = PolynomialRing(QQ, 'u')
    L, v = PolynomialRing(R, 'v').objgen()
    K = R.extension(v*v - R(data['hyper_poly']), 'v')
    v = K.gen()

    from sage.categories.homset import Hom
    h = Hom(K,K)(-v)

    A = K(data['A4'])
    B = K(data['A6'])
    Abar = h(A)*l**2
    Bbar = -h(B)*l**3
    s1 = K(data['A2'])

    d = (l-1)//2
    s = [K(1)]
    t = [d, s1, ((1-10*d)*A - Abar) / 30]
    t.append(((1-28*d)*B - 42*t[1]*A - Bbar) / 70)
    c = [0, 6*t[2] + 2*A*t[0], 10*t[3] + 6*A*t[1] + 4*B*t[0]]
    for n in range(2, d):
        k = sum(c[i]*c[n-i] for i in range(1, n))
        c.append((3*k - (2*n-1)*(n-1)*A*c[n-1] - (2*n-2)*(n-2)*B*c[n-2]) / ((2*n+5)*(n-1)))
    for n in range(3, d):
        t.append((c[n] - (4*n-2)*A*t[n-1] - (4*n-4)*B*t[n-2]) / (4*n+2))
    for n in range(1, d+1):
        s.append(sum((-1)**i*t[i]*s[n-i] for i in range(1, n+1)) / -n)

    R = PolynomialRing(QQ, ['x','u','v'])
    x = R.gen(0)
    return sum((-1)**i * x**(d-i) * s[i].lift() for i in range(d+1))


def isogenies_prime_degree_genus_plus_0(E, l=None, minimal_models=True):
    """
    Return list of ``l``-isogenies with domain ``E``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``l`` -- either ``None`` or 11, 17, 19, 23, 29, 31, 41, 47, 59, or 71

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) When ``l`` is None a list of all isogenies of degree 11, 17, 19, 23,
    29, 31, 41, 47, 59, or 71, otherwise a list of isogenies of the given degree.

    .. NOTE::

        This function would normally be invoked indirectly via
        ``E.isogenies_prime_degree(l)``, which automatically calls the appropriate function.

    ALGORITHM:

    See [KT2013]_, Chapter 5.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree_genus_plus_0

        sage: E = EllipticCurve('121a1')
        sage: isogenies_prime_degree_genus_plus_0(E, 11)
        [Isogeny of degree 11
          from Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 30*x - 76
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 - 305*x + 7888
               over Rational Field]

        sage: E = EllipticCurve([1, 1, 0, -660, -7600])
        sage: isogenies_prime_degree_genus_plus_0(E, 17)
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 660*x - 7600
               over Rational Field
            to Elliptic Curve defined by y^2 + x*y = x^3 + x^2 - 878710*x + 316677750
               over Rational Field]

        sage: E = EllipticCurve([0, 0, 1, -1862, -30956])
        sage: isogenies_prime_degree_genus_plus_0(E, 19)
        [Isogeny of degree 19
          from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956
               over Rational Field
            to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489
               over Rational Field]

        sage: # needs sage.rings.number_field
        sage: K = QuadraticField(-295,'a')
        sage: a = K.gen()
        sage: E = EllipticCurve_from_j(-484650135/16777216*a + 4549855725/16777216)
        sage: isogenies_prime_degree_genus_plus_0(E, 23)
        [Isogeny of degree 23
          from Elliptic Curve defined by
               y^2 = x^3 + (-14460494784192904095/140737488355328*a+270742665778826768325/140737488355328)*x
                      + (37035998788154488846811217135/590295810358705651712*a-1447451882571839266752561148725/590295810358705651712)
               over Number Field in a with defining polynomial x^2 + 295
               with a = 17.17556403731767?*I
            to Elliptic Curve defined by
               y^2 = x^3 + (-5130542435555445498495/140737488355328*a+173233955029127361005925/140737488355328)*x
                      + (-1104699335561165691575396879260545/590295810358705651712*a+3169785826904210171629535101419675/590295810358705651712)
               over Number Field in a with defining polynomial x^2 + 295
               with a = 17.17556403731767?*I]

        sage: # needs sage.rings.number_field
        sage: K = QuadraticField(-199,'a')
        sage: a = K.gen()
        sage: E = EllipticCurve_from_j(94743000*a + 269989875)
        sage: isogenies_prime_degree_genus_plus_0(E, 29)
        [Isogeny of degree 29
          from Elliptic Curve defined by
               y^2 = x^3 + (-153477413215038000*a+5140130723072965125)*x
                      + (297036215130547008455526000*a+2854277047164317800973582250)
               over Number Field in a with defining polynomial x^2 + 199
               with a = 14.106735979665884?*I
            to Elliptic Curve defined by
               y^2 = x^3 + (251336161378040805000*a-3071093219933084341875)*x
                      + (-8411064283162168580187643221000*a+34804337770798389546017184785250)
               over Number Field in a with defining polynomial x^2 + 199
               with a = 14.106735979665884?*I]

        sage: # needs sage.rings.number_field
        sage: K = QuadraticField(253,'a')
        sage: a = K.gen()
        sage: E = EllipticCurve_from_j(208438034112000*a - 3315409892960000)
        sage: isogenies_prime_degree_genus_plus_0(E, 31)
        [Isogeny of degree 31
          from Elliptic Curve defined by
               y^2 = x^3 + (4146345122185433034677956608000*a-65951656549965037259634800640000)*x
                      + (-18329111516954473474583425393698245080252416000*a+291542366110383928366510368064204147260129280000)
               over Number Field in a with defining polynomial x^2 - 253
               with a = 15.905973720586867?
            to Elliptic Curve defined by
               y^2 = x^3 + (200339763852548615776123686912000*a-3186599019027216904280948275200000)*x
                      + (7443671791411479629112717260182286294850207744000*a-118398847898864757209685951728838895495168655360000)
               over Number Field in a with defining polynomial x^2 - 253
               with a = 15.905973720586867?]

        sage: E = EllipticCurve_from_j(GF(5)(1))
        sage: isogenies_prime_degree_genus_plus_0(E, 41)
        [Isogeny of degree 41
          from Elliptic Curve defined by y^2 = x^3 + x + 2 over Finite Field of size 5
            to Elliptic Curve defined by y^2 = x^3 + x + 3 over Finite Field of size 5,
         Isogeny of degree 41
          from Elliptic Curve defined by y^2 = x^3 + x + 2 over Finite Field of size 5
            to Elliptic Curve defined by y^2 = x^3 + x + 3 over Finite Field of size 5]

        sage: # needs sage.rings.number_field
        sage: K = QuadraticField(5,'a')
        sage: a = K.gen()
        sage: E = EllipticCurve_from_j(184068066743177379840*a
        ....:                          - 411588709724712960000)
        sage: isogenies_prime_degree_genus_plus_0(E, 47)        # long time
        [Isogeny of degree 47
          from Elliptic Curve defined by
               y^2 = x^3 + (454562028554080355857852049849975895490560*a-1016431595837124114668689286176511361024000)*x
                      + (-249456798429896080881440540950393713303830363999480904280965120*a+557802358738710443451273320227578156598454035482869042774016000)
               over Number Field in a with defining polynomial x^2 - 5
               with a = 2.236067977499790?
            to Elliptic Curve defined by
               y^2 = x^3 + (39533118442361013730577638493616965245992960*a-88398740199669828340617478832005245173760000)*x
                      + (214030321479466610282320528611562368963830105830555363061803253760*a-478586348074220699687616322532666163722004497458452316582576128000)
               over Number Field in a with defining polynomial x^2 - 5
               with a = 2.236067977499790?]

        sage: K = QuadraticField(-66827,'a')                                            # needs sage.rings.number_field
        sage: a = K.gen()                                                               # needs sage.rings.number_field
        sage: E = EllipticCurve_from_j(-98669236224000*a + 4401720074240000)            # needs sage.rings.number_field
        sage: isogenies_prime_degree_genus_plus_0(E, 59)   # long time (5s)
        [Isogeny of degree 59
          from Elliptic Curve defined by
               y^2 = x^3 + (2605886146782144762297974784000*a+1893681048912773634944634716160000)*x
                      + (-116918454256410782232296183198067568744071168000*a+17012043538294664027185882358514011304812871680000)
               over Number Field in a with defining polynomial x^2 + 66827
               with a = 258.5091874576221?*I
            to Elliptic Curve defined by
               y^2 = x^3 + (-19387084027159786821400775098368000*a-4882059104868154225052787156713472000)*x
                      + (-25659862010101415428713331477227179429538847260672000*a-2596038148441293485938798119003462972840818381946880000)
               over Number Field in a with defining polynomial x^2 + 66827
               with a = 258.5091874576221?*I]

        sage: E = EllipticCurve_from_j(GF(13)(5))
        sage: isogenies_prime_degree_genus_plus_0(E, 71)
        [Isogeny of degree 71
          from Elliptic Curve defined by y^2 = x^3 + x + 4 over Finite Field of size 13
            to Elliptic Curve defined by y^2 = x^3 + 10*x + 7 over Finite Field of size 13,
         Isogeny of degree 71
          from Elliptic Curve defined by y^2 = x^3 + x + 4 over Finite Field of size 13
            to Elliptic Curve defined by y^2 = x^3 + 10*x + 7 over Finite Field of size 13]

        sage: E = EllipticCurve(GF(13), [0,1,1,1,0])
        sage: isogenies_prime_degree_genus_plus_0(E)
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 10*x + 1 over Finite Field of size 13,
         Isogeny of degree 17
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 4 over Finite Field of size 13,
         Isogeny of degree 29
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 6 over Finite Field of size 13,
         Isogeny of degree 29
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 5*x + 6 over Finite Field of size 13,
         Isogeny of degree 41
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 4 over Finite Field of size 13,
         Isogeny of degree 41
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 5*x + 6 over Finite Field of size 13]
    """
    if l is None:
        return sum([isogenies_prime_degree_genus_plus_0(E, ell, minimal_models=minimal_models)
                    for ell in hyperelliptic_primes],[])

    if l not in hyperelliptic_primes:
        raise ValueError(f"{l} must be one of {hyperelliptic_primes}")

    F = E.base_ring()
    j = E.j_invariant()
    if F.characteristic() in [2, 3, l]:
        raise NotImplementedError("11, 17, 19, 23, 29, 31, 41, 47, 59, and 71-isogenies are not yet implemented in characteristic 2 and 3, and when the characteristic is the same as the degree of the isogeny")

    if not j:
        return isogenies_prime_degree_genus_plus_0_j0(E, l, minimal_models=minimal_models)
    if j == 1728:
        return isogenies_prime_degree_genus_plus_0_j1728(E, l, minimal_models=minimal_models)

    Fu, u = PolynomialRing(F, 'u').objgen()
    Fuv = PolynomialRing(F, ['u','v'])
    Fxuv = PolynomialRing(F, ['x','u','v'])
    data = _hyperelliptic_isogeny_data(l)
    a = Fu(data['alpha'])
    b = Fu(data['beta'])
    f = Fu(data['hyper_poly'])
    Q = Fu((a**2 - f*b**2) / 4)
    u_list = (j**2 - a*j + Q).roots(multiplicities=False)

    S = []
    for u0 in u_list:
        if b(u0) == 0:
            S.extend([u0, v0] for v0 in f(u0).sqrt(all=True, extend=False))
        else:
            S.append([u0, (2*j - a(u0)) / b(u0)])
    if not S:
        return []
    S.sort()

    c4, c6 = E.c_invariants()
    b2 = E.b2()
    kernels = []

    psi = Fxuv(Psi2(l))
    for u0, v0 in S:
        A4 = Fuv(data['A4'])(u0, v0)  # nonzero since j!=0
        A6 = Fuv(data['A6'])(u0, v0)  # nonzero since j!=1728
        T = (c4*A6) / (2*c6*A4)
        kernels.append(psi((36*u + 3*b2) * T, u0, v0).monic())

    return [E.isogeny(ker) for ker in kernels]


def isogenies_prime_degree_genus_plus_0_j0(E, l, minimal_models=True):
    """
    Return a list of hyperelliptic ``l``-isogenies with domain ``E`` when `j(E)=0`.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 0

    - ``l`` -- 11, 17, 19, 23, 29, 31, 41, 47, 59, or 71

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) a list of all isogenies of degree 11, 17, 19, 23, 29, 31, 41, 47, 59, or 71.

    .. NOTE::

        This implementation requires that the characteristic is not 2, 3 or ``l``.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(l)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree_genus_plus_0_j0

        sage: u = polygen(QQ)
        sage: K.<a> = NumberField(u^4 + 228*u^3 + 486*u^2 - 540*u + 225)                # needs sage.rings.number_field
        sage: E = EllipticCurve(K, [0, -121/5*a^3 - 20691/5*a^2 - 29403/5*a + 3267])    # needs sage.rings.number_field
        sage: isogenies_prime_degree_genus_plus_0_j0(E, 11)                             # needs sage.rings.number_field
        [Isogeny of degree 11
          from Elliptic Curve defined by
               y^2 = x^3 + (-121/5*a^3-20691/5*a^2-29403/5*a+3267) over
               Number Field in a with defining polynomial x^4 + 228*x^3 + 486*x^2 - 540*x + 225
            to Elliptic Curve defined by
               y^2 = x^3 + (-44286*a^2+178596*a-32670)*x
                      + (-17863351/5*a^3+125072739/5*a^2-74353653/5*a-682803) over
               Number Field in a with defining polynomial x^4 + 228*x^3 + 486*x^2 - 540*x + 225,
         Isogeny of degree 11
          from Elliptic Curve defined by
               y^2 = x^3 + (-121/5*a^3-20691/5*a^2-29403/5*a+3267) over
               Number Field in a with defining polynomial x^4 + 228*x^3 + 486*x^2 - 540*x + 225
            to Elliptic Curve defined by
               y^2 = x^3 + (-3267*a^3-740157*a^2+600039*a-277695)*x
                      + (-17863351/5*a^3-4171554981/5*a^2+3769467867/5*a-272366523) over
               Number Field in a with defining polynomial x^4 + 228*x^3 + 486*x^2 - 540*x + 225]

        sage: E = EllipticCurve(GF(5^6,'a'),[0,1])
        sage: isogenies_prime_degree_genus_plus_0_j0(E, 17)
        [Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6, Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in a of size 5^6 to Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field in a of size 5^6]
    """
    if l not in hyperelliptic_primes:
        raise ValueError(f"{l} must be one of {hyperelliptic_primes}")
    F = E.base_field()
    if E.j_invariant() != 0:
        raise ValueError("j-invariant must be 0")
    if F.characteristic() in [2, 3, l]:
        raise NotImplementedError("not implemented in characteristic 2, 3 or l")

    Fu, u = PolynomialRing(F, 'u').objgen()
    Fuv = PolynomialRing(F, ['u','v'])
    Fxuv = PolynomialRing(F, ['x','u','v'])
    data = _hyperelliptic_isogeny_data(l)
    a = Fu(data['alpha'])
    b = Fu(data['beta'])
    f = Fu(data['hyper_poly'])
    Q = Fu((a**2 - f*b**2) / 4)
    u_list = Q.roots(multiplicities=False)
    c6, b2 = E.c6(), E.b2()
    kernels = []

    if l % 3 == 1 and F(-3).is_square():
        endo = Fxuv(data['endo'])
        for p in F(-3).sqrt(all=True, extend=False):
            kernels.append(endo(36*u + 3*b2, p, -54*c6).monic())

    S = []
    for u0 in u_list:
        if l % 3 == 1 and u0 == F(data['endo_u']):
            continue
        if b(u0) == 0:
            S.extend([u0, v0] for v0 in f(u0).sqrt(all=True, extend=False))
        else:
            S.append([u0, -a(u0) / b(u0)])
    if not S and not kernels:
        return []
    S.sort()

    psi = Fxuv(Psi2(l))
    for u0, v0 in S:
        A6 = Fuv(data['A6'])(u0, v0)  # nonzero since j!=1728
        kernels.extend(psi((36*u + 3*b2) * T, u0, v0).monic() for T in (u**3 - A6/(-54*c6)).roots(multiplicities=False))

    return [E.isogeny(ker) for ker in kernels]


def isogenies_prime_degree_genus_plus_0_j1728(E, l, minimal_models=True):
    """
    Return a list of ``l``-isogenies with domain ``E`` when `j(E)=1728`.

    INPUT:

    - ``E`` -- an elliptic curve with j-invariant 1728

    - ``l`` -- 11, 17, 19, 23, 29, 31, 41, 47, 59, or 71

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    (list) a list of all isogenies of degree 11, 17, 19, 23, 29, 31, 41, 47, 59, or 71.

    .. NOTE::

        This implementation requires that the characteristic is not 2, 3 or ``l``.

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree(l)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree_genus_plus_0_j1728

        sage: # needs sage.rings.number_field
        sage: u = polygen(QQ)
        sage: K.<a> = NumberField(u^6 - 522*u^5 - 10017*u^4
        ....:                     + 2484*u^3 - 5265*u^2 + 12150*u - 5103)
        sage: E = EllipticCurve(K, [-75295/1335852*a^5 + 13066735/445284*a^4
        ....:                       + 44903485/74214*a^3 + 17086861/24738*a^2
        ....:                       + 11373021/16492*a - 1246245/2356, 0])
        sage: isogenies_prime_degree_genus_plus_0_j1728(E, 11)
        [Isogeny of degree 11
          from Elliptic Curve defined by
               y^2 = x^3 + (-75295/1335852*a^5+13066735/445284*a^4+44903485/74214*a^3+17086861/24738*a^2+11373021/16492*a-1246245/2356)*x
               over Number Field in a with defining polynomial
               x^6 - 522*x^5 - 10017*x^4 + 2484*x^3 - 5265*x^2 + 12150*x - 5103
            to Elliptic Curve defined by
               y^2 = x^3 + (9110695/1335852*a^5-1581074935/445284*a^4-5433321685/74214*a^3-3163057249/24738*a^2+1569269691/16492*a+73825125/2356)*x
                      + (-3540460*a^3+30522492*a^2-7043652*a-5031180)
               over Number Field in a with defining polynomial
               x^6 - 522*x^5 - 10017*x^4 + 2484*x^3 - 5265*x^2 + 12150*x - 5103,
         Isogeny of degree 11
          from Elliptic Curve defined by
               y^2 = x^3 + (-75295/1335852*a^5+13066735/445284*a^4+44903485/74214*a^3+17086861/24738*a^2+11373021/16492*a-1246245/2356)*x
               over Number Field in a with defining polynomial
               x^6 - 522*x^5 - 10017*x^4 + 2484*x^3 - 5265*x^2 + 12150*x - 5103
            to Elliptic Curve defined by
               y^2 = x^3 + (9110695/1335852*a^5-1581074935/445284*a^4-5433321685/74214*a^3-3163057249/24738*a^2+1569269691/16492*a+73825125/2356)*x
                      + (3540460*a^3-30522492*a^2+7043652*a+5031180)
               over Number Field in a with defining polynomial
               x^6 - 522*x^5 - 10017*x^4 + 2484*x^3 - 5265*x^2 + 12150*x - 5103]
        sage: i = QuadraticField(-1,'i').gen()
        sage: E = EllipticCurve([-1 - 2*i, 0])
        sage: isogenies_prime_degree_genus_plus_0_j1728(E, 17)
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 = x^3 + (-2*i-1)*x
               over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
            to Elliptic Curve defined by y^2 = x^3 + (-82*i-641)*x
               over Number Field in i with defining polynomial x^2 + 1 with i = 1*I,
         Isogeny of degree 17
          from Elliptic Curve defined by y^2 = x^3 + (-2*i-1)*x
               over Number Field in i with defining polynomial x^2 + 1 with i = 1*I
            to Elliptic Curve defined by y^2 = x^3 + (-562*i+319)*x
               over Number Field in i with defining polynomial x^2 + 1 with i = 1*I]
        sage: Emin = E.global_minimal_model()
        sage: [(p, len(isogenies_prime_degree_genus_plus_0_j1728(Emin, p)))
        ....:  for p in [17, 29, 41]]
        [(17, 2), (29, 2), (41, 2)]
    """
    if l not in hyperelliptic_primes:
        raise ValueError(f"{l} must be one of {hyperelliptic_primes}")
    F = E.base_ring()
    if E.j_invariant() != 1728:
        raise ValueError("j-invariant must be 1728")
    if F.characteristic() in [2, 3, l]:
        raise NotImplementedError("not implemented in characteristic 2, 3 or l")

    Fu, u = PolynomialRing(F, 'u').objgen()
    Fuv = PolynomialRing(F, ['u','v'])
    Fxuv = PolynomialRing(F, ['x','u','v'])
    data = _hyperelliptic_isogeny_data(l)
    a = Fu(data['alpha'])
    b = Fu(data['beta'])
    f = Fu(data['hyper_poly'])
    Q = Fu((a**2 - f*b**2) / 4)
    u_list = (1728**2 - a*1728 + Q).roots(multiplicities=False)
    c4, b2 = E.c4(), E.b2()
    kernels = []

    if l % 4 == 1 and F(-1).is_square():
        endo = Fxuv(data['endo'])
        for i in F(-1).sqrt(all=True, extend=False):
            kernels.append(endo(36*u + 3*b2, i, -27*c4).monic())

    S = []
    for u0 in u_list:
        if l % 4 == 1 and u0 == F(data['endo_u']):
            continue
        if b(u0) == 0:
            S.extend([u0, v0] for v0 in f(u0).sqrt(all=True, extend=False))
        else:
            S.append([u0, (2*1728 - a(u0)) / b(u0)])
    if not S and not kernels:
        return []
    S.sort()

    psi = Fxuv(Psi2(l))
    for u0, v0 in S:
        A4 = Fuv(data['A4'])(u0, v0)  # nonzero since j!=0
        kernels.extend(psi((36*u + 3*b2) * T, u0, v0).monic() for T in (A4 / (-27*c4)).sqrt(all=True, extend=False))

    return [E.isogeny(ker) for ker in kernels]


@cached_function
def _least_semi_primitive(p):
    r"""
    Return the smallest semi-primitive root modulo `p`, i.e., generator of the group `(\ZZ/p\ZZ)^*/\{1,-1\}`.

    INPUT:

    - ``p`` -- an odd prime power

    OUTPUT: the smallest semi-primitive root modulo `p`

    .. NOTE::

        This function would normally be invoked indirectly via ``E.isogenies_prime_degree_general(l)``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import _least_semi_primitive
        sage: _least_semi_primitive(5)
        2
        sage: _least_semi_primitive(13)
        2
        sage: _least_semi_primitive(17)
        3
        sage: _least_semi_primitive(73)
        5
        sage: _least_semi_primitive(997)
        7
    """
    if p % 2 == 0 or not p.is_prime_power():
        raise ValueError("{} is not an odd prime power".format(p))

    from sage.arith.misc import euler_phi
    from sage.rings.finite_rings.integer_mod_ring import Integers
    phi_p = euler_phi(p)
    order = phi_p if p % 4 == 1 else phi_p // 2
    R = Integers(p)
    return next((a for a in range(2, p) if p.gcd(a).is_one()
                 and R(a).multiplicative_order() >= order), 0)


def is_kernel_polynomial(E, m, f):
    r"""
    Test whether ``E`` has a cyclic isogeny of degree ``m`` with kernel
    polynomial ``f``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``m`` -- positive integer

    - ``f`` -- a polynomial over the base field of ``E``

    OUTPUT:

    boolean; ``True`` if ``E`` has a cyclic isogeny of degree ``m`` with
    kernel polynomial ``f``, else ``False``.

    ALGORITHM:

    `f` must have degree `(m-1)/2` (if `m` is odd) or degree `m/2` (if
    `m` is even), and have the property that for each root `x` of `f`,
    `\mu(x)` is also a root where `\mu` is the multiplication-by-`m`
    map on `E` and `m` runs over a set of generators of
    `(\ZZ/m\ZZ)^*/\{1,-1\}`.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import is_kernel_polynomial
        sage: E = EllipticCurve([0, -1, 1, -10, -20])
        sage: x = polygen(QQ)
        sage: is_kernel_polynomial(E, 5, x^2 + x - 29/5)
        True
        sage: is_kernel_polynomial(E, 5, (x - 16) * (x - 5))
        True

    An example from [KT2013]_, where the 13-division polynomial splits
    into 14 factors each of degree 6, but only two of these is a
    kernel polynomial for a 13-isogeny::

        sage: F = GF(3)
        sage: E = EllipticCurve(F, [0,0,0,-1,0])
        sage: f13 = E.division_polynomial(13)
        sage: factors = [f for f, e in f13.factor()]
        sage: all(f.degree() == 6 for f in factors)
        True
        sage: [is_kernel_polynomial(E, 13, f) for f in factors]
        [True,
        True,
        False,
        False,
        False,
        False,
        False,
        False,
        False,
        False,
        False,
        False,
        False,
        False]

    See :issue:`22232`::

        sage: # needs sage.rings.finite_rings
        sage: K = GF(47^2)
        sage: E = EllipticCurve([0, K.gen()])
        sage: psi7 = E.division_polynomial(7)
        sage: f = psi7.factor()[4][0]
        sage: f
        x^3 + (7*z2 + 11)*x^2 + (25*z2 + 33)*x + 25*z2
        sage: f.divides(psi7)
        True
        sage: is_kernel_polynomial(E, 7, f)
        False
    """
    m = ZZ(m)
    m2 = m // 2
    if f.degree() != m2:
        return False
    if m.is_one():
        return True

    # Compute the quotient polynomial ring mod (f)
    S = f.parent().quotient_ring(f)

    # test if the m-division polynomial is a multiple of f by computing it in the quotient:
    if E.division_polynomial(m, x=S.gen()):
        return False

    if m == 2 or m == 3:
        return True

    # For each a in a set of generators of (Z/mZ)^*/{1,-1} we check
    # that the multiplication-by-a map permutes the roots of f.
    # If m is prime (or more generally, has a primitive root) then
    # only one a will be needed.

    if m & 1 and m.is_prime_power():
        gens = _least_semi_primitive(m),
    else:
        from sage.rings.finite_rings.integer_mod_ring import Integers
        gens = Integers(m).unit_gens()

    for a in gens:
        mu = E.multiplication_by_m(a, x_only=True)
        if f(S(mu.numerator()) / S(mu.denominator())):
            return False
    return True


def isogenies_prime_degree_general(E, l, minimal_models=True):
    """
    Return all separable ``l``-isogenies with domain ``E``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``l`` -- a prime

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.

    OUTPUT:

    A list of all separable isogenies of degree `l` with domain ``E``
    (up to post-isomorphism).

    ALGORITHM:

    This algorithm factors the ``l``-division polynomial, then
    combines its factors to obtain kernels.
    Originally this was done using [KT2013]_, Chapter 3, but nowadays
    the recombination step is instead delegated to
    :meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.kernel_polynomial_from_divisor`.

    .. NOTE::

        This function works for any prime `l`.  Normally one should use
        the function :meth:`isogenies_prime_degree` which uses special
        functions for certain small primes.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree_general
        sage: E = EllipticCurve_from_j(GF(2^6,'a')(1))                                  # needs sage.rings.finite_rings
        sage: isogenies_prime_degree_general(E, 7)                                      # needs sage.rings.finite_rings
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 + x*y = x^3 + 1
               over Finite Field in a of size 2^6
            to Elliptic Curve defined by y^2 + x*y = x^3 + x
               over Finite Field in a of size 2^6]
        sage: E = EllipticCurve_from_j(GF(3^12,'a')(2))                                 # needs sage.rings.finite_rings
        sage: isogenies_prime_degree_general(E, 17)                                     # needs sage.rings.finite_rings
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2
               over Finite Field in a of size 3^12
            to Elliptic Curve defined by y^2 = x^3 + 2*x^2 + x + 2
               over Finite Field in a of size 3^12,
         Isogeny of degree 17
          from Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2
               over Finite Field in a of size 3^12
            to Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2*x
               over Finite Field in a of size 3^12]
        sage: E = EllipticCurve('50a1')
        sage: isogenies_prime_degree_general(E, 3)
        [Isogeny of degree 3
          from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 - 126*x - 552
               over Rational Field]
        sage: isogenies_prime_degree_general(E, 5)
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 - 76*x + 298
               over Rational Field]
        sage: E = EllipticCurve([0, 0, 1, -1862, -30956])
        sage: isogenies_prime_degree_general(E, 19)
        [Isogeny of degree 19
          from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956
               over Rational Field
            to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489
               over Rational Field]
        sage: E = EllipticCurve([0, -1, 0, -6288, 211072])
        sage: isogenies_prime_degree_general(E, 37)  # long time (2s)
        [Isogeny of degree 37
          from Elliptic Curve defined by y^2 = x^3 - x^2 - 6288*x + 211072
               over Rational Field
            to Elliptic Curve defined by y^2 = x^3 - x^2 - 163137088*x - 801950801728
               over Rational Field]

        sage: E = EllipticCurve([-3440, 77658])
        sage: isogenies_prime_degree_general(E, 43)  # long time (2s)
        [Isogeny of degree 43
          from Elliptic Curve defined by y^2 = x^3 - 3440*x + 77658 over Rational Field
            to Elliptic Curve defined by y^2 = x^3 - 6360560*x - 6174354606
               over Rational Field]

    Isogenies of degree equal to the characteristic are computed (but
    only the separable isogeny).  In the following example we consider
    an elliptic curve which is supersingular in characteristic 2
    only::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree_general
        sage: ainvs = (0,1,1,-1,-1)
        sage: for l in prime_range(50):
        ....:     E = EllipticCurve(GF(l), ainvs)
        ....:     isogenies_prime_degree_general(E, l)
        []
        [Isogeny of degree 3
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 2*x + 2
               over Finite Field of size 3
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 3]
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 4*x + 4
               over Finite Field of size 5
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 4*x + 4
               over Finite Field of size 5]
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 6*x + 6
               over Finite Field of size 7
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 4 over Finite Field of size 7]
        [Isogeny of degree 11
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 10*x + 10
               over Finite Field of size 11
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + x + 1
               over Finite Field of size 11]
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 12
               over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 12
               over Finite Field of size 13]
        [Isogeny of degree 17 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 16*x + 16 over Finite Field of size 17 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 15 over Finite Field of size 17]
        [Isogeny of degree 19 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 18*x + 18 over Finite Field of size 19 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 3*x + 12 over Finite Field of size 19]
        [Isogeny of degree 23 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 22*x + 22 over Finite Field of size 23 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 22*x + 22 over Finite Field of size 23]
        [Isogeny of degree 29 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 28*x + 28 over Finite Field of size 29 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 7*x + 27 over Finite Field of size 29]
        [Isogeny of degree 31 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 30*x + 30 over Finite Field of size 31 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 15*x + 16 over Finite Field of size 31]
        [Isogeny of degree 37 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 36*x + 36 over Finite Field of size 37 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 16*x + 17 over Finite Field of size 37]
        [Isogeny of degree 41 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 40*x + 40 over Finite Field of size 41 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 33*x + 16 over Finite Field of size 41]
        [Isogeny of degree 43 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 42*x + 42 over Finite Field of size 43 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 36 over Finite Field of size 43]
        [Isogeny of degree 47 from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 46*x + 46 over Finite Field of size 47 to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 42*x + 34 over Finite Field of size 47]

    Note that not all factors of degree `(l-1)/2` of the `l`-division
    polynomial are kernel polynomials.  In this example, the
    13-division polynomial factors as a product of 14 irreducible
    factors of degree 6 each, but only two those are kernel
    polynomials::

        sage: F3 = GF(3)
        sage: E = EllipticCurve(F3, [0,0,0,-1,0])
        sage: Psi13 = E.division_polynomial(13)
        sage: len([f for f, e in Psi13.factor() if f.degree() == 6])
        14
        sage: len(E.isogenies_prime_degree(13))
        2

    Over GF(9) the other factors of degree 6 split into pairs of
    cubics which can be rearranged to give the remaining 12 kernel
    polynomials::

        sage: len(E.change_ring(GF(3^2,'a')).isogenies_prime_degree(13))                # needs sage.rings.finite_rings
        14

    See :issue:`18589`: the following example took 20s before, now only 4s::

        sage: K.<i> = QuadraticField(-1)                                                # needs sage.rings.number_field
        sage: E = EllipticCurve(K,[0,0,0,1,0])                                          # needs sage.rings.number_field
        sage: [phi.codomain().ainvs()                   # long time                     # needs sage.rings.number_field
        ....:  for phi in E.isogenies_prime_degree(37)]
        [(0, 0, 0, 840*i + 1081, 0),
         (0, 0, 0, -840*i + 1081, 0)]
    """
    if not l.is_prime():
        raise ValueError(f"{l} is not prime")
    if l == 2:
        return isogenies_2(E, minimal_models=minimal_models)
    if l == 3:
        return isogenies_3(E, minimal_models=minimal_models)

    psi_l = E.division_polynomial(l)

    factors = [h for h,_ in psi_l.factor() if h.degree().divides(l//2)]

    kernels = []  # will store all kernel polynomials found

    while factors:
        h = factors.pop()
        try:
            ker = E.kernel_polynomial_from_divisor(h, l, check=False)
        except ValueError:
            continue
        assert ker.degree() == l//2 and ker.divides(psi_l)
        kernels.append(ker)
        factors = [h for h in factors if not h.divides(ker)]

    return [E.isogeny(ker, check=False) for ker in kernels]


def isogenies_prime_degree(E, l, minimal_models=True):
    """
    Return all separable ``l``-isogenies with domain ``E``.

    INPUT:

    - ``E`` -- an elliptic curve

    - ``l`` -- a prime

    - ``minimal_models`` -- boolean (default: ``True``); if ``True``, all
      curves computed will be minimal or semi-minimal models.  Over
      fields of larger degree it can be expensive to compute these so
      set to ``False``.  Ignored except over number fields other than
      `QQ`.

    OUTPUT: list of all separable isogenies of degree `l` with domain ``E``

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree
        sage: E = EllipticCurve_from_j(GF(2^6,'a')(1))                                  # needs sage.rings.finite_rings
        sage: isogenies_prime_degree(E, 7)                                              # needs sage.rings.finite_rings
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 + x*y = x^3 + 1
               over Finite Field in a of size 2^6
            to Elliptic Curve defined by y^2 + x*y = x^3 + x
               over Finite Field in a of size 2^6]
        sage: E = EllipticCurve_from_j(GF(3^12,'a')(2))                                 # needs sage.rings.finite_rings
        sage: isogenies_prime_degree(E, 17)                                             # needs sage.rings.finite_rings
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2
               over Finite Field in a of size 3^12
            to Elliptic Curve defined by y^2 = x^3 + 2*x^2 + x + 2
               over Finite Field in a of size 3^12,
         Isogeny of degree 17
          from Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2
               over Finite Field in a of size 3^12
            to Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2*x
               over Finite Field in a of size 3^12]
        sage: E = EllipticCurve('50a1')
        sage: isogenies_prime_degree(E, 3)
        [Isogeny of degree 3
          from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 - 126*x - 552 over Rational Field]
        sage: isogenies_prime_degree(E, 5)
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 + x*y + y = x^3 - x - 2 over Rational Field
            to Elliptic Curve defined by y^2 + x*y + y = x^3 - 76*x + 298 over Rational Field]
        sage: E = EllipticCurve([0, 0, 1, -1862, -30956])
        sage: isogenies_prime_degree(E, 19)
        [Isogeny of degree 19
          from Elliptic Curve defined by y^2 + y = x^3 - 1862*x - 30956
               over Rational Field
            to Elliptic Curve defined by y^2 + y = x^3 - 672182*x + 212325489
               over Rational Field]
        sage: E = EllipticCurve([0, -1, 0, -6288, 211072])
        sage: isogenies_prime_degree(E, 37)
        [Isogeny of degree 37
          from Elliptic Curve defined by y^2 = x^3 - x^2 - 6288*x + 211072
               over Rational Field
            to Elliptic Curve defined by y^2 = x^3 - x^2 - 163137088*x - 801950801728
               over Rational Field]

    Isogenies of degree equal to the characteristic are computed (but
    only the separable isogeny).  In the following example we consider
    an elliptic curve which is supersingular in characteristic 2
    only::

        sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_prime_degree
        sage: ainvs = (0,1,1,-1,-1)
        sage: for l in prime_range(50):
        ....:     E = EllipticCurve(GF(l), ainvs)
        ....:     isogenies_prime_degree(E, l)
        []
        [Isogeny of degree 3
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 2*x + 2 over Finite Field of size 3
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + x over Finite Field of size 3]
        [Isogeny of degree 5
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 4*x + 4 over Finite Field of size 5
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 4*x + 4 over Finite Field of size 5]
        [Isogeny of degree 7
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 6*x + 6 over Finite Field of size 7
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 4 over Finite Field of size 7]
        [Isogeny of degree 11
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 10*x + 10 over Finite Field of size 11
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + x + 1 over Finite Field of size 11]
        [Isogeny of degree 13
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 12 over Finite Field of size 13
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 12*x + 12 over Finite Field of size 13]
        [Isogeny of degree 17
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 16*x + 16 over Finite Field of size 17
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 15 over Finite Field of size 17]
        [Isogeny of degree 19
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 18*x + 18 over Finite Field of size 19
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 3*x + 12 over Finite Field of size 19]
        [Isogeny of degree 23
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 22*x + 22 over Finite Field of size 23
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 22*x + 22 over Finite Field of size 23]
        [Isogeny of degree 29
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 28*x + 28 over Finite Field of size 29
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 7*x + 27 over Finite Field of size 29]
        [Isogeny of degree 31
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 30*x + 30 over Finite Field of size 31
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 15*x + 16 over Finite Field of size 31]
        [Isogeny of degree 37
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 36*x + 36 over Finite Field of size 37
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 16*x + 17 over Finite Field of size 37]
        [Isogeny of degree 41
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 40*x + 40 over Finite Field of size 41
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 33*x + 16 over Finite Field of size 41]
        [Isogeny of degree 43
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 42*x + 42 over Finite Field of size 43
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 36 over Finite Field of size 43]
        [Isogeny of degree 47
          from Elliptic Curve defined by y^2 + y = x^3 + x^2 + 46*x + 46 over Finite Field of size 47
            to Elliptic Curve defined by y^2 + y = x^3 + x^2 + 42*x + 34 over Finite Field of size 47]

    Note that the computation is faster for degrees equal to one of
    the genus 0 primes (2, 3, 5, 7, 13) or one of the hyperelliptic
    primes (11, 17, 19, 23, 29, 31, 41, 47, 59, 71) than when the
    generic code must be used::

        sage: E = EllipticCurve(GF(101), [-3440, 77658])
        sage: E.isogenies_prime_degree(71)  # fast
        []
        sage: E.isogenies_prime_degree(73)      # long time
        []

    Test that :issue:`32269` is fixed::

        sage: K = QuadraticField(-11)                                                   # needs sage.rings.number_field
        sage: E = EllipticCurve(K, [0,1,0,-117,-541])                                   # needs sage.rings.number_field
        sage: E.isogenies_prime_degree(37)      # long time                             # needs sage.rings.number_field
        [Isogeny of degree 37
          from Elliptic Curve defined by y^2 = x^3 + x^2 + (-117)*x + (-541)
               over Number Field in a with defining polynomial x^2 + 11
               with a = 3.316624790355400?*I
            to Elliptic Curve defined by
               y^2 = x^3 + x^2 + (-30800*a+123963)*x + (-3931312*a-21805005)
               over Number Field in a with defining polynomial x^2 + 11
               with a = 3.316624790355400?*I,
         Isogeny of degree 37
          from Elliptic Curve defined by y^2 = x^3 + x^2 + (-117)*x + (-541)
               over Number Field in a with defining polynomial x^2 + 11
               with a = 3.316624790355400?*I
            to Elliptic Curve defined by
               y^2 = x^3 + x^2 + (30800*a+123963)*x + (3931312*a-21805005)
               over Number Field in a with defining polynomial x^2 + 11
               with a = 3.316624790355400?*I]
    """
    if not l.is_prime():
        raise ValueError(f"{l} is not prime")
    if l == 2:
        return isogenies_2(E, minimal_models=minimal_models)
    if l == 3:
        return isogenies_3(E, minimal_models=minimal_models)

    p = E.base_ring().characteristic()
    if l == p:
        return isogenies_prime_degree_general(E, l, minimal_models=minimal_models)

    if l in [5, 7, 13] and p not in [2, 3]:
        return isogenies_prime_degree_genus_0(E, l, minimal_models=minimal_models)

    if l in hyperelliptic_primes and p not in [2, 3]:
        return isogenies_prime_degree_genus_plus_0(E, l, minimal_models=minimal_models)

    j = E.j_invariant()
    if j in QQ and E.base_field() is QQ:
        j = QQ(j)
        if j in sporadic_j:
            return isogenies_sporadic_Q(E, l, minimal_models=minimal_models)

    return isogenies_prime_degree_general(E, l, minimal_models=minimal_models)
