@@ -2565,10 +2565,12 @@ def is_j_supersingular(j, proof=True):
25652565 return E .trace_of_frobenius () % p == 0
25662566
25672567
2568- def special_supersingular_curve (F , * , endomorphism = False ):
2568+ def special_supersingular_curve (F , q = None , * , endomorphism = False ):
25692569 r"""
2570- Given a finite field ``F``, construct a "special" supersingular
2571- elliptic curve `E` defined over ``F``.
2570+ Given a finite field ``F`` of characteristic `p`, and optionally
2571+ a positive integer `q` such that the Hilbert conductor of `-q`
2572+ and `-p` equals `p`, construct a "special" supersingular elliptic
2573+ curve `E` defined over ``F``.
25722574
25732575 Such a curve
25742576
@@ -2577,21 +2579,32 @@ def special_supersingular_curve(F, *, endomorphism=False):
25772579 - has group structure `E(\mathbb F_p) \cong \ZZ/(p+1)` and
25782580 `E(\mathbb F_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`;
25792581
2580- - has an endomorphism `\vartheta` of small degree `q` that
2582+ - has an endomorphism `\vartheta` of degree `q` that
25812583 anticommutes with the `\mathbb F_p`-Frobenius on `E`.
25822584
25832585 (The significance of `\vartheta` is that any such endomorphism,
25842586 together with the `\mathbb F_p`-Frobenius, generates the endomorphism
25852587 algebra `\mathrm{End}(E) \otimes \QQ`.)
25862588
2589+ The complexity grows exponentially in `\log(q)`. Automatically
2590+ chosen values of `q` lie in `O((\log p)^2)` assuming GRH.
2591+
25872592 INPUT:
25882593
2589- - ``F`` -- finite field `\mathbb F_{p^r}`;
2594+ - ``F`` -- finite field `\mathbb F_{p^r}`
2595+
2596+ - ``q`` -- positive integer (optional, default ``None``)
25902597
25912598 - ``endomorphism`` -- boolean (default: ``False``); when set to ``True``,
25922599 it is required that `2 \mid r`, and the function then additionally
25932600 returns `\vartheta`
25942601
2602+ .. WARNING::
2603+
2604+ Due to :issue:`38481`, calling this function with a value of `q`
2605+ larger than approximately `p/4` may currently fail. This failure
2606+ will not occur for automatically chosen values of `q`.
2607+
25952608 EXAMPLES::
25962609
25972610 sage: special_supersingular_curve(GF(1013^2), endomorphism=True)
@@ -2604,8 +2617,8 @@ def special_supersingular_curve(F, *, endomorphism=False):
26042617 Via: (u,r,s,t) = (389*z2 + 241, 0, 0, 0))
26052618
26062619 sage: special_supersingular_curve(GF(1021^2), endomorphism=True)
2607- (Elliptic Curve defined by y^2 = x^3 + 785 *x + 794 over Finite Field in z2 of size 1021^2,
2608- Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 785 *x + 794 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 785 *x + 794 over Finite Field in z2 of size 1021^2)
2620+ (Elliptic Curve defined by y^2 = x^3 + 791 *x + 230 over Finite Field in z2 of size 1021^2,
2621+ Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 791 *x + 230 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 791 *x + 230 over Finite Field in z2 of size 1021^2)
26092622
26102623 sage: special_supersingular_curve(GF(1031^2), endomorphism=True)
26112624 (Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2,
@@ -2630,6 +2643,20 @@ def special_supersingular_curve(F, *, endomorphism=False):
26302643 Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2
26312644 Via: (u,r,s,t) = (922*z2 + 129, 0, 0, 0))
26322645
2646+ We can also supply a suitable value of `q` ourselves::
2647+
2648+ sage: special_supersingular_curve(GF(1019), q=99)
2649+ Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field of size 1019
2650+
2651+ sage: special_supersingular_curve(GF(1019^2), q=99, endomorphism=True)
2652+ (Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2,
2653+ Isogeny of degree 99 from Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2 to Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2)
2654+
2655+ sage: special_supersingular_curve(GF(1013), q=99)
2656+ Traceback (most recent call last):
2657+ ...
2658+ ValueError: invalid choice of q
2659+
26332660 TESTS::
26342661
26352662 sage: p = random_prime(1000)
@@ -2678,6 +2705,35 @@ def special_supersingular_curve(F, *, endomorphism=False):
26782705 sage: pi * endo == -endo * pi
26792706 True
26802707
2708+ Also try it when `q` is given:
2709+
2710+ sage: p = random_prime(300, lbound=10)
2711+ sage: k = ZZ(randrange(1, 5))
2712+ sage: while True:
2713+ ....: q = randrange(1, p//4) # upper bound p//4 is a workaround for #38481
2714+ ....: if QuaternionAlgebra(-q, -p).discriminant() == p:
2715+ ....: break
2716+ sage: E = special_supersingular_curve(GF((p, k)), q)
2717+ sage: E.is_supersingular()
2718+ True
2719+ sage: F.<t> = GF((p, 2*k))
2720+ sage: E, endo = special_supersingular_curve(F, q, endomorphism=True)
2721+ sage: E.is_supersingular()
2722+ True
2723+ sage: E.j_invariant() in GF(p)
2724+ True
2725+ sage: endo.domain() is endo.codomain() is E
2726+ True
2727+ sage: endo.degree() == q
2728+ True
2729+ sage: endo.trace()
2730+ 0
2731+ sage: pi = E.frobenius_isogeny()
2732+ sage: pi.codomain() is pi.domain() is E
2733+ True
2734+ sage: pi * endo == -endo * pi
2735+ True
2736+
26812737 .. NOTE::
26822738
26832739 This function makes no guarantees about the distribution of
@@ -2694,42 +2750,49 @@ def special_supersingular_curve(F, *, endomorphism=False):
26942750 if endomorphism and deg % 2 :
26952751 raise ValueError ('endomorphism was requested but is not defined over given field' )
26962752
2697- E = None
2753+ if q is not None :
2754+ from sage .arith .misc import hilbert_conductor
2755+ if p .divides (q ) or hilbert_conductor (- q , - p ) != p :
2756+ raise ValueError ('invalid choice of q' )
26982757
26992758 # first find the degree q of our special endomorphism
2700- if p == 2 :
2701- q = 3
2702- E = EllipticCurve (F , [0 ,0 ,1 ,0 ,0 ])
2703-
2704- elif p % 4 == 3 :
2705- q = 1
2706- E = EllipticCurve (F , [1 ,0 ])
2707-
2708- elif p % 3 == 2 :
2709- q = 3
2710- E = EllipticCurve (F , [0 ,1 ])
2711-
2712- elif p % 8 == 5 :
2713- q = 2
2714- E = EllipticCurve (F , [- 4320 , 96768 ])
2715-
2716- else :
2717- from sage .arith .misc import legendre_symbol
2718- for q in map (ZZ , range (3 ,p ,4 )):
2719- if not q .is_prime ():
2720- continue
2721- if legendre_symbol (- q , p ) == - 1 :
2722- break
2759+ if q is None :
2760+ if p == 2 :
2761+ q = 3
2762+ elif p % 4 == 3 :
2763+ q = 1
2764+ elif p % 3 == 2 :
2765+ q = 3
2766+ elif p % 8 == 5 :
2767+ q = 2
27232768 else :
2724- assert False # should never happen
2769+ from sage .arith .misc import legendre_symbol
2770+ for q in map (ZZ , range (3 ,p ,4 )):
2771+ if not q .is_prime ():
2772+ continue
2773+ if legendre_symbol (- q , p ) == - 1 :
2774+ break
2775+ else : # should never happen
2776+ assert False , 'bug in special_supersingular_curve()'
2777+ q = ZZ (q )
27252778
2726- if E is None :
2727- from sage .arith .misc import fundamental_discriminant
2728- from sage .schemes .elliptic_curves .cm import hilbert_class_polynomial
2729- H = hilbert_class_polynomial (fundamental_discriminant (- q ))
2730- j = H .change_ring (GF (p )).any_root ()
2779+ from sage .arith .misc import fundamental_discriminant
2780+ from sage .schemes .elliptic_curves .cm import hilbert_class_polynomial
2781+ H = hilbert_class_polynomial (fundamental_discriminant (- q ))
2782+ j = H .change_ring (GF (p )).any_root ()
2783+ if j .is_zero ():
2784+ if p == 2 :
2785+ ainvs = [0 ,0 ,1 ,0 ,0 ]
2786+ elif p == 3 :
2787+ ainvs = [1 ,0 ]
2788+ else :
2789+ ainvs = [0 ,1 ]
2790+ elif j == 1728 :
2791+ ainvs = [1 ,0 ]
2792+ else :
27312793 a = 27 * j / (4 * (1728 - j ))
2732- E = EllipticCurve (F , [a ,- a ])
2794+ ainvs = [a ,- a ]
2795+ E = EllipticCurve (F , ainvs )
27332796
27342797 if ZZ (2 ).divides (deg ):
27352798 k = deg // 2
@@ -2740,23 +2803,26 @@ def special_supersingular_curve(F, *, endomorphism=False):
27402803 if not endomorphism :
27412804 return E
27422805
2743- if q == 1 or p <= 13 :
2744- if q == 1 :
2745- endos = E .automorphisms ()
2746- else :
2747- endos = (iso * phi for phi in E .isogenies_prime_degree (q )
2748- for iso in phi .codomain ().isomorphisms (E ))
2749- endo = next (endo for endo in endos if endo .trace ().is_zero ())
2750-
2806+ if q .is_one ():
2807+ endo = next (auto for auto in E .automorphisms () if auto .trace ().is_zero ())
27512808 else :
2752- from sage .schemes .elliptic_curves .weierstrass_morphism import WeierstrassIsomorphism
2753- iso = WeierstrassIsomorphism (None , (F (- q ).sqrt (),0 ,0 ,0 ), E )
2754- if q == 3 and E .a_invariants () == (0 ,0 ,0 ,0 ,1 ):
2755- # workaround for #21883
2756- endo = E .isogeny (E (0 ,1 ))
2757- else :
2758- endo = E .isogeny (None , iso .domain (), degree = q )
2759- endo = iso * endo
2809+ iso = E .isomorphism (F (- q ).sqrt (), is_codomain = True )
2810+ try :
2811+ endo = iso * E .isogeny (None , iso .domain (), degree = q )
2812+ except (NotImplementedError , ValueError ): #FIXME catching ValueError here is a workaround for #38481
2813+ #FIXME this code could be simplified/optimized after #37388 and/or #35949
2814+ def _isogs (E , d ):
2815+ if d .is_one ():
2816+ yield E .identity_morphism ()
2817+ return
2818+ l = d .prime_factors ()[- 1 ]
2819+ for phi in E .isogenies_prime_degree (l ):
2820+ for psi in _isogs (phi .codomain (), d // l ):
2821+ yield psi * phi
2822+ endos = (iso * phi for phi in _isogs (E , q ) for iso in phi .codomain ().isomorphisms (E ))
2823+ # endos = (iso*phi for phi in E.isogenies_degree(q)
2824+ # for iso in phi.codomain().isomorphisms(E))
2825+ endo = next (endo for endo in endos if endo .trace ().is_zero ())
27602826
27612827 endo ._degree = ZZ (q )
27622828 endo .trace .set_cache (ZZ .zero ())
0 commit comments