@@ -2465,10 +2465,12 @@ def is_j_supersingular(j, proof=True):
24652465
24662466 return E .trace_of_frobenius () % p == 0
24672467
2468- def special_supersingular_curve (F , * , endomorphism = False ):
2468+ def special_supersingular_curve (F , q = None , * , endomorphism = False ):
24692469 r"""
2470- Given a finite field ``F``, construct a "special" supersingular
2471- elliptic curve `E` defined over ``F``.
2470+ Given a finite field ``F`` of characteristic `p`, and optionally
2471+ a positive integer `q` such that the Hilbert conductor of `-q`
2472+ and `-p` equals `p`, construct a "special" supersingular elliptic
2473+ curve `E` defined over ``F``.
24722474
24732475 Such a curve
24742476
@@ -2477,21 +2479,32 @@ def special_supersingular_curve(F, *, endomorphism=False):
24772479 - has group structure `E(\mathbb F_p) \cong \ZZ/(p+1)` and
24782480 `E(\mathbb F_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`;
24792481
2480- - has an endomorphism `\vartheta` of small degree `q` that
2482+ - has an endomorphism `\vartheta` of degree `q` that
24812483 anticommutes with the `\mathbb F_p`-Frobenius on `E`.
24822484
24832485 (The significance of `\vartheta` is that any such endomorphism,
24842486 together with the `\mathbb F_p`-Frobenius, generates the endomorphism
24852487 algebra `\mathrm{End}(E) \otimes \QQ`.)
24862488
2489+ The complexity grows exponentially in `\log(q)`. Automatically
2490+ chosen values of `q` lie in `O((\log p)^2)` assuming GRH.
2491+
24872492 INPUT:
24882493
2489- - ``F`` -- finite field `\mathbb F_{p^r}`;
2494+ - ``F`` -- finite field `\mathbb F_{p^r}`
2495+
2496+ - ``q`` -- positive integer (optional, default ``None``)
24902497
24912498 - ``endomorphism`` -- boolean (default: ``False``); when set to ``True``,
24922499 it is required that `2 \mid r`, and the function then additionally
24932500 returns `\vartheta`
24942501
2502+ .. WARNING::
2503+
2504+ Due to :issue:`38381`, calling this function with a value of `q`
2505+ larger than approximately `p/4` may currently fail. This failure
2506+ will not occur for automatically chosen values of `q`.
2507+
24952508 EXAMPLES::
24962509
24972510 sage: special_supersingular_curve(GF(1013^2), endomorphism=True)
@@ -2504,8 +2517,8 @@ def special_supersingular_curve(F, *, endomorphism=False):
25042517 Via: (u,r,s,t) = (389*z2 + 241, 0, 0, 0))
25052518
25062519 sage: special_supersingular_curve(GF(1021^2), endomorphism=True)
2507- (Elliptic Curve defined by y^2 = x^3 + 785 *x + 794 over Finite Field in z2 of size 1021^2,
2508- 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)
2520+ (Elliptic Curve defined by y^2 = x^3 + 791 *x + 230 over Finite Field in z2 of size 1021^2,
2521+ 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)
25092522
25102523 sage: special_supersingular_curve(GF(1031^2), endomorphism=True)
25112524 (Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2,
@@ -2530,6 +2543,20 @@ def special_supersingular_curve(F, *, endomorphism=False):
25302543 Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2
25312544 Via: (u,r,s,t) = (922*z2 + 129, 0, 0, 0))
25322545
2546+ We can also supply a suitable value of `q` ourselves::
2547+
2548+ sage: special_supersingular_curve(GF(1019), q=99)
2549+ Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field of size 1019
2550+
2551+ sage: special_supersingular_curve(GF(1019^2), q=99, endomorphism=True)
2552+ (Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2,
2553+ 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)
2554+
2555+ sage: special_supersingular_curve(GF(1013), q=99)
2556+ Traceback (most recent call last):
2557+ ...
2558+ ValueError: invalid choice of q
2559+
25332560 TESTS::
25342561
25352562 sage: p = random_prime(1000)
@@ -2578,6 +2605,35 @@ def special_supersingular_curve(F, *, endomorphism=False):
25782605 sage: pi * endo == -endo * pi
25792606 True
25802607
2608+ Also try it when `q` is given:
2609+
2610+ sage: p = random_prime(300, lbound=10)
2611+ sage: k = ZZ(randrange(1, 5))
2612+ sage: while True:
2613+ ....: q = randrange(1, p//4) # upper bound p//4 is a workaround for #38481
2614+ ....: if QuaternionAlgebra(-q, -p).discriminant() == p:
2615+ ....: break
2616+ sage: E = special_supersingular_curve(GF((p, k)), q)
2617+ sage: E.is_supersingular()
2618+ True
2619+ sage: F.<t> = GF((p, 2*k))
2620+ sage: E, endo = special_supersingular_curve(F, q, endomorphism=True)
2621+ sage: E.is_supersingular()
2622+ True
2623+ sage: E.j_invariant() in GF(p)
2624+ True
2625+ sage: endo.domain() is endo.codomain() is E
2626+ True
2627+ sage: endo.degree() == q
2628+ True
2629+ sage: endo.trace()
2630+ 0
2631+ sage: pi = E.frobenius_isogeny()
2632+ sage: pi.codomain() is pi.domain() is E
2633+ True
2634+ sage: pi * endo == -endo * pi
2635+ True
2636+
25812637 .. NOTE::
25822638
25832639 This function makes no guarantees about the distribution of
@@ -2594,42 +2650,49 @@ def special_supersingular_curve(F, *, endomorphism=False):
25942650 if endomorphism and deg % 2 :
25952651 raise ValueError ('endomorphism was requested but is not defined over given field' )
25962652
2597- E = None
2653+ if q is not None :
2654+ from sage .arith .misc import hilbert_conductor
2655+ if p .divides (q ) or hilbert_conductor (- q , - p ) != p :
2656+ raise ValueError ('invalid choice of q' )
25982657
25992658 # first find the degree q of our special endomorphism
2600- if p == 2 :
2601- q = 3
2602- E = EllipticCurve (F , [0 ,0 ,1 ,0 ,0 ])
2603-
2604- elif p % 4 == 3 :
2605- q = 1
2606- E = EllipticCurve (F , [1 ,0 ])
2607-
2608- elif p % 3 == 2 :
2609- q = 3
2610- E = EllipticCurve (F , [0 ,1 ])
2611-
2612- elif p % 8 == 5 :
2613- q = 2
2614- E = EllipticCurve (F , [- 4320 , 96768 ])
2615-
2616- else :
2617- from sage .arith .misc import legendre_symbol
2618- for q in map (ZZ , range (3 ,p ,4 )):
2619- if not q .is_prime ():
2620- continue
2621- if legendre_symbol (- q , p ) == - 1 :
2622- break
2659+ if q is None :
2660+ if p == 2 :
2661+ q = 3
2662+ elif p % 4 == 3 :
2663+ q = 1
2664+ elif p % 3 == 2 :
2665+ q = 3
2666+ elif p % 8 == 5 :
2667+ q = 2
26232668 else :
2624- assert False # should never happen
2625-
2626- if E is None :
2627- from sage .arith .misc import fundamental_discriminant
2628- from sage .schemes .elliptic_curves .cm import hilbert_class_polynomial
2629- H = hilbert_class_polynomial (fundamental_discriminant (- q ))
2630- j = H .change_ring (GF (p )).any_root ()
2669+ from sage .arith .misc import legendre_symbol
2670+ for q in map (ZZ , range (3 ,p ,4 )):
2671+ if not q .is_prime ():
2672+ continue
2673+ if legendre_symbol (- q , p ) == - 1 :
2674+ break
2675+ else : # should never happen
2676+ assert False , 'bug in special_supersingular_curve()'
2677+ q = ZZ (q )
2678+
2679+ from sage .arith .misc import fundamental_discriminant
2680+ from sage .schemes .elliptic_curves .cm import hilbert_class_polynomial
2681+ H = hilbert_class_polynomial (fundamental_discriminant (- q ))
2682+ j = H .change_ring (GF (p )).any_root ()
2683+ if j .is_zero ():
2684+ if p == 2 :
2685+ ainvs = [0 ,0 ,1 ,0 ,0 ]
2686+ elif p == 3 :
2687+ ainvs = [1 ,0 ]
2688+ else :
2689+ ainvs = [0 ,1 ]
2690+ elif j == 1728 :
2691+ ainvs = [1 ,0 ]
2692+ else :
26312693 a = 27 * j / (4 * (1728 - j ))
2632- E = EllipticCurve (F , [a ,- a ])
2694+ ainvs = [a ,- a ]
2695+ E = EllipticCurve (F , ainvs )
26332696
26342697 if ZZ (2 ).divides (deg ):
26352698 k = deg // 2
@@ -2640,23 +2703,26 @@ def special_supersingular_curve(F, *, endomorphism=False):
26402703 if not endomorphism :
26412704 return E
26422705
2643- if q == 1 or p <= 13 :
2644- if q == 1 :
2645- endos = E .automorphisms ()
2646- else :
2647- endos = (iso * phi for phi in E .isogenies_prime_degree (q )
2648- for iso in phi .codomain ().isomorphisms (E ))
2649- endo = next (endo for endo in endos if endo .trace ().is_zero ())
2650-
2706+ if q .is_one ():
2707+ endo = next (auto for auto in E .automorphisms () if auto .trace ().is_zero ())
26512708 else :
2652- from sage .schemes .elliptic_curves .weierstrass_morphism import WeierstrassIsomorphism
2653- iso = WeierstrassIsomorphism (None , (F (- q ).sqrt (),0 ,0 ,0 ), E )
2654- if q == 3 and E .a_invariants () == (0 ,0 ,0 ,0 ,1 ):
2655- # workaround for #21883
2656- endo = E .isogeny (E (0 ,1 ))
2657- else :
2658- endo = E .isogeny (None , iso .domain (), degree = q )
2659- endo = iso * endo
2709+ iso = E .isomorphism (F (- q ).sqrt (), is_codomain = True )
2710+ try :
2711+ endo = iso * E .isogeny (None , iso .domain (), degree = q )
2712+ except (NotImplementedError , ValueError ): #FIXME catching ValueError here is a workaround for #38481
2713+ #FIXME this code could be simplified/optimized after #37388 and/or #35949
2714+ def _isogs (E , d ):
2715+ if d .is_one ():
2716+ yield E .identity_morphism ()
2717+ return
2718+ l = d .prime_factors ()[- 1 ]
2719+ for phi in E .isogenies_prime_degree (l ):
2720+ for psi in _isogs (phi .codomain (), d // l ):
2721+ yield psi * phi
2722+ endos = (iso * phi for phi in _isogs (E , q ) for iso in phi .codomain ().isomorphisms (E ))
2723+ # endos = (iso*phi for phi in E.isogenies_degree(q)
2724+ # for iso in phi.codomain().isomorphisms(E))
2725+ endo = next (endo for endo in endos if endo .trace ().is_zero ())
26602726
26612727 endo ._degree = ZZ (q )
26622728 endo .trace .set_cache (ZZ .zero ())
0 commit comments