Code coverage tests

This page documents the degree to which the PARI/GP source code is tested by our public test suite, distributed with the source distribution in directory src/test/. This is measured by the gcov utility; we then process gcov output using the lcov frond-end.

We test a few variants depending on Configure flags on the pari.math.u-bordeaux.fr machine (x86_64 architecture), and agregate them in the final report:

The target is to exceed 90% coverage for all mathematical modules (given that branches depending on DEBUGLEVEL or DEBUGMEM are not covered). This script is run to produce the results below.

LCOV - code coverage report
Current view: top level - basemath - FpE.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.18.1 lcov report (development 30802-b11b770002) Lines: 1102 1211 91.0 %
Date: 2026-04-11 09:26:21 Functions: 123 132 93.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2009  The PARI group.
       2             : 
       3             : This file is part of the PARI/GP package.
       4             : 
       5             : PARI/GP is free software; you can redistribute it and/or modify it under the
       6             : terms of the GNU General Public License as published by the Free Software
       7             : Foundation; either version 2 of the License, or (at your option) any later
       8             : version. It is distributed in the hope that it will be useful, but WITHOUT
       9             : ANY WARRANTY WHATSOEVER.
      10             : 
      11             : Check the License for details. You should have received a copy of it, along
      12             : with the package; see the file 'COPYING'. If not, write to the Free Software
      13             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14             : 
      15             : #include "pari.h"
      16             : #include "paripriv.h"
      17             : 
      18             : #define DEBUGLEVEL DEBUGLEVEL_ellcard
      19             : 
      20             : /* Not so fast arithmetic with points over elliptic curves over Fp */
      21             : 
      22             : /***********************************************************************/
      23             : /**                                                                   **/
      24             : /**                              FpJ                                  **/
      25             : /**                                                                   **/
      26             : /***********************************************************************/
      27             : /* Arithmetic is implemented using Jacobian coordinates, representing
      28             :  * a projective point (x : y : z) on E by [z*x, z^2*y, z].  This is
      29             :  * probably not the fastest representation available for the given
      30             :  * problem, but they're easy to implement and up to 60% faster than
      31             :  * the school-book method used in FpE_mulu(). */
      32             : 
      33             : static GEN
      34       44642 : ellinf_FpJ(void)
      35       44642 : { return mkvec3(gen_1, gen_1, gen_0); }
      36             : 
      37             : /* Cost: 1M + 8S + 1*a + 10add + 1*8 + 2*2 + 1*3.
      38             :  * Source: http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl */
      39             : GEN
      40     6265474 : FpJ_dbl(GEN P, GEN a4, GEN p)
      41             : {
      42             :   GEN X1, Y1, Z1;
      43             :   GEN XX, YY, YYYY, ZZ, S, M, T, Q;
      44             : 
      45     6265474 :   if (signe(gel(P,3)) == 0) return ellinf_FpJ();
      46             : 
      47     6256822 :   X1 = gel(P,1); Y1 = gel(P,2); Z1 = gel(P,3);
      48             : 
      49     6256822 :   XX = Fp_sqr(X1, p);
      50     6220830 :   YY = Fp_sqr(Y1, p);
      51     6221488 :   YYYY = Fp_sqr(YY, p);
      52     6222165 :   ZZ = Fp_sqr(Z1, p);
      53     6222973 :   S = Fp_double(Fp_sub(Fp_sqr(Fp_add(X1,YY,p), p), Fp_add(XX,YYYY,p), p), p);
      54     6172996 :   M = Fp_addmul(Fp_mulu(XX, 3, p), a4, Fp_sqr(ZZ, p),  p);
      55     6211796 :   T = Fp_sub(Fp_sqr(M, p), Fp_double(S, p), p);
      56     6194535 :   Q = cgetg(4, t_VEC);
      57     6235361 :   gel(Q,1) = T;
      58     6235361 :   gel(Q,2) = Fp_sub(Fp_mul(M, Fp_sub(S, T, p), p), Fp_mulu(YYYY, 8, p), p);
      59     6196342 :   gel(Q,3) = Fp_sub(Fp_sqr(Fp_add(Y1, Z1, p), p), Fp_add(YY, ZZ, p), p);
      60     6194233 :   return Q;
      61             : }
      62             : 
      63             : /* Cost: 11M + 5S + 9add + 4*2.
      64             :  * Source: http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl */
      65             : GEN
      66     1128045 : FpJ_add(GEN P, GEN Q, GEN a4, GEN p)
      67             : {
      68             :   GEN X1, Y1, Z1, X2, Y2, Z2;
      69             :   GEN Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V, W, R;
      70             : 
      71     1128045 :   if (signe(gel(Q,3)) == 0) return gcopy(P);
      72     1128045 :   if (signe(gel(P,3)) == 0) return gcopy(Q);
      73             : 
      74     1107933 :   X1 = gel(P,1); Y1 = gel(P,2); Z1 = gel(P,3);
      75     1107933 :   X2 = gel(Q,1); Y2 = gel(Q,2); Z2 = gel(Q,3);
      76             : 
      77     1107933 :   Z1Z1 = Fp_sqr(Z1, p);
      78     1107484 :   Z2Z2 = Fp_sqr(Z2, p);
      79     1107374 :   U1 = Fp_mul(X1, Z2Z2, p);
      80     1107433 :   U2 = Fp_mul(X2, Z1Z1, p);
      81     1107539 :   S1 = mulii(Y1, Fp_mul(Z2, Z2Z2, p));
      82     1106292 :   S2 = mulii(Y2, Fp_mul(Z1, Z1Z1, p));
      83     1106263 :   H = Fp_sub(U2, U1, p);
      84     1107210 :   r = Fp_double(Fp_sub(S2, S1, p), p);
      85             : 
      86             :   /* If points are equal we must double. */
      87     1106630 :   if (signe(H)== 0) {
      88       37040 :     if (signe(r) == 0)
      89             :       /* Points are equal so double. */
      90        1050 :       return FpJ_dbl(P, a4, p);
      91             :     else
      92       35990 :       return ellinf_FpJ();
      93             :   }
      94     1069590 :   I = Fp_sqr(Fp_double(H, p), p);
      95     1070569 :   J = Fp_mul(H, I, p);
      96     1070551 :   V = Fp_mul(U1, I, p);
      97     1070594 :   W = Fp_sub(Fp_sqr(r, p), Fp_add(J, Fp_double(V, p), p), p);
      98     1069816 :   R = cgetg(4, t_VEC);
      99     1070649 :   gel(R,1) = W;
     100     1070649 :   gel(R,2) = Fp_sub(mulii(r, subii(V, W)),
     101             :                     shifti(mulii(S1, J), 1), p);
     102     1071062 :   gel(R,3) = Fp_mul(Fp_sub(Fp_sqr(Fp_add(Z1, Z2, p), p),
     103             :                            Fp_add(Z1Z1, Z2Z2, p), p), H, p);
     104     1070546 :   return R;
     105             : }
     106             : 
     107             : GEN
     108           0 : FpJ_neg(GEN Q, GEN p)
     109             : {
     110           0 :   return mkvec3(icopy(gel(Q,1)), Fp_neg(gel(Q,2), p), icopy(gel(Q,3)));
     111             : }
     112             : 
     113             : GEN
     114      199154 : FpE_to_FpJ(GEN P)
     115             : {
     116      199154 :   return ell_is_inf(P) ? ellinf_FpJ()
     117      199154 :        : mkvec3(icopy(gel(P,1)),icopy(gel(P,2)), gen_1);
     118             : }
     119             : 
     120             : GEN
     121      198621 : FpJ_to_FpE(GEN P, GEN p)
     122             : {
     123      198621 :   if (signe(gel(P,3)) == 0) return ellinf();
     124             :   else
     125             :   {
     126      163077 :     GEN Z = Fp_inv(gel(P,3), p);
     127      163051 :     GEN Z2 = Fp_sqr(Z, p), Z3 = Fp_mul(Z, Z2, p);
     128      163051 :     retmkvec2(Fp_mul(gel(P,1), Z2, p), Fp_mul(gel(P,2), Z3, p));
     129             :   }
     130             : }
     131             : 
     132             : struct _FpE { GEN p,a4,a6; };
     133             : static GEN
     134     6245702 : _FpJ_dbl(void *E, GEN P)
     135             : {
     136     6245702 :   struct _FpE *ell = (struct _FpE *) E;
     137     6245702 :   return FpJ_dbl(P, ell->a4, ell->p);
     138             : }
     139             : static GEN
     140     1127535 : _FpJ_add(void *E, GEN P, GEN Q)
     141             : {
     142     1127535 :   struct _FpE *ell=(struct _FpE *) E;
     143     1127535 :   return FpJ_add(P, Q, ell->a4, ell->p);
     144             : }
     145             : static GEN
     146        5725 : _FpJ_mul(void *E, GEN P, GEN n)
     147             : {
     148        5725 :   pari_sp av = avma;
     149        5725 :   struct _FpE *e=(struct _FpE *) E;
     150        5725 :   long s = signe(n);
     151        5725 :   if (!s || signe(gel(P,3))==0) return ellinf_FpJ();
     152        5725 :   if (s < 0) P = FpJ_neg(P, e->p);
     153        5725 :   if (is_pm1(n)) return s > 0 ? gcopy(P): P;
     154        5725 :   return gc_GEN(av, gen_pow_i(P, n, e, &_FpJ_dbl, &_FpJ_add));
     155             : }
     156             : 
     157             : GEN
     158        5725 : FpJ_mul(GEN P, GEN n, GEN a4, GEN p)
     159             : {
     160             :   struct _FpE E;
     161        5725 :   E.a4= a4; E.p = p;
     162        5725 :   return _FpJ_mul(&E, P, n);
     163             : }
     164             : 
     165             : /***********************************************************************/
     166             : /**                                                                   **/
     167             : /**                              FpE                                  **/
     168             : /**                                                                   **/
     169             : /***********************************************************************/
     170             : /* These functions deal with point over elliptic curves over Fp defined
     171             :  * by an equation of the form y^2=x^3+a4*x+a6.
     172             :  * Most of the time a6 is omitted since it can be recovered from any point
     173             :  * on the curve. */
     174             : 
     175             : int
     176        1087 : RgE_is_FpE(GEN x, GEN *pp)
     177             : {
     178        1087 :   if (ell_is_inf(x)) return 1;
     179        1087 :   if(!Rg_is_Fp(gel(x,1), pp)) return 0;
     180        1080 :   if(!Rg_is_Fp(gel(x,2), pp)) return 0;
     181        1079 :   return 1;
     182             : }
     183             : 
     184             : GEN
     185        2730 : RgE_to_FpE(GEN x, GEN p)
     186             : {
     187        2730 :   if (ell_is_inf(x)) return x;
     188        2730 :   retmkvec2(Rg_to_Fp(gel(x,1),p),Rg_to_Fp(gel(x,2),p));
     189             : }
     190             : 
     191             : GEN
     192        1052 : FpE_to_mod(GEN x, GEN p)
     193             : {
     194        1052 :   if (ell_is_inf(x)) return x;
     195         989 :   retmkvec2(Fp_to_mod(gel(x,1),p),Fp_to_mod(gel(x,2),p));
     196             : }
     197             : 
     198             : GEN
     199        1724 : FpE_changepoint(GEN P, GEN ch, GEN p)
     200             : {
     201        1724 :   pari_sp av = avma;
     202             :   GEN c, z, u, r, s, t, v, v2, v3;
     203        1724 :   if (ell_is_inf(P)) return P;
     204        1661 :   if (lgefint(p) == 3)
     205             :   {
     206         719 :     ulong pp = p[2];
     207         719 :     z = Fle_changepoint(ZV_to_Flv(P, pp), ZV_to_Flv(ch, pp), pp);
     208         719 :     return gc_upto(av, Flv_to_ZV(z));
     209             :   }
     210         942 :   u = gel(ch,1); r = gel(ch,2); s = gel(ch,3); t = gel(ch,4);
     211         942 :   v = Fp_inv(u, p); v2 = Fp_sqr(v,p); v3 = Fp_mul(v,v2,p);
     212         942 :   c = Fp_sub(gel(P,1),r,p);
     213         942 :   z = cgetg(3,t_VEC);
     214         942 :   gel(z,1) = Fp_mul(v2, c, p);
     215         942 :   gel(z,2) = Fp_mul(v3, Fp_sub(gel(P,2), Fp_add(Fp_mul(s,c, p),t, p),p),p);
     216         942 :   return gc_upto(av, z);
     217             : }
     218             : 
     219             : GEN
     220        2732 : FpE_changepointinv(GEN P, GEN ch, GEN p)
     221             : {
     222             :   GEN u, r, s, t, u2, u3, c, z;
     223        2732 :   if (ell_is_inf(P)) return P;
     224        2732 :   if (lgefint(p) == 3)
     225             :   {
     226        1738 :     ulong pp = p[2];
     227        1738 :     z = Fle_changepointinv(ZV_to_Flv(P, pp), ZV_to_Flv(ch, pp), pp);
     228        1738 :     return Flv_to_ZV(z);
     229             :   }
     230         994 :   u = gel(ch,1); r = gel(ch,2); s = gel(ch,3); t = gel(ch,4);
     231         994 :   u2 = Fp_sqr(u, p); u3 = Fp_mul(u,u2,p);
     232         993 :   c = Fp_mul(u2, gel(P,1), p);
     233         992 :   z = cgetg(3, t_VEC);
     234         992 :   gel(z,1) = Fp_add(c,r,p);
     235         993 :   gel(z,2) = Fp_add(Fp_mul(u3,gel(P,2),p), Fp_add(Fp_mul(s,c,p), t, p), p);
     236         992 :   return z;
     237             : }
     238             : 
     239             : static GEN
     240         420 : random_nonsquare_Fp(GEN p)
     241             : {
     242         420 :   pari_sp av = avma;
     243             :   GEN a;
     244         420 :   switch(mod8(p))
     245             :   { /* easy special cases */
     246         420 :     case 3: case 5: return gen_2;
     247           0 :     case 7: return subiu(p, 1);
     248             :   }
     249             :   do
     250             :   {
     251           0 :     set_avma(av);
     252           0 :     a = randomi(p);
     253           0 :   } while (kronecker(a, p) >= 0);
     254           0 :   return a;
     255             : }
     256             : 
     257             : void
     258           0 : Fp_elltwist(GEN a4, GEN a6, GEN p, GEN *pt_a4, GEN *pt_a6)
     259             : {
     260           0 :   GEN d = random_nonsquare_Fp(p), d2 = Fp_sqr(d, p), d3 = Fp_mul(d2, d, p);
     261           0 :   *pt_a4 = Fp_mul(a4, d2, p);
     262           0 :   *pt_a6 = Fp_mul(a6, d3, p);
     263           0 : }
     264             : 
     265             : static GEN
     266      259264 : FpE_dbl_slope(GEN P, GEN a4, GEN p, GEN *slope)
     267             : {
     268             :   GEN x, y, Q;
     269      259264 :   if (ell_is_inf(P) || !signe(gel(P,2))) return ellinf();
     270      132823 :   x = gel(P,1); y = gel(P,2);
     271      132823 :   *slope = Fp_div(Fp_add(Fp_mulu(Fp_sqr(x,p), 3, p), a4, p),
     272             :                   Fp_mulu(y, 2, p), p);
     273      132823 :   Q = cgetg(3,t_VEC);
     274      132823 :   gel(Q, 1) = Fp_sub(Fp_sqr(*slope, p), Fp_mulu(x, 2, p), p);
     275      132823 :   gel(Q, 2) = Fp_sub(Fp_mul(*slope, Fp_sub(x, gel(Q, 1), p), p), y, p);
     276      132823 :   return Q;
     277             : }
     278             : 
     279             : GEN
     280      258670 : FpE_dbl(GEN P, GEN a4, GEN p)
     281             : {
     282      258670 :   pari_sp av = avma;
     283             :   GEN slope;
     284      258670 :   return gc_upto(av, FpE_dbl_slope(P,a4,p,&slope));
     285             : }
     286             : 
     287             : static GEN
     288      916619 : FpE_add_slope(GEN P, GEN Q, GEN a4, GEN p, GEN *slope)
     289             : {
     290             :   GEN Px, Py, Qx, Qy, R;
     291      916619 :   if (ell_is_inf(P)) return Q;
     292      916129 :   if (ell_is_inf(Q)) return P;
     293      916129 :   Px = gel(P,1); Py = gel(P,2);
     294      916129 :   Qx = gel(Q,1); Qy = gel(Q,2);
     295      916129 :   if (equalii(Px, Qx))
     296             :   {
     297         574 :     if (equalii(Py, Qy))
     298         553 :       return FpE_dbl_slope(P, a4, p, slope);
     299             :     else
     300          21 :       return ellinf();
     301             :   }
     302      915555 :   *slope = Fp_div(Fp_sub(Py, Qy, p), Fp_sub(Px, Qx, p), p);
     303      915555 :   R = cgetg(3,t_VEC);
     304      915555 :   gel(R, 1) = Fp_sub(Fp_sub(Fp_sqr(*slope, p), Px, p), Qx, p);
     305      915555 :   gel(R, 2) = Fp_sub(Fp_mul(*slope, Fp_sub(Px, gel(R, 1), p), p), Py, p);
     306      915555 :   return R;
     307             : }
     308             : 
     309             : GEN
     310      916615 : FpE_add(GEN P, GEN Q, GEN a4, GEN p)
     311             : {
     312      916615 :   pari_sp av = avma;
     313             :   GEN slope;
     314      916615 :   return gc_upto(av, FpE_add_slope(P,Q,a4,p,&slope));
     315             : }
     316             : 
     317             : static GEN
     318           0 : FpE_neg_i(GEN P, GEN p)
     319             : {
     320           0 :   if (ell_is_inf(P)) return P;
     321           0 :   return mkvec2(gel(P,1), Fp_neg(gel(P,2), p));
     322             : }
     323             : 
     324             : GEN
     325      362490 : FpE_neg(GEN P, GEN p)
     326             : {
     327      362490 :   if (ell_is_inf(P)) return ellinf();
     328      362490 :   return mkvec2(gcopy(gel(P,1)), Fp_neg(gel(P,2), p));
     329             : }
     330             : 
     331             : GEN
     332           0 : FpE_sub(GEN P, GEN Q, GEN a4, GEN p)
     333             : {
     334           0 :   pari_sp av = avma;
     335             :   GEN slope;
     336           0 :   return gc_upto(av, FpE_add_slope(P, FpE_neg_i(Q, p), a4, p, &slope));
     337             : }
     338             : 
     339             : static GEN
     340      258670 : _FpE_dbl(void *E, GEN P)
     341             : {
     342      258670 :   struct _FpE *ell = (struct _FpE *) E;
     343      258670 :   return FpE_dbl(P, ell->a4, ell->p);
     344             : }
     345             : 
     346             : static GEN
     347      897344 : _FpE_add(void *E, GEN P, GEN Q)
     348             : {
     349      897344 :   struct _FpE *ell=(struct _FpE *) E;
     350      897344 :   return FpE_add(P, Q, ell->a4, ell->p);
     351             : }
     352             : 
     353             : static GEN
     354      891054 : _FpE_mul(void *E, GEN P, GEN n)
     355             : {
     356      891054 :   pari_sp av = avma;
     357      891054 :   struct _FpE *e=(struct _FpE *) E;
     358      891054 :   long s = signe(n);
     359             :   GEN Q;
     360      891054 :   if (!s || ell_is_inf(P)) return ellinf();
     361      891047 :   if (s<0) P = FpE_neg(P, e->p);
     362      891047 :   if (is_pm1(n)) return s>0? gcopy(P): P;
     363      457321 :   if (equalis(n,2)) return _FpE_dbl(E, P);
     364      198650 :   Q = gen_pow_i(FpE_to_FpJ(P), n, e, &_FpJ_dbl, &_FpJ_add);
     365      198621 :   return gc_upto(av, FpJ_to_FpE(Q, e->p));
     366             : }
     367             : 
     368             : GEN
     369        1314 : FpE_mul(GEN P, GEN n, GEN a4, GEN p)
     370             : {
     371             :   struct _FpE E;
     372        1314 :   E.a4 = a4; E.p = p;
     373        1314 :   return _FpE_mul(&E, P, n);
     374             : }
     375             : 
     376             : /* Finds a random nonsingular point on E */
     377             : 
     378             : GEN
     379      188642 : random_FpE(GEN a4, GEN a6, GEN p)
     380             : {
     381      188642 :   pari_sp ltop = avma;
     382             :   GEN x, x2, y, rhs;
     383             :   do
     384             :   {
     385      328909 :     set_avma(ltop);
     386      328909 :     x   = randomi(p); /*  x^3+a4*x+a6 = x*(x^2+a4)+a6  */
     387      328909 :     x2  = Fp_sqr(x, p);
     388      328909 :     rhs = Fp_add(Fp_mul(x, Fp_add(x2, a4, p), p), a6, p);
     389       35127 :   } while ((!signe(rhs) && !signe(Fp_add(Fp_mulu(x2,3,p),a4,p)))
     390      364036 :           || kronecker(rhs, p) < 0);
     391      188643 :   y = Fp_sqrt(rhs, p);
     392      188643 :   if (!y) pari_err_PRIME("random_FpE", p);
     393      188643 :   return gc_GEN(ltop, mkvec2(x, y));
     394             : }
     395             : 
     396             : static GEN
     397      186256 : _FpE_rand(void *E)
     398             : {
     399      186256 :   struct _FpE *e=(struct _FpE *) E;
     400      186256 :   return random_FpE(e->a4, e->a6, e->p);
     401             : }
     402             : 
     403             : static const struct bb_group FpE_group={_FpE_add,_FpE_mul,_FpE_rand,hash_GEN,ZV_equal,ell_is_inf,NULL};
     404             : 
     405             : const struct bb_group *
     406         903 : get_FpE_group(void ** pt_E, GEN a4, GEN a6, GEN p)
     407             : {
     408         903 :   struct _FpE *e = (struct _FpE *) stack_malloc(sizeof(struct _FpE));
     409         903 :   e->a4 = a4; e->a6 = a6; e->p  = p;
     410         903 :   *pt_E = (void *) e;
     411         903 :   return &FpE_group;
     412             : }
     413             : 
     414             : GEN
     415         737 : FpE_order(GEN z, GEN o, GEN a4, GEN p)
     416             : {
     417         737 :   pari_sp av = avma;
     418             :   struct _FpE e;
     419             :   GEN r;
     420         737 :   if (lgefint(p) == 3)
     421             :   {
     422         631 :     ulong pp = p[2];
     423         631 :     r = Fle_order(ZV_to_Flv(z, pp), o, umodiu(a4,pp), pp);
     424             :   }
     425             :   else
     426             :   {
     427         106 :     e.a4 = a4;
     428         106 :     e.p = p;
     429         106 :     r = gen_order(z, o, (void*)&e, &FpE_group);
     430             :   }
     431         737 :   return gc_INT(av, r);
     432             : }
     433             : 
     434             : GEN
     435          49 : FpE_log(GEN a, GEN b, GEN o, GEN a4, GEN p)
     436             : {
     437          49 :   pari_sp av = avma;
     438             :   struct _FpE e;
     439             :   GEN r;
     440          49 :   if (lgefint(p) == 3)
     441             :   {
     442          49 :     ulong pp = p[2];
     443          49 :     r = Fle_log(ZV_to_Flv(a,pp), ZV_to_Flv(b,pp), o, umodiu(a4,pp), pp);
     444             :   }
     445             :   else
     446             :   {
     447           0 :     e.a4 = a4;
     448           0 :     e.p = p;
     449           0 :     r = gen_PH_log(a, b, o, (void*)&e, &FpE_group);
     450             :   }
     451          49 :   return gc_INT(av, r);
     452             : }
     453             : 
     454             : /***********************************************************************/
     455             : /**                                                                   **/
     456             : /**                            Pairings                               **/
     457             : /**                                                                   **/
     458             : /***********************************************************************/
     459             : 
     460             : /* Derived from APIP from and by Jerome Milan, 2012 */
     461             : 
     462             : static GEN
     463         140 : FpE_vert(GEN P, GEN Q, GEN a4, GEN p)
     464             : {
     465         140 :   if (ell_is_inf(P))
     466          51 :     return gen_1;
     467          89 :   if (!equalii(gel(Q, 1), gel(P, 1)))
     468          87 :     return Fp_sub(gel(Q, 1), gel(P, 1), p);
     469           2 :   if (signe(gel(P,2))!=0) return gen_1;
     470           2 :   return Fp_inv(Fp_add(Fp_mulu(Fp_sqr(gel(P,1),p), 3, p), a4, p), p);
     471             : }
     472             : 
     473             : static GEN
     474          45 : FpE_Miller_line(GEN R, GEN Q, GEN slope, GEN a4, GEN p)
     475             : {
     476          45 :   GEN x = gel(Q, 1), y = gel(Q, 2);
     477          45 :   GEN tmp1 = Fp_sub(x, gel(R, 1), p);
     478          45 :   GEN tmp2 = Fp_add(Fp_mul(tmp1, slope, p), gel(R,2), p);
     479          45 :   if (!equalii(y, tmp2))
     480          44 :     return Fp_sub(y, tmp2, p);
     481           1 :   if (signe(y) == 0)
     482           1 :     return gen_1;
     483             :   else
     484             :   {
     485             :     GEN s1, s2;
     486           0 :     GEN y2i = Fp_inv(Fp_mulu(y, 2, p), p);
     487           0 :     s1 = Fp_mul(Fp_add(Fp_mulu(Fp_sqr(x, p), 3, p), a4, p), y2i, p);
     488           0 :     if (!equalii(s1, slope))
     489           0 :       return Fp_sub(s1, slope, p);
     490           0 :     s2 = Fp_mul(Fp_sub(Fp_mulu(x, 3, p), Fp_sqr(s1, p), p), y2i, p);
     491           0 :     return signe(s2)!=0 ? s2: y2i;
     492             :   }
     493             : }
     494             : 
     495             : /* Computes the equation of the line tangent to R and returns its
     496             :    evaluation at the point Q. Also doubles the point R.
     497             :  */
     498             : 
     499             : static GEN
     500          92 : FpE_tangent_update(GEN R, GEN Q, GEN a4, GEN p, GEN *pt_R)
     501             : {
     502          92 :   if (ell_is_inf(R))
     503             :   {
     504           7 :     *pt_R = ellinf();
     505           7 :     return gen_1;
     506             :   }
     507          85 :   else if (signe(gel(R,2)) == 0)
     508             :   {
     509          44 :     *pt_R = ellinf();
     510          44 :     return FpE_vert(R, Q, a4, p);
     511             :   } else {
     512             :     GEN slope;
     513          41 :     *pt_R = FpE_dbl_slope(R, a4, p, &slope);
     514          41 :     return FpE_Miller_line(R, Q, slope, a4, p);
     515             :   }
     516             : }
     517             : 
     518             : /* Computes the equation of the line through R and P, and returns its
     519             :    evaluation at the point Q. Also adds P to the point R.
     520             :  */
     521             : 
     522             : static GEN
     523           4 : FpE_chord_update(GEN R, GEN P, GEN Q, GEN a4, GEN p, GEN *pt_R)
     524             : {
     525           4 :   if (ell_is_inf(R))
     526             :   {
     527           0 :     *pt_R = gcopy(P);
     528           0 :     return FpE_vert(P, Q, a4, p);
     529             :   }
     530           4 :   else if (ell_is_inf(P))
     531             :   {
     532           0 :     *pt_R = gcopy(R);
     533           0 :     return FpE_vert(R, Q, a4, p);
     534             :   }
     535           4 :   else if (equalii(gel(P, 1), gel(R, 1)))
     536             :   {
     537           0 :     if (equalii(gel(P, 2), gel(R, 2)))
     538           0 :       return FpE_tangent_update(R, Q, a4, p, pt_R);
     539             :     else {
     540           0 :       *pt_R = ellinf();
     541           0 :       return FpE_vert(R, Q, a4, p);
     542             :     }
     543             :   } else {
     544             :     GEN slope;
     545           4 :     *pt_R = FpE_add_slope(P, R, a4, p, &slope);
     546           4 :     return FpE_Miller_line(R, Q, slope, a4, p);
     547             :   }
     548             : }
     549             : 
     550             : struct _FpE_miller { GEN p, a4, P; };
     551             : static GEN
     552          92 : FpE_Miller_dbl(void* E, GEN d)
     553             : {
     554          92 :   struct _FpE_miller *m = (struct _FpE_miller *)E;
     555          92 :   GEN p = m->p, a4 = m->a4, P = m->P;
     556             :   GEN v, line;
     557          92 :   GEN N = Fp_sqr(gel(d,1), p);
     558          92 :   GEN D = Fp_sqr(gel(d,2), p);
     559          92 :   GEN point = gel(d,3);
     560          92 :   line = FpE_tangent_update(point, P, a4, p, &point);
     561          92 :   N  = Fp_mul(N, line, p);
     562          92 :   v = FpE_vert(point, P, a4, p);
     563          92 :   D = Fp_mul(D, v, p); return mkvec3(N, D, point);
     564             : }
     565             : static GEN
     566           4 : FpE_Miller_add(void* E, GEN va, GEN vb)
     567             : {
     568           4 :   struct _FpE_miller *m = (struct _FpE_miller *)E;
     569           4 :   GEN p = m->p, a4= m->a4, P = m->P;
     570             :   GEN v, line, point;
     571           4 :   GEN na = gel(va,1), da = gel(va,2), pa = gel(va,3);
     572           4 :   GEN nb = gel(vb,1), db = gel(vb,2), pb = gel(vb,3);
     573           4 :   GEN N = Fp_mul(na, nb, p);
     574           4 :   GEN D = Fp_mul(da, db, p);
     575           4 :   line = FpE_chord_update(pa, pb, P, a4, p, &point);
     576           4 :   N = Fp_mul(N, line, p);
     577           4 :   v = FpE_vert(point, P, a4, p);
     578           4 :   D = Fp_mul(D, v, p); return mkvec3(N, D, point);
     579             : }
     580             : 
     581             : /* Returns the Miller function f_{m, Q} evaluated at the point P using
     582             :  * the standard Miller algorithm. */
     583             : static GEN
     584          44 : FpE_Miller(GEN Q, GEN P, GEN m, GEN a4, GEN p)
     585             : {
     586          44 :   pari_sp av = avma;
     587             :   struct _FpE_miller d;
     588             :   GEN v, N, D;
     589             : 
     590          44 :   d.a4 = a4; d.p = p; d.P = P;
     591          44 :   v = gen_pow_i(mkvec3(gen_1,gen_1,Q), m, (void*)&d,
     592             :                 FpE_Miller_dbl, FpE_Miller_add);
     593          44 :   N = gel(v,1); D = gel(v,2);
     594          44 :   return gc_INT(av, Fp_div(N, D, p));
     595             : }
     596             : 
     597             : GEN
     598       72968 : FpE_weilpairing(GEN P, GEN Q, GEN m, GEN a4, GEN p)
     599             : {
     600       72968 :   pari_sp av = avma;
     601             :   GEN N, D, w;
     602       72968 :   if (ell_is_inf(P) || ell_is_inf(Q) || ZV_equal(P,Q)) return gen_1;
     603       48411 :   if (lgefint(p)==3 && lgefint(m)==3)
     604             :   {
     605       48389 :     ulong pp = p[2];
     606       48389 :     GEN Pp = ZV_to_Flv(P, pp), Qp = ZV_to_Flv(Q, pp);
     607       48389 :     ulong w = Fle_weilpairing(Pp, Qp, itou(m), umodiu(a4, pp), pp);
     608       48389 :     return gc_utoi(av, w);
     609             :   }
     610          22 :   N = FpE_Miller(P, Q, m, a4, p);
     611          22 :   D = FpE_Miller(Q, P, m, a4, p);
     612          22 :   w = Fp_div(N, D, p);
     613          22 :   if (mpodd(m)) w  = Fp_neg(w, p);
     614          22 :   return gc_INT(av, w);
     615             : }
     616             : 
     617             : GEN
     618         203 : FpE_tatepairing(GEN P, GEN Q, GEN m, GEN a4, GEN p)
     619             : {
     620         203 :   if (ell_is_inf(P) || ell_is_inf(Q)) return gen_1;
     621         203 :   if (lgefint(p)==3 && lgefint(m)==3)
     622             :   {
     623         203 :     pari_sp av = avma;
     624         203 :     ulong pp = p[2];
     625         203 :     GEN Pp = ZV_to_Flv(P, pp), Qp = ZV_to_Flv(Q, pp);
     626         203 :     ulong w = Fle_tatepairing(Pp, Qp, itou(m), umodiu(a4, pp), pp);
     627         203 :     return gc_utoi(av,w);
     628             :   }
     629           0 :   return FpE_Miller(P, Q, m, a4, p);
     630             : }
     631             : 
     632             : /***********************************************************************/
     633             : /**                                                                   **/
     634             : /**                   CM by principal order                           **/
     635             : /**                                                                   **/
     636             : /***********************************************************************/
     637             : 
     638             : /* is jn/jd = J (mod p) */
     639             : static int
     640      659029 : is_CMj(long J, GEN jn, GEN jd, GEN p)
     641      659029 : { return dvdii(subii(mulis(jd,J), jn), p); }
     642             : #ifndef LONG_IS_64BIT
     643             : /* is jn/jd = -(2^32 a + b) (mod p) */
     644             : static int
     645       14579 : u2_is_CMj(ulong a, ulong b, GEN jn, GEN jd, GEN p)
     646             : {
     647       14579 :   GEN mJ = uu32toi(a,b);
     648       14579 :   return dvdii(addii(mulii(jd,mJ), jn), p);
     649             : }
     650             : #endif
     651             : 
     652             : static long
     653       53269 : Fp_ellj_get_CM(GEN jn, GEN jd, GEN p)
     654             : {
     655             : #define CHECK(CM,J) if (is_CMj(J,jn,jd,p)) return CM;
     656       53269 :   CHECK(-3,  0);
     657       53152 :   CHECK(-4,  1728);
     658       53012 :   CHECK(-7,  -3375);
     659       52766 :   CHECK(-8,  8000);
     660       52558 :   CHECK(-11, -32768);
     661       52315 :   CHECK(-12, 54000);
     662       52079 :   CHECK(-16, 287496);
     663       51902 :   CHECK(-19, -884736);
     664       51676 :   CHECK(-27, -12288000);
     665       51456 :   CHECK(-28, 16581375);
     666       51256 :   CHECK(-43, -884736000);
     667             : #ifdef LONG_IS_64BIT
     668       43753 :   CHECK(-67, -147197952000L);
     669       43632 :   CHECK(-163, -262537412640768000L);
     670             : #else
     671        7300 :   if (u2_is_CMj(0x00000022UL,0x45ae8000UL,jn,jd,p)) return -67;
     672        7279 :   if (u2_is_CMj(0x03a4b862UL,0xc4b40000UL,jn,jd,p)) return -163;
     673             : #endif
     674             : #undef CHECK
     675       50726 :   return 0;
     676             : }
     677             : 
     678             : /***********************************************************************/
     679             : /**                                                                   **/
     680             : /**                            issupersingular                        **/
     681             : /**                                                                   **/
     682             : /***********************************************************************/
     683             : 
     684             : /* assume x reduced mod p, monic. Return one root, or NULL if irreducible */
     685             : static GEN
     686       71367 : FqX_quad_root(GEN x, GEN T, GEN p)
     687             : {
     688       71367 :   GEN b = gel(x,3), c = gel(x,2);
     689       71367 :   GEN D = Fq_sub(Fq_sqr(b, T, p), Fq_mulu(c,4, T, p), T, p);
     690       71367 :   GEN s = Fq_sqrt(D,T, p);
     691       71367 :   if (!s) return NULL;
     692       68168 :   return Fq_halve(Fq_sub(s, b, T, p), T, p);
     693             : }
     694             : 
     695             : static GEN
     696        1244 : FpX_quad_root(GEN x, GEN p)
     697             : {
     698        1244 :   GEN s, b = gel(x,3), c = gel(x,2);
     699        1244 :   GEN D = Fp_sub(Fp_sqr(b, p), shifti(c,2), p);
     700        1244 :   if (kronecker(D,p) == -1) return NULL;
     701         789 :   s = Fp_sqrt(D,p);
     702         789 :   return Fp_halve(Fp_sub(s, b, p), p);
     703             : }
     704             : 
     705             : /* pol is the modular polynomial of level 2 modulo p.
     706             :  *
     707             :  * (T, p) defines the field FF_{p^2} in which j_prev and j live. */
     708             : static long
     709        4881 : Fq_path_extends_to_floor(GEN j_prev, GEN j, GEN T, GEN p, GEN Phi2, long max_len)
     710             : {
     711        4881 :   pari_sp ltop = avma;
     712        4881 :   long d, i, l = lg(j);
     713             : 
     714             :   /* A path made its way to the floor if (i) its length was cut off
     715             :    * before reaching max_path_len, or (ii) it reached max_path_len but
     716             :    * only has one neighbour. */
     717       32057 :   for (d = 1; d <= max_len; ++d)
     718             :   {
     719       80209 :     for (i = 1; i < l; i++)
     720             :     {
     721       53033 :       GEN Phi2_j = FqX_div_by_X_x(FqXY_evalx(Phi2, gel(j,i), T, p), gel(j_prev,i), T, p, NULL);
     722       53033 :       GEN j_next = FqX_quad_root(Phi2_j, T, p);
     723       53033 :       if (!j_next)
     724        3199 :         return  gc_long(ltop, 1);
     725       49834 :       gel(j_prev,i) = gel(j, i); gel(j,i) = j_next;
     726             :     }
     727       27176 :     if (gc_needed(ltop, 2))
     728           0 :       (void)gc_all(ltop, 2, &j, &j_prev);
     729             :   }
     730        1682 :   return gc_long(ltop, 0);
     731             : }
     732             : 
     733             : static long
     734         455 : Fp_path_extends_to_floor(GEN j_prev, GEN j, GEN p, GEN Phi2, long max_len, GEN *pt_j, GEN *pt_j_prev)
     735             : {
     736         455 :   pari_sp ltop = avma;
     737         455 :   long d, i, l = lg(j);
     738             : 
     739             :   /* A path made its way to the floor if (i) its length was cut off
     740             :    * before reaching max_path_len, or (ii) it reached max_path_len but
     741             :    * only has one neighbour. */
     742         622 :   for (d = 1; d <= max_len; ++d)
     743             :   {
     744        1411 :     for (i = 1; i < l; i++)
     745             :     {
     746        1244 :       GEN Phi2_j = FpX_div_by_X_x(FpXY_evalx(Phi2, gel(j,i), p), gel(j_prev,i), p, NULL);
     747        1244 :       GEN j_next = FpX_quad_root(Phi2_j, p);
     748        1244 :       if (!j_next)
     749             :       {
     750         455 :         *pt_j = gel(j,i);
     751         455 :         *pt_j_prev = gel(j_prev,i);
     752         455 :         return 1;
     753             :       }
     754         789 :       gel(j_prev,i) = gel(j, i); gel(j,i) = j_next;
     755             :     }
     756         167 :     if (gc_needed(ltop, 2))
     757           0 :       (void)gc_all(ltop, 2, &j, &j_prev);
     758             :   }
     759           0 :   return gc_long(ltop, 0);
     760             : }
     761             : 
     762             : 
     763             : static int
     764        2788 : Fp_jissupersingular(GEN j, GEN p)
     765             : {
     766        2788 :   long max_path_len = expi(p)+1;
     767        2788 :   GEN Phi2 = FpXX_red(polmodular_ZXX(2,0,0,1), p);
     768        2788 :   GEN Phi2_j = FpXY_evalx(Phi2, j, p);
     769        2788 :   GEN roots = FpX_roots(Phi2_j, p);
     770        2788 :   long nbroots = lg(roots)-1;
     771        2788 :   GEN S, j_prev = NULL;
     772             : 
     773             :   /* Every node in a supersingular L-volcano has L + 1 neighbours. */
     774             :   /* Note: a multiple root only occur when j has CM by sqrt(-15). */
     775        2788 :   if (nbroots==0)
     776         665 :     return 0;
     777        2123 :   S = deg2pol_shallow(gen_1, gen_0, Fp_neg(Fp_2gener(p),p),1);
     778        2123 :   if (nbroots==1 && FpX_is_squarefree(Phi2_j, p))
     779        1668 :   { j_prev = j; j = FqX_quad_root(FpX_div_by_X_x(Phi2_j, gel(roots,1), p, NULL), S, p); }
     780             :   else
     781         455 :     if (!Fp_path_extends_to_floor(const_vec(nbroots,j), roots, p, Phi2, max_path_len, &j, &j_prev))
     782           0 :       return 1;
     783        2123 :   return !Fq_path_extends_to_floor(mkvec(j_prev), mkvec(j), S, p, Phi2, max_path_len);
     784             : }
     785             : 
     786             : static int
     787       14007 : jissupersingular(GEN j, GEN S, GEN p)
     788             : {
     789       14007 :   long max_path_len = expi(p)+1;
     790       14007 :   GEN Phi2 = FpXX_red(polmodular_ZXX(2,0,0,1), p);
     791       14007 :   GEN Phi2_j = FqXY_evalx(Phi2, j, S, p);
     792       14007 :   GEN roots = FpXQX_roots(Phi2_j, S, p);
     793       14007 :   long nbroots = lg(roots)-1;
     794             : 
     795             :   /* Every node in a supersingular L-volcano has L + 1 neighbours. */
     796             :   /* Note: a multiple root only occur when j has CM by sqrt(-15). */
     797       14007 :   if (nbroots==0 || (nbroots==1 && FqX_is_squarefree(Phi2_j, S, p)))
     798       11249 :     return 0;
     799             :   else
     800        2758 :     return !Fq_path_extends_to_floor(const_vec(nbroots,j), roots, S, p, Phi2, max_path_len);
     801             : }
     802             : 
     803             : int
     804        3780 : Fp_elljissupersingular(GEN j, GEN p)
     805             : {
     806             :   long CM;
     807        3780 :   if (abscmpiu(p, 5) <= 0) return signe(j) == 0; /* valid if p <= 5 */
     808        3640 :   CM = Fp_ellj_get_CM(j, gen_1, p);
     809        3640 :   if (CM < 0) return krosi(CM, p) < 0; /* valid if p > 3 */
     810             :   else
     811        2788 :     return Fp_jissupersingular(j, p);
     812             : }
     813             : 
     814             : /***********************************************************************/
     815             : /**                                                                   **/
     816             : /**                            Cardinal                               **/
     817             : /**                                                                   **/
     818             : /***********************************************************************/
     819             : 
     820             : /*assume a4,a6 reduced mod p odd */
     821             : static ulong
     822      766441 : Fl_elltrace_naive(ulong a4, ulong a6, ulong p)
     823             : {
     824             :   ulong i, j;
     825      766441 :   long a = 0;
     826             :   long d0, d1, d2, d3;
     827      766441 :   GEN k = const_vecsmall(p, -1);
     828      766499 :   k[1] = 0;
     829   134820792 :   for (i=1, j=1; i < p; i += 2, j = Fl_add(j, i, p))
     830   134054738 :     k[j+1] = 1;
     831      766054 :   d0 = 6%p; d1 = d0; d2 = Fl_add(a4, 1, p); d3 = a6;
     832      766068 :   for(i=0;; i++)
     833             :   {
     834   259861065 :     a -= k[1+d3];
     835   259861065 :     if (i==p-1) break;
     836   259094952 :     d3 = Fl_add(d3, d2, p);
     837   259085626 :     d2 = Fl_add(d2, d1, p);
     838   259095026 :     d1 = Fl_add(d1, d0, p);
     839             :   }
     840      766113 :   return a;
     841             : }
     842             : 
     843             : /* z1 <-- z1 + z2, with precomputed inverse */
     844             : static void
     845      305694 : FpE_add_ip(GEN z1, GEN z2, GEN a4, GEN p, GEN p2inv)
     846             : {
     847             :   GEN p1,x,x1,x2,y,y1,y2;
     848             : 
     849      305694 :   x1 = gel(z1,1); y1 = gel(z1,2);
     850      305694 :   x2 = gel(z2,1); y2 = gel(z2,2);
     851      305694 :   if (x1 == x2)
     852          67 :     p1 = Fp_add(a4, mulii(x1,mului(3,x1)), p);
     853             :   else
     854      305627 :     p1 = Fp_sub(y2,y1, p);
     855             : 
     856      305694 :   p1 = Fp_mul(p1, p2inv, p);
     857      305694 :   x = Fp_sub(sqri(p1), addii(x1,x2), p);
     858      305694 :   y = Fp_sub(mulii(p1,subii(x1,x)), y1, p);
     859      305694 :   affii(x, x1);
     860      305694 :   affii(y, y1);
     861      305694 : }
     862             : 
     863             : /* make sure *x has lgefint >= k */
     864             : static void
     865       19196 : _fix(GEN x, long k)
     866             : {
     867       19196 :   GEN y = (GEN)*x;
     868       19196 :   if (lgefint(y) < k) { GEN p1 = cgeti(k); affii(y,p1); *x = (long)p1; }
     869       19196 : }
     870             : 
     871             : /* Return the lift of a (mod b), which is closest to c */
     872             : static GEN
     873      255387 : closest_lift(GEN a, GEN b, GEN c)
     874             : {
     875      255387 :   return addii(a, mulii(b, diviiround(subii(c,a), b)));
     876             : }
     877             : 
     878             : static long
     879          79 : get_table_size(GEN pordmin, GEN B)
     880             : {
     881          79 :   pari_sp av = avma;
     882          79 :   GEN t = ceilr( sqrtr( divri(itor(pordmin, DEFAULTPREC), B) ) );
     883          79 :   if (is_bigint(t))
     884           0 :     pari_err_OVERFLOW("ellap [large prime: install the 'seadata' package]");
     885          79 :   set_avma(av);
     886          79 :   return itos(t) >> 1;
     887             : }
     888             : 
     889             : /* Find x such that kronecker(u = x^3+c4x+c6, p) is KRO.
     890             :  * Return point [x*u,u^2] on E (KRO=1) / E^twist (KRO=-1) */
     891             : static GEN
     892           0 : Fp_ellpoint(long KRO, ulong *px, GEN c4, GEN c6, GEN p)
     893             : {
     894           0 :   ulong x = *px;
     895             :   GEN u;
     896             :   for(;;)
     897             :   {
     898           0 :     x++; /* u = x^3 + c4 x + c6 */
     899           0 :     u = modii(addii(c6, mului(x, addii(c4, sqru(x)))), p);
     900           0 :     if (kronecker(u,p) == KRO) break;
     901             :   }
     902           0 :   *px = x;
     903           0 :   return mkvec2(modii(mului(x,u),p), Fp_sqr(u,p));
     904             : }
     905             : static GEN
     906        7269 : Fl_ellpoint(long KRO, ulong *px, ulong c4, ulong c6, ulong p)
     907             : {
     908        7269 :   ulong t, u, x = *px;
     909             :   for(;;)
     910             :   {
     911       14290 :     if (++x >= p) pari_err_PRIME("ellap",utoi(p));
     912       14290 :     t = Fl_add(c4, Fl_sqr(x,p), p);
     913       14290 :     u = Fl_add(c6, Fl_mul(x, t, p), p);
     914       14290 :     if (krouu(u,p) == KRO) break;
     915             :   }
     916        7269 :   *px = x;
     917        7269 :   return mkvecsmall2(Fl_mul(x,u,p), Fl_sqr(u,p));
     918             : }
     919             : 
     920             : /* y <- x, both are pairs of t_INT */
     921             : static void
     922        9440 : affii2(GEN x, GEN y)
     923             : {
     924        9440 :   affii(gel(x,1), gel(y,1));
     925        9440 :   affii(gel(x,2), gel(y,2));
     926        9440 : }
     927             : 
     928             : static GEN ap_j1728(GEN a4,GEN p);
     929             : /* compute a_p using Shanks/Mestre + Montgomery's trick. Assume p > 457 */
     930             : static GEN
     931          79 : Fp_ellcard_Shanks(GEN c4, GEN c6, GEN p)
     932             : {
     933             :   pari_timer T;
     934             :   long *tx, *ty, *ti, pfinal, i, j, s, KRO, nb;
     935             :   ulong x;
     936          79 :   pari_sp av = avma, av2;
     937             :   GEN p1, P, mfh, h, F,f, fh,fg, pordmin, u, v, p1p, p2p, A, B, a4, pts;
     938          79 :   tx = NULL;
     939          79 :   ty = ti = NULL; /* gcc -Wall */
     940             : 
     941          79 :   if (!signe(c6)) {
     942           0 :     GEN ap = ap_j1728(c4, p);
     943           0 :     return gc_INT(av, subii(addiu(p,1), ap));
     944             :   }
     945             : 
     946          79 :   if (DEBUGLEVEL >= 6) timer_start(&T);
     947             :   /* once #E(Fp) is know mod B >= pordmin, it is completely determined */
     948          79 :   pordmin = addiu(sqrti(gmul2n(p,4)), 1); /* ceil( 4sqrt(p) ) */
     949          79 :   p1p = addiu(p, 1);
     950          79 :   p2p = shifti(p1p, 1);
     951          79 :   x = 0; KRO = 0;
     952             :   /* how many 2-torsion points ? */
     953          79 :   switch(FpX_nbroots(mkpoln(4, gen_1, gen_0, c4, c6), p))
     954             :   {
     955           9 :     case 3:  A = gen_0; B = utoipos(4); break;
     956          32 :     case 1:  A = gen_0; B = gen_2; break;
     957          38 :     default: A = gen_1; B = gen_2; break; /* 0 */
     958             :   }
     959             :   for(;;)
     960             :   {
     961          79 :     h = closest_lift(A, B, p1p);
     962          79 :     if (!KRO) /* first time, initialize */
     963             :     {
     964          79 :       KRO = kronecker(c6,p);
     965          79 :       f = mkvec2(gen_0, Fp_sqr(c6,p));
     966             :     }
     967             :     else
     968             :     {
     969           0 :       KRO = -KRO;
     970           0 :       f = Fp_ellpoint(KRO, &x, c4,c6,p);
     971             :     }
     972             :     /* [ux, u^2] is on E_u: y^2 = x^3 + c4 u^2 x + c6 u^3
     973             :      * E_u isomorphic to E (resp. E') iff KRO = 1 (resp. -1)
     974             :      * #E(F_p) = p+1 - a_p, #E'(F_p) = p+1 + a_p
     975             :      *
     976             :      * #E_u(Fp) = A (mod B),  h is close to #E_u(Fp) */
     977          79 :     a4 = modii(mulii(c4, gel(f,2)), p); /* c4 for E_u */
     978          79 :     fh = FpE_mul(f, h, a4, p);
     979          79 :     if (ell_is_inf(fh)) goto FOUND;
     980             : 
     981          79 :     s = get_table_size(pordmin, B);
     982             :     /* look for h s.t f^h = 0 */
     983          79 :     if (!tx)
     984             :     { /* first time: initialize */
     985          79 :       tx = newblock(3*(s+1));
     986          79 :       ty = tx + (s+1);
     987          79 :       ti = ty + (s+1);
     988             :     }
     989          79 :     F = FpE_mul(f,B,a4,p);
     990          79 :     *tx = evaltyp(t_VECSMALL) | evallg(s+1);
     991             : 
     992             :     /* F = B.f */
     993          79 :     P = gcopy(fh);
     994          79 :     if (s < 3)
     995             :     { /* we're nearly done: naive search */
     996           0 :       GEN q1 = P, mF = FpE_neg(F, p); /* -F */
     997           0 :       for (i=1;; i++)
     998             :       {
     999           0 :         P = FpE_add(P,F,a4,p); /* h.f + i.F */
    1000           0 :         if (ell_is_inf(P)) { h = addii(h, mului(i,B)); goto FOUND; }
    1001           0 :         q1 = FpE_add(q1,mF,a4,p); /* h.f - i.F */
    1002           0 :         if (ell_is_inf(q1)) { h = subii(h, mului(i,B)); goto FOUND; }
    1003             :       }
    1004             :     }
    1005             :     /* Baby Step/Giant Step */
    1006          79 :     nb = minss(128, s >> 1); /* > 0. Will do nb pts at a time: faster inverse */
    1007          79 :     pts = cgetg(nb+1, t_VEC);
    1008          79 :     j = lgefint(p);
    1009        9677 :     for (i=1; i<=nb; i++)
    1010             :     { /* baby steps */
    1011        9598 :       gel(pts,i) = P; /* h.f + (i-1).F */
    1012        9598 :       _fix(P+1, j); tx[i] = mod2BIL(gel(P,1));
    1013        9598 :       _fix(P+2, j); ty[i] = mod2BIL(gel(P,2));
    1014        9598 :       P = FpE_add(P,F,a4,p); /* h.f + i.F */
    1015        9598 :       if (ell_is_inf(P)) { h = addii(h, mului(i,B)); goto FOUND; }
    1016             :     }
    1017          79 :     mfh = FpE_neg(fh, p);
    1018          79 :     fg = FpE_add(P,mfh,a4,p); /* h.f + nb.F - h.f = nb.F */
    1019          79 :     if (ell_is_inf(fg)) { h = mului(nb,B); goto FOUND; }
    1020          79 :     u = cgetg(nb+1, t_VEC);
    1021          79 :     av2 = avma; /* more baby steps, nb points at a time */
    1022        1357 :     while (i <= s)
    1023             :     {
    1024             :       long maxj;
    1025      164239 :       for (j=1; j<=nb; j++) /* adding nb.F (part 1) */
    1026             :       {
    1027      162961 :         P = gel(pts,j); /* h.f + (i-nb-1+j-1).F */
    1028      162961 :         gel(u,j) = subii(gel(fg,1), gel(P,1));
    1029      162961 :         if (!signe(gel(u,j))) /* sum = 0 or doubling */
    1030             :         {
    1031           2 :           long k = i+j-2;
    1032           2 :           if (equalii(gel(P,2),gel(fg,2))) k -= 2*nb; /* fg == P */
    1033           2 :           h = addii(h, mulsi(k,B)); goto FOUND;
    1034             :         }
    1035             :       }
    1036        1278 :       v = FpV_inv(u, p);
    1037        1278 :       maxj = (i-1 + nb <= s)? nb: s % nb;
    1038      160545 :       for (j=1; j<=maxj; j++,i++) /* adding nb.F (part 2) */
    1039             :       {
    1040      159267 :         P = gel(pts,j);
    1041      159267 :         FpE_add_ip(P,fg, a4,p, gel(v,j));
    1042      159267 :         tx[i] = mod2BIL(gel(P,1));
    1043      159267 :         ty[i] = mod2BIL(gel(P,2));
    1044             :       }
    1045        1278 :       set_avma(av2);
    1046             :     }
    1047          77 :     P = FpE_add(gel(pts,j-1),mfh,a4,p); /* = (s-1).F */
    1048          77 :     if (ell_is_inf(P)) { h = mului(s-1,B); goto FOUND; }
    1049          77 :     if (DEBUGLEVEL >= 6)
    1050           0 :       timer_printf(&T, "[Fp_ellcard_Shanks] baby steps, s = %ld",s);
    1051             : 
    1052             :     /* giant steps: fg = s.F */
    1053          77 :     fg = FpE_add(P,F,a4,p);
    1054          77 :     if (ell_is_inf(fg)) { h = mului(s,B); goto FOUND; }
    1055          77 :     pfinal = mod2BIL(p); av2 = avma;
    1056             :     /* Goal of the following: sort points by increasing x-coordinate hash.
    1057             :      * Done in a complicated way to avoid allocating a large temp vector */
    1058          77 :     p1 = vecsmall_indexsort(tx); /* = permutation sorting tx */
    1059      168784 :     for (i=1; i<=s; i++) ti[i] = tx[p1[i]];
    1060             :     /* ti = tx sorted */
    1061      168784 :     for (i=1; i<=s; i++) { tx[i] = ti[i]; ti[i] = ty[p1[i]]; }
    1062             :     /* tx is sorted. ti = ty sorted */
    1063      168784 :     for (i=1; i<=s; i++) { ty[i] = ti[i]; ti[i] = p1[i]; }
    1064             :     /* ty is sorted. ti = permutation sorting tx */
    1065          77 :     if (DEBUGLEVEL >= 6) timer_printf(&T, "[Fp_ellcard_Shanks] sorting");
    1066          77 :     set_avma(av2);
    1067             : 
    1068          77 :     affii2(fg, gel(pts,1));
    1069        9440 :     for (j=2; j<=nb; j++) /* pts[j] = j.fg = (s*j).F */
    1070             :     {
    1071        9363 :       P = FpE_add(gel(pts,j-1),fg,a4,p);
    1072        9363 :       if (ell_is_inf(P)) { h = mulii(mulss(s,j), B); goto FOUND; }
    1073        9363 :       affii2(P, gel(pts,j));
    1074             :     }
    1075             :     /* replace fg by nb.fg since we do nb points at a time */
    1076          77 :     set_avma(av2);
    1077          77 :     fg = gcopy(gel(pts,nb)); /* copy: we modify (temporarily) pts[nb] below */
    1078          77 :     av2 = avma;
    1079             : 
    1080          77 :     for (i=1,j=1; ; i++)
    1081      152075 :     {
    1082      152152 :       GEN ftest = gel(pts,j);
    1083      152152 :       long m, l = 1, r = s+1;
    1084             :       long k, k2, j2;
    1085             : 
    1086      152152 :       set_avma(av2);
    1087      152152 :       k = mod2BIL(gel(ftest,1));
    1088     1930966 :       while (l < r)
    1089             :       {
    1090     1778814 :         m = (l+r) >> 1;
    1091     1778814 :         if (tx[m] < k) l = m+1; else r = m;
    1092             :       }
    1093      152152 :       if (r <= s && tx[r] == k)
    1094             :       {
    1095         154 :         while (r && tx[r] == k) r--;
    1096          77 :         k2 = mod2BIL(gel(ftest,2));
    1097          77 :         for (r++; r <= s && tx[r] == k; r++)
    1098          77 :           if (ty[r] == k2 || ty[r] == pfinal - k2)
    1099             :           { /* [h+j2] f == +/- ftest (= [i.s] f)? */
    1100          77 :             j2 = ti[r] - 1;
    1101          77 :             if (DEBUGLEVEL >=6)
    1102           0 :               timer_printf(&T, "[Fp_ellcard_Shanks] giant steps, i = %ld",i);
    1103          77 :             P = FpE_add(FpE_mul(F,stoi(j2),a4,p),fh,a4,p);
    1104          77 :             if (equalii(gel(P,1), gel(ftest,1)))
    1105             :             {
    1106          77 :               if (equalii(gel(P,2), gel(ftest,2))) i = -i;
    1107          77 :               h = addii(h, mulii(addis(mulss(s,i), j2), B));
    1108          77 :               goto FOUND;
    1109             :             }
    1110             :           }
    1111             :       }
    1112      152075 :       if (++j > nb)
    1113             :       { /* compute next nb points */
    1114        1149 :         long save = 0; /* gcc -Wall */;
    1115      147576 :         for (j=1; j<=nb; j++)
    1116             :         {
    1117      146427 :           P = gel(pts,j);
    1118      146427 :           gel(u,j) = subii(gel(fg,1), gel(P,1));
    1119      146427 :           if (gel(u,j) == gen_0) /* occurs once: i = j = nb, P == fg */
    1120             :           {
    1121          67 :             gel(u,j) = shifti(gel(P,2),1);
    1122          67 :             save = fg[1]; fg[1] = P[1];
    1123             :           }
    1124             :         }
    1125        1149 :         v = FpV_inv(u, p);
    1126      147576 :         for (j=1; j<=nb; j++)
    1127      146427 :           FpE_add_ip(gel(pts,j),fg,a4,p, gel(v,j));
    1128        1149 :         if (i == nb) { fg[1] = save; }
    1129        1149 :         j = 1;
    1130             :       }
    1131             :     }
    1132          79 : FOUND: /* found a point of exponent h on E_u */
    1133          79 :     h = FpE_order(f, h, a4, p);
    1134             :     /* h | #E_u(Fp) = A (mod B) */
    1135          79 :     A = Z_chinese_all(A, gen_0, B, h, &B);
    1136          79 :     if (cmpii(B, pordmin) >= 0) break;
    1137             :     /* not done: update A mod B for the _next_ curve, isomorphic to
    1138             :      * the quadratic twist of this one */
    1139           0 :     A = remii(subii(p2p,A), B); /* #E(Fp)+#E'(Fp) = 2p+2 */
    1140             :   }
    1141          79 :   if (tx) killblock(tx);
    1142          79 :   h = closest_lift(A, B, p1p);
    1143          79 :   return gc_INT(av, KRO==1? h: subii(p2p,h));
    1144             : }
    1145             : 
    1146             : typedef struct
    1147             : {
    1148             :   ulong x,y,i;
    1149             : } multiple;
    1150             : 
    1151             : static int
    1152    15376319 : compare_multiples(const void *A, const void *B)
    1153             : {
    1154    15376319 :   multiple *a = (multiple*)A, *b = (multiple*)B;
    1155    15376319 :   return a->x > b->x? 1: a->x < b->x? -1: 0;
    1156             : }
    1157             : 
    1158             : /* find x such that h := a + b x is closest to c and return h:
    1159             :  * x = round((c-a) / b) = floor( (2(c-a) + b) / 2b )
    1160             :  * Assume 0 <= a < b < c  and b + 2c < 2^BIL */
    1161             : static ulong
    1162      262489 : uclosest_lift(ulong a, ulong b, ulong c)
    1163             : {
    1164      262489 :   ulong x = (b + ((c-a) << 1)) / (b << 1);
    1165      262489 :   return a + b * x;
    1166             : }
    1167             : 
    1168             : static long
    1169      227102 : Fle_dbl_inplace(GEN P, ulong a4, ulong p)
    1170             : {
    1171             :   ulong x, y, slope;
    1172      227102 :   if (!P[2]) return 1;
    1173      227074 :   x = P[1]; y = P[2];
    1174      227074 :   slope = Fl_div(Fl_add(Fl_triple(Fl_sqr(x,p), p), a4, p),
    1175             :                  Fl_double(y, p), p);
    1176      227117 :   P[1] = Fl_sub(Fl_sqr(slope, p), Fl_double(x, p), p);
    1177      227101 :   P[2] = Fl_sub(Fl_mul(slope, Fl_sub(x, P[1], p), p), y, p);
    1178      227093 :   return 0;
    1179             : }
    1180             : 
    1181             : static long
    1182     5784502 : Fle_add_inplace(GEN P, GEN Q, ulong a4, ulong p)
    1183             : {
    1184             :   ulong Px, Py, Qx, Qy, slope;
    1185     5784502 :   if (ell_is_inf(Q)) return 0;
    1186     5784638 :   Px = P[1]; Py = P[2];
    1187     5784638 :   Qx = Q[1]; Qy = Q[2];
    1188     5784638 :   if (Px==Qx)
    1189      238549 :     return Py==Qy ? Fle_dbl_inplace(P, a4, p): 1;
    1190     5546089 :   slope = Fl_div(Fl_sub(Py, Qy, p), Fl_sub(Px, Qx, p), p);
    1191     5551344 :   P[1] = Fl_sub(Fl_sub(Fl_sqr(slope, p), Px, p), Qx, p);
    1192     5548424 :   P[2] = Fl_sub(Fl_mul(slope, Fl_sub(Px, P[1], p), p), Py, p);
    1193     5546243 :   return 0;
    1194             : }
    1195             : 
    1196             : /* assume 99 < p < 2^(BIL-1) - 2^((BIL+1)/2) and e has good reduction at p.
    1197             :  * Should use Barett reduction + multi-inverse. See Fp_ellcard_Shanks() */
    1198             : static long
    1199      255240 : Fl_ellcard_Shanks(ulong c4, ulong c6, ulong p)
    1200             : {
    1201             :   GEN f, fh, fg, ftest, F;
    1202             :   ulong i, l, r, s, h, x, cp4, p1p, p2p, pordmin,A,B;
    1203             :   long KRO;
    1204      255240 :   pari_sp av = avma;
    1205             :   multiple *table;
    1206             : 
    1207      255240 :   if (!c6) {
    1208          14 :     GEN ap = ap_j1728(utoi(c4), utoipos(p));
    1209          14 :     return gc_long(av, p+1 - itos(ap));
    1210             :   }
    1211             : 
    1212      255226 :   pordmin = (ulong)(1 + 4*sqrt((double)p));
    1213      255226 :   p1p = p+1;
    1214      255226 :   p2p = p1p << 1;
    1215      255226 :   x = 0; KRO = 0;
    1216      255226 :   switch(Flx_nbroots(mkvecsmall5(0L, c6,c4,0L,1L), p))
    1217             :   {
    1218       51862 :     case 3:  A = 0; B = 4; break;
    1219      124777 :     case 1:  A = 0; B = 2; break;
    1220       78610 :     default: A = 1; B = 2; break; /* 0 */
    1221             :   }
    1222             :   for(;;)
    1223             :   { /* see comments in Fp_ellcard_Shanks */
    1224      262518 :     h = uclosest_lift(A, B, p1p);
    1225      262489 :     if (!KRO) /* first time, initialize */
    1226             :     {
    1227      255222 :       KRO = krouu(c6,p); /* != 0 */
    1228      255244 :       f = mkvecsmall2(0, Fl_sqr(c6,p));
    1229             :     }
    1230             :     else
    1231             :     {
    1232        7267 :       KRO = -KRO;
    1233        7267 :       f = Fl_ellpoint(KRO, &x, c4,c6,p);
    1234             :     }
    1235      262516 :     cp4 = Fl_mul(c4, f[2], p);
    1236      262517 :     fh = Fle_mulu(f, h, cp4, p);
    1237      262266 :     if (ell_is_inf(fh)) goto FOUND;
    1238             : 
    1239      255499 :     s = (ulong) (sqrt(((double)pordmin)/B) / 2);
    1240      255499 :     if (!s) s = 1;
    1241      255499 :     table = (multiple *) stack_malloc((s+1) * sizeof(multiple));
    1242      255519 :     F = Fle_mulu(f, B, cp4, p);
    1243     3342448 :     for (i=0; i < s; i++)
    1244             :     {
    1245     3098214 :       table[i].x = fh[1];
    1246     3098214 :       table[i].y = fh[2];
    1247     3098214 :       table[i].i = i;
    1248     3098214 :       if (Fle_add_inplace(fh, F, cp4, p)) { h += B*(i+1); goto FOUND; }
    1249             :     }
    1250      244234 :     qsort(table,s,sizeof(multiple),compare_multiples);
    1251      244302 :     fg = Fle_mulu(F, s, cp4, p); ftest = zv_copy(fg);
    1252      244080 :     if (ell_is_inf(ftest)) {
    1253           0 :       if (!uisprime(p)) pari_err_PRIME("ellap",utoi(p));
    1254           0 :       pari_err_BUG("ellap (f^(i*s) = 1)");
    1255             :     }
    1256     2935393 :     for (i=1; ; i++)
    1257             :     {
    1258     2935393 :       l=0; r=s;
    1259    20624306 :       while (l<r)
    1260             :       {
    1261    17688913 :         ulong m = (l+r) >> 1;
    1262    17688913 :         if (table[m].x < uel(ftest,1)) l=m+1; else r=m;
    1263             :       }
    1264     2935393 :       if (r < s && table[r].x == uel(ftest,1)) break;
    1265     2691114 :       if (Fle_add_inplace(ftest, fg, cp4, p)) pari_err_PRIME("ellap",utoi(p));
    1266             :     }
    1267      244279 :     h += table[r].i * B;
    1268      244279 :     if (table[r].y == uel(ftest,2))
    1269      126870 :       h -= s * i * B;
    1270             :     else
    1271      117409 :       h += s * i * B;
    1272      262528 : FOUND:
    1273      262528 :     h = itou(Fle_order(f, utoipos(h), cp4, p));
    1274             :     /* h | #E_u(Fp) = A (mod B) */
    1275             :     {
    1276             :       GEN C;
    1277      262409 :       A = itou( Z_chinese_all(gen_0, utoi(A), utoipos(h), utoipos(B), &C) );
    1278      262493 :       if (abscmpiu(C, pordmin) >= 0) { /* uclosest_lift could overflow */
    1279      255224 :         h = itou( closest_lift(utoi(A), C, utoipos(p1p)) );
    1280      255243 :         break;
    1281             :       }
    1282        7269 :       B = itou(C);
    1283             :     }
    1284        7269 :     A = (p2p - A) % B; set_avma(av);
    1285             :   }
    1286      255243 :   return gc_long(av, KRO==1? h: p2p-h);
    1287             : }
    1288             : 
    1289             : /** ellap from CM (original code contributed by Mark Watkins) **/
    1290             : 
    1291             : static GEN
    1292       99690 : ap_j0(GEN a6,GEN p)
    1293             : {
    1294             :   GEN a, b, e, d;
    1295       99690 :   if (umodiu(p,3) != 1) return gen_0;
    1296       42198 :   (void)cornacchia2(utoipos(27),p, &a,&b);
    1297       42441 :   if (umodiu(a, 3) == 1) a = negi(a);
    1298       42434 :   d = mulis(a6,-108);
    1299       42356 :   e = diviuexact(shifti(p,-1), 3); /* (p-1) / 6 */
    1300       42277 :   return centermod(mulii(a, Fp_pow(d, e, p)), p);
    1301             : }
    1302             : static GEN
    1303     2874232 : ap_j1728(GEN a4,GEN p)
    1304             : {
    1305             :   GEN a, b, e;
    1306     2874232 :   if (mod4(p) != 1) return gen_0;
    1307     1318854 :   (void)cornacchia2(utoipos(4),p, &a,&b);
    1308     1319683 :   if (Mod4(a)==0) a = b;
    1309     1319669 :   if (Mod2(a)==1) a = shifti(a,1);
    1310     1319257 :   if (Mod8(a)==6) a = negi(a);
    1311     1319052 :   e = shifti(p,-2); /* (p-1) / 4 */
    1312     1318164 :   return centermod(mulii(a, Fp_pow(a4, e, p)), p);
    1313             : }
    1314             : static GEN
    1315         133 : ap_j8000(GEN a6, GEN p)
    1316             : {
    1317             :   GEN a, b;
    1318         133 :   long r = mod8(p), s = 1;
    1319         133 :   if (r != 1 && r != 3) return gen_0;
    1320          49 :   (void)cornacchia2(utoipos(8),p, &a,&b);
    1321          49 :   switch(Mod16(a)) {
    1322          14 :     case 2: case 6:   if (Mod4(b)) s = -s;
    1323          14 :       break;
    1324          35 :     case 10: case 14: if (!Mod4(b)) s = -s;
    1325          35 :       break;
    1326             :   }
    1327          49 :   if (kronecker(mulis(a6, 42), p) < 0) s = -s;
    1328          49 :   return s > 0? a: negi(a);
    1329             : }
    1330             : static GEN
    1331         140 : ap_j287496(GEN a6, GEN p)
    1332             : {
    1333             :   GEN a, b;
    1334         140 :   long s = 1;
    1335         140 :   if (mod4(p) != 1) return gen_0;
    1336          70 :   (void)cornacchia2(utoipos(4),p, &a,&b);
    1337          70 :   if (Mod4(a)==0) a = b;
    1338          70 :   if (Mod2(a)==1) a = shifti(a,1);
    1339          70 :   if (Mod8(a)==6) s = -s;
    1340          70 :   if (krosi(2,p) < 0) s = -s;
    1341          70 :   if (kronecker(mulis(a6, -14), p) < 0) s = -s;
    1342          70 :   return s > 0? a: negi(a);
    1343             : }
    1344             : static GEN
    1345        1400 : ap_cm(int CM, long A6B, GEN a6, GEN p)
    1346             : {
    1347             :   GEN a, b;
    1348        1400 :   long s = 1;
    1349        1400 :   if (krosi(CM,p) < 0) return gen_0;
    1350         644 :   (void)cornacchia2(utoipos(-CM),p, &a, &b);
    1351         644 :   if ((CM&3) == 0) CM >>= 2;
    1352         644 :   if ((krois(a, -CM) > 0) ^ (CM == -7)) s = -s;
    1353         644 :   if (kronecker(mulis(a6,A6B), p) < 0) s = -s;
    1354         644 :   return s > 0? a: negi(a);
    1355             : }
    1356             : static GEN
    1357      495352 : ec_ap_cm(int CM, GEN a4, GEN a6, GEN p)
    1358             : {
    1359      495352 :   switch(CM)
    1360             :   {
    1361       29014 :     case  -3: return ap_j0(a6, p);
    1362      464668 :     case  -4: return ap_j1728(a4, p);
    1363         133 :     case  -8: return ap_j8000(a6, p);
    1364         140 :     case -16: return ap_j287496(a6, p);
    1365         161 :     case  -7: return ap_cm(CM, -2, a6, p);
    1366         161 :     case -11: return ap_cm(CM, 21, a6, p);
    1367         175 :     case -12: return ap_cm(CM, 22, a6, p);
    1368         147 :     case -19: return ap_cm(CM, 1, a6, p);
    1369         154 :     case -27: return ap_cm(CM, 253, a6, p);
    1370         147 :     case -28: return ap_cm(-7, -114, a6, p); /* yes, -7 ! */
    1371         161 :     case -43: return ap_cm(CM, 21, a6, p);
    1372         147 :     case -67: return ap_cm(CM, 217, a6, p);
    1373         147 :     case -163:return ap_cm(CM, 185801, a6, p);
    1374           0 :     default: return NULL;
    1375             :   }
    1376             : }
    1377             : 
    1378             : static GEN
    1379       49720 : Fp_ellj_nodiv(GEN a4, GEN a6, GEN p)
    1380             : {
    1381       49720 :   GEN a43 = Fp_mulu(Fp_powu(a4, 3, p), 4, p);
    1382       49731 :   GEN a62 = Fp_mulu(Fp_sqr(a6, p), 27, p);
    1383       49731 :   return mkvec2(Fp_mulu(a43, 1728, p), Fp_add(a43, a62, p));
    1384             : }
    1385             : 
    1386             : GEN
    1387          98 : Fp_ellj(GEN a4, GEN a6, GEN p)
    1388             : {
    1389          98 :   pari_sp av = avma;
    1390             :   GEN z;
    1391          98 :   if (lgefint(p) == 3)
    1392             :   {
    1393           0 :     ulong pp = p[2];
    1394           0 :     return utoi(Fl_ellj(umodiu(a4,pp), umodiu(a6,pp), pp));
    1395             :   }
    1396          98 :   z = Fp_ellj_nodiv(a4, a6, p);
    1397          98 :   return gc_INT(av,Fp_div(gel(z,1),gel(z,2),p));
    1398             : }
    1399             : 
    1400             : void
    1401        1092 : Fp_ellj_to_a4a6(GEN j, GEN p, GEN *pt_a4, GEN *pt_a6)
    1402             : {
    1403        1092 :   j = modii(j, p);
    1404        1092 :   if (signe(j) == 0)    { *pt_a4 = gen_0; *pt_a6 = gen_1; }
    1405         749 :   else if (equaliu(j,umodui(1728,p))) { *pt_a4 = gen_1; *pt_a6 = gen_0; }
    1406             :   else
    1407             :   {
    1408         588 :     GEN k = Fp_sub(utoi(1728), j, p);
    1409         588 :     GEN kj = Fp_mul(k, j, p);
    1410         588 :     GEN k2j = Fp_mul(kj, k, p);
    1411         588 :     *pt_a4 = Fp_mulu(kj, 3, p);
    1412         588 :     *pt_a6 = Fp_double(k2j, p);
    1413             :   }
    1414        1092 : }
    1415             : 
    1416             : static GEN /* Only compute a mod p, so assume p>=17 */
    1417     2529627 : Fp_ellcard_CM(GEN a4, GEN a6, GEN p)
    1418             : {
    1419     2529627 :   pari_sp av = avma;
    1420             :   GEN a;
    1421     2529627 :   if (!signe(a4)) a = ap_j0(a6,p);
    1422     2458957 :   else if (!signe(a6)) a = ap_j1728(a4,p);
    1423             :   else
    1424             :   {
    1425       49599 :     GEN j = Fp_ellj_nodiv(a4, a6, p);
    1426       49629 :     long CM = Fp_ellj_get_CM(gel(j,1), gel(j,2), p);
    1427       49610 :     if (!CM) return gc_NULL(av);
    1428        1673 :     a = ec_ap_cm(CM,a4,a6,p);
    1429             :   }
    1430     2481909 :   return gc_INT(av, subii(addiu(p,1),a));
    1431             : }
    1432             : 
    1433             : GEN
    1434     2833866 : Fp_ellcard(GEN a4, GEN a6, GEN p)
    1435             : {
    1436     2833866 :   long lp = expi(p);
    1437     2833845 :   ulong pp = p[2];
    1438     2833845 :   if (lp < 11)
    1439      304270 :     return utoi(pp+1 - Fl_elltrace_naive(umodiu(a4,pp), umodiu(a6,pp), pp));
    1440     2529575 :   { GEN a = Fp_ellcard_CM(a4,a6,p); if (a) return a; }
    1441       47939 :   if (lp >= 56)
    1442         868 :     return Fp_ellcard_SEA(a4, a6, p, 0);
    1443       47071 :   if (lp <= BITS_IN_LONG-2)
    1444       46990 :     return utoi(Fl_ellcard_Shanks(umodiu(a4,pp), umodiu(a6,pp), pp));
    1445          81 :   return Fp_ellcard_Shanks(a4, a6, p);
    1446             : }
    1447             : 
    1448             : long
    1449      622174 : Fl_elltrace(ulong a4, ulong a6, ulong p)
    1450             : {
    1451             :   pari_sp av;
    1452             :   long lp;
    1453             :   GEN a;
    1454      622174 :   if (p < (1<<11)) return Fl_elltrace_naive(a4, a6, p);
    1455      208230 :   lp = expu(p);
    1456      208233 :   if (lp <= minss(56, BITS_IN_LONG-2)) return p+1-Fl_ellcard_Shanks(a4, a6, p);
    1457           0 :   av = avma; a = subui(p+1, Fp_ellcard(utoi(a4), utoi(a6), utoipos(p)));
    1458           0 :   return gc_long(av, itos(a));
    1459             : }
    1460             : long
    1461     1163637 : Fl_elltrace_CM(long CM, ulong a4, ulong a6, ulong p)
    1462             : {
    1463             :   pari_sp av;
    1464             :   GEN a;
    1465     1163637 :   if (!CM) return Fl_elltrace(a4,a6,p);
    1466      542282 :   if (p < (1<<11)) return Fl_elltrace_naive(a4, a6, p);
    1467      494054 :   av = avma; a = ec_ap_cm(CM, utoi(a4), utoi(a6), utoipos(p));
    1468      490947 :   return gc_long(av, itos(a));
    1469             : }
    1470             : 
    1471             : static GEN
    1472       72723 : _FpE_pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)
    1473             : {
    1474       72723 :   struct _FpE *e = (struct _FpE *) E;
    1475       72723 :   return  Fp_order(FpE_weilpairing(P,Q,m,e->a4,e->p), F, e->p);
    1476             : }
    1477             : 
    1478             : GEN
    1479      120715 : Fp_ellgroup(GEN a4, GEN a6, GEN N, GEN p, GEN *pt_m)
    1480             : {
    1481             :   struct _FpE e;
    1482      120715 :   e.a4=a4; e.a6=a6; e.p=p;
    1483      120715 :   return gen_ellgroup(N, subiu(p,1), pt_m, (void*)&e, &FpE_group, _FpE_pairorder);
    1484             : }
    1485             : 
    1486             : GEN
    1487         574 : Fp_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN p)
    1488             : {
    1489             :   GEN P;
    1490         574 :   pari_sp av = avma;
    1491             :   struct _FpE e;
    1492         574 :   e.a4=a4; e.a6=a6; e.p=p;
    1493         574 :   switch(lg(D)-1)
    1494             :   {
    1495         476 :   case 1:
    1496         476 :     P = gen_gener(gel(D,1), (void*)&e, &FpE_group);
    1497         476 :     P = mkvec(FpE_changepoint(P, ch, p));
    1498         476 :     break;
    1499          98 :   default:
    1500          98 :     P = gen_ellgens(gel(D,1), gel(D,2), m, (void*)&e, &FpE_group, _FpE_pairorder);
    1501          98 :     gel(P,1) = FpE_changepoint(gel(P,1), ch, p);
    1502          98 :     gel(P,2) = FpE_changepoint(gel(P,2), ch, p);
    1503          98 :     break;
    1504             :   }
    1505         574 :   return gc_GEN(av, P);
    1506             : }
    1507             : 
    1508             : /* Not so fast arithmetic with points over elliptic curves over FpXQ */
    1509             : 
    1510             : /***********************************************************************/
    1511             : /**                                                                   **/
    1512             : /**                              FpXQE                                  **/
    1513             : /**                                                                   **/
    1514             : /***********************************************************************/
    1515             : /* These functions deal with point over elliptic curves over FpXQ defined
    1516             :  * by an equation of the form y^2=x^3+a4*x+a6.
    1517             :  * Most of the time a6 is omitted since it can be recovered from any point
    1518             :  * on the curve. */
    1519             : 
    1520             : int
    1521      249081 : RgE_is_FpXQE(GEN x, GEN *pT, GEN *pp)
    1522             : {
    1523      249081 :   if (ell_is_inf(x)) return 1;
    1524      249081 :   if(!Rg_is_FpXQ(gel(x,1), pT, pp)) return 0;
    1525           7 :   if(!Rg_is_FpXQ(gel(x,2), pT, pp)) return 0;
    1526           7 :   return 1;
    1527             : }
    1528             : 
    1529             : GEN
    1530          42 : RgE_to_FpXQE(GEN x, GEN T, GEN p)
    1531             : {
    1532          42 :   if (ell_is_inf(x)) return x;
    1533          42 :   retmkvec2(Rg_to_FpXQ(gel(x,1),T,p),Rg_to_FpXQ(gel(x,2),T,p));
    1534             : }
    1535             : 
    1536             : GEN
    1537         942 : FpXQE_changepoint(GEN x, GEN ch, GEN T, GEN p)
    1538             : {
    1539         942 :   pari_sp av = avma;
    1540             :   GEN p1,z,u,r,s,t,v,v2,v3;
    1541         942 :   if (ell_is_inf(x)) return x;
    1542         942 :   u = gel(ch,1); r = gel(ch,2);
    1543         942 :   s = gel(ch,3); t = gel(ch,4);
    1544         942 :   v = FpXQ_inv(u, T, p); v2 = FpXQ_sqr(v, T, p); v3 = FpXQ_mul(v,v2, T, p);
    1545         942 :   p1 = FpX_sub(gel(x,1),r, p);
    1546         942 :   z = cgetg(3,t_VEC);
    1547         942 :   gel(z,1) = FpXQ_mul(v2, p1, T, p);
    1548         942 :   gel(z,2) = FpXQ_mul(v3, FpX_sub(gel(x,2), FpX_add(FpXQ_mul(s,p1, T, p),t, p), p), T, p);
    1549         942 :   return gc_upto(av, z);
    1550             : }
    1551             : 
    1552             : GEN
    1553          42 : FpXQE_changepointinv(GEN x, GEN ch, GEN T, GEN p)
    1554             : {
    1555             :   GEN u, r, s, t, X, Y, u2, u3, u2X, z;
    1556          42 :   if (ell_is_inf(x)) return x;
    1557          42 :   X = gel(x,1); Y = gel(x,2);
    1558          42 :   u = gel(ch,1); r = gel(ch,2);
    1559          42 :   s = gel(ch,3); t = gel(ch,4);
    1560          42 :   u2 = FpXQ_sqr(u, T, p); u3 = FpXQ_mul(u,u2, T, p);
    1561          42 :   u2X = FpXQ_mul(u2,X, T, p);
    1562          42 :   z = cgetg(3, t_VEC);
    1563          42 :   gel(z,1) = FpX_add(u2X,r, p);
    1564          42 :   gel(z,2) = FpX_add(FpXQ_mul(u3,Y, T, p), FpX_add(FpXQ_mul(s,u2X, T, p), t, p), p);
    1565          42 :   return z;
    1566             : }
    1567             : 
    1568             : static GEN
    1569         840 : random_nonsquare_FpXQ(GEN T, GEN p)
    1570             : {
    1571         840 :   pari_sp av = avma;
    1572         840 :   long n = degpol(T), v = varn(T);
    1573             :   GEN a;
    1574         840 :   if (odd(n))
    1575             :   {
    1576         420 :     GEN z = cgetg(3, t_POL);
    1577         420 :     z[1] = evalsigne(1) | evalvarn(v);
    1578         420 :     gel(z,2) = random_nonsquare_Fp(p); return z;
    1579             :   }
    1580             :   do
    1581             :   {
    1582         791 :     set_avma(av);
    1583         791 :     a = random_FpX(n, v, p);
    1584         791 :   } while (FpXQ_issquare(a, T, p));
    1585         420 :   return a;
    1586             : }
    1587             : 
    1588             : void
    1589         840 : FpXQ_elltwist(GEN a4, GEN a6, GEN T, GEN p, GEN *pt_a4, GEN *pt_a6)
    1590             : {
    1591         840 :   GEN d = random_nonsquare_FpXQ(T, p);
    1592         840 :   GEN d2 = FpXQ_sqr(d, T, p), d3 = FpXQ_mul(d2, d, T, p);
    1593         840 :   *pt_a4 = FpXQ_mul(a4, d2, T, p);
    1594         840 :   *pt_a6 = FpXQ_mul(a6, d3, T, p);
    1595         840 : }
    1596             : 
    1597             : static GEN
    1598       72606 : FpXQE_dbl_slope(GEN P, GEN a4, GEN T, GEN p, GEN *slope)
    1599             : {
    1600             :   GEN x, y, Q;
    1601       72606 :   if (ell_is_inf(P) || !signe(gel(P,2))) return ellinf();
    1602       72544 :   x = gel(P,1); y = gel(P,2);
    1603       72544 :   *slope = FpXQ_div(FpX_add(FpX_mulu(FpXQ_sqr(x, T, p), 3, p), a4, p),
    1604             :                             FpX_mulu(y, 2, p), T, p);
    1605       72544 :   Q = cgetg(3,t_VEC);
    1606       72544 :   gel(Q, 1) = FpX_sub(FpXQ_sqr(*slope, T, p), FpX_mulu(x, 2, p), p);
    1607       72544 :   gel(Q, 2) = FpX_sub(FpXQ_mul(*slope, FpX_sub(x, gel(Q, 1), p), T, p), y, p);
    1608       72544 :   return Q;
    1609             : }
    1610             : 
    1611             : GEN
    1612       68504 : FpXQE_dbl(GEN P, GEN a4, GEN T, GEN p)
    1613             : {
    1614       68504 :   pari_sp av = avma;
    1615             :   GEN slope;
    1616       68504 :   return gc_upto(av, FpXQE_dbl_slope(P,a4,T,p,&slope));
    1617             : }
    1618             : 
    1619             : static GEN
    1620      222516 : FpXQE_add_slope(GEN P, GEN Q, GEN a4, GEN T, GEN p, GEN *slope)
    1621             : {
    1622             :   GEN Px, Py, Qx, Qy, R;
    1623      222516 :   if (ell_is_inf(P)) return Q;
    1624      222502 :   if (ell_is_inf(Q)) return P;
    1625      222502 :   Px = gel(P,1); Py = gel(P,2);
    1626      222502 :   Qx = gel(Q,1); Qy = gel(Q,2);
    1627      222502 :   if (ZX_equal(Px, Qx))
    1628             :   {
    1629         203 :     if (ZX_equal(Py, Qy))
    1630           7 :       return FpXQE_dbl_slope(P, a4, T, p, slope);
    1631             :     else
    1632         196 :       return ellinf();
    1633             :   }
    1634      222299 :   *slope = FpXQ_div(FpX_sub(Py, Qy, p), FpX_sub(Px, Qx, p), T, p);
    1635      222299 :   R = cgetg(3,t_VEC);
    1636      222299 :   gel(R, 1) = FpX_sub(FpX_sub(FpXQ_sqr(*slope, T, p), Px, p), Qx, p);
    1637      222299 :   gel(R, 2) = FpX_sub(FpXQ_mul(*slope, FpX_sub(Px, gel(R, 1), p), T, p), Py, p);
    1638      222299 :   return R;
    1639             : }
    1640             : 
    1641             : GEN
    1642      221956 : FpXQE_add(GEN P, GEN Q, GEN a4, GEN T, GEN p)
    1643             : {
    1644      221956 :   pari_sp av = avma;
    1645             :   GEN slope;
    1646      221956 :   return gc_upto(av, FpXQE_add_slope(P,Q,a4,T,p,&slope));
    1647             : }
    1648             : 
    1649             : static GEN
    1650           0 : FpXQE_neg_i(GEN P, GEN p)
    1651             : {
    1652           0 :   if (ell_is_inf(P)) return P;
    1653           0 :   return mkvec2(gel(P,1), FpX_neg(gel(P,2), p));
    1654             : }
    1655             : 
    1656             : GEN
    1657       73329 : FpXQE_neg(GEN P, GEN T, GEN p)
    1658             : {
    1659             :   (void) T;
    1660       73329 :   if (ell_is_inf(P)) return ellinf();
    1661       73329 :   return mkvec2(gcopy(gel(P,1)), FpX_neg(gel(P,2), p));
    1662             : }
    1663             : 
    1664             : GEN
    1665           0 : FpXQE_sub(GEN P, GEN Q, GEN a4, GEN T, GEN p)
    1666             : {
    1667           0 :   pari_sp av = avma;
    1668             :   GEN slope;
    1669           0 :   return gc_upto(av, FpXQE_add_slope(P, FpXQE_neg_i(Q, p), a4, T, p, &slope));
    1670             : }
    1671             : 
    1672             : struct _FpXQE { GEN a4,a6,T,p; };
    1673             : static GEN
    1674       68504 : _FpXQE_dbl(void *E, GEN P)
    1675             : {
    1676       68504 :   struct _FpXQE *ell = (struct _FpXQE *) E;
    1677       68504 :   return FpXQE_dbl(P, ell->a4, ell->T, ell->p);
    1678             : }
    1679             : static GEN
    1680      221956 : _FpXQE_add(void *E, GEN P, GEN Q)
    1681             : {
    1682      221956 :   struct _FpXQE *ell=(struct _FpXQE *) E;
    1683      221956 :   return FpXQE_add(P, Q, ell->a4, ell->T, ell->p);
    1684             : }
    1685             : static GEN
    1686       80841 : _FpXQE_mul(void *E, GEN P, GEN n)
    1687             : {
    1688       80841 :   pari_sp av = avma;
    1689       80841 :   struct _FpXQE *e=(struct _FpXQE *) E;
    1690       80841 :   long s = signe(n);
    1691       80841 :   if (!s || ell_is_inf(P)) return ellinf();
    1692       80841 :   if (s<0) P = FpXQE_neg(P, e->T, e->p);
    1693       80841 :   if (is_pm1(n)) return s>0? gcopy(P): P;
    1694        7420 :   return gc_GEN(av, gen_pow_i(P, n, e, &_FpXQE_dbl, &_FpXQE_add));
    1695             : }
    1696             : 
    1697             : GEN
    1698           0 : FpXQE_mul(GEN P, GEN n, GEN a4, GEN T, GEN p)
    1699             : {
    1700             :   struct _FpXQE E;
    1701           0 :   E.a4= a4; E.T = T; E.p = p;
    1702           0 :   return _FpXQE_mul(&E, P, n);
    1703             : }
    1704             : 
    1705             : /* Finds a random nonsingular point on E */
    1706             : 
    1707             : GEN
    1708        1081 : random_FpXQE(GEN a4, GEN a6, GEN T, GEN p)
    1709             : {
    1710        1081 :   pari_sp ltop = avma;
    1711             :   GEN x, x2, y, rhs;
    1712        1081 :   long v = get_FpX_var(T), d = get_FpX_degree(T);
    1713             :   do
    1714             :   {
    1715        2204 :     set_avma(ltop);
    1716        2204 :     x   = random_FpX(d,v,p); /*  x^3+a4*x+a6 = x*(x^2+a4)+a6  */
    1717        2204 :     x2  = FpXQ_sqr(x, T, p);
    1718        2204 :     rhs = FpX_add(FpXQ_mul(x, FpX_add(x2, a4, p), T, p), a6, p);
    1719           0 :   } while ((!signe(rhs) && !signe(FpX_add(FpX_mulu(x2,3,p), a4, p)))
    1720        2204 :           || !FpXQ_issquare(rhs, T, p));
    1721        1081 :   y = FpXQ_sqrt(rhs, T, p);
    1722        1081 :   if (!y) pari_err_PRIME("random_FpE", p);
    1723        1081 :   return gc_GEN(ltop, mkvec2(x, y));
    1724             : }
    1725             : 
    1726             : static GEN
    1727         147 : _FpXQE_rand(void *E)
    1728             : {
    1729         147 :   struct _FpXQE *e=(struct _FpXQE *) E;
    1730         147 :   return random_FpXQE(e->a4, e->a6, e->T, e->p);
    1731             : }
    1732             : 
    1733             : static const struct bb_group FpXQE_group={_FpXQE_add,_FpXQE_mul,_FpXQE_rand,hash_GEN,ZXV_equal,ell_is_inf};
    1734             : 
    1735             : const struct bb_group *
    1736          16 : get_FpXQE_group(void ** pt_E, GEN a4, GEN a6, GEN T, GEN p)
    1737             : {
    1738          16 :   struct _FpXQE *e = (struct _FpXQE *) stack_malloc(sizeof(struct _FpXQE));
    1739          16 :   e->a4 = a4; e->a6 = a6; e->T = T; e->p = p;
    1740          16 :   *pt_E = (void *) e;
    1741          16 :   return &FpXQE_group;
    1742             : }
    1743             : 
    1744             : GEN
    1745          14 : FpXQE_order(GEN z, GEN o, GEN a4, GEN T, GEN p)
    1746             : {
    1747          14 :   pari_sp av = avma;
    1748             :   struct _FpXQE e;
    1749          14 :   e.a4=a4; e.T=T; e.p=p;
    1750          14 :   return gc_INT(av, gen_order(z, o, (void*)&e, &FpXQE_group));
    1751             : }
    1752             : 
    1753             : GEN
    1754           0 : FpXQE_log(GEN a, GEN b, GEN o, GEN a4, GEN T, GEN p)
    1755             : {
    1756           0 :   pari_sp av = avma;
    1757             :   struct _FpXQE e;
    1758           0 :   e.a4=a4; e.T=T; e.p=p;
    1759           0 :   return gc_INT(av, gen_PH_log(a, b, o, (void*)&e, &FpXQE_group));
    1760             : }
    1761             : 
    1762             : /***********************************************************************/
    1763             : /**                                                                   **/
    1764             : /**                            Pairings                               **/
    1765             : /**                                                                   **/
    1766             : /***********************************************************************/
    1767             : 
    1768             : /* Derived from APIP from and by Jerome Milan, 2012 */
    1769             : 
    1770             : static GEN
    1771        4788 : FpXQE_vert(GEN P, GEN Q, GEN a4, GEN T, GEN p)
    1772             : {
    1773        4788 :   long vT = get_FpX_var(T);
    1774        4788 :   if (ell_is_inf(P))
    1775          70 :     return pol_1(get_FpX_var(T));
    1776        4718 :   if (!ZX_equal(gel(Q, 1), gel(P, 1)))
    1777        4718 :     return FpX_sub(gel(Q, 1), gel(P, 1), p);
    1778           0 :   if (signe(gel(P,2))!=0) return pol_1(vT);
    1779           0 :   return FpXQ_inv(FpX_add(FpX_mulu(FpXQ_sqr(gel(P,1), T, p), 3, p),
    1780             :                   a4, p), T, p);
    1781             : }
    1782             : 
    1783             : static GEN
    1784        4655 : FpXQE_Miller_line(GEN R, GEN Q, GEN slope, GEN a4, GEN T, GEN p)
    1785             : {
    1786        4655 :   long vT = get_FpX_var(T);
    1787        4655 :   GEN x = gel(Q, 1), y = gel(Q, 2);
    1788        4655 :   GEN tmp1  = FpX_sub(x, gel(R, 1), p);
    1789        4655 :   GEN tmp2  = FpX_add(FpXQ_mul(tmp1, slope, T, p), gel(R, 2), p);
    1790        4655 :   if (!ZX_equal(y, tmp2))
    1791        4655 :     return FpX_sub(y, tmp2, p);
    1792           0 :   if (signe(y) == 0)
    1793           0 :     return pol_1(vT);
    1794             :   else
    1795             :   {
    1796             :     GEN s1, s2;
    1797           0 :     GEN y2i = FpXQ_inv(FpX_mulu(y, 2, p), T, p);
    1798           0 :     s1 = FpXQ_mul(FpX_add(FpX_mulu(FpXQ_sqr(x, T, p), 3, p), a4, p), y2i, T, p);
    1799           0 :     if (!ZX_equal(s1, slope))
    1800           0 :       return FpX_sub(s1, slope, p);
    1801           0 :     s2 = FpXQ_mul(FpX_sub(FpX_mulu(x, 3, p), FpXQ_sqr(s1, T, p), p), y2i, T, p);
    1802           0 :     return signe(s2)!=0 ? s2: y2i;
    1803             :   }
    1804             : }
    1805             : 
    1806             : /* Computes the equation of the line tangent to R and returns its
    1807             :    evaluation at the point Q. Also doubles the point R.
    1808             :  */
    1809             : 
    1810             : static GEN
    1811        4158 : FpXQE_tangent_update(GEN R, GEN Q, GEN a4, GEN T, GEN p, GEN *pt_R)
    1812             : {
    1813        4158 :   if (ell_is_inf(R))
    1814             :   {
    1815           7 :     *pt_R = ellinf();
    1816           7 :     return pol_1(get_FpX_var(T));
    1817             :   }
    1818        4151 :   else if (!signe(gel(R,2)))
    1819             :   {
    1820          56 :     *pt_R = ellinf();
    1821          56 :     return FpXQE_vert(R, Q, a4, T, p);
    1822             :   } else {
    1823             :     GEN slope;
    1824        4095 :     *pt_R = FpXQE_dbl_slope(R, a4, T, p, &slope);
    1825        4095 :     return FpXQE_Miller_line(R, Q, slope, a4, T, p);
    1826             :   }
    1827             : }
    1828             : 
    1829             : /* Computes the equation of the line through R and P, and returns its
    1830             :    evaluation at the point Q. Also adds P to the point R.
    1831             :  */
    1832             : 
    1833             : static GEN
    1834         567 : FpXQE_chord_update(GEN R, GEN P, GEN Q, GEN a4, GEN T, GEN p, GEN *pt_R)
    1835             : {
    1836         567 :   if (ell_is_inf(R))
    1837             :   {
    1838           0 :     *pt_R = gcopy(P);
    1839           0 :     return FpXQE_vert(P, Q, a4, T, p);
    1840             :   }
    1841         567 :   else if (ell_is_inf(P))
    1842             :   {
    1843           0 :     *pt_R = gcopy(R);
    1844           0 :     return FpXQE_vert(R, Q, a4, T, p);
    1845             :   }
    1846         567 :   else if (ZX_equal(gel(P, 1), gel(R, 1)))
    1847             :   {
    1848           7 :     if (ZX_equal(gel(P, 2), gel(R, 2)))
    1849           0 :       return FpXQE_tangent_update(R, Q, a4, T, p, pt_R);
    1850             :     else
    1851             :     {
    1852           7 :       *pt_R = ellinf();
    1853           7 :       return FpXQE_vert(R, Q, a4, T, p);
    1854             :     }
    1855             :   } else {
    1856             :     GEN slope;
    1857         560 :     *pt_R = FpXQE_add_slope(P, R, a4, T, p, &slope);
    1858         560 :     return FpXQE_Miller_line(R, Q, slope, a4, T, p);
    1859             :   }
    1860             : }
    1861             : 
    1862             : struct _FpXQE_miller { GEN p, T, a4, P; };
    1863             : static GEN
    1864        4158 : FpXQE_Miller_dbl(void* E, GEN d)
    1865             : {
    1866        4158 :   struct _FpXQE_miller *m = (struct _FpXQE_miller *)E;
    1867        4158 :   GEN p  = m->p;
    1868        4158 :   GEN T = m->T, a4 = m->a4, P = m->P;
    1869             :   GEN v, line;
    1870        4158 :   GEN N = FpXQ_sqr(gel(d,1), T, p);
    1871        4158 :   GEN D = FpXQ_sqr(gel(d,2), T, p);
    1872        4158 :   GEN point = gel(d,3);
    1873        4158 :   line = FpXQE_tangent_update(point, P, a4, T, p, &point);
    1874        4158 :   N = FpXQ_mul(N, line, T, p);
    1875        4158 :   v = FpXQE_vert(point, P, a4, T, p);
    1876        4158 :   D = FpXQ_mul(D, v, T, p); return mkvec3(N, D, point);
    1877             : }
    1878             : 
    1879             : static GEN
    1880         567 : FpXQE_Miller_add(void* E, GEN va, GEN vb)
    1881             : {
    1882         567 :   struct _FpXQE_miller *m = (struct _FpXQE_miller *)E;
    1883         567 :   GEN p = m->p;
    1884         567 :   GEN T = m->T, a4 = m->a4, P = m->P;
    1885             :   GEN v, line, point;
    1886         567 :   GEN na = gel(va,1), da = gel(va,2), pa = gel(va,3);
    1887         567 :   GEN nb = gel(vb,1), db = gel(vb,2), pb = gel(vb,3);
    1888         567 :   GEN N = FpXQ_mul(na, nb, T, p);
    1889         567 :   GEN D = FpXQ_mul(da, db, T, p);
    1890         567 :   line = FpXQE_chord_update(pa, pb, P, a4, T, p, &point);
    1891         567 :   N = FpXQ_mul(N, line, T, p);
    1892         567 :   v = FpXQE_vert(point, P, a4, T, p);
    1893         567 :   D = FpXQ_mul(D, v, T, p); return mkvec3(N, D, point);
    1894             : }
    1895             : 
    1896             : /* Returns the Miller function f_{m, Q} evaluated at the point P using
    1897             :  * the standard Miller algorithm. */
    1898             : static GEN
    1899          63 : FpXQE_Miller(GEN Q, GEN P, GEN m, GEN a4, GEN T, GEN p)
    1900             : {
    1901          63 :   pari_sp av = avma;
    1902             :   struct _FpXQE_miller d;
    1903             :   GEN v, N, D, g1;
    1904             : 
    1905          63 :   d.a4 = a4; d.T = T; d.p = p; d.P = P;
    1906          63 :   g1 = pol_1(get_FpX_var(T));
    1907          63 :   v = gen_pow_i(mkvec3(g1,g1,Q), m, (void*)&d,
    1908             :                 FpXQE_Miller_dbl, FpXQE_Miller_add);
    1909          63 :   N = gel(v,1); D = gel(v,2);
    1910          63 :   return gc_upto(av, FpXQ_div(N, D, T, p));
    1911             : }
    1912             : 
    1913             : GEN
    1914          28 : FpXQE_weilpairing(GEN P, GEN Q, GEN m, GEN a4, GEN T, GEN p)
    1915             : {
    1916          28 :   pari_sp av = avma;
    1917             :   GEN N, D, w;
    1918          28 :   if (ell_is_inf(P) || ell_is_inf(Q) || ZXV_equal(P,Q))
    1919           0 :     return pol_1(get_FpX_var(T));
    1920          28 :   N = FpXQE_Miller(P, Q, m, a4, T, p);
    1921          28 :   D = FpXQE_Miller(Q, P, m, a4, T, p);
    1922          28 :   w = FpXQ_div(N, D, T, p);
    1923          28 :   if (mpodd(m)) w = FpX_neg(w, p);
    1924          28 :   return gc_upto(av, w);
    1925             : }
    1926             : 
    1927             : GEN
    1928           7 : FpXQE_tatepairing(GEN P, GEN Q, GEN m, GEN a4, GEN T, GEN p)
    1929             : {
    1930           7 :   if (ell_is_inf(P) || ell_is_inf(Q)) return pol_1(get_FpX_var(T));
    1931           7 :   return FpXQE_Miller(P, Q, m, a4, T, p);
    1932             : }
    1933             : 
    1934             : /***********************************************************************/
    1935             : /**                                                                   **/
    1936             : /**                           issupersingular                         **/
    1937             : /**                                                                   **/
    1938             : /***********************************************************************/
    1939             : 
    1940             : GEN
    1941        1718 : FpXQ_ellj(GEN a4, GEN a6, GEN T, GEN p)
    1942             : {
    1943        1718 :   if (absequaliu(p,3)) return pol_0(get_FpX_var(T));
    1944             :   else
    1945             :   {
    1946        1718 :     pari_sp av=avma;
    1947        1718 :     GEN a43 = FpXQ_mul(a4,FpXQ_sqr(a4,T,p),T,p);
    1948        1718 :     GEN a62 = FpXQ_sqr(a6,T,p);
    1949        1718 :     GEN num = FpX_mulu(a43,6912,p);
    1950        1718 :     GEN den = FpX_add(FpX_mulu(a43,4,p),FpX_mulu(a62,27,p),p);
    1951        1718 :     return gc_leaf(av, FpXQ_div(num, den, T, p));
    1952             :   }
    1953             : }
    1954             : 
    1955             : static GEN
    1956       33530 : FpXQ_is_quad(GEN x, GEN T, GEN p)
    1957             : {
    1958       33530 :   pari_sp av = avma;
    1959             :   GEN K;
    1960       33530 :   long d = degpol(T);
    1961       33530 :   x = FpXQ_red(x,T,p);
    1962       33530 :   if (lgpol(x)<=1) return NULL;
    1963       33530 :   if (d==2) return FpXQ_minpoly(x, T, p);
    1964       33530 :   if (odd(degpol(T))) return NULL;
    1965       33530 :   K = FpM_ker(FpXQ_matrix_pow(x, d, 3, T, p), p);
    1966       33530 :   if (lg(K)!=2) return gc_NULL(av);
    1967         588 :   return RgV_to_RgX(gel(K,1), get_FpX_var(T));
    1968             : }
    1969             : 
    1970             : int
    1971      165515 : FpXQ_elljissupersingular(GEN j, GEN T, GEN p)
    1972             : {
    1973      165515 :   pari_sp ltop = avma;
    1974             : 
    1975             :   /* All supersingular j-invariants are in FF_{p^2}, so we first check
    1976             :    * whether j is in FF_{p^2}.  If d is odd, then FF_{p^2} is not a
    1977             :    * subfield of FF_{p^d} so the j-invariants are all in FF_p.  Hence
    1978             :    * the j-invariants are in FF_{p^{2 - e}}. */
    1979      165515 :   ulong d = get_FpX_degree(T);
    1980             :   GEN S;
    1981      165515 :   if (degpol(j) <= 0) return Fp_elljissupersingular(constant_coeff(j), p);
    1982      164612 :   j = FpXQ_red(j, T, p);
    1983      164612 :   if (degpol(j) <= 0) return gc_bool(ltop, Fp_elljissupersingular(constant_coeff(j), p));
    1984             :   /* Now j is not in F_p */
    1985      164612 :   if (abscmpiu(p, 5) <= 0) return gc_bool(ltop,0); /* j != 0*/
    1986      164605 :   if (odd(d)) return 0;
    1987             :   /* Set S so that FF_p[T]/(S) is isomorphic to FF_{p^2}: */
    1988       46949 :   if (d == 2)
    1989       13419 :     S = T;
    1990             :   else /* d > 2 */
    1991             :   {
    1992       33530 :     S = FpXQ_is_quad(j, T, p);
    1993       33530 :     if (!S) return gc_bool(ltop,0);
    1994         588 :     j = pol_x(varn(S));
    1995             :   }
    1996       14007 :   return gc_bool(ltop, jissupersingular(j,S,p));
    1997             : }
    1998             : 
    1999             : int
    2000        1050 : Fq_elljissupersingular(GEN j, GEN T, GEN p)
    2001         959 : { return typ(j)==t_INT? Fp_elljissupersingular(j, p)
    2002        2009 :                       : FpXQ_elljissupersingular(j, T, p); }
    2003             : 
    2004             : /* p > 5 prime; return d such that (-d/p) = -1 */
    2005             : static ulong
    2006        1183 : find_inert_disc(GEN p)
    2007             : {
    2008        1183 :   long s = mod4(p) == 1? -1: 1; /* - (-1/p) */
    2009        1183 :   ulong d = 3;
    2010             :   while(1)
    2011             :   {
    2012        1190 :     if (kroui(d,p) == s) return d; /* = 3 mod (16) */
    2013         595 :     d++;
    2014         595 :     if (kroui(d>>2,p) == s) return d; /* = 4 mod (16) */
    2015         266 :     d += 3;
    2016         266 :     if (kroui(d,p) == s) return d; /* = 7 mod (16) */
    2017         105 :     d++;
    2018         105 :     if (kroui(d>>2,p) == s) return d; /* = 8 mod (16) */
    2019          35 :     d += 3;
    2020          35 :     if (kroui(d,p) == s) return d; /* = 11 mod (16) */
    2021           7 :     d += 4;
    2022           7 :     if (kroui(d,p) == s) return d; /* = 15 mod (16) */
    2023           7 :     d += 4;
    2024             :   }
    2025             : }
    2026             : 
    2027             : /* p > 5 */
    2028             : static GEN
    2029        1183 : ellsupersingularj_easy_FpXQ(GEN T, GEN p)
    2030             : {
    2031        1183 :   long d = find_inert_disc(p);
    2032        1183 :   GEN R = FpXQX_roots(polclass(stoi(-d), 0, 0), T, p);
    2033        1183 :   return gel(R,1);
    2034             : }
    2035             : 
    2036             : GEN
    2037        1204 : ellsupersingularj_FpXQ(GEN T, GEN p)
    2038             : {
    2039             :   GEN j, j2, R, Phi2;
    2040             :   long i, ep, lp;
    2041        1204 :   if (cmpiu(p, 5) <= 0) return pol_0(get_FpX_var(T));
    2042        1183 :   j2 = ellsupersingularj_easy_FpXQ(T, p);
    2043        1183 :   Phi2 = polmodular_ZXX(2,0,0,1);
    2044        1183 :   R = FpXQX_roots(FqXY_evalx(Phi2, j2, T, p), T, p);
    2045        1183 :   j = gel(R,1+random_Fl(lg(R)-1));
    2046        1183 :   ep = expi(p); lp = ep + random_Fl(ep);
    2047       17849 :   for (i = 1; i <= lp; i++)
    2048             :   {
    2049       16666 :     GEN Phi2_j = FqX_div_by_X_x(FqXY_evalx(Phi2, j, T, p), j2, T, p, NULL);
    2050       16666 :     R = FqX_quad_root(Phi2_j, T, p);
    2051       16666 :     if (!R) pari_err_PRIME("ellsupersingularj",p);
    2052       16666 :     j2 = j; j = random_bits(1) ? R: Fq_neg(Fq_add(gel(Phi2_j,3), R, T, p), T, p);
    2053             :   }
    2054        1183 :   return j;
    2055             : }
    2056             : 
    2057             : /***********************************************************************/
    2058             : /**                                                                   **/
    2059             : /**                           Point counting                          **/
    2060             : /**                                                                   **/
    2061             : /***********************************************************************/
    2062             : 
    2063             : GEN
    2064       15470 : elltrace_extension(GEN t, long n, GEN q)
    2065             : {
    2066       15470 :   pari_sp av = avma;
    2067       15470 :   GEN v = RgX_to_RgC(RgXQ_powu(pol_x(0), n, mkpoln(3,gen_1,negi(t),q)),2);
    2068       15470 :   GEN te = addii(shifti(gel(v,1),1), mulii(t,gel(v,2)));
    2069       15470 :   return gc_INT(av, te);
    2070             : }
    2071             : 
    2072             : GEN
    2073       14707 : Fp_ffellcard(GEN a4, GEN a6, GEN q, long n, GEN p)
    2074             : {
    2075       14707 :   pari_sp av = avma;
    2076       14707 :   GEN ap = subii(addiu(p, 1), Fp_ellcard(a4, a6, p));
    2077       14707 :   GEN te = elltrace_extension(ap, n, p);
    2078       14707 :   return gc_INT(av, subii(addiu(q, 1), te));
    2079             : }
    2080             : 
    2081             : static GEN
    2082        1687 : FpXQ_ellcardj(GEN a4, GEN a6, GEN j, GEN T, GEN q, GEN p, long n)
    2083             : {
    2084        1687 :   GEN q1 = addiu(q,1);
    2085        1687 :   if (signe(j)==0)
    2086             :   {
    2087             :     GEN W, w, t, N;
    2088         560 :     if (umodiu(q,6)!=1) return q1;
    2089         420 :     N = Fp_ffellcard(gen_0,gen_1,q,n,p);
    2090         420 :     t = subii(q1, N);
    2091         420 :     W = FpXQ_pow(a6,diviuexact(shifti(q,-1), 3),T,p);
    2092         420 :     if (degpol(W)>0) /*p=5 mod 6*/
    2093         105 :       return ZX_equal1(FpXQ_powu(W,3,T,p)) ? addii(q1,shifti(t,-1)):
    2094          35 :                                              subii(q1,shifti(t,-1));
    2095         350 :     w = modii(gel(W,2),p);
    2096         350 :     if (equali1(w))  return N;
    2097         259 :     if (equalii(w,subiu(p,1))) return addii(q1,t);
    2098             :     else /*p=1 mod 6*/
    2099             :     {
    2100         168 :       GEN u = shifti(t,-1), v = sqrtint(diviuexact(subii(q,sqri(u)),3));
    2101         168 :       GEN a = addii(u,v), b = shifti(v,1);
    2102         168 :       if (equali1(Fp_powu(w,3,p)))
    2103             :       {
    2104          84 :         if (dvdii(addmulii(a, w, b), p))
    2105          56 :           return subii(q1,subii(shifti(b,1),a));
    2106             :         else
    2107          28 :           return addii(q1,addii(a,b));
    2108             :       }
    2109             :       else
    2110             :       {
    2111          84 :         if (dvdii(submulii(a, w, b), p))
    2112          56 :           return subii(q1,subii(a,shifti(b,1)));
    2113             :         else
    2114          28 :           return subii(q1,addii(a,b));
    2115             :       }
    2116             :     }
    2117        1127 :   } else if (equalii(j,modsi(1728,p)))
    2118             :   {
    2119             :     GEN w, W, N, t;
    2120         567 :     if (mod4(q)==3) return q1;
    2121         427 :     W = FpXQ_pow(a4,shifti(q,-2),T,p);
    2122         427 :     if (degpol(W)>0) return q1; /*p=3 mod 4*/
    2123         357 :     w = modii(gel(W,2),p);
    2124         357 :     N = Fp_ffellcard(gen_1,gen_0,q,n,p);
    2125         357 :     if (equali1(w)) return N;
    2126         238 :     t = subii(q1, N);
    2127         238 :     if (equalii(w,subiu(p,1))) return addii(q1,t);
    2128             :     else /*p=1 mod 4*/
    2129             :     {
    2130         112 :       GEN u = shifti(t,-1), v = sqrtint(subii(q,sqri(u)));
    2131         112 :       if (dvdii(addmulii(u, w, v), p))
    2132          56 :         return subii(q1,shifti(v,1));
    2133             :       else
    2134          56 :         return addii(q1,shifti(v,1));
    2135             :     }
    2136             :   } else
    2137             :   {
    2138         560 :     GEN g = Fp_div(j, Fp_sub(utoi(1728), j, p), p);
    2139         560 :     GEN l = FpXQ_div(FpX_mulu(a6,3,p),FpX_mulu(a4,2,p),T,p);
    2140         560 :     GEN N = Fp_ffellcard(Fp_mulu(g,3,p),Fp_double(g,p),q,n,p);
    2141         560 :     if (FpXQ_issquare(l,T,p)) return N;
    2142         280 :     return subii(shifti(q1,1),N);
    2143             :   }
    2144             : }
    2145             : 
    2146             : static GEN
    2147           8 : FpXQ_ffellcard(GEN a4, GEN a6, GEN M, GEN q, GEN T, GEN p, long n)
    2148             : {
    2149           8 :   long m = degpol(M);
    2150           8 :   GEN j = pol_x(get_FpX_var(T));
    2151           8 :   GEN g = FpXQ_div(j, Fp_FpX_sub(utoi(1728), j, p), M, p);
    2152           8 :   GEN N = FpXQ_ellcard(FpX_mulu(g,3,p),FpX_mulu(g,2,p),M,p);
    2153           8 :   GEN qm = powiu(p, m), q1 = addiu(q, 1), qm1 = addiu(qm, 1);
    2154           8 :   GEN l = FpXQ_mul(FpX_mulu(a6,3,p),FpX_mulu(a4,2,p),T,p);
    2155           8 :   GEN te = elltrace_extension(subii(qm1, N), n/m, qm);
    2156           8 :   return FpXQ_issquare(l,T,p) ? subii(q1, te): addii(q1, te);
    2157             : }
    2158             : 
    2159             : static int
    2160           7 : FpXQ_is4power(GEN x, GEN T, GEN p)
    2161             : {
    2162           7 :   long d = get_FpX_degree(T);
    2163           7 :   if (lg(x) == 2 || absequalui(2, p)) return 1;
    2164           7 :   if (Mod4(p)==1)
    2165           7 :     return equali1(Fp_pow(FpXQ_norm(x,T,p),shifti(p,-2), p));
    2166           0 :   if (odd(d))
    2167           0 :     return FpXQ_issquare(x, T, p);
    2168           0 :   return ZX_equal1(FpXQ_pow(x, shifti(powiu(p, d),-2), T, p));
    2169             : }
    2170             : 
    2171             : /* http://www.numdam.org/article/ASENS_1969_4_2_4_521_0.pdf */
    2172             : 
    2173             : GEN
    2174           7 : FpXQ_ellcard_supersingular(GEN a4, GEN a6, GEN T, GEN p)
    2175             : {
    2176           7 :   pari_sp av = avma;
    2177           7 :   long d = get_FpX_degree(T);
    2178             :   GEN r;
    2179           7 :   if (equaliu(p,3))
    2180           0 :     r = Flxq_ellcard(ZX_to_Flx(a4,3), ZX_to_Flx(a6,3), ZXT_to_FlxT(T,3), 3);
    2181           7 :   else if (signe(a4)==0)
    2182           0 :     r = FpXQ_ellcardj(a4, a6, gen_0, T, powiu(p, d), p, d);
    2183           7 :   else if (signe(a6)==0)
    2184           0 :     r = FpXQ_ellcardj(a4, a6, modsi(1728,p), T, powiu(p, d), p, d);
    2185             :   else
    2186             :   {
    2187             :     GEN q, q2, t, D;
    2188           7 :     long qm4 = (odd(d>>1) && Mod4(p)==3);
    2189           7 :     if (odd(d)) return gen_0;
    2190           7 :     q2 = powiu(p, d>>1); q = sqri(q2);
    2191           7 :     t = shifti(q2, 1);
    2192           7 :     D = FpX_sub(FpX_Fp_mul(FpXQ_powu(a4,3,T,p), stoi(-4), p),
    2193             :                 FpX_mulu(FpXQ_sqr(a6,T,p), 27, p), p);
    2194          14 :     r = qm4 ^ FpXQ_is4power(D, T, p) ? subii(addiu(q, 1), t)
    2195           7 :                                      : addii(addiu(q, 1), t);
    2196             :   }
    2197           7 :   return gc_INT(av, r);
    2198             : }
    2199             : 
    2200             : GEN
    2201          21 : Fq_ellcard_supersingular(GEN a4, GEN a6, GEN T, GEN p)
    2202          21 : { return T ? FpXQ_ellcard_supersingular(a4, a6, T, p) : addiu(p, 1); }
    2203             : 
    2204             : static GEN
    2205        8508 : FpXQ_ellcard_i(GEN a4, GEN a6, GEN T, GEN p)
    2206             : {
    2207        8508 :   long n = get_FpX_degree(T);
    2208        8508 :   GEN q = powiu(p, n);
    2209        8508 :   if (degpol(a4)<=0 && degpol(a6)<=0)
    2210         763 :     return Fp_ffellcard(constant_coeff(a4),constant_coeff(a6),q,n,p);
    2211        7745 :   if (lgefint(p)==3)
    2212             :   {
    2213        6027 :     ulong pp = p[2];
    2214        6027 :     return Flxq_ellcard(ZX_to_Flx(a4,pp),ZX_to_Flx(a6,pp),ZX_to_Flx(T,pp),pp);
    2215             :   }
    2216             :   else
    2217             :   {
    2218        1718 :     GEN J = FpXQ_ellj(a4,a6,T,p), M;
    2219        1718 :     if (degpol(J) <= 0)
    2220        1687 :       return FpXQ_ellcardj(a4,a6,constant_coeff(J),T,q,p,n);
    2221          31 :     M = FpXQ_minpoly(J,T,p);
    2222          31 :     if (degpol(M) < degpol(T))
    2223           8 :       return FpXQ_ffellcard(a4, a6, M, q, T, p, n);
    2224          23 :     return Fq_ellcard_SEA(a4, a6, q, T, p, 0);
    2225             :   }
    2226             : }
    2227             : 
    2228             : GEN
    2229        8508 : FpXQ_ellcard(GEN a4, GEN a6, GEN T, GEN p)
    2230             : {
    2231        8508 :   pari_sp av = avma;
    2232        8508 :   return gc_INT(av, FpXQ_ellcard_i(a4, a6, T, p));
    2233             : }
    2234             : 
    2235             : static GEN
    2236          21 : _FpXQE_pairorder(void *E, GEN P, GEN Q, GEN m, GEN F)
    2237             : {
    2238          21 :   struct _FpXQE *e = (struct _FpXQE *) E;
    2239          21 :   return  FpXQ_order(FpXQE_weilpairing(P,Q,m,e->a4,e->T,e->p), F, e->T, e->p);
    2240             : }
    2241             : 
    2242             : GEN
    2243          15 : FpXQ_ellgroup(GEN a4, GEN a6, GEN N, GEN T, GEN p, GEN *pt_m)
    2244             : {
    2245             :   struct _FpXQE e;
    2246          15 :   GEN q = powiu(p, get_FpX_degree(T));
    2247          15 :   e.a4=a4; e.a6=a6; e.T=T; e.p=p;
    2248          15 :   return gen_ellgroup(N, subiu(q,1), pt_m, (void*)&e, &FpXQE_group, _FpXQE_pairorder);
    2249             : }
    2250             : 
    2251             : GEN
    2252           8 : FpXQ_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, GEN p)
    2253             : {
    2254             :   GEN P;
    2255           8 :   pari_sp av = avma;
    2256             :   struct _FpXQE e;
    2257           8 :   e.a4=a4; e.a6=a6; e.T=T; e.p=p;
    2258           8 :   switch(lg(D)-1)
    2259             :   {
    2260           8 :   case 1:
    2261           8 :     P = gen_gener(gel(D,1), (void*)&e, &FpXQE_group);
    2262           8 :     P = mkvec(FpXQE_changepoint(P, ch, T, p));
    2263           8 :     break;
    2264           0 :   default:
    2265           0 :     P = gen_ellgens(gel(D,1), gel(D,2), m, (void*)&e, &FpXQE_group, _FpXQE_pairorder);
    2266           0 :     gel(P,1) = FpXQE_changepoint(gel(P,1), ch, T, p);
    2267           0 :     gel(P,2) = FpXQE_changepoint(gel(P,2), ch, T, p);
    2268           0 :     break;
    2269             :   }
    2270           8 :   return gc_GEN(av, P);
    2271             : }

Generated by: LCOV version 1.16