Repository: sagemath/sage

Issue:
add .twists() to EllipticCurve_finite_field
<div id="comment:0"></div>

This patch adds a method `.twists()` to elliptic curves over finite fields `k`, which computes a set of `k`-isomorphism representatives of the `K`-isomorphism class where `K` is the algebraic closure of `k`.

This can be useful, for instance, to compute a list of all elliptic curves defined over `k` up to `k`-isomorphism:

```sage
def all_curves(F):
    return [E for j in F for E in EllipticCurve(j=j).twists()]
```

CC:  @JohnCremona @defeo

Component: **elliptic curves**

Authors: **Lorenz Panny**, **John Cremona**

_Issue created by migration from https://trac.sagemath.org/ticket/34782_




Discussion:

Resolving patch:
diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py
--- a/src/sage/schemes/elliptic_curves/ell_field.py
+++ b/src/sage/schemes/elliptic_curves/ell_field.py
@@ -232,7 +232,7 @@ def quartic_twist(self, D):
 
         INPUT:
 
-        - ``D`` (must be nonzero) -- the twisting parameter..
+        - ``D`` (must be nonzero) -- the twisting parameter
 
         .. NOTE::
 
@@ -271,11 +271,11 @@ def quartic_twist(self, D):
 
     def sextic_twist(self, D):
         r"""
-        Return the quartic twist of this curve by `D`.
+        Return the sextic twist of this curve by `D`.
 
         INPUT:
 
-        - ``D`` (must be nonzero) -- the twisting parameter..
+        - ``D`` (must be nonzero) -- the twisting parameter
 
         .. NOTE::
 
diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py
--- a/src/sage/schemes/elliptic_curves/ell_finite_field.py
+++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py
@@ -10,7 +10,9 @@
 - John Cremona (2008-02): Point counting and group structure for
   non-prime fields, Frobenius endomorphism and order, elliptic logs
 
-- Mariah Lenox (2011-03): Added set_order method
+- Mariah Lenox (2011-03): Added ``set_order`` method
+
+- Lorenz Panny, John Cremona (2023-02): ``.twists()``
 """
 
 # ****************************************************************************
@@ -1353,6 +1355,482 @@ def _fetch_cached_order(self, other):
         if n is not None:
             self._order = n
 
+    def twists(self):
+        r"""
+        Return a list of `k`-isomorphism representatives of all
+        twists of this elliptic curve, where `k` is the base field.
+
+        The input curve appears as the first entry of the result.
+
+        .. NOTE::
+
+            A *twist* of `E/k` is an elliptic curve `E'` defined over
+            `k` that is isomorphic to `E` over the algebraic closure
+            `\bar k`.
+
+            Most elliptic curves over a finite field only admit a
+            single nontrivial twist (the quadratic twist); the only
+            exceptions are curves with `j`-invariant `0` or `1728`.
+
+            In all cases the sum over all the twists `E'` of `1/|Aut(E')|` is 1.
+
+        .. SEEALSO::
+
+            - :meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.quadratic_twist`
+            - :meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.quartic_twist`
+            - :meth:`~sage.schemes.elliptic_curves.ell_field.EllipticCurve_field.sextic_twist`
+
+        EXAMPLES::
+
+            sage: E = EllipticCurve(GF(97), [1,1])
+            sage: E.j_invariant()
+            54
+            sage: E.twists()
+            [Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97]
+
+        ::
+
+            sage: E = EllipticCurve(GF(97), [1,0])
+            sage: E.j_invariant()
+            79
+            sage: E.twists()
+            [Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97]
+
+        ::
+
+            sage: E = EllipticCurve(GF(97), [0,1])
+            sage: E.j_invariant()
+            0
+            sage: E.twists()
+            [Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97,
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 97]
+
+        This can be useful to quickly compute a list of all elliptic curves
+        over a finite field `k` up to `k`-isomorphism::
+
+            sage: Es = [E for j in GF(13) for E in EllipticCurve(j=j).twists()]
+            sage: len(Es)
+            32
+            sage: Es
+            [Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 13,
+             ...
+             Elliptic Curve defined by y^2 = x^3 + ... over Finite Field of size 13]
+
+        In characteristic 3, the number of twists is 2 except for
+        `j=0=1728`, when there are either 4 or 6 depending on whether the
+        field has odd or even degree over `\GF{3}`::
+
+            sage: K = GF(3**5)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(1)).twists()]
+            [(0, 1, 0, 0, 2), (0, z5, 0, 0, 2*z5^3)]
+
+            sage: K = GF(3**5)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(0)).twists()] # random
+            [(0, 0, 0, 1, 0),
+            (0, 0, 0, 2, 0),
+            (0, 0, 0, 2, z5^4 + z5^3 + z5^2),
+            (0, 0, 0, 2, 2*z5^4 + 2*z5^3 + 2*z5^2)]
+
+            sage: K = GF(3**4)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(1)).twists()]
+            [(0, 1, 0, 0, 2), (0, z4, 0, 0, 2*z4^3)]
+
+            sage: K = GF(3**4)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(0)).twists()] # random
+            [(0, 0, 0, 1, 0),
+            (0, 0, 0, 2, 2*z4^3 + 2*z4^2 + 2*z4 + 2),
+            (0, 0, 0, 1, 0),
+            (0, 0, 0, 1, 2*z4^3 + 2*z4^2 + 2*z4 + 2),
+            (0, 0, 0, z4, 0),
+            (0, 0, 0, z4^3, 0)]
+
+        In characteristic 2, the number of twists is 2 except for
+        `j=0=1728`, when there are either 3 or 7 depending on whether the
+        field has odd or even degree over `\GF{2}`::
+
+            sage: K = GF(2**7)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(1)).twists()]
+            [(1, 0, 0, 0, 1), (1, 1, 0, 0, 1)]
+
+            sage: K = GF(2**7)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(0)).twists()]
+            [(0, 0, 1, 0, 0), (0, 0, 1, 1, 0), (0, 0, 1, 1, 1)]
+
+            sage: K = GF(2**8)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(1)).twists()] # random
+            [(1, 0, 0, 0, 1), (1, z8^7 + z8^6 + z8^5 + z8^4 + z8^2 + z8, 0, 0, 1)]
+
+            sage: K = GF(2**8)
+            sage: [E.ainvs() for E in EllipticCurve(j=K(0)).twists()] # random
+            [(0, 0, 1, 0, 0),
+            (0, 0, 1, 0, z8^5 + z8^4 + z8^3),
+            (0, 0, 1, z8^6 + z8^5 + z8^2 + 1, 0),
+            (0, 0, z8^4 + z8^3 + z8^2 + 1, 0, 0),
+            (0, 0, z8^4 + z8^3 + z8^2 + 1, 0, z8^3 + z8^2 + 1),
+            (0, 0, z8^6 + z8^3 + z8^2, 0, 0),
+            (0, 0, z8^6 + z8^3 + z8^2, 0, z8^3 + z8^2)]
+
+        TESTS:
+
+        Randomized check that we find all twists and there are no duplicates::
+
+            sage: p = next_prime(randrange(2,100))
+            sage: e = randrange(1,10)
+            sage: F.<t> = GF((p,e))
+            sage: while True:
+            ....:     try:
+            ....:         E = EllipticCurve([F.random_element() for _ in range(5)])
+            ....:     except ArithmeticError:
+            ....:         pass
+            ....:     else:
+            ....:         break
+            sage: twists1 = E.twists()
+            sage: {sum(E1.is_isomorphic(E2) for E2 in twists1) == 1 for E1 in twists1}
+            {True}
+            sage: j = E.j_invariant()
+            sage: A,B = polygens(F, 'A,B')
+            sage: eq = 1728*4*A**3 - j * (4*A**3 + 27*B**2)
+            sage: twists2 = []
+            sage: for _ in range(10):
+            ....:     V = Ideal([eq, A + B - F.random_element()]).variety()
+            ....:     if not V:
+            ....:         continue
+            ....:     sol = choice(V)
+            ....:     a, b = sol[A], sol[B]
+            ....:     try:
+            ....:         twists2.append(EllipticCurve([a, b]))
+            ....:     except ArithmeticError:
+            ....:         pass
+            sage: all(any(E2.is_isomorphic(E1) for E1 in twists1) for E2 in twists2)
+            True
+        """
+        K = self.base_field()
+        j = self.j_invariant()
+        twists = None
+        if not j:
+            twists = curves_with_j_0(K)
+        elif j==1728:
+            twists = curves_with_j_1728(K)
+        if twists: # i.e. if j=0 or 1728
+            # replace the one isomorphic to self with self and move to front
+            for i,t in enumerate(twists):
+                if self.is_isomorphic(t):
+                    twists[i] = twists[0]
+                    twists[0] = self
+                break
+            return twists
+
+        # Now j is not 0 or 1728, and we only have a quadratic twist
+
+        if K.characteristic() == 2: # find D with trace 1 for the additive twist
+            D = K.one() # will work if degree is odd
+            while D.trace() == 0:
+                D = K.random_element()
+        else: # find a nonsquare D
+            D = K.gen()
+            q2 = (K.cardinality()-1)//2
+            while not D or D**q2 == 1:
+                D = K.random_element()
+        return [self, self.quadratic_twist(D)]
+
+def curves_with_j_0(K):
+    r"""
+    Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 0 over the finite field `K`.
+
+        .. NOTE::
+
+            In characteristics 2 and 3 this function simply calls ``curves_with_j_0_char2`` or
+            ``curves_with_j_0_char3``.  Otherwise there are either 2 or 6 curves, parametrised by
+            `K^*/(K^*)^6`.
+
+    Examples:
+
+    For `K=\GF{q}` where `q\equiv1\mod{6}` there are six curves, the sextic twists of `y^2=x^3+1`::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_0
+        sage: sorted(curves_with_j_0(GF(7)), key = lambda E: E.a_invariants())
+        [Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7,
+        Elliptic Curve defined by y^2 = x^3 + 2 over Finite Field of size 7,
+        Elliptic Curve defined by y^2 = x^3 + 3 over Finite Field of size 7,
+        Elliptic Curve defined by y^2 = x^3 + 4 over Finite Field of size 7,
+        Elliptic Curve defined by y^2 = x^3 + 5 over Finite Field of size 7,
+        Elliptic Curve defined by y^2 = x^3 + 6 over Finite Field of size 7]
+        sage: curves_with_j_0(GF(25))
+        [Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 5^2,
+        Elliptic Curve defined by y^2 = x^3 + z2 over Finite Field in z2 of size 5^2,
+        Elliptic Curve defined by y^2 = x^3 + (z2+3) over Finite Field in z2 of size 5^2,
+        Elliptic Curve defined by y^2 = x^3 + (4*z2+3) over Finite Field in z2 of size 5^2,
+        Elliptic Curve defined by y^2 = x^3 + (2*z2+2) over Finite Field in z2 of size 5^2,
+        Elliptic Curve defined by y^2 = x^3 + (4*z2+1) over Finite Field in z2 of size 5^2]
+
+    For `K=\GF{q}` where `q\equiv5\mod{6}` there are two curves,
+    quadratic twists of each other by `-3`: `y^2=x^3+1` and
+    `y^2=x^3-27`::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_0
+        sage: curves_with_j_0(GF(5))
+        [Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 5,
+        Elliptic Curve defined by y^2 = x^3 + 3 over Finite Field of size 5]
+        sage: curves_with_j_0(GF(11))
+        [Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 11,
+        Elliptic Curve defined by y^2 = x^3 + 6 over Finite Field of size 11]
+    """
+    if not K.is_finite():
+        raise ValueError("field must be finite")
+    p = K.characteristic()
+    if p==2:
+        return curves_with_j_0_char2(K)
+    if p==3:
+        return curves_with_j_0_char3(K)
+    q = K.cardinality()
+    if q%3==2:
+        # Then we only have two quadratic twists (and -3 is non-square)
+        return [EllipticCurve(K, [0,a]) for a in [1,-27]]
+    # Now we have genuine sextic twists, find D generating K* mod 6th powers
+    q2 = (q-1)//2
+    q3 = (q-1)//3
+    D = K.gen()
+    while not D or D**q2 == 1 or D**q3==1:
+        D = K.random_element()
+    return [EllipticCurve(K, [0,D**i]) for i in range(6)]
+
+def curves_with_j_1728(K):
+    r"""
+    Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 1728 over the finite field `K`.
+
+        .. NOTE::
+
+            In characteristics 2 and 3 (so 0=1728) this function simply calls ``curves_with_j_0_char2`` or
+            ``curves_with_j_0_char3``.  Otherwise there are either 2 or 4 curves, parametrised by
+            `K^*/(K^*)^4`.
+
+    Examples:
+
+    For `K=\GF{q}` where `q\equiv1\mod{4} there are four curves, the quartic twists of `y^2=x^3+x`::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_1728
+        sage: sorted(curves_with_j_1728(GF(5)), key = lambda E: E.a_invariants())
+        [Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 5,
+        Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 5,
+        Elliptic Curve defined by y^2 = x^3 + 3*x over Finite Field of size 5,
+        Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 5]
+        sage: curves_with_j_1728(GF(49))
+        [Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 7^2,
+        Elliptic Curve defined by y^2 = x^3 + z2*x over Finite Field in z2 of size 7^2,
+        Elliptic Curve defined by y^2 = x^3 + (z2+4)*x over Finite Field in z2 of size 7^2,
+        Elliptic Curve defined by y^2 = x^3 + (5*z2+4)*x over Finite Field in z2 of size 7^2]
+
+    For `K=\GF{q}` where `q\equiv3\mod{4} there are two curves,
+    quadratic twists of each other by `-1`: `y^2=x^3+x` and
+    `y^2=x^3-x`::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_1728
+        sage: curves_with_j_1728(GF(7))
+        [Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7,
+        Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7]
+        sage: curves_with_j_1728(GF(11))
+        [Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 11,
+        Elliptic Curve defined by y^2 = x^3 + 10*x over Finite Field of size 11]
+    """
+    if not K.is_finite():
+        raise ValueError("field must be finite")
+    p = K.characteristic()
+    if p==2:
+        return curves_with_j_0_char2(K)
+    if p==3:
+        return curves_with_j_0_char3(K)
+    q = K.cardinality()
+    if q%4==3:
+        return [EllipticCurve(K, [a,0]) for a in [1,-1]]
+    # Now we have genuine quartic twists, find D generating K* mod 4th powers
+    q2 = (q-1)//2
+    D = K.gen()
+    while not D or D**q2 == 1:
+        D = K.random_element()
+    return [EllipticCurve(K, [D**i,0]) for i in range(4)]
+
+def curves_with_j_0_char2(K):
+    r"""
+    Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 0 over the finite field `K` of characteristic 2.
+
+        .. NOTE::
+
+            The number of twists is either 3 or 7 depending on whether
+            the field has odd or even degree over `\GF{2}`.  See
+            [Connell1999]_, pages 429-431.
+
+    Examples:
+
+    In odd degree, there are three isomorphism classes all with representatives defined over `\GF{2}`::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_0_char2
+        sage: K = GF(2**7)
+        sage: curves = curves_with_j_0_char2(K)
+        sage: len(curves)
+        3
+        sage: [E.ainvs() for E in curves]
+        [(0, 0, 1, 0, 0), (0, 0, 1, 1, 0), (0, 0, 1, 1, 1)]
+
+    Check that the curves are mutually non-isomorphic::
+
+        sage: all((e1 == e2 or not e1.is_isomorphic(e2)) for e1 in curves for e2 in curves)
+        True
+
+    Check that the weight formula holds::
+
+        sage: sum(1/len(E.automorphisms()) for E in curves) == 1
+        True
+
+    In even degree there are seven isomorphism classes::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_0_char2
+        sage: K = GF(2**8)
+        sage: curves = EllipticCurve(j=K(0)).twists()
+        sage: len(curves)
+        7
+        sage: [E.ainvs() for E in curves] # random
+        [(0, 0, 1, 0, 0),
+        (0, 0, 1, 0, z8^5 + z8^4 + z8^3),
+        (0, 0, 1, z8^6 + z8^5 + z8^2 + 1, 0),
+        (0, 0, z8^4 + z8^3 + z8^2 + 1, 0, 0),
+        (0, 0, z8^4 + z8^3 + z8^2 + 1, 0, z8^3 + z8^2 + 1),
+        (0, 0, z8^6 + z8^3 + z8^2, 0, 0),
+        (0, 0, z8^6 + z8^3 + z8^2, 0, z8^3 + z8^2)]
+
+    Check that the twists are mutually non-isomorphic::
+
+        sage: all((e1 == e2 or not e1.is_isomorphic(e2)) for e1 in curves for e2 in curves)
+        True
+
+    Check that the weight formula holds::
+
+        sage: sum(1/len(E.automorphisms()) for E in curves) == 1
+        True
+    """
+    if not K.is_finite() or K.characteristic() != 2:
+        raise ValueError("field must be finite of characteristic 2")
+    if K.degree()%2:
+        return [EllipticCurve(K,[0,0,1,0,0]), EllipticCurve(K,[0,0,1,1,0]),  EllipticCurve(K,[0,0,1,1,1])]
+    # find a,b,c,d,e such that
+    # a is not a cube, i.e. a**((q-1)//3)!=1
+    # Tr(b)=1
+    # X^4+X+c irreducible
+    # X^2+a*X+d irreducible
+    # X^2+a^2*X+e irreducible
+    a = b = c = d = e = None
+    x = polygen(K)
+    q3 = (K.cardinality()-1)//3
+    while not a or a**q3==1:
+        a = K.random_element()
+    asq = a*a
+    while not b or not b.trace():
+        b = K.random_element()
+    c = K.one() # OK if degree is 2 mod 4
+    if K.degree()%4 == 0:
+        while (x**4+x+c).roots():
+            c = K.random_element()
+    while not d or (x**2+a*x+d).roots():
+        d = K.random_element()
+    while not e or (x**2+asq*x+e).roots():
+        e = K.random_element()
+    return [EllipticCurve(K, ai) for ai in
+            [[0,0,1,0,0], [0,0,1,0,b], [0,0,1,c,0], [0,0,a,0,0], [0,0,a,0,d], [0,0,asq,0,0], [0,0,asq,0,e]]]
+
+def curves_with_j_0_char3(K):
+    r"""
+    Return a complete list of pairwise nonisomorphic elliptic curves with `j`-invariant 0 over the finite field `K` of characteristic 3.
+
+        .. NOTE::
+
+            The number of twists is either 4 or 6 depending on whether
+            the field has odd or even degree over `\GF{3}`.  See
+            [Connell1999]_, pages 429-431.
+
+    Examples:
+
+    In odd degree, there are four isomorphism classes::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_0_char3
+        sage: K = GF(3**5)
+        sage: curves = curves_with_j_0_char3(K)
+        sage: len(curves)
+        4
+        sage: [E.ainvs() for E in curves] # random
+        [(0, 0, 0, 1, 0),
+        (0, 0, 0, 2, 0),
+        (0, 0, 0, 2, z5^4 + z5^3 + z5^2),
+        (0, 0, 0, 2, 2*z5^4 + 2*z5^3 + 2*z5^2)]
+
+    Check that the twists are mutually non-isomorphic::
+
+        sage: all((e1 == e2 or not e1.is_isomorphic(e2)) for e1 in curves for e2 in curves)
+        True
+
+    Check that the weight formula holds::
+
+        sage: sum(1/len(E.automorphisms()) for E in curves) == 1
+        True
+
+    In even degree, there are six isomorphism classes::
+
+        sage: from sage.schemes.elliptic_curves.ell_finite_field import curves_with_j_0_char3
+        sage: K = GF(3**4)
+        sage: curves = EllipticCurve(j=K(0)).twists()
+        sage: len(curves)
+        6
+        sage: [E.ainvs() for E in curves] # random
+        [(0, 0, 0, 1, 0),
+        (0, 0, 0, 2, 2*z4^3 + 2*z4^2 + 2*z4 + 2),
+        (0, 0, 0, 1, 0),
+        (0, 0, 0, 1, 2*z4^3 + 2*z4^2 + 2*z4 + 2),
+        (0, 0, 0, z4, 0),
+        (0, 0, 0, z4^3, 0)]
+
+    Check that the twists are mutually non-isomorphic::
+
+        sage: all((e1 == e2 or not e1.is_isomorphic(e2)) for e1 in curves for e2 in curves)
+        True
+
+    Check that the weight formula holds::
+
+        sage: sum(1/len(E.automorphisms()) for E in curves) == 1
+        True
+    """
+    if not K.is_finite() or K.characteristic() != 3:
+        raise ValueError("field must be finite of characteristic 3")
+    # find b with nonzero trace
+    b = None
+    while not b or not b.trace():
+        b = K.random_element()
+
+    if K.degree()%2:
+        return [EllipticCurve(K, a4a6) for a4a6 in
+            [[1,0], [-1,0], [-1,b], [-1,-b]]]
+
+    # find a, i, c where:
+    # a generates K* mod 4th powers, i.e. non-square,
+    # i^2=-1
+    # c with x^3+a^2*x+c irreducible
+    a = K.gen()
+    q2 = (K.cardinality()-1)//2
+    while not a or a**q2 == 1:
+        a = K.random_element()
+    x = polygen(K)
+    i = (x**2+1).roots()[0][0]
+    c = None
+    while not c or (x**3 + a**2*x + c).roots():
+        c = K.random_element()
+    return [EllipticCurve(K, a4a6) for a4a6 in
+            [[1,0], [1,i*b], [a,0], [a**2,0], [a**2,c], [a**3,0]]]
+
 
 # dict to hold precomputed coefficient vectors of supersingular j values (excluding 0, 1728):
 


Test patch: