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 - ellrank.c (source / functions) Coverage Total Hit
Test: PARI/GP v2.18.1 lcov report (development 31042-0fbe168e69) Lines: 93.2 % 1228 1145
Test Date: 2026-07-23 17:04:59 Functions: 93.9 % 114 107
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Copyright (C) 2020  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. It is distributed in the hope that it will be useful, but WITHOUT
       8              : ANY WARRANTY WHATSOEVER.
       9              : 
      10              : Check the License for details. You should have received a copy of it, along
      11              : with the package; see the file 'COPYING'. If not, write to the Free Software
      12              : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13              : 
      14              : #include "pari.h"
      15              : #include "paripriv.h"
      16              : 
      17              : #define DEBUGLEVEL DEBUGLEVEL_ellrank
      18              : 
      19              : static long LIM1 = 10000;
      20              : static long LIMTRIV = 10000;
      21              : 
      22              : /*******************************************************************/
      23              : /*               NFHILBERT and LOCAL SOLUBILITY                    */
      24              : /*       adapted from Denis Simon's original C implementation      */
      25              : /*******************************************************************/
      26              : /* p > 2, T ZX, p prime, x t_INT */
      27              : static long
      28            0 : lemma6(GEN T, GEN p, long nu, GEN x)
      29              : {
      30              :   long la, mu;
      31            0 :   GEN y = ZX_Z_eval(T, x);
      32              : 
      33            0 :   if (Zp_issquare(y, p)) return 1;
      34            0 :   la = Z_pval(y, p); y = ZX_Z_eval(ZX_deriv(T), x);
      35            0 :   if (!signe(y)) return la >= (nu<<1)? 0: -1;
      36            0 :   mu = Z_pval(y,p); if (la > (mu<<1)) return 1;
      37            0 :   return (la >= (nu<<1) && mu >= nu)? 0: -1;
      38              : }
      39              : static long
      40            0 : lemma7_aux(long nu, long la, long r)
      41              : {
      42            0 :   long nu2 = nu << 1;
      43            0 :   return (la >= nu2 || (la == nu2 - 2 && r == 1))? 0: -1;
      44              : }
      45              : /* p = 2, T ZX, x t_INT: return 1 = yes, -1 = no, 0 = inconclusive */
      46              : static long
      47            0 : lemma7(GEN T, long nu, GEN x)
      48              : {
      49              :   long r, la, mu;
      50            0 :   GEN y = ZX_Z_eval(T, x);
      51              : 
      52            0 :   if (!signe(y)) return 1;
      53            0 :   la = Z_lvalrem(y, 2, &y);
      54            0 :   r = Mod8(y); if (!odd(la) && r == 1) return 1;
      55            0 :   r &= 3; /* T(x) / 2^oo mod 4 */
      56            0 :   y = ZX_Z_eval(ZX_deriv(T), x);
      57            0 :   if (!signe(y)) return lemma7_aux(nu, la, r);
      58            0 :   mu = vali(y); if (la > mu<<1) return 1;
      59            0 :   if (nu <= mu) return lemma7_aux(nu, la, r);
      60              :   /* la <= 2mu, mu < nu */
      61            0 :   if (!odd(la) && mu + nu - la <= (r == 1? 2: 1)) return 1;
      62            0 :   return -1;
      63              : }
      64              : 
      65              : /* T a ZX, p a prime, pnu = p^nu, x0 t_INT */
      66              : static long
      67            0 : zpsol(GEN T, GEN p, long nu, GEN pnu, GEN x0)
      68              : {
      69              :   long i, res;
      70            0 :   pari_sp av = avma, btop;
      71              :   GEN x, pnup;
      72              : 
      73            0 :   res = absequaliu(p,2)? lemma7(T,nu,x0): lemma6(T,p,nu,x0);
      74            0 :   set_avma(av);
      75            0 :   if (res== 1) return 1;
      76            0 :   if (res==-1) return 0;
      77            0 :   x = x0; pnup = mulii(pnu,p);
      78            0 :   btop = avma;
      79            0 :   for (i=0; i < itos(p); i++)
      80              :   {
      81            0 :     x = addii(x,pnu);
      82            0 :     if (zpsol(T,p,nu+1,pnup,x)) return gc_long(av,1);
      83            0 :     if (gc_needed(btop, 2))
      84              :     {
      85            0 :       x = gc_upto(btop, x);
      86            0 :       if (DEBUGMEM > 1)
      87            0 :         pari_warn(warnmem, "hyperell_locally_soluble: %ld/%Ps",i,p);
      88              :     }
      89              :   }
      90            0 :   return gc_long(av,0);
      91              : }
      92              : 
      93              : /* return 1 if equation y^2=T(x) has a rational p-adic solution (possibly
      94              :  * infinite), 0 otherwise. */
      95              : long
      96            0 : hyperell_locally_soluble(GEN T,GEN p)
      97              : {
      98            0 :   pari_sp av = avma;
      99              :   long res;
     100            0 :   if (typ(T)!=t_POL) pari_err_TYPE("hyperell_locally_soluble",T);
     101            0 :   if (typ(p)!=t_INT) pari_err_TYPE("hyperell_locally_soluble",p);
     102            0 :   RgX_check_ZX(T, "hyperell_locally_soluble");
     103            0 :   res = zpsol(T,p,0,gen_1,gen_0) || zpsol(RgX_recip_i(T), p, 1, p, gen_0);
     104            0 :   return gc_long(av, res);
     105              : }
     106              : 
     107              : /* is t a square in (O_K/pr) ? Assume v_pr(t) = 0 */
     108              : static long
     109          140 : quad_char(GEN nf, GEN t, GEN pr)
     110              : {
     111          140 :   GEN T, p, modpr = zk_to_Fq_init(nf, &pr,&T,&p);
     112          140 :   return Fq_issquare(nf_to_Fq(nf,t,modpr), T, p)? 1: -1;
     113              : }
     114              : /* quad_char(x), x in Z, nonzero mod p */
     115              : static long
     116          161 : Z_quad_char(GEN x, GEN pr)
     117              : {
     118          161 :   long f = pr_get_f(pr);
     119          161 :   if (!odd(f)) return 1;
     120          154 :   return kronecker(x, pr_get_p(pr));
     121              : }
     122              : 
     123              : /* (pr,2) = 1. return 1 if x in Z_K is a square in Z_{K_pr}, 0 otherwise.
     124              :  * modpr = zkmodprinit(nf,pr) */
     125              : static long
     126            0 : psquarenf(GEN nf,GEN x,GEN pr,GEN modpr)
     127              : {
     128            0 :   pari_sp av = avma;
     129            0 :   GEN p = pr_get_p(pr);
     130              :   long v;
     131              : 
     132            0 :   x = nf_to_scalar_or_basis(nf, x);
     133            0 :   if (typ(x) == t_INT) {
     134            0 :     if (!signe(x)) return 1;
     135            0 :     v = Z_pvalrem(x, p, &x) * pr_get_e(pr);
     136            0 :     if (v&1) return 0;
     137            0 :     v = (Z_quad_char(x, pr) == 1);
     138              :   } else {
     139            0 :     v = ZC_nfvalrem(x, pr, &x);
     140            0 :     if (v&1) return 0;
     141            0 :     v = (quad_char(nf, x, modpr) == 1);
     142              :   }
     143            0 :   return gc_long(av,v);
     144              : }
     145              : 
     146              : static long
     147         5908 : ZV_iseven(GEN zlog)
     148              : {
     149         5908 :   long i, l = lg(zlog);
     150        21546 :   for (i = 1; i < l; i++)
     151        21413 :     if (mpodd(gel(zlog,i))) return 0;
     152          133 :   return 1;
     153              : }
     154              : 
     155              : /* pr | 2, project to principal units (trivializes later discrete log) */
     156              : static GEN
     157       165739 : to_principal_unit(GEN nf, GEN x, GEN pr, GEN sprk)
     158              : {
     159       165739 :   if (pr_get_f(pr) != 1)
     160              :   {
     161        18200 :     GEN prk = gel(sprk,3);
     162        18200 :     x = nfpowmodideal(nf, x, gmael(sprk,5,1), prk);
     163              :   }
     164       165739 :   return x;
     165              : }
     166              : /* pr | 2. Return 1 if x in Z_K is square in Z_{K_pr}, 0 otherwise */
     167              : static int
     168          511 : psquare2nf(GEN nf, GEN x, GEN pr, GEN sprk)
     169              : {
     170          511 :   long v = nfvalrem(nf, x, pr, &x);
     171          511 :   if (v == LONG_MAX) return 1; /* x = 0 */
     172              :   /* (x,pr) = 1 */
     173          511 :   if (odd(v)) return 0;
     174          490 :   x = to_principal_unit(nf, x, pr, sprk); /* = 1 mod pr */
     175          490 :   return ZV_iseven(sprk_log_prk1(nf, x, sprk));
     176              : }
     177              : 
     178              : /* pr above an odd prime */
     179              : static long
     180            0 : lemma6nf(GEN nf, GEN T, GEN pr, long nu, GEN x, GEN modpr)
     181              : {
     182              :   long la, mu;
     183            0 :   GEN y = nfpoleval(nf, T, x);
     184              : 
     185            0 :   if (psquarenf(nf,y,pr,modpr)) return 1;
     186            0 :   la = nfval(nf, y, pr); y = nfpoleval(nf, RgX_deriv(T), x);
     187            0 :   if (gequal0(y)) return la >= (nu<<1)? 0: -1;
     188            0 :   mu = nfval(nf, y, pr); if (la > (mu<<1)) return 1;
     189            0 :   return (la >= (nu<<1) && mu >= nu)? 0: -1;
     190              : }
     191              : /* pr above 2 */
     192              : static long
     193         5642 : lemma7nf(GEN nf, GEN T, GEN pr, long nu, GEN x, GEN sprk)
     194              : {
     195              :   long i, res, la, mu, q, e, v;
     196         5642 :   GEN M, y, gpx, loggx = NULL, gx = nfpoleval(nf, T, x);
     197              : 
     198         5642 :   la = nfvalrem(nf, gx, pr, &gx); /* gx /= pi^la, pi a pr-uniformizer */
     199         5642 :   if (la == LONG_MAX) return 1;
     200         5635 :   if (!odd(la))
     201              :   {
     202         5418 :     gx = to_principal_unit(nf, gx, pr, sprk); /* now 1 mod pr */
     203         5418 :     loggx = sprk_log_prk1(nf, gx, sprk); /* cheap */
     204         5418 :     if (ZV_iseven(loggx)) return 1;
     205              :   }
     206         5509 :   gpx = nfpoleval(nf, RgX_deriv(T), x);
     207         5509 :   mu = gequal0(gpx)? la+nu+1 /* oo */: nfval(nf,gpx,pr);
     208              : 
     209         5509 :   if (la > (mu << 1)) return 1;
     210         5509 :   if (nu > mu)
     211              :   {
     212           35 :     if (odd(la)) return -1;
     213           35 :     q = mu+nu-la; res = 1;
     214              :   }
     215              :   else
     216              :   {
     217         5474 :     q = (nu << 1) - la;
     218         5474 :     if (q <= 0) return 0;
     219         5173 :     if (odd(la)) return -1;
     220         4977 :     res = 0;
     221              :   }
     222              :   /* la even */
     223         5012 :   e = pr_get_e(pr);
     224         5012 :   if (q > e << 1)  return -1;
     225         4935 :   if (q == 1) return res;
     226              : 
     227              :   /* gx = 1 mod pr; square mod pi^q ? */
     228         4935 :   v = nfvalrem(nf, nfadd(nf, gx, gen_m1), pr, &y);
     229         4935 :   if (v >= q) return res;
     230              :   /* 1 + pi^v y = (1 + pi^vz z)^2 mod pr^q ? v < q <= 2e => vz < e => vz = vy/2
     231              :    * => y = x^2 mod pr^(min(q-v, e+v/2)), (y,pr) = 1 */
     232         4690 :   if (odd(v)) return -1;
     233              :   /* e > 1 */
     234         2037 :   M = cgetg(2*e+1 - q + 1, t_VEC);
     235         4074 :   for (i = q+1; i <= 2*e+1; i++) gel(M, i-q) = sprk_log_gen_pr(nf, sprk, i);
     236         2037 :   M = ZM_hnfmodid(shallowconcat1(M), gen_2);
     237         2037 :   return hnf_solve(M,loggx)? res: -1;
     238              : }
     239              : /* zinit either a sprk (pr | 2) or a modpr structure (pr | p odd).
     240              :    pnu = pi^nu, pi a uniformizer */
     241              : static long
     242         5642 : zpsolnf(GEN nf,GEN T,GEN pr,long nu,GEN pnu,GEN x0,GEN repr,GEN zinit)
     243              : {
     244              :   long i, res;
     245         5642 :   pari_sp av = avma;
     246              :   GEN pnup;
     247              : 
     248         5642 :   res = typ(zinit) == t_VEC? lemma7nf(nf,T,pr,nu,x0,zinit)
     249         5642 :                            : lemma6nf(nf,T,pr,nu,x0,zinit);
     250         5642 :   set_avma(av);
     251         5642 :   if (res== 1) return 1;
     252         5509 :   if (res==-1) return 0;
     253          574 :   pnup = nfmul(nf, pnu, pr_get_gen(pr));
     254          574 :   nu++;
     255         5558 :   for (i=1; i<lg(repr); i++)
     256              :   {
     257         5250 :     GEN x = nfadd(nf, x0, nfmul(nf,pnu,gel(repr,i)));
     258         5250 :     if (zpsolnf(nf,T,pr,nu,pnup,x,repr,zinit)) return gc_long(av,1);
     259              :   }
     260          308 :   return gc_long(av,0);
     261              : }
     262              : 
     263              : /* Let y = copy(x); y[k] := j; return y */
     264              : static GEN
     265         3206 : ZC_add_coeff(GEN x, long k, long j)
     266         3206 : { GEN y = shallowcopy(x); gel(y, k) = utoipos(j); return y; }
     267              : 
     268              : /* system of representatives for Zk/pr */
     269              : static GEN
     270          252 : repres(GEN nf, GEN pr)
     271              : {
     272          252 :   long f = pr_get_f(pr), N = nf_get_degree(nf), p = itos(pr_get_p(pr));
     273          252 :   long i, j, k, pi, pf = upowuu(p, f);
     274          252 :   GEN perm = pr_basis_perm(nf, pr), rep = cgetg(pf+1,t_VEC);
     275              : 
     276          252 :   gel(rep,1) = zerocol(N);
     277         1141 :   for (pi=i=1; i<=f; i++,pi*=p)
     278              :   {
     279          889 :     long t = perm[i];
     280         1778 :     for (j=1; j<p; j++)
     281         4095 :       for (k=1; k<=pi; k++) gel(rep, j*pi+k) = ZC_add_coeff(gel(rep,k), t, j);
     282              :   }
     283          252 :   return rep;
     284              : }
     285              : 
     286              : /* = 1 if equation y^2 = z^deg(T) * T(x/z) has a pr-adic rational solution
     287              :  * (possibly (1,y,0) = oo), 0 otherwise.
     288              :  * coeffs of T are algebraic integers in nf */
     289              : static long
     290          259 : locally_soluble(GEN nf,GEN T,GEN pr)
     291              : {
     292              :   GEN repr, zinit;
     293              : 
     294          259 :   if (typ(T)!=t_POL) pari_err_TYPE("nf_hyperell_locally_soluble",T);
     295          259 :   if (gequal0(T)) return 1;
     296          259 :   checkprid(pr);
     297          259 :   if (absequaliu(pr_get_p(pr), 2))
     298              :   { /* tough case */
     299          259 :     zinit = log_prk_init(nf, pr, 1+2*pr_get_e(pr), NULL);
     300          259 :     if (psquare2nf(nf,constant_coeff(T),pr,zinit)) return 1;
     301          252 :     if (psquare2nf(nf, leading_coeff(T),pr,zinit)) return 1;
     302              :   }
     303              :   else
     304              :   {
     305            0 :     zinit = zkmodprinit(nf, pr);
     306            0 :     if (psquarenf(nf,constant_coeff(T),pr,zinit)) return 1;
     307            0 :     if (psquarenf(nf, leading_coeff(T),pr,zinit)) return 1;
     308              :   }
     309          252 :   repr = repres(nf,pr); /* FIXME: inefficient if Npr is large */
     310          392 :   return zpsolnf(nf, T, pr, 0, gen_1, gen_0, repr, zinit) ||
     311          140 :          zpsolnf(nf, RgX_recip_i(T), pr, 1, pr_get_gen(pr),
     312              :                  gen_0, repr, zinit);
     313              : }
     314              : long
     315          259 : nf_hyperell_locally_soluble(GEN nf,GEN T,GEN pr)
     316              : {
     317          259 :   pari_sp av = avma;
     318          259 :   return gc_long(av, locally_soluble(nf, T, pr));
     319              : }
     320              : 
     321              : /* return a * denom(a)^2, as an 'liftalg' */
     322              : static GEN
     323          518 : den_remove(GEN nf, GEN a)
     324              : {
     325              :   GEN da;
     326          518 :   a = nf_to_scalar_or_basis(nf, a);
     327          518 :   switch(typ(a))
     328              :   {
     329           49 :     case t_INT: return a;
     330            0 :     case t_FRAC: return mulii(gel(a,1), gel(a,2));
     331          469 :     case t_COL:
     332          469 :       a = Q_remove_denom(a, &da);
     333          469 :       if (da) a = ZC_Z_mul(a, da);
     334          469 :       return nf_to_scalar_or_alg(nf, a);
     335            0 :     default: pari_err_TYPE("nfhilbert",a);
     336              :       return NULL;/*LCOV_EXCL_LINE*/
     337              :   }
     338              : }
     339              : 
     340              : static long
     341          259 : hilb2nf(GEN nf,GEN a,GEN b,GEN p)
     342              : {
     343          259 :   pari_sp av = avma;
     344              :   GEN pol;
     345          259 :   a = den_remove(nf, a);
     346          259 :   b = den_remove(nf, b);
     347          259 :   pol = mkpoln(3, a, gen_0, b);
     348              :   /* varn(nf.pol) = 0, pol is not a valid GEN  [as in Pol([x,x], x)].
     349              :    * But it is only used as a placeholder, hence it is not a problem */
     350          259 :   return gc_long(av, nf_hyperell_locally_soluble(nf,pol,p)? 1: -1);
     351              : }
     352              : 
     353              : /* local quadratic Hilbert symbol (a,b)_pr, for a,b (nonzero) in nf */
     354              : static long
     355          567 : nfhilbertp(GEN nf, GEN a, GEN b, GEN pr)
     356              : {
     357              :   GEN t;
     358              :   long va, vb;
     359          567 :   pari_sp av = avma;
     360              : 
     361          567 :   if (absequaliu(pr_get_p(pr), 2)) return hilb2nf(nf,a,b,pr);
     362              : 
     363              :   /* pr not above 2, compute t = tame symbol */
     364          308 :   va = nfval(nf,a,pr);
     365          308 :   vb = nfval(nf,b,pr);
     366          308 :   if (!odd(va) && !odd(vb)) return gc_long(av,1);
     367              :   /* Trick: pretend the exponent is 2, result is OK up to squares ! */
     368          301 :   t = famat_makecoprime(nf, mkvec2(a,b), mkvec2s(vb, -va),
     369              :                         pr, pr_hnf(nf, pr), gen_2);
     370              :   /* quad. symbol is image of t = (-1)^(v(a)v(b)) a^v(b) b^(-v(a))
     371              :    * by the quadratic character  */
     372          301 :   switch(typ(t))
     373              :   {
     374          147 :     default: /* t_COL */
     375          147 :       if (!ZV_isscalar(t)) break;
     376            7 :       t = gel(t,1); /* fall through */
     377          161 :     case t_INT:
     378          161 :       if (odd(va) && odd(vb)) t = negi(t);
     379          161 :       return gc_long(av,  Z_quad_char(t, pr));
     380              :   }
     381          140 :   if (odd(va) && odd(vb)) t = ZC_neg(t);
     382          140 :   return gc_long(av, quad_char(nf, t, pr));
     383              : }
     384              : 
     385              : /* Global quadratic Hilbert symbol (a,b):
     386              :  *  =  1 if X^2 - aY^2 - bZ^2 has a point in projective plane
     387              :  *  = -1 otherwise
     388              :  * a, b should be nonzero */
     389              : long
     390           21 : nfhilbert(GEN nf, GEN a, GEN b)
     391              : {
     392           21 :   pari_sp av = avma;
     393              :   long i, l;
     394              :   GEN S, S2, Sa, Sb, sa, sb;
     395              : 
     396           21 :   a = nf_to_scalar_or_basis(nf, a);
     397           21 :   b = nf_to_scalar_or_basis(nf, b);
     398              :   /* local solutions in real completions ? [ error in nfsign if arg is 0 ]*/
     399           21 :   sa = nfsign(nf, a);
     400           21 :   sb = nfsign(nf, b); l = lg(sa);
     401           35 :   for (i=1; i<l; i++)
     402           21 :     if (sa[i] && sb[i])
     403              :     {
     404            7 :       if (DEBUGLEVEL>3)
     405            0 :         err_printf("nfhilbert not soluble at real place %ld\n",i);
     406            7 :       return gc_long(av,-1);
     407              :     }
     408              : 
     409              :   /* local solutions in finite completions ? (pr | 2ab)
     410              :    * primes above 2 are toughest. Try the others first */
     411           14 :   Sa = idealfactor(nf, a);
     412           14 :   Sb = idealfactor(nf, b);
     413           14 :   S2 = idealfactor(nf, gen_2);
     414           14 :   S = merge_factor(Sa, Sb, (void*)&cmp_prime_ideal, &cmp_nodata);
     415           14 :   S = merge_factor(S,  S2, (void*)&cmp_prime_ideal, &cmp_nodata);
     416           14 :   S = gel(S,1);
     417              :   /* product of all hilbertp is 1 ==> remove one prime (above 2!) */
     418           28 :   for (i=lg(S)-1; i>1; i--)
     419           21 :     if (nfhilbertp(nf,a,b,gel(S,i)) < 0)
     420              :     {
     421            7 :       if (DEBUGLEVEL>3)
     422            0 :         err_printf("nfhilbert not soluble at finite place %Ps\n",S[i]);
     423            7 :       return gc_long(av,-1);
     424              :     }
     425            7 :   return gc_long(av,1);
     426              : }
     427              : 
     428              : long
     429          581 : nfhilbert0(GEN nf,GEN a,GEN b,GEN p)
     430              : {
     431          581 :   nf = checknf(nf);
     432          581 :   if (p) {
     433          560 :     checkprid(p);
     434          560 :     if (gequal0(a)) pari_err_DOMAIN("nfhilbert", "a", "=", gen_0, a);
     435          553 :     if (gequal0(b)) pari_err_DOMAIN("nfhilbert", "b", "=", gen_0, b);
     436          546 :     return nfhilbertp(nf,a,b,p);
     437              :   }
     438           21 :   return nfhilbert(nf,a,b);
     439              : }
     440              : 
     441              : /*******************************************************************/
     442              : /*                      HYPERELL_LOCAL_SOLVE                       */
     443              : /*******************************************************************/
     444              : 
     445              : /* Based on
     446              : T.A. Fisher and G.F. Sills
     447              : Local solubility and height bounds for coverings of elliptic curves
     448              : https://www.dpmms.cam.ac.uk/~taf1000/papers/htbounds.pdf
     449              : */
     450              : 
     451              : static int
     452       119336 : FpX_issquare(GEN q, GEN p)
     453              : {
     454       119336 :   GEN F = FpX_factor_squarefree(q,p);
     455       119336 :   long i, l = lg(F);
     456       161658 :   for (i = 1; i < l; i+=2)
     457       120582 :     if (degpol(gel(F,i)) > 0) return 0;
     458        41076 :   return 1;
     459              : }
     460              : 
     461              : static GEN
     462       118580 : hyperell_red(GEN q, GEN p)
     463              : {
     464              :   GEN Q;
     465       118580 :   long v = ZX_pvalrem(q, p, &Q);
     466       118580 :   if (v == 1) return q;
     467       118580 :   return odd(v)? ZX_Z_mul(Q, p): Q;
     468              : }
     469              : 
     470              : static GEN
     471       121659 : hyperell_reg_point(GEN q, GEN p)
     472              : {
     473              :   GEN Q, F;
     474       121659 :   long i, l, v = ZX_pvalrem(q, p, &Q);
     475       121659 :   if (v != 1) q = odd(v)? ZX_Z_mul(Q, p): Q;
     476       121659 :   if (!odd(v))
     477              :   {
     478       119336 :     GEN qr = FpX_red(q, p);
     479       119336 :     if (!FpX_issquare(qr,p) || Fp_issquare(leading_coeff(qr), p))
     480       118580 :       return mkvec2s(0,1);
     481              :   }
     482         3079 :   F = FpX_roots(Q, p); l = lg(F);
     483         3086 :   for (i = 1; i < l; i++)
     484              :   {
     485         3079 :     GEN r = gel(F,i), s = hyperell_reg_point(ZX_affine(q, p, r), p);
     486         3079 :     if (s)
     487         3072 :       retmkvec2(addii(r,mulii(gel(s,1),p)), mulii(gel(s,2),p));
     488              :   }
     489            7 :   return NULL;
     490              : }
     491              : 
     492              : static GEN
     493         1432 : hyperell_lift(GEN q, GEN x, GEN p)
     494              : {
     495              :   long e;
     496         1432 :   GEN qy2 = ZX_Z_sub(q, sqri(p));
     497         1432 :   for (e = 2; ; e<<=1)
     498          702 :   {
     499         2134 :     pari_sp av = avma;
     500         2134 :     GEN z = ZpX_liftroot(qy2, x, p, e);
     501         2134 :     if (signe(z) == 0) z = powiu(p, e);
     502         2134 :     if (Zp_issquare(poleval(q, z), p)) return z;
     503          702 :     set_avma(av);
     504              :   }
     505              : }
     506              : 
     507              : static GEN
     508        59290 : affine_apply(GEN r, GEN x)
     509              : {
     510        59290 :   return addii(mulii(gel(r,2),x), gel(r,1));
     511              : }
     512              : 
     513              : static GEN
     514        59290 : Qp_hyperell_solve_odd(GEN q, GEN p)
     515              : {
     516        59290 :   GEN qi = RgX_recip_shallow(q);
     517        59290 :   GEN r = hyperell_reg_point(q,  p), qr = NULL, qrp = NULL;
     518        59290 :   GEN s = hyperell_reg_point(qi, p), qs = NULL, qsp = NULL;
     519        59290 :   if (!r && !s) return NULL;
     520        59290 :   if (r)
     521              :   {
     522        59290 :     qr = hyperell_red(ZX_affine(q,  gel(r,2), gel(r,1)), p);
     523        59290 :     qrp = FpX_deriv(qr, p);
     524              :   }
     525        59290 :   if (s)
     526              :   {
     527        59290 :     qs = hyperell_red(ZX_affine(qi, gel(s,2), gel(s,1)), p);
     528        59290 :     qsp = FpX_deriv(qs, p);
     529              :   }
     530              :   while(1)
     531        14882 :   {
     532        74172 :     GEN x = randomi(p);
     533        74172 :     if (r)
     534              :     {
     535        74172 :       GEN y2 = FpX_eval(qr, x, p);
     536        74172 :       if (Fp_issquare(y2,p))
     537              :       {
     538        49015 :          if (signe(y2))
     539        44589 :            return affine_apply(r,x);
     540         4426 :          if (signe(FpX_eval(qrp, x, p)))
     541              :          {
     542         1105 :            x = hyperell_lift(qr, x, p);
     543         1105 :            return affine_apply(r,x);
     544              :          }
     545              :       }
     546              :     }
     547        28478 :     if (s)
     548              :     {
     549        28478 :       GEN y2 = FpX_eval(qs, x, p);
     550        28478 :       if (Fp_issquare(y2,p))
     551              :       {
     552        15617 :          if (signe(x)==0) x = p;
     553        15617 :          if (signe(y2))
     554        13269 :            return ginv(affine_apply(s,x));
     555         2348 :          if (signe(FpX_eval(qsp, x, p)))
     556              :          {
     557          327 :            GEN xl = hyperell_lift(qs, x, p);
     558          327 :            return ginv(affine_apply(s,xl));
     559              :          }
     560              :       }
     561              :     }
     562              :   }
     563              : }
     564              : 
     565              : static GEN
     566          490 : Q2_hyperell_lift(GEN p, GEN q, long x, long y)
     567              : {
     568              :   GEN T, h;
     569              :   long e;
     570          490 :   if (y==0) y = 2;
     571          490 :   T = ZX_sub(p, ZX_Z_add(ZX_mulu(q, y), sqru(y)));
     572          490 :   h = ZX_add(ZX_sqr(q), ZX_shifti(p, 2));
     573          490 :   for (e = 3; ; e++)
     574          952 :   {
     575         1442 :     pari_sp av = avma;
     576         1442 :     GEN r = ZpX_liftroot(T, utoi(x), gen_2, e);
     577         1442 :     if (signe(r) == 0) r = int2n(e);
     578         1442 :     if (Zp_issquare(poleval(h, r), gen_2)) return r;
     579          952 :     set_avma(av);
     580              :   }
     581              :   return NULL;/*LCOV_EXCL_LINE*/
     582              : }
     583              : 
     584              : static GEN
     585         2947 : Q2_hyperell_regpoint(GEN P, GEN Q)
     586              : {
     587              :   long x;
     588         2947 :   GEN p = ZX_to_F2x(P), dp = F2x_deriv(p);
     589         2947 :   GEN q = ZX_to_F2x(Q), dq = F2x_deriv(q);
     590              : 
     591         4907 :   for (x = 0; x <= 1; x++)
     592              :   {
     593         4326 :     long px = F2x_eval(p,x), qx = F2x_eval(q,x);
     594              :     long dpx, dqx;
     595         4326 :     if (qx == 1)
     596              :     {
     597         2100 :       if(px == 0) return x==0 ? gen_2: gen_1;
     598          224 :       continue;
     599              :     }
     600         2226 :     dpx = F2x_eval(dp,x);
     601         2226 :     dqx = F2x_eval(dq,x);
     602         2226 :     if (odd(dqx * px + dpx))
     603          490 :       return Q2_hyperell_lift(P, Q, x, px);
     604              :   }
     605          581 :   return NULL;
     606              : }
     607              : 
     608              : static GEN
     609         2947 : Q2_hyperell_solve_affine(GEN p, GEN q)
     610              : {
     611         2947 :   pari_sp av = avma;
     612              :   GEN R, p4, q4;
     613              :   long x;
     614              :   while(1)
     615         2394 :   {
     616              :     GEN pp, p0, p1;
     617         5341 :     long vp = ZX_lval(p, 2);
     618         5341 :     long vq = ZX_lval(q, 2);
     619         5341 :     long w = minss(vp>>1, vq);
     620         5341 :     p = ZX_shifti(p, -2*w);
     621         5341 :     q = ZX_shifti(q, -w);
     622         5341 :     if (ZX_lval(q,2)==0) break;
     623         2667 :     pp = RgX_splitting(p,2); p0 = gel(pp,1); p1 = gel(pp,2);
     624         2667 :     if (ZX_lval(p1,2)==0 || ZX_lval(p0,2)>=1) break;
     625         2394 :     p = ZX_sub(p, ZX_mul(p0, ZX_add(q, p0)));
     626         2394 :     q = ZX_add(q, ZX_shifti(p0, 1));
     627              :   }
     628         2947 :   R = Q2_hyperell_regpoint(p, q);
     629         2947 :   if (R) return gc_INT(av, R);
     630          581 :   p4 = ZX_to_Flx(p,4);
     631          581 :   q4 = ZX_to_Flx(q,4);
     632          763 :   for (x = 0; x <= 1; x++)
     633              :   {
     634          742 :     ulong px = Flx_eval(p4, x, 4);
     635          742 :     ulong qx = Flx_eval(q4, x, 4);
     636          742 :     if (px == 0 || (1+qx+3*px)%4==0)
     637              :     {
     638          560 :       GEN p2 = ZX_affine(p, gen_2, utoi(x));
     639          560 :       GEN q2 = ZX_affine(q, gen_2, utoi(x));
     640          560 :       GEN S = Q2_hyperell_solve_affine(p2, q2);
     641          560 :       if (S) return gc_INT(av, addiu(shifti(S,1),x));
     642              :     }
     643              :   }
     644           21 :   return gc_NULL(av);
     645              : }
     646              : 
     647              : static GEN
     648         2366 : Q2_hyperell_solve(GEN P)
     649              : {
     650         2366 :   long v = varn(P);
     651         2366 :   GEN S = Q2_hyperell_solve_affine(P, pol_0(v));
     652         2366 :   if (!S) S = ginv(Q2_hyperell_solve_affine(RgX_recip(P), pol_0(v)));
     653         2366 :   return S;
     654              : }
     655              : 
     656              : static GEN
     657        61656 : hyperell_local_solve(GEN q, GEN p)
     658              : {
     659        61656 :   if (equaliu(p,2))
     660         2366 :     return Q2_hyperell_solve(q);
     661        59290 :   return Qp_hyperell_solve_odd(q, p);
     662              : }
     663              : 
     664              : /*******************************************************************/
     665              : /*                         BINARY QUARTIC                          */
     666              : /*******************************************************************/
     667              : static int
     668       107653 : Qp_issquare(GEN a, GEN p)
     669              : {
     670       107653 :   GEN b = typ(a) == t_INT? a: mulii(gel(a,1), gel(a,2));
     671       107653 :   return Zp_issquare(b, p);
     672              : }
     673              : 
     674              : static GEN
     675        18780 : quartic_IJ(GEN g)
     676              : {
     677        18780 :   GEN a = gel(g, 6), b = gel(g, 5), c = gel(g, 4), d = gel(g, 3), e = gel(g, 2);
     678        18780 :   GEN ae = gmul(a,e), bd = gmul(b,d), c2 = gsqr(c);
     679              :   /* 12ae - 3bd + c^2 */
     680        18780 :   GEN I = gadd(gsub(gmulsg(12, ae), gmulsg(3, bd)), c2);
     681              :   /* c(72ae + 9bd - 2c^2) - 27ad^2 - 27eb^2*/
     682        18780 :   GEN J = gsub(gmul(c, gsub(gadd(gmulsg(72,ae), gmulsg(9,bd)), gmul2n(c2,1))),
     683              :                gmulsg(27, gadd(gmul(a, gsqr(d)), gmul(gsqr(b), e))));
     684        18780 :   return mkvec2(I, J);
     685              : }
     686              : 
     687              : static GEN
     688         2366 : quartic_hessiandd(GEN g)
     689              : {
     690         2366 :   GEN a = gel(g, 6), b = gel(g, 5), c = gel(g, 4), d = gel(g, 3), e = gel(g, 2);
     691         2366 :   GEN a8 = gmul2n(a, 3), p4 = gsub(gmulsg(3, gsqr(b)), gmul(a8, c));
     692         2366 :   GEN p3 = gsub(gmul(b, c), gmul(gmulsg(6, a), d));
     693         2366 :   GEN p2 = gsub(gmulsg(8, gsqr(c)), gmulsg(12, gadd(gmul(b, d), gmul(a8, e))));
     694         2366 :   return deg2pol_shallow(gmulgu(p4,12), gmulgu(p3,24), p2, 1);
     695              : }
     696              : 
     697              : static GEN
     698        13090 : quartic_cubic(GEN g, long v)
     699              : {
     700        13090 :   GEN a = gel(g, 6), b = gel(g, 5), c = gel(g, 4);
     701        13090 :   GEN a3 = gdivgu(a,3);
     702        13090 :   return deg1pol_shallow(gmul2n(a3,2), gsub(gsqr(b),gmul2n(gmul(a3,c),3)), v);
     703              : }
     704              : 
     705              : static GEN
     706         6972 : quarticinv_pol(GEN IJ)
     707              : {
     708         6972 :   GEN I = gel(IJ,1), J = gel(IJ,2);
     709         6972 :   return mkpoln(4, gen_1, gen_0, gmulgs(I,-3), J);
     710              : }
     711              : static GEN
     712         2366 : quartic_H(GEN g, GEN *pT)
     713              : {
     714         2366 :   GEN a = gel(g, 6), b = gel(g, 5), c = gel(g, 4);
     715         2366 :   GEN IJ = quartic_IJ(g), I = gel(IJ, 1);
     716         2366 :   GEN ddh = quartic_hessiandd(g);
     717         2366 :   GEN ddg = deg2pol_shallow(gmulgu(a,12), gmulgu(b,6), gmulgu(c,2), 1);
     718         2366 :   *pT = quarticinv_pol(IJ);
     719         2366 :   return deg2pol_shallow(stoi(-8), gmul2n(ddg,2), gadd(ddh,gmul2n(I,3)),0);
     720              : }
     721              : 
     722              : static GEN
     723         4606 : quartic_disc(GEN q)
     724              : {
     725         4606 :   GEN IJ = quartic_IJ(q), I = gel(IJ,1), J = gel(IJ,2);
     726         4606 :   return gsub(gmul2n(gpowgs(I, 3), 2), gsqr(J));
     727              : }
     728              : 
     729              : static GEN
     730         7202 : quartic_minim_all(GEN F, GEN discF)
     731              : {
     732         7202 :   GEN IJ = quartic_IJ(F), I = gel(IJ,1), J = gel(IJ,2);
     733         7202 :   GEN g = Z_ppo(gcdii(I,J), gel(discF,1));
     734         7202 :   GEN plist = ZV_sort_uniq_shallow(shallowconcat(gel(absZ_factor(g),1),gel(discF,2)));
     735         7202 :   GEN W, C, PQ = hyperellminimalmodel(F, &C, plist);
     736         7202 :   GEN P = gel(PQ,1), Q = gel(PQ,2);
     737         7202 :   if (signe(Q)==0)
     738          798 :     W = mkvec2(P, C);
     739              :   else
     740         6404 :     W = mkvec2(ZX_add(ZX_shifti(P,2),ZX_sqr(Q)),mkvec2(shifti(gel(C,1),-1),gel(C,2)));
     741         7202 :   return W;
     742              : }
     743              : 
     744              : /*******************************************************************/
     745              : /*                         Cassels' pairing                        */
     746              : /*******************************************************************/
     747              : 
     748              : static GEN
     749         6230 : nfsqrt(GEN nf, GEN z)
     750              : {
     751         6230 :   long v = fetch_var_higher();
     752         6230 :   GEN R = nfroots(nf, deg2pol_shallow(gen_m1, gen_0, z, v));
     753         6230 :   delete_var();
     754         6230 :   return lg(R)==1 ? NULL: gel(R,1);
     755              : }
     756              : 
     757              : static GEN
     758         6230 : nfsqrt_safe(GEN nf, GEN z)
     759              : {
     760         6230 :   GEN r = nfsqrt(nf, z);
     761         6230 :   if (!r) pari_err_BUG("ellrank");
     762         6230 :   return r;
     763              : }
     764              : 
     765              : static GEN
     766         2366 : vecnfsqrtmod(GEN x, GEN P)
     767         8596 : { pari_APPLY_same(gmodulo(nfsqrt_safe(gel(x,i), P), gel(x,i))) }
     768              : 
     769              : static GEN
     770         2366 : enfsqrt(GEN T, GEN P)
     771              : {
     772         2366 :   GEN F = gel(ZX_factor(T),1);
     773         2366 :   return liftpol(chinese1(vecnfsqrtmod(F,P)));
     774              : }
     775              : 
     776              : /* Quartic q, at most quadratic g s.t. lc(g) > 0. There exist a real r s.t.
     777              :  * q(r) > 0 and g(r) != 0. Return sign(g(r)) */
     778              : static int
     779          602 : cassels_oo_solve_i(GEN q, GEN g)
     780              : {
     781          602 :   long dg = degpol(g);
     782              :   GEN D, a, b, c;
     783              : 
     784          602 :   if (dg == 0 || signe(leading_coeff(q)) > 0) return 1;
     785          329 :   if (signe(gel(q,2)) > 0) return signe(gel(g,2));
     786          301 :   c = gel(g,2); b = gel(g,3);
     787              :   /* g = bx+c, b>0, is negative on I=]-oo,-c/b[: if q has a root there,
     788              :    * then g(r) < 0. Else it has the sign of q(oo) < 0 on I */
     789          301 :   if (dg == 1) return ZX_sturmpart(q, mkvec2(mkmoo(), gdiv(negi(c), b)))? -1: 1;
     790          287 :   a = gel(g,4); D = subii(sqri(b), shifti(mulii(a,c), 2)); /* g = ax^2+bx+c */
     791          287 :   if (signe(D) <= 0) return 1; /* sign(g) = 1 is constant */
     792              :   /* Rescale q and g: x->(x - b)/2a; roots of new g are \pm sqrt(D) */
     793          266 :   q = ZX_Z_translate(ZX_rescale(q, shifti(a,1)), negi(b));
     794              :   /* Now g has sign -1 in I=[-sqrt(D),sqrt(D)] and 1 elsewhere.
     795              :    * Check if q vanishes in I <=> Graeffe(q) vanishes on [0,D].
     796              :    * If so or if q(0) > 0 we take r in there; else r is outside of I */
     797          252 :   return (signe(gel(q,2)) > 0 ||
     798          518 :           ZX_sturmpart(ZX_graeffe(q), mkvec2(gen_0, D)))? -1: 1;
     799              : }
     800              : static int
     801          602 : cassels_oo_solve(GEN q, GEN g)
     802          602 : { pari_sp av = avma; return gc_int(av, cassels_oo_solve_i(q, g)); }
     803              : 
     804              : static GEN
     805        61656 : cassels_Qp_solve(GEN q, GEN gam, GEN p)
     806              : {
     807        61656 :   pari_sp av = avma;
     808        61656 :   GEN a = hyperell_local_solve(q, p);
     809        61656 :   GEN c = poleval(gam,a);
     810              :   long e;
     811        61656 :   if (!gequal0(c)) return c;
     812           42 :   for (e = 2; ; e++)
     813            0 :   {
     814           42 :     GEN b = gadd(a, powiu(p,e));
     815           42 :     if (Qp_issquare(poleval(q, b), p))
     816              :     {
     817           42 :       c = poleval(gam, b);
     818           42 :       if (!gequal0(c)) return gc_upto(av,c);
     819              :     }
     820              :   }
     821              : }
     822              : 
     823              : static GEN
     824         4732 : to_ZX(GEN a, long v) { return typ(a)==t_INT? scalarpol_shallow(a,v): a; }
     825              : 
     826              : static GEN
     827         4606 : quartic_findunit(GEN D, GEN q)
     828              : {
     829         4606 :   GEN T = quarticinv_pol(quartic_IJ(q));
     830              :   while(1)
     831         1386 :   {
     832         5992 :     pari_sp av = avma;
     833         5992 :     GEN z = quartic_cubic(q,0);
     834         5992 :     if (signe(QXQ_norm(z,T)))
     835         4606 :       return absequalii(quartic_disc(q), D)? q: ZX_shifti(q, 2);
     836         1386 :     set_avma(av);
     837         1386 :     q = ZX_Z_translate(RgX_recip(q), gen_1);
     838              :   }
     839              : }
     840              : 
     841              : /* Crude implementation of an algorithm by Tom Fisher
     842              :  * On binary quartics and the Cassels-Tate pairing
     843              :  * https://www.dpmms.cam.ac.uk/~taf1000/papers/bq-ctp.pdf */
     844              : 
     845              : /* FD = primes | 2*3*5*7*D, q1,q2,q3 have discriminant D */
     846              : static long
     847         2366 : casselspairing(GEN FD, GEN q1, GEN q2, GEN q3)
     848              : {
     849         2366 :   pari_sp av = avma;
     850         2366 :   GEN T, H = quartic_H(q1, &T);
     851         2366 :   GEN z1 = quartic_cubic(q1, 0);
     852         2366 :   GEN z2 = quartic_cubic(q2, 0);
     853         2366 :   GEN z3 = quartic_cubic(q3, 0);
     854         2366 :   GEN m = to_ZX(enfsqrt(T, QXQ_mul(QX_mul(z1,z2),z3,T)), 0);
     855         2366 :   GEN Hm = RgXQ_mul(QXQ_div(m, z1, T), H, T); /* deg(Hm) >= 2 */
     856         2366 :   GEN gam = to_ZX(Q_primpart(gel(Hm,4)),1);
     857         2366 :   GEN a = leading_coeff(q2), Fa = gel(absZ_factor(a),1);
     858         2366 :   GEN F = ZV_sort_uniq_shallow(shallowconcat1(mkvec2(Fa, FD)));
     859         2366 :   long i, e = 0, lF = lg(F);
     860         2366 :   if (signe(a) <= 0)
     861              :   {
     862          602 :     if (signe(leading_coeff(gam)) < 0) gam = ZX_neg(gam);
     863          602 :     if (cassels_oo_solve(q1, gam) < 0) e = 1;
     864              :   }
     865        64022 :   for (i = 1; i < lF; i++)
     866              :   {
     867        61656 :     GEN p = gel(F, i);
     868        61656 :     GEN c = cassels_Qp_solve(q1, gam, p);
     869        61656 :     if (hilbert(c, a, p) < 0) e = !e;
     870              :   }
     871         2366 :   return gc_long(av,e);
     872              : }
     873              : 
     874              : static GEN
     875          315 : matcassels(GEN F, GEN M)
     876              : {
     877          315 :   long i, j, n = lg(M)-1;
     878          315 :   GEN C = zero_F2m_copy(n,n);
     879          315 :   pari_sp av = avma;
     880         1974 :   for (i = 1; i <= n; i++)
     881              :   {
     882         1659 :     GEN Mii = gcoeff(M,i,i);
     883         1659 :     if (isintzero(Mii)) continue;
     884         4606 :     for (j = 1; j < i; j++)
     885              :     {
     886         3549 :       GEN Mjj = gcoeff(M,j,j);
     887         3549 :       if (!isintzero(Mjj) && casselspairing(F, Mii, Mjj, gcoeff(M,i,j)))
     888          399 :       { F2m_set(C,i,j); F2m_set(C,j,i); }
     889              :     }
     890              :   }
     891          315 :   if (DEBUGLEVEL>=2) err_printf("Cassels Pairing: %Ps\n", F2m_to_ZM(C));
     892          315 :   return gc_const(av, C);
     893              : }
     894              : 
     895              : /*******************************************************************/
     896              : /*                         ELLRANK                                 */
     897              : /*******************************************************************/
     898              : /* This section is a port by Bill Allombert of ellQ.gp by Denis Simon
     899              :  * Copyright (C) 2019 Denis Simon
     900              :  * Distributed under the terms of the GNU General Public License (GPL) */
     901              : 
     902              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
     903              : /*    FUNCTIONS FOR POLYNOMIALS                \\ */
     904              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
     905              : 
     906              : static GEN
     907         1757 : ell2pol(GEN ell)
     908         1757 : { return mkpoln(4, gen_1, ell_get_a2(ell), ell_get_a4(ell), ell_get_a6(ell)); }
     909              : 
     910              : /* find point (x:y:z) on y^2 = pol, return [x,z]~ and set *py = y */
     911              : static GEN
     912         4504 : projratpointxz(GEN pol, long lim, GEN *py)
     913              : {
     914              :   pari_timer ti;
     915              :   GEN P;
     916         4504 :   if (issquareall(leading_coeff(pol), py)) return mkcol2(gen_1, gen_0);
     917         4455 :   if (DEBUGLEVEL) timer_start(&ti);
     918         4455 :   P = hyperellratpoints(pol, stoi(lim), 1);
     919         4455 :   if (DEBUGLEVEL) timer_printf(&ti,"hyperellratpoints(%ld)",lim);
     920         4455 :   if (lg(P) == 1) return NULL;
     921         1736 :   P = gel(P,1); *py = gel(P,2); return mkcol2(gel(P,1), gen_1);
     922              : }
     923              : 
     924              : /* N = K f^2 where K = core(N) is assumed > 1; let p = P^-(K)
     925              :  * Return p, K/p and f*p */
     926              : static GEN
     927          972 : first_core_prime(GEN N, GEN *Kp, GEN *fp)
     928              : {
     929          972 :   GEN v, p = NULL, fa = Z_factor(N), P = gel(fa,1), E = gel(fa,2);
     930          972 :   long i, j, l = lg(P);
     931          972 :   for (i = 1; i < l; i++)
     932          972 :     if (mpodd(gel(E,i))) { p = gel(P,i); break; }
     933          972 :   v = cgetg(l-1, t_VEC);
     934         3751 :   for (j = 1, i++; i < l; i++)
     935         2779 :     if (mpodd(gel(E,i))) gel(v,j++) = gel(P,i);
     936          972 :   setlg(v, j); *Kp = ZV_prod(v);
     937          972 :   *fp = mulii(p, sqrtint(diviiexact(N, mulii(*Kp,p))));
     938          972 :   return p;
     939              : }
     940              : 
     941              : /* find point (x:y:z) on y^2 = T, return [x,z]~ and set *py = y */
     942              : static GEN
     943         1784 : projratpointxz2(GEN T, long lim, long effort, GEN *py)
     944              : {
     945         1784 :   pari_sp av = avma;
     946         1784 :   GEN list = mkvec(mkvec4(T, matid(2), gen_1, gen_1));
     947         1784 :   long j, c, ntry = effort * 10;
     948              : 
     949         5035 :   for (c = 1; lg(list) > 1 && c <= ntry; c++)
     950              :   {
     951              :     GEN K, k, f, p, M, C, r, pol, L;
     952              :     pari_sp av2;
     953              :     long lr;
     954              : 
     955         3293 :     if (gc_needed(av, 1))
     956              :     {
     957            0 :       if (DEBUGMEM > 1)
     958            0 :         pari_warn(warnmem, "projratpointxz2: #list = %ld",lg(list)-1);
     959            0 :       list = gc_GEN(av, list);
     960              :     }
     961         3293 :     L = gel(list, 1);
     962         3293 :     list = vecsplice(list, 1);
     963         3293 :     av2 = avma;
     964         3293 :     pol = Q_primitive_part(gel(L,1), &K);
     965         3293 :     M = gel(L,2); /* content = 1 */
     966         3293 :     K = K? mulii(gel(L,3), K): gel(L,3);
     967         3293 :     C = gel(L,4);
     968         3293 :     if (Z_issquareall(K, &k))
     969              :     {
     970              :       GEN xz, y, aux, U;
     971         3256 :       if (c==1) { set_avma(av2); continue; }
     972          977 :       pol = ZX_hyperellred(pol, &U);
     973          977 :       if (DEBUGLEVEL>1) err_printf("  reduced quartic: Y^2 = %Ps\n", pol);
     974          977 :       xz = projratpointxz(pol, lim, &y); if (!xz) { set_avma(av2); continue; }
     975           42 :       *py = gmul(y, mulii(C, k));
     976           42 :       aux = RgM_RgC_mul(ZM2_mul(M, U), xz);
     977           84 :       if (gequal0(gel(aux, 2))) return mkcol2(gel(aux,1), gen_0);
     978           42 :       *py = gdiv(*py, gpowgs(gel(aux, 2), degpol(pol)>>1));
     979           42 :       return mkcol2(gdiv(gel(aux, 1), gel(aux, 2)), gen_1);
     980              :     }
     981          972 :     p = first_core_prime(K, &K, &f);
     982          972 :     C = mulii(C, f);
     983              :     /* root at infinity */
     984         1300 :     if (dvdii(leading_coeff(pol), p) &&
     985          510 :         (!dvdii(gcoeff(M,1,1), p) || !dvdii(gcoeff(M,2,1), p)))
     986              :     {
     987          146 :       GEN U = mkmat2(gel(M,1), ZC_Z_mul(gel(M,2), p));
     988          146 :       GEN t = ZX_Z_divexact(ZX_rescale(pol, p), p);
     989          146 :       list = vec_append(list, mkvec4(t, U, K, C));
     990              :     }
     991          972 :     r = FpC_center(FpX_roots(pol, p), p, shifti(p,-1)); lr = lg(r);
     992         3028 :     for (j = 1; j < lr; j++)
     993              :     {
     994         2056 :       pari_sp av3 = avma;
     995         2056 :       GEN t, U = ZM2_mul(M, mkmat22(p, gel(r,j), gen_0, gen_1));
     996         2056 :       if (dvdii(gcoeff(U,1,2), p) && dvdii(gcoeff(U,2,2), p))
     997           28 :       { set_avma(av3); continue; } /* content(U) != 1 (in fact p) */
     998         2028 :       t = ZX_unscale_div(ZX_Z_translate(pol, gel(r,j)), p);
     999         2028 :       list = vec_append(list, mkvec4(t, U, K, C));
    1000              :     }
    1001              :   }
    1002         1742 :   return NULL;
    1003              : }
    1004              : 
    1005              : static GEN
    1006         8540 : polrootsmodpn(GEN pol, GEN p)
    1007              : {
    1008         8540 :   pari_sp av = avma, av2;
    1009         8540 :   long j, l, i = 1, vd = Z_pval(ZX_disc(pol), p);
    1010              :   GEN v, r, P;
    1011              : 
    1012         8540 :   if (!vd) { set_avma(av); retmkvec(zerovec(2)); }
    1013         6314 :   pol = Q_primpart(pol);
    1014         6314 :   P = gpowers0(p, vd-1, p); av2 = avma;
    1015         6314 :   v = FpX_roots(pol, p); l = lg(v);
    1016        18270 :   for (j = 1; j < l; j++) gel(v,j) = mkvec2(gel(v,j), gen_1);
    1017        72849 :   while (i < lg(v))
    1018              :   {
    1019        66535 :     GEN pol2, pe, roe = gel(v, i), ro = gel(roe,1);
    1020        66535 :     long e = itou(gel(roe,2));
    1021              : 
    1022        67529 :     if (e >= vd) { i++; continue; }
    1023        51282 :     pe = gel(P, e);
    1024        51282 :     (void)ZX_pvalrem(ZX_affine(pol, pe, ro), p, &pol2);
    1025        51282 :     r = FpX_roots(pol2, p); l = lg(r);
    1026        51282 :     if (l == 1) { i++; continue; }
    1027       104867 :     for (j = 1; j < l; j++)
    1028        54579 :       gel(r, j) = mkvec2(addii(ro, mulii(pe, gel(r, j))), utoi(e + 1));
    1029              :     /* roots with higher precision = ro + r*p^(e+1) */
    1030        50288 :     if (l > 2) v = shallowconcat(v, vecslice(r, 2, l-1));
    1031        50288 :     gel(v, i) = gel(r, 1);
    1032        50288 :     if (gc_needed(av2, 1)) (void)gc_all(av2, 1, &v);
    1033              :   }
    1034         6314 :   if (lg(v) == 1) { set_avma(av); retmkvec(zerovec(2)); }
    1035         6314 :   return gc_GEN(av, v);
    1036              : }
    1037              : 
    1038              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1039              : /*    FUNCTIONS FOR LOCAL COMPUTATIONS         \\ */
    1040              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1041              : 
    1042              : /* z is integral; sprk true (pr | 2) [t_VEC] or modpr structure (pr | p odd)
    1043              :  * [t_COL] */
    1044              : static GEN
    1045      1626772 : kpmodsquares1(GEN nf, GEN z, GEN sprk)
    1046              : {
    1047      1626772 :   GEN modpr = (typ(sprk) == t_VEC)? gmael(sprk, 4, 1): sprk;
    1048      1626772 :   GEN pr = modpr_get_pr(modpr), p = pr_get_p(pr);
    1049      1626772 :   long v = nfvalrem(nf, z, pr, &z);
    1050      1626772 :   if (equaliu(p, 2))
    1051              :   {
    1052              :     GEN c;
    1053       159831 :     z = to_principal_unit(nf, z, pr, sprk); /* = 1 mod pr */
    1054       159831 :     c = ZV_to_Flv(sprk_log_prk1(nf, z, sprk), 2);
    1055       159831 :     return vecsmall_prepend(c, odd(v));
    1056              :   }
    1057              :   else
    1058              :   {
    1059      1466941 :     GEN T = modpr_get_T(modpr);
    1060      1466941 :     long c = !Fq_issquare(nf_to_Fq(nf, z, modpr), T, p);
    1061      1466941 :     return mkvecsmall2(odd(v), c);
    1062              :   }
    1063              : }
    1064              : 
    1065              : static GEN
    1066       794255 : kpmodsquares(GEN vnf, GEN z, GEN PP)
    1067              : {
    1068       794255 :   pari_sp av = avma;
    1069       794255 :   long i, j, l = lg(vnf);
    1070       794255 :   GEN dz, vchar = cgetg(l, t_VEC);
    1071       794255 :   z = Q_remove_denom(z, &dz); if (dz) z = ZX_Z_mul(z, dz);
    1072      1895740 :   for (i = 1; i < l; i++)
    1073              :   {
    1074      1101485 :     GEN nf = gel(vnf, i), pp = gel(PP, i);
    1075      1101485 :     GEN kp, delta = ZX_rem(z, nf_get_pol(nf));
    1076      1101485 :     long lp = lg(pp);
    1077      1101485 :     kp = cgetg(lp, t_VEC);
    1078      2728257 :     for (j = 1; j < lp; j++) gel(kp, j) = kpmodsquares1(nf, delta, gel(pp,j));
    1079      1101485 :     gel(vchar, i) = shallowconcat1(kp);
    1080              :   }
    1081       794255 :   return gc_leaf(av, shallowconcat1(vchar));
    1082              : }
    1083              : 
    1084              : static GEN
    1085        17080 : veckpmodsquares(GEN x, GEN vnf, GEN PP)
    1086       789180 : { pari_APPLY_type(t_MAT, kpmodsquares(vnf, gel(x, i), PP)) }
    1087              : 
    1088              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1089              : /*    GENERIC FUNCTIONS FOR ELLIPTIC CURVES    \\ */
    1090              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1091              : 
    1092              : static GEN
    1093         3913 : ellabs(GEN P)
    1094         3913 : { return ell_is_inf(P) ? P: mkvec2(gel(P,1), Q_abs_shallow(gel(P,2))); }
    1095              : static GEN
    1096         4697 : vecellabs(GEN x) { pari_APPLY_same(ellabs(gel(x,i))) }
    1097              : 
    1098              : /* y^2 = x^3 + K a2 x + K^2 a4 x + K^3 a6 */
    1099              : static GEN
    1100          840 : elltwistequation(GEN ell, GEN K)
    1101              : {
    1102              :   GEN K2, a2, a4, a6;
    1103          840 :   if (!K || equali1(K)) return ell;
    1104           84 :   K2 = sqri(K);
    1105           84 :   a2 = mulii(ell_get_a2(ell), K);
    1106           84 :   a4 = mulii(ell_get_a4(ell), K2);
    1107           84 :   a6 = mulii(ell_get_a6(ell), mulii(K, K2));
    1108           84 :   return ellinit(mkvec5(gen_0, a2, gen_0, a4, a6), NULL, DEFAULTPREC);
    1109              : }
    1110              : 
    1111              : /* P=[x,y] a point on Ky^2 =  pol(x); [Kx, K^2y] point on Y^2 = K^3pol(X/K) */
    1112              : static GEN
    1113          224 : elltwistpoint(GEN P, GEN K, GEN K2)
    1114              : {
    1115          224 :   if (ell_is_inf(P)) return ellinf();
    1116          224 :   return mkvec2(gmul(gel(P,1), K), gmul(gel(P,2), K2));
    1117              : }
    1118              : 
    1119              : static GEN
    1120         1673 : elltwistpoints(GEN x, GEN K)
    1121              : {
    1122              :   GEN K2;
    1123         1673 :   if (!K || gequal1(K)) return x;
    1124          126 :   K2 = gsqr(K);
    1125          350 :   pari_APPLY_same(elltwistpoint(gel(x,i), K, K2))
    1126              : }
    1127              : 
    1128              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1129              : /*    FUNCTIONS FOR NUMBER FIELDS              \\ */
    1130              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1131              : 
    1132              : /* return a set S2 of prime ideals disjoint from S such that
    1133              :  * Cl_{S \cup S2}(K) has no p-torsion */
    1134              : static GEN
    1135          441 : bestS(GEN bnf,GEN S, ulong p)
    1136              : {
    1137          441 :   GEN v, S2, h = bnf_get_no(bnf), cyc = bnf_get_cyc(bnf);
    1138              :   long i, lS2;
    1139              :   ulong l, vD;
    1140              :   forprime_t P;
    1141              : 
    1142          441 :   if (!dvdiu(h, p)) return cgetg(1,t_VEC);
    1143          322 :   if (!S)
    1144              :   {
    1145            0 :     v = diagonal_shallow(cyc);
    1146            0 :     vD = Z_lval(h, p);
    1147              :   }
    1148              :   else
    1149              :   {
    1150          322 :     long lS = lg(S);
    1151          322 :     v = cgetg(lS,t_MAT);
    1152         1022 :     for (i = 1; i < lS; i++) gel(v,i) = isprincipal(bnf, gel(S,i));
    1153          322 :     v = ZM_hnfmodid(v, cyc);
    1154          322 :     vD = Z_lval(ZM_det(v), p); if (!vD) return cgetg(1, t_VEC);
    1155              :   }
    1156          301 :   S2 = cgetg(vD+2, t_VEC); lS2 = 1;
    1157          301 :   u_forprime_init(&P,2,ULONG_MAX);
    1158         2569 :   while ((l = u_forprime_next(&P)))
    1159              :   {
    1160         2569 :     pari_sp av = avma;
    1161         2569 :     GEN w, Sl = idealprimedec(bnf, utoi(l));
    1162         2569 :     long nSl = lg(Sl)-1;
    1163              :     ulong vDl;
    1164         4074 :     for (i = 1; i < nSl; i++) /* remove one prime ideal */
    1165              :     {
    1166         1806 :       w = ZM_hnf(shallowconcat(v, isprincipal(bnf, gel(Sl,i))));
    1167         1806 :       vDl = Z_lval(ZM_det(w), p);
    1168         1806 :       if (vDl < vD)
    1169              :       {
    1170         1239 :         gel(S2,lS2++) = gel(Sl,i);
    1171         1239 :         vD = vDl; v = w; av = avma;
    1172         1239 :         if (!vD) { setlg(S2, lS2); return S2; }
    1173              :       }
    1174              :     }
    1175         2268 :     set_avma(av);
    1176              :   }
    1177              :   return NULL;/*LCOV_EXCL_LINE*/
    1178              : }
    1179              : 
    1180              : static GEN
    1181          882 : nfC_prV_val(GEN nf, GEN G, GEN P)
    1182              : {
    1183          882 :   long i, j, lG = lg(G), lP = lg(P);
    1184          882 :   GEN M = cgetg(lG, t_MAT);
    1185         5418 :   for (i = 1; i < lG; i++)
    1186              :   {
    1187         4536 :     GEN V = cgetg(lP, t_COL);
    1188        19390 :     for (j = 1; j < lP; j++)
    1189        14854 :       gel(V,j) = gpnfvalrem(nf, gel(G,i), gel(P,j), NULL);
    1190         4536 :     gel(M,i) = V;
    1191              :   }
    1192          882 :   return M;
    1193              : }
    1194              : 
    1195              : static GEN
    1196         5614 : _factorbackmod(GEN nf, GEN g, GEN e, ulong p)
    1197              : {
    1198         5614 :   GEN y = nffactorback(nf, g, e), den;
    1199         5614 :   GEN z = nfmul(nf, y, nfsqr(nf, idealredmodpower(nf, y, p, 0)));
    1200         5614 :   z = Q_remove_denom(z, &den);
    1201         5614 :   if (den)
    1202              :   {
    1203            0 :     if (p != 2) den = powiu(den, p-1);
    1204            0 :     z = gmul(z, den);
    1205              :   }
    1206         5614 :   return z;
    1207              : }
    1208              : static GEN
    1209          441 : nfV_factorbackmod(GEN nf, GEN x, ulong p)
    1210              : {
    1211          441 :   long i, l = lg(x);
    1212          441 :   GEN v = cgetg(l, t_VEC);
    1213         3794 :   for (i = 1; i < l; i++)
    1214              :   {
    1215         3353 :     GEN y = gel(x,i), g = gel(y,1), e = gel(y,2);
    1216         3353 :     gel(v,i) = _factorbackmod(nf, g, ZV_to_Flv(e,p), p);
    1217              :   }
    1218          441 :   return v;
    1219              : }
    1220              : static GEN
    1221          441 : nfV_zm_factorback(GEN nf, GEN G, GEN x, long p)
    1222         2702 : { pari_APPLY_type(t_VEC, _factorbackmod(nf, G, gel(x,i), p)) }
    1223              : 
    1224              : static GEN
    1225          441 : prV_ZM_factorback(GEN nf, GEN S, GEN x)
    1226         2702 : { pari_APPLY_type(t_VEC,idealfactorback(nf, S, gel(x,i), 0)) }
    1227              : 
    1228              : /* shortcut for bnf = Q and p = 2 */
    1229              : static GEN
    1230          826 : bnfselmerQ(GEN S)
    1231              : {
    1232          826 :   GEN g = vec_prepend(prV_primes(S), gen_m1), e;
    1233          826 :   long n = lg(S)-1;
    1234          826 :   e = n? shallowconcat(zerocol(n), matid(n)): zeromat(0, 1);
    1235          826 :   return mkvec3(g, e, const_vec(n + 1, gen_1));
    1236              : }
    1237              : 
    1238              : static GEN
    1239          441 : bnfselmer(GEN bnf, GEN S)
    1240              : {
    1241          441 :   const long p = 2;
    1242          441 :   pari_sp av = avma;
    1243          441 :   GEN nf = bnf_get_nf(bnf), S2, S3, e, f, e2, kerval, LS2gen, LS2fu, LS2all;
    1244          441 :   long n = lg(S)-1, n3, n2all, r;
    1245              : 
    1246          441 :   S2 = bestS(bnf, S, p);
    1247          441 :   S3 = shallowconcat(S, S2);
    1248          441 :   LS2all = nfV_factorbackmod(nf, gel(bnfunits(bnf, S3), 1), p);
    1249          441 :   n3 = lg(S3)-1; n2all = lg(LS2all)-1;
    1250          441 :   LS2gen = vecslice(LS2all,1,n3);
    1251          441 :   LS2fu  = vecslice(LS2all,n3+1, n2all-1);
    1252          441 :   e2 = nfC_prV_val(nf, LS2gen, S2);
    1253          441 :   kerval = Flm_ker(ZM_to_Flm(e2, p), p);
    1254          441 :   LS2gen = nfV_zm_factorback(nf, LS2gen, kerval, p);
    1255          441 :   e = nfC_prV_val(nf, LS2gen, S);
    1256          441 :   e2 = ZM_divexactu(ZM_zm_mul(e2, kerval), p);
    1257          441 :   f = prV_ZM_factorback(nf, S2, e2);
    1258          441 :   LS2gen = shallowconcat(LS2fu, LS2gen);
    1259          441 :   LS2gen = nfV_to_scalar_or_alg(nf, vec_prepend(LS2gen, bnf_get_tuU(bnf)));
    1260          441 :   r = n2all - n3;
    1261          441 :   e = shallowconcat(zeromat(n, r), e);
    1262          441 :   f = shallowconcat(const_vec(r, gen_1), f);
    1263          441 :   return gc_GEN(av, mkvec3(LS2gen,e,f));
    1264              : }
    1265              : 
    1266              : static GEN
    1267          259 : get_kerval(GEN nf, GEN S2, GEN LS2gen)
    1268              : {
    1269          259 :   long i, j, lS2 = lg(S2), l = lg(LS2gen);
    1270          259 :   GEN e = cgetg(l, t_MAT);
    1271         3864 :   for (i = 1; i < l; i++)
    1272              :   {
    1273         3605 :     GEN v = cgetg(lS2, t_VECSMALL);
    1274        25739 :     for (j=1; j < lS2; j++) v[j] = odd(idealval(nf, gel(LS2gen, i), gel(S2,j)));
    1275         3605 :     gel(e, i) = Flv_to_F2v(v);
    1276              :   }
    1277          259 :   return F2m_ker(e);
    1278              : }
    1279              : static GEN
    1280          259 : nf2selmer_quad(GEN nf, GEN S)
    1281              : {
    1282          259 :   pari_sp ltop = avma;
    1283          259 :   GEN D = nf_get_disc(nf), factD = nf_get_ramified_primes(nf);
    1284          259 :   GEN SlistQ = prV_primes(S), QS2gen, gen, Hlist, H, KerH, LS2gen;
    1285              :   GEN chpol, Q, kerval, S2, G, e, f, b, c, bad;
    1286          259 :   long lS = lg(S), l, lHlist, i, j, k;
    1287          259 :   GEN nfa = nf_get_ramified_primes(nf);
    1288              : 
    1289          259 :   QS2gen = vec_prepend(SlistQ, gen_m1);
    1290          259 :   bad = ZV_sort_uniq_shallow(shallowconcat(factD, SlistQ));
    1291          259 :   Hlist = ZV_search(bad, gen_2)? bad: vec_prepend(bad, gen_2);
    1292          259 :   l = lg(QS2gen);
    1293          259 :   lHlist = lg(Hlist);
    1294          259 :   H = cgetg(l, t_MAT);
    1295         2450 :   for (j = 1; j < l; j++)
    1296              :   {
    1297         2191 :     GEN v = cgetg(lHlist, t_VECSMALL);
    1298        33705 :     for (i = 1; i < lHlist; i++)
    1299        31514 :       v[i] = hilbertii(D, gel(QS2gen, j), gel(Hlist, i)) < 0;
    1300         2191 :     gel(H, j) = Flv_to_F2v(v);
    1301              :   }
    1302          259 :   KerH = F2m_ker(H); l = lg(KerH);
    1303          259 :   LS2gen = cgetg(l, t_VEC);
    1304          259 :   chpol = QXQ_charpoly(gel(nf_get_zk(nf), 2), nf_get_pol(nf), 0);
    1305          259 :   b = negi(gel(chpol, 3));
    1306          259 :   c = shifti(gel(chpol, 2),1);
    1307          259 :   Q = mkmat3(mkcol3(gen_2, b, gen_0),
    1308              :              mkcol3(b, c, gen_0),
    1309              :              mkcol3(gen_0, gen_0, gen_0));
    1310         1470 :   for (k = 1; k < l; k++)
    1311              :   {
    1312         1211 :     GEN P = RgV_F2v_extract_shallow(QS2gen, gel(KerH, k));
    1313         1211 :     GEN F = ZV_sort_uniq_shallow(shallowconcat(nfa, P)), sol;
    1314         1211 :     gcoeff(Q, 3, 3) = mulis(ZV_prod(P), -2);
    1315         1211 :     sol = qfsolve(mkvec2(Q, F)); /* must be solvable */
    1316         1211 :     sol = Q_primpart(mkcol2(gel(sol,1), gel(sol,2)));
    1317         1211 :     gel(LS2gen, k) = basistoalg(nf, sol);
    1318              :   }
    1319          259 :   if (equalis(D, -4)) G = bad;
    1320              :   else
    1321              :   {
    1322          259 :     G = vecsplice(bad, ZV_search(bad, veclast(factD)));
    1323          259 :     G = vec_prepend(G, gen_m1);
    1324              :   }
    1325          259 :   LS2gen = shallowconcat(G, LS2gen);
    1326          259 :   l = lg(SlistQ); S2 = cgetg(l, t_VEC);
    1327          259 :   if (l > 1)
    1328              :   {
    1329         2184 :     for (i = 1; i < l; i++) gel(S2, i) = idealprimedec(nf, gel(SlistQ, i));
    1330          252 :     S2 = setminus(shallowconcat1(S2), S);
    1331              :   }
    1332          259 :   kerval = get_kerval(nf, S2, LS2gen); l = lg(kerval);
    1333          259 :   gen = cgetg(l, t_VEC);
    1334          259 :   e = cgetg(l, t_MAT);
    1335          259 :   f = cgetg(l, t_VEC);
    1336         2905 :   for (i = 1; i < l; i++)
    1337              :   {
    1338         2646 :     GEN id, ei, x = nffactorback(nf, LS2gen, F2v_to_Flv(gel(kerval, i)));
    1339         2646 :     gel(e,i) = ei = cgetg(lS, t_COL);
    1340        40726 :     for (j = 1; j < lS; j++) gel(ei,j) = stoi(idealval(nf, x, gel(S,j)));
    1341         2646 :     id = idealdiv(nf, x, idealfactorback(nf, S, gel(e,i), 0));
    1342         2646 :     if (!idealispower(nf, id, 2, &gel(f,i))) pari_err_BUG("nf2selmer_quad");
    1343         2646 :     gel(gen, i) = nf_to_scalar_or_alg(nf, x);
    1344              :   }
    1345          259 :   return gc_GEN(ltop, mkvec3(gen, e, f));
    1346              : }
    1347              : 
    1348              : static GEN
    1349          868 : makevbnf(GEN ell, long prec)
    1350              : {
    1351          868 :   GEN vbnf, P = gel(ZX_factor(ell2pol(ell)), 1);
    1352          868 :   long k, l = lg(P);
    1353          868 :   vbnf = cgetg(l, t_VEC);
    1354         2387 :   for (k = 1; k < l; k++)
    1355              :   {
    1356         1519 :     GEN t = gel(P,k);
    1357         1519 :     gel(vbnf,k) = degpol(t) <= 2? nfinit(t, prec): Buchall(t, nf_FORCE, prec);
    1358              :   }
    1359          868 :   return vbnf;
    1360              : }
    1361              : 
    1362              : static GEN
    1363          889 : kernorm(GEN G, GEN S, GEN pol, GEN signs)
    1364              : {
    1365          889 :   long i, j, lS = lg(S), lG = lg(G), lv = signs? lS+2: lS+1;
    1366          889 :   GEN M = cgetg(lG, t_MAT);
    1367        14315 :   for (j = 1; j < lG; j++)
    1368              :   {
    1369        13426 :     GEN v, N = QXQ_norm(gel(G,j), pol);
    1370        13426 :     gel(M, j) = v = cgetg(lv, t_VECSMALL);
    1371        13426 :     v[1] = gsigne(N) < 0;
    1372       191506 :     for (i = 1; i < lS; i++) v[i+1] = odd(Q_pvalrem(N, gel(S,i), &N));
    1373        13426 :     if (signs) v[i+1] = signs[j];
    1374              :   }
    1375          889 :   return Flm_ker(M, 2);
    1376              : }
    1377              : 
    1378              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1379              : /*    FUNCTIONS FOR 2-DESCENT                  \\ */
    1380              : /* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ */
    1381              : /* vector of t_VEC; return total number of entries */
    1382              : static long
    1383         8540 : RgVV_nb(GEN v)
    1384              : {
    1385         8540 :   long i, l = lg(v), n = 0;
    1386        24794 :   for (i = 1; i < l; i++) n += lg(gel(v,i)) - 1;
    1387         8540 :   return n;
    1388              : }
    1389              : 
    1390              : /* return an Fp basis */
    1391              : static GEN
    1392         8540 : elllocalimage(GEN pol, GEN K, GEN vnf, GEN p, GEN pp, GEN pts)
    1393              : {
    1394         8540 :   long n, p2 = RgVV_nb(pp), prank = equaliu(p, 2)? p2: p2 - 1;
    1395         8540 :   GEN R = polrootsmodpn(pol, p), bound = addiu(p, 6);
    1396              : 
    1397         8540 :   for(n = 1;; n++)
    1398        22155 :   {
    1399              :     pari_sp btop;
    1400              :     GEN x, y2, d;
    1401        30695 :     pts = Flm_image(pts, 2); if (lg(pts)-1 == prank) break;
    1402        22155 :     if ((n & 0xf) == 0) bound = mulii(bound, p);
    1403        22155 :     btop = avma;
    1404              :     do
    1405              :     {
    1406       107786 :       GEN r = gel(R, random_Fl(lg(R)-1)+1);
    1407       107786 :       long pprec = random_Fl(itou(gel(r, 2)) + 3) - 2; /* >= -2 */
    1408       107786 :       set_avma(btop);
    1409       107786 :       x = gadd(gel(r, 1), gmul(powis(p, pprec), randomi(bound)));
    1410       107786 :       y2 = gmul(K, poleval(pol, x));
    1411       107786 :     } while (gequal0(y2) || !Qp_issquare(y2, p));
    1412        22155 :     d = deg1pol_shallow(negi(K), gmul(K, x), 0);
    1413        22155 :     pts = vec_append(pts, kpmodsquares(vnf, d, pp));
    1414              :   }
    1415         8540 :   return pts;
    1416              : }
    1417              : 
    1418              : static GEN
    1419          889 : ellLS2image(GEN pol, GEN vP, GEN K, GEN vpol, GEN vcrt)
    1420              : {
    1421          889 :   long i, l = lg(vP);
    1422              :   GEN v;
    1423              : 
    1424          889 :   if (l == 1) return cgetg(1, t_VEC);
    1425          756 :   v = cgetg(l, t_VEC);
    1426        60263 :   for (i = 1; i < l; i++)
    1427              :   {
    1428        59507 :     GEN P = gel(vP, i), x, t;
    1429        59507 :     if (ell_is_inf(P)) { gel(v, i) = gen_1; continue; }
    1430        59507 :     x = gel(P,1);
    1431        59507 :     t = deg1pol_shallow(negi(K), gmul(K, x), 0);
    1432        59507 :     if (gequal0(gel(P,2)))
    1433              :     { /* 2-torsion, x now integer and a root of exactly one linear vpol[k]=T */
    1434          623 :       long k, lp = lg(vpol);
    1435              :       GEN a;
    1436         1099 :       for (k = 1; k < lp; k++)
    1437              :       {
    1438         1099 :         GEN T = gel(vpol,k), z = gel(T,2);
    1439         1099 :         if (absequalii(x, z) && signe(x) == -signe(z)) break; /* T = X-x */
    1440              :       }
    1441          623 :       a = ZX_Z_eval(ZX_deriv(pol), x);
    1442          623 :       t = gadd(a, gmul(gel(vcrt,k), gsub(t, a))); /* a mod T, t mod pol/T*/
    1443              :     }
    1444        59507 :     gel(v, i) = t;
    1445              :   }
    1446          756 :   return v;
    1447              : }
    1448              : 
    1449              : /* find small points on ell; 2-torsion points must be returned first */
    1450              : static GEN
    1451          889 : ellsearchtrivialpoints(GEN ell, GEN lim, GEN help)
    1452              : {
    1453          889 :   pari_sp av = avma;
    1454          889 :   GEN tors2 = gel(elltors_psylow(ell,2),3);
    1455          889 :   GEN triv = lim ? ellratpoints(ell, lim, 0): cgetg(1,t_VEC);
    1456          889 :   if (help) triv = shallowconcat(triv, help);
    1457          889 :   return gc_GEN(av, shallowconcat(tors2, triv));
    1458              : }
    1459              : 
    1460              : GEN
    1461           63 : ellrankinit(GEN ell, long prec)
    1462              : {
    1463           63 :   pari_sp av = avma;
    1464              :   GEN urst;
    1465           63 :   checkell_Q(ell); ell = ellminimalbmodel(ell, &urst);
    1466           63 :   return gc_GEN(av, mkvec3(ell, urst, makevbnf(ell, prec)));
    1467              : }
    1468              : 
    1469              : INLINE GEN
    1470       256109 : ZV_isneg(GEN x) { pari_APPLY_long(signe(gel(x,i)) < 0) }
    1471              : 
    1472              : static GEN
    1473        72933 : selmersign(GEN x, GEN vpol, GEN L)
    1474       164115 : { pari_APPLY_same(ZV_isneg(nfeltsign(gel(x, i), RgX_rem(L, gel(vpol, i)), NULL))) }
    1475              : 
    1476              : static GEN
    1477         1778 : matselmersign(GEN vnf, GEN vpol, GEN x)
    1478        74711 : { pari_APPLY_type(t_MAT, shallowconcat1(selmersign(vnf, vpol, gel(x, i)))) }
    1479              : 
    1480              : static GEN
    1481       118974 : _trace(GEN z, GEN T)
    1482              : {
    1483       118974 :   long n = degpol(T)-1;
    1484       118974 :   if (degpol(z) < n) return gen_0;
    1485        69236 :   return gdiv(gel(z, 2+n), gel(T, 3+n));
    1486              : }
    1487              : static GEN
    1488        19829 : tracematrix(GEN zc, GEN b, GEN T)
    1489              : {
    1490              :   long i, j;
    1491        19829 :   GEN q = cgetg(4, t_MAT);
    1492        79316 :   for (j = 1; j <= 3; j++) gel(q,j) = cgetg(4, t_COL);
    1493        79316 :   for (j = 1; j <= 3; j++)
    1494              :   {
    1495       118974 :     for (i = 1; i < j; i++) gcoeff(q,i,j) = gcoeff(q,j,i) =
    1496        59487 :       _trace(QXQ_mul(zc, QXQ_mul(gel(b,i), gel(b,j), T), T), T);
    1497        59487 :     gcoeff(q,i,i) = _trace(QXQ_mul(zc, QXQ_sqr(gel(b,i), T), T), T);
    1498              :   }
    1499        19829 :   return q;
    1500              : }
    1501              : 
    1502              : static GEN
    1503        21630 : RgXV_cxeval(GEN x, GEN r, GEN ri)
    1504        86520 : { pari_APPLY_same(RgX_cxeval(gel(x,i), r, ri)) }
    1505              : 
    1506              : static GEN
    1507         7202 : redquadric(GEN base, GEN pol, GEN zc)
    1508              : {
    1509         7202 :   pari_sp av = avma;
    1510         7202 :   long prec = nbits2prec(gexpo(pol)+gexpo(zc)) + EXTRAPRECWORD;
    1511              :   for (;;)
    1512            8 :   {
    1513         7210 :     GEN R = roots(pol, prec), s = NULL;
    1514         7210 :     long i, l = lg(R);
    1515        28840 :     for (i = 1; i < l; ++i)
    1516              :     {
    1517        21630 :       GEN r = gel(R,i), ri = gexpo(r) > 1? ginv(r): NULL;
    1518        21630 :       GEN b = RgXV_cxeval(base, r, ri), z = RgX_cxeval(zc, r, ri);
    1519        21630 :       GEN M = RgC_RgV_mulrealsym(RgV_Rg_mul(b, gabs(z, prec)), gconj(b));
    1520        21630 :       s = s? RgM_add(s, M): M;
    1521              :     }
    1522         7210 :     s = RgM_Cholesky(s, prec);
    1523         7210 :     if (s) return gc_upto(av, lll(s));
    1524            8 :     prec = precdbl(prec); set_avma(av);
    1525              :   }
    1526              : }
    1527              : 
    1528              : static GEN
    1529         5425 : RgXV_homogenous_eval(GEN x, GEN a, GEN b, long d)
    1530        21700 : { pari_APPLY_same(RgX_homogenous_eval(gel(x,i), a, b, d)) }
    1531              : 
    1532              : static void
    1533           42 : check_oncurve(GEN ell, GEN v)
    1534              : {
    1535           42 :   long i, l = lg(v);
    1536           49 :   for (i = 1; i < l; i++)
    1537              :   {
    1538            7 :     GEN P = gel(v, i);
    1539            7 :     if (!checkellpt_i(P) || ell_is_inf(P)
    1540            7 :                          || !RgV_is_QV(P)
    1541            7 :                          || !ellisoncurve_i(ell,P)) pari_err_TYPE("ellrank",P);
    1542              :   }
    1543           42 : }
    1544              : 
    1545              : static GEN
    1546        19178 : basis(GEN nf, GEN x, GEN crt, GEN pol)
    1547              : {
    1548        19178 :   long i, l = lg(x);
    1549        19178 :   GEN b = cgetg(l, t_COL);
    1550        42884 :   for (i = 1; i < l; i++)
    1551              :   {
    1552        23706 :     GEN z = nf_to_scalar_or_alg(nf, gel(x, i));
    1553        23706 :     gel(b, i) = grem(gsub(z, gmul(crt, z)), pol); /* z mod T, 0 mod (pol/T) */
    1554              :   }
    1555        19178 :   return b;
    1556              : }
    1557              : 
    1558              : static GEN
    1559          889 : vecsmallbasis(GEN x, GEN vcrt, GEN pol)
    1560         2415 : { pari_APPLY_same(basis(gel(x,i), nf_get_zk(gel(x,i)), gel(vcrt,i), pol)) }
    1561              : 
    1562              : static GEN
    1563       270372 : ZC_shifti(GEN x, long n) { pari_APPLY_type(t_COL, shifti(gel(x,i), n)) }
    1564              : 
    1565              : /* true nf */
    1566              : static GEN
    1567        17652 : selmerbasis(GEN nf, GEN ek, GEN sqrtLS2, GEN factLS2,
    1568              :             GEN badprimes, GEN crt, GEN pol)
    1569              : {
    1570        17652 :   GEN sqrtzc = idealfactorback(nf, sqrtLS2, zv_neg(ek), 0);
    1571        17652 :   GEN E = ZC_shifti(ZM_zc_mul(factLS2, ek), -1);
    1572              : 
    1573        17652 :   if (ZV_equal0(E))
    1574        15812 :     sqrtzc = idealhnf_shallow(nf, sqrtzc);
    1575              :   else
    1576         1840 :     sqrtzc = idealmul(nf, sqrtzc, idealfactorback(nf, badprimes, ZC_neg(E), 0));
    1577        17652 :   return basis(nf, sqrtzc, crt, pol);
    1578              : }
    1579              : 
    1580              : static long
    1581          567 : randu(void) { return random_Fl(127) - 63; }
    1582              : static GEN
    1583          189 : randS(GEN b)
    1584              : {
    1585          567 :   return gadd(gmulgs(gel(b,1), randu()),
    1586          378 :               gadd(gmulgs(gel(b,2), randu()), gmulgs(gel(b,3), randu())));
    1587              : }
    1588              : 
    1589              : static GEN
    1590         7013 : liftselmerinit(GEN expo, GEN vnf, GEN sqrtLS2, GEN factLS2,
    1591              :                GEN badprimes, GEN vcrt, GEN pol)
    1592              : {
    1593         7013 :   long n = lg(vnf)-1, k, t;
    1594         7013 :   GEN b = cgetg(n+1, t_VEC);
    1595        24665 :   for (k = t = 1; k <= n; k++)
    1596              :   {
    1597        17652 :     GEN fak = gel(factLS2,k), ek;
    1598        17652 :     long m = lg(fak)-1;
    1599        17652 :     ek = vecslice(expo, t, t + m-1); t += m;
    1600        17652 :     gel(b,k) = selmerbasis(gel(vnf,k), ek, gel(sqrtLS2,k),
    1601        17652 :                            fak, gel(badprimes,k), gel(vcrt,k), pol);
    1602              :   }
    1603         7013 :   return shallowconcat1(b);
    1604              : }
    1605              : 
    1606              : static GEN
    1607         7202 : qf_disc_fact(GEN q, GEN L)
    1608              : {
    1609         7202 :   GEN D = ZM_det(q), P, E;
    1610         7202 :   GEN H = Z_smoothen(D, L, &P, &E);
    1611         7202 :   if (H)
    1612          455 :     P = shallowconcat(P, gel(Z_factor(H),1));
    1613         7202 :   return mkvec2(q, P);
    1614              : }
    1615              : 
    1616              : static GEN
    1617         3640 : liftselmer_cover(GEN b, GEN expo, GEN LS2, GEN pol, GEN discF, GEN K)
    1618              : {
    1619              :   GEN P, Q, Q4, R, den, q0, q1, q2, xz, x, y, y2m, U, param, newb;
    1620              :   GEN ttheta, tttheta, zc, polprime;
    1621              :   GEN QM, zden;
    1622         3640 :   zc = RgXQV_factorback(LS2, expo, pol);
    1623         3640 :   if (typ(zc)==t_INT) zc = scalarpol(zc, varn(pol));
    1624         3640 :   ttheta = RgX_shift_shallow(pol,-2);
    1625         3640 :   tttheta = RgX_shift_shallow(pol, -1);
    1626         3640 :   polprime = ZX_deriv(pol);
    1627         3640 :   q2 = Q_primpart(tracematrix(zc, b, pol));
    1628         3640 :   U = redquadric(b, pol, QXQ_div(zc, polprime, pol));
    1629         3640 :   q2 = qf_RgM_apply(q2, U);
    1630         3640 :   newb = RgV_RgM_mul(b, U);
    1631         3640 :   param = Q_primpart(qfparam(q2, qfsolve(qf_disc_fact(q2,gel(discF,2))), 1));
    1632         3640 :   param = RgM_to_RgXV_reverse(shallowtrans(param), 0);
    1633         3640 :   q1 = RgM_neg(tracematrix(QXQ_mul(zc, ttheta, pol), newb, pol));
    1634         3640 :   q1 = Q_remove_denom(qfeval(q1, param), &den);
    1635         3640 :   if (den) q1 = ZX_Z_mul(q1, den);
    1636         3640 :   if (!equali1(K)) q1 = RgX_Rg_mul(q1, K);
    1637         3640 :   QM = quartic_minim_all(q1, discF);
    1638         3640 :   q1 = gel(QM,1);
    1639         3640 :   zden = gmael(QM,2,1);
    1640         3640 :   Q = ZX_hyperellred(q1, &R);
    1641         3640 :   R = gmul(gmael(QM,2,2), R);
    1642         3640 :   if (DEBUGLEVEL>1) err_printf("  reduced quartic: Y^2 = %Ps\n", Q);
    1643         3640 :   xz = mkcol2(pol_x(0),gen_1);
    1644         3640 :   P = RgM_RgC_mul(R, xz); x = gel(P,1); y = gel(P,2);
    1645         3640 :   param = RgXV_homogenous_eval(param, x, y, 2);
    1646         3640 :   param = gmul(param, gdiv(den? mulii(K, den): K, zden));
    1647         3640 :   q0 = tracematrix(QXQ_mul(zc, tttheta, pol), newb, pol);
    1648         3640 :   x = gdiv(qfeval(q0, param), K);
    1649         3640 :   Q4 = gpowers(Q,4);
    1650         3640 :   y2m = gmul(RgX_homogenous_evalpow(pol, x, Q4, 4), Q);
    1651         3640 :   if (!issquareall(gdiv(y2m, K), &y))
    1652            0 :     pari_err_BUG("liftselmer_cover");
    1653         3640 :   y = gdiv(y, gel(Q4,2));
    1654         3640 :   P = mkvec2(gdiv(gmul(K,x),pol_xn(2,1)),gdiv(gmul(gsqr(K),y),pol_xn(3,1)));
    1655         3640 :   return mkvec2(Q,P);
    1656              : }
    1657              : 
    1658              : static GEN
    1659         3373 : liftselmer(GEN b, GEN expo, GEN sbase, GEN LS2, GEN pol, GEN discF, GEN K, long ntry, GEN *pt_Q)
    1660              : {
    1661         3373 :   pari_sp av = avma, av2;
    1662         3373 :   long t, lim = ntry * LIM1;
    1663              :   GEN ttheta, tttheta, z, polprime;
    1664              :   hashtable h;
    1665         3373 :   hash_init_GEN(&h, ntry, ZX_equal, 1);
    1666         3373 :   z = RgXQV_factorback(LS2, expo, pol);
    1667         3373 :   ttheta = RgX_shift_shallow(pol,-2);
    1668         3373 :   tttheta = RgX_shift_shallow(pol, -1);
    1669         3373 :   polprime = ZX_deriv(pol); av2 = avma;
    1670         4051 :   for (t = 1; t <= ntry; t++, set_avma(av2))
    1671              :   {
    1672              :     GEN P, Q, Qk, R, den, q0, q1, q2, xz, x, y, zz, zc, U, param, newb, zden, QM;
    1673              :     long idx;
    1674         3562 :     if (t == 1) zc = z;
    1675              :     else
    1676              :     {
    1677              :       GEN r;
    1678          189 :       do r = randS(sbase); while (degpol(QX_gcd(r, pol)));
    1679          189 :       zc = QXQ_mul(z, QXQ_sqr(r, pol), pol);
    1680              :     }
    1681         3562 :     q2 = Q_primpart(tracematrix(zc, b, pol));
    1682         3562 :     U = redquadric(b, pol, QXQ_div(zc, polprime, pol));
    1683         4240 :     if (lg(U) < 4) continue;
    1684         3562 :     q2 = qf_RgM_apply(q2, U);
    1685         3562 :     newb = RgV_RgM_mul(b, U);
    1686         3562 :     param = Q_primpart(qfparam(q2, qfsolve(qf_disc_fact(q2,gel(discF,2))), 1));
    1687         3562 :     param = RgM_to_RgXV_reverse(shallowtrans(param), 0);
    1688         3562 :     q1 = RgM_neg(tracematrix(QXQ_mul(zc, ttheta, pol), newb, pol));
    1689         3562 :     q1 = Q_remove_denom(qfeval(q1, param), &den);
    1690         3562 :     if (den) q1 = ZX_Z_mul(q1, den);
    1691         3562 :     if (!equali1(K)) q1 = RgX_Rg_mul(q1, K);
    1692         3562 :     QM = quartic_minim_all(q1, discF);
    1693         3562 :     q1 = gel(QM,1);
    1694         3562 :     zden = gmael(QM,2,1);
    1695         3562 :     Q = ZX_hyperellred(q1, &R);
    1696         3562 :     R = gmul(gmael(QM,2,2), R);
    1697         3562 :     if (pt_Q) *pt_Q = Q;
    1698         3562 :     Qk = shallowcopy(Q);
    1699         3562 :     (void) ZX_canon_neg(Qk);
    1700         3562 :     if (hash_haskey_long(&h, (void*)Qk, &idx)) continue;
    1701         3527 :     hash_insert_long(&h, (void*)Qk, 1); av2 = avma;
    1702         3527 :     if (DEBUGLEVEL>1) err_printf("  reduced quartic: Y^2 = %Ps\n", Q);
    1703              : 
    1704         3527 :     xz = projratpointxz(Q, lim, &zz);
    1705         3527 :     if (!xz)
    1706              :     {
    1707         1784 :       xz = projratpointxz2(Q, lim, ntry, &zz);
    1708         1784 :       if (!xz)
    1709              :       {
    1710         3527 :         if (pt_Q) return NULL; else continue;
    1711              :       }
    1712              :     }
    1713         1785 :     P = RgM_RgC_mul(R, xz); x = gel(P,1); y = gel(P,2);
    1714         1785 :     param = RgXV_homogenous_eval(param, x, y, 2);
    1715         1785 :     param = gmul(param, gdiv(den? mulii(K, den): K, gmul(zz, zden)));
    1716         1785 :     q0 = tracematrix(QXQ_mul(zc, tttheta, pol), newb, pol);
    1717         1785 :     x = gdiv(qfeval(q0, param), K);
    1718         1785 :     if (!issquareall(gdiv(poleval(pol, x), K), &y)) /* K y^2 = pol(x) */
    1719            0 :       pari_err_BUG("ellrank");
    1720         1785 :     P = mkvec2(x, y);
    1721         1785 :     if (DEBUGLEVEL) err_printf("Found point: %Ps\n", P);
    1722         1785 :     if (pt_Q) *pt_Q = gen_0;
    1723         1785 :     return gc_GEN(av, P);
    1724              :   }
    1725          489 :   return NULL;
    1726              : }
    1727              : 
    1728              : static void
    1729          784 : gtoset_inplace(GEN x)
    1730          784 : { gen_sort_inplace(x, (void*)&cmp_universal, cmp_nodata, NULL); }
    1731              : 
    1732              : /* FIXME: export */
    1733              : static void
    1734         8540 : setlgall(GEN x, long L)
    1735              : {
    1736         8540 :   long i, l = lg(x);
    1737       188972 :   for(i = 1; i < l; i++) setlg(gel(x,i), L);
    1738         8540 : }
    1739              : 
    1740              : static long
    1741         8540 : dim_selmer(GEN p, GEN pol, GEN K, GEN vnf, GEN LS2, GEN helpLS2,
    1742              :            GEN *selmer, GEN *LS2chars, GEN *helpchars)
    1743              : {
    1744              :   pari_sp av;
    1745         8540 :   long dim, k, lvnf = lg(vnf);
    1746         8540 :   GEN X, L, LS2image, helpimage, pp = cgetg(lvnf, t_VEC);
    1747         8540 :   int pis2 = equaliu(p, 2);
    1748              : 
    1749        24794 :   for (k = 1; k < lvnf; k++)
    1750              :   {
    1751        16254 :     GEN v, nf = gel(vnf,k), PR = idealprimedec(nf, p);
    1752        16254 :     long j, l = lg(PR);
    1753        16254 :     gel(pp, k) = v = cgetg(l, t_VEC);
    1754        36358 :     for (j = 1; j < l; j++)
    1755              :     {
    1756        20104 :       GEN pr = gel(PR,j);
    1757        20104 :       gel(v,j) = pis2? log_prk_init(nf, pr, 1 + 2 * pr_get_e(pr), NULL)
    1758        20104 :                      : zkmodprinit(nf, pr);
    1759              :     }
    1760              :   }
    1761         8540 :   LS2image = veckpmodsquares(LS2, vnf, pp);
    1762         8540 :   *LS2chars = vconcat(*LS2chars, LS2image);
    1763         8540 :   helpimage = veckpmodsquares(helpLS2, vnf, pp);
    1764         8540 :   *helpchars = vconcat(*helpchars, helpimage);
    1765         8540 :   av = avma;
    1766         8540 :   L = elllocalimage(pol, K, vnf, p, pp, helpimage);
    1767         8540 :   X = Flm_ker(shallowconcat(LS2image, L), 2); setlgall(X, lg(LS2image));
    1768              :   /* intersect(LS2image, locim) = LS2image.X */
    1769         8540 :   *selmer = Flm_intersect_i(*selmer, shallowconcat(Flm_ker(LS2image,2), X), 2);
    1770         8540 :   *selmer = gc_upto(av, Flm_image(*selmer, 2));
    1771         8540 :   dim = lg(*selmer)-1; return (dim == Flm_rank(helpimage,2))? dim: -1;
    1772              : }
    1773              : 
    1774              : /* Assume there are 3 real roots, if K>0 return the smallest, otherwise the largest */
    1775              : static long
    1776          490 : get_row(GEN vnf, GEN K)
    1777              : {
    1778          490 :   long k, sK = signe(K), n = lg(vnf)-1;
    1779              :   GEN R;
    1780          490 :   if (n == 1) return sK > 0? 1: 3;
    1781          294 :   if (n == 2)
    1782              :   {
    1783          105 :     GEN P = nf_get_pol(gel(vnf,2));
    1784          105 :     GEN z = negi(constant_coeff(nf_get_pol(gel(vnf,1))));
    1785          105 :     GEN y = poleval(P,z);
    1786          105 :     GEN b = gel(P,3), a = gel(P,4);
    1787          105 :     if (signe(y) != signe(a))
    1788              :       /* 1 is between 2 and 3 */
    1789            7 :       return sK > 0? 2: 3;
    1790           98 :     else if (cmpii(mulii(z,mulis(a,-2)), b) == signe(a))
    1791           21 :       return sK > 0? 1: 3;
    1792              :     else
    1793           77 :       return sK > 0? 2: 1;
    1794              :   }
    1795          189 :   R = cgetg(4, t_VEC);
    1796          756 :   for (k = 1; k <= 3; k++) gel(R, k) = gel(nf_get_roots(gel(vnf,k)), 1);
    1797          189 :   return sK > 0? vecindexmin(R): vecindexmax(R);
    1798              : }
    1799              : 
    1800              : static GEN
    1801         1337 : Z_factor_addprimes(GEN N, GEN P)
    1802              : {
    1803              :   GEN Pnew, Enew;
    1804         1337 :   GEN H = Z_smoothen(N, P, &Pnew, &Enew);
    1805         1337 :   return H ? shallowconcat(P, gel(Z_factor(H),1)): P;
    1806              : }
    1807              : 
    1808              : static GEN
    1809          889 : vbnf_discfactors(GEN vbnf)
    1810              : {
    1811          889 :   long n = lg(vbnf)-1;
    1812          889 :   switch (n)
    1813              :   {
    1814          441 :     case 1:
    1815              :     {
    1816          441 :       GEN nf = bnf_get_nf(gel(vbnf,1));
    1817          441 :       GEN L = shallowtrans(nf_get_ramified_primes(nf));
    1818          441 :       return Z_factor_addprimes(nf_get_index(nf), L);
    1819              :     }
    1820          259 :     case 2:
    1821              :     {
    1822          259 :       GEN nf = gel(vbnf,2), R;
    1823          259 :       GEN L = shallowtrans(nf_get_ramified_primes(nf));
    1824          259 :       L = Z_factor_addprimes(nf_get_index(nf), L);
    1825          259 :       R = absi(ZX_resultant(nf_get_pol(gel(vbnf,1)), nf_get_pol(nf)));
    1826          259 :       return Z_factor_addprimes(R, L);
    1827              :     }
    1828          189 :     case 3:
    1829              :     {
    1830          189 :       GEN P1 = nf_get_pol(gel(vbnf,1));
    1831          189 :       GEN P2 = nf_get_pol(gel(vbnf,2));
    1832          189 :       GEN P3 = nf_get_pol(gel(vbnf,3));
    1833          189 :       GEN L = gel(Z_factor(absi(ZX_resultant(P1,P2))),1);
    1834          189 :       L = Z_factor_addprimes(absi(ZX_resultant(P2,P3)),L);
    1835          189 :       return Z_factor_addprimes(absi(ZX_resultant(P3,P1)),L);
    1836              :     }
    1837            0 :     default: pari_err_BUG("vbnf_discfactors");
    1838              :       return NULL;/*LCOV_EXCL_LINE*/
    1839              :   }
    1840              : }
    1841              : 
    1842              : static GEN
    1843          889 : ell2selmer(GEN ell, GEN ell_K, GEN help, GEN K, GEN vbnf,
    1844              :            long effort, long flag, long prec)
    1845              : {
    1846              :   GEN KP, pol, vnf, vpol, vcrt, sbase, LS2, factLS2, sqrtLS2, signs;
    1847              :   GEN selmer, helpLS2, LS2chars, helpchars, newselmer, factdisc, badprimes;
    1848              :   GEN helplist, listpoints, p, covers, disc, discF;
    1849          889 :   long i, k, n, tors2, mwrank, dim, nbpoints, lfactdisc, t, u, sha2 = 0;
    1850              :   forprime_t T;
    1851              : 
    1852          889 :   pol = ell2pol(ell);
    1853          889 :   help = ellsearchtrivialpoints(ell_K, flag ? NULL:muluu(LIMTRIV,effort+1), help);
    1854          889 :   help = elltwistpoints(help, ginv(K)); /* points on K Y^2 = pol(X) */
    1855          889 :   n = lg(vbnf) - 1; tors2 = n - 1;
    1856          889 :   KP = gel(absZ_factor(K), 1);
    1857          889 :   disc = ZX_disc(pol);
    1858          889 :   factdisc = mkvec3(KP, mkcol(gen_2), vbnf_discfactors(vbnf));
    1859          889 :   factdisc = ZV_sort_uniq_shallow(shallowconcat1(factdisc));
    1860          889 :   discF = mkvec2(gmul(K,disc), factdisc);
    1861          889 :   badprimes = cgetg(n+1, t_VEC);
    1862          889 :   vnf = cgetg(n+1, t_VEC);
    1863          889 :   vpol = cgetg(n+1, t_VEC);
    1864          889 :   vcrt = cgetg(n+1, t_VEC);
    1865          889 :   LS2 = cgetg(n+1, t_VEC);
    1866          889 :   factLS2 = cgetg(n+1, t_VEC);
    1867          889 :   sqrtLS2 = cgetg(n+1, t_VEC);
    1868         2415 :   for (k = 1; k <= n; k++)
    1869              :   {
    1870         1526 :     GEN T, Tinv, id, f, sel, L, S, nf, NF = gel(vbnf,k);
    1871              :     long i, lk;
    1872         1526 :     nf = (n == 1)? bnf_get_nf(NF): NF;
    1873         1526 :     gel(vnf, k) = nf;
    1874         1526 :     gel(vpol, k) = T = nf_get_pol(nf);
    1875         1526 :     Tinv = RgX_div(pol, gel(vpol, k));
    1876         1526 :     gel(vcrt, k) = QX_mul(QXQ_inv(T, Tinv), T); /* 0 mod T, 1 mod pol/T */
    1877              : 
    1878         1526 :     id = idealadd(nf, nf_get_index(nf), ZX_deriv(T));
    1879         1526 :     f = nf_pV_to_prV(nf, KP); settyp(f, t_COL);
    1880         1526 :     f = mkvec3(gel(idealfactor_partial(nf, Tinv, factdisc), 1),
    1881         1526 :                gel(idealfactor(nf, id), 1), f);
    1882         1526 :     gel(badprimes, k) = S = gtoset(shallowconcat1(f));
    1883         1526 :     if (n == 1)
    1884              :     {
    1885          441 :       sel = bnfselmer(NF, S);
    1886          441 :       obj_free(NF); /* units */
    1887              :     }
    1888         1085 :     else if (degpol(T) == 1)
    1889          826 :       sel = bnfselmerQ(S);
    1890              :     else /* degree 2 */
    1891          259 :       sel = nf2selmer_quad(NF, S);
    1892         1526 :     gel(LS2, k) = L = gel(sel, 1); lk = lg(L);
    1893         1526 :     gel(factLS2, k) = gel(sel, 2);
    1894         1526 :     gel(sqrtLS2, k) = gel(sel, 3);
    1895        14952 :     for (i = 1; i < lk; i++)
    1896              :     {
    1897        13426 :       GEN z = gel(L,i); /* z mod T, 1 mod (pol/T) */
    1898        13426 :       gel(L,i) = grem(gadd(z, gmul(gsubsg(1,z), gel(vcrt,k))), pol);
    1899              :     }
    1900              :   }
    1901          889 :   sbase = shallowconcat1(vecsmallbasis(vnf, vcrt, pol));
    1902          889 :   if (DEBUGLEVEL>2) err_printf("   local badprimes = %Ps\n", badprimes);
    1903          889 :   LS2 = shallowconcat1(LS2);
    1904          889 :   helpLS2 = ellLS2image(pol, help, K, vpol, vcrt);
    1905          889 :   LS2chars = matselmersign(vnf, vpol, LS2);
    1906          889 :   helpchars = matselmersign(vnf, vpol, helpLS2);
    1907          889 :   signs = NULL;
    1908          889 :   if (signe(ell_get_disc(ell)) > 0) signs = Flm_row(LS2chars, get_row(vnf,K));
    1909          889 :   selmer = kernorm(LS2, factdisc, pol, signs);
    1910          889 :   forprime_init(&T, gen_2, NULL); lfactdisc = lg(factdisc); dim = -1;
    1911         7378 :   for (i = 1; dim < 0 && i < lfactdisc; i++)
    1912         6489 :     dim = dim_selmer(gel(factdisc,i), pol, K, vnf, LS2, helpLS2,
    1913              :                      &selmer,&LS2chars,&helpchars);
    1914         2940 :   while (dim < 0 && Flm_rank(Flm_mul(LS2chars, selmer, 2), 2) < lg(selmer)-1)
    1915              :   {
    1916         2513 :     while ((p = forprime_next(&T)) && ZV_search(factdisc, p));
    1917         2051 :     dim = dim_selmer(p, pol, K, vnf, LS2, helpLS2,
    1918              :                      &selmer,&LS2chars,&helpchars);
    1919              :   }
    1920              :   /* transpose the matrix to get the solution that contains 1..tors2*/
    1921          889 :   helplist = gel(Flm_indexrank(Flm_transpose(helpchars),2), 1);
    1922          889 :   help = shallowextract(help, helplist);
    1923          889 :   helpchars = shallowextract(helpchars, helplist);
    1924          889 :   dim = lg(selmer)-1;
    1925          889 :   if (DEBUGLEVEL) err_printf("Selmer rank: %ld\n", dim);
    1926          889 :   newselmer = Flm_invimage(Flm_mul(LS2chars, selmer, 2), helpchars, 2);
    1927          889 :   if (!newselmer) pari_err_BUG("ellrank, wrong nfeltsign");
    1928          889 :   nbpoints = lg(help) - 1;
    1929          889 :   if (flag==1)
    1930              :   {
    1931           49 :     GEN u = nbpoints? Flm_mul(selmer,Flm_suppl(newselmer,2), 2): selmer;
    1932           49 :     long l = lg(u);
    1933           49 :     GEN z = cgetg(l, t_VEC);
    1934          154 :     for (i = 1; i < l; i++) gel(z,i) = RgXQV_factorback(LS2, gel(u,i), pol);
    1935           49 :     return mkvec2(mkvec3(vnf,sbase,pol), z);
    1936              :   }
    1937          840 :   else if (flag==2)
    1938              :   {
    1939           56 :     GEN u = nbpoints ? Flm_mul(selmer,Flm_suppl(newselmer,2), 2): selmer;
    1940           56 :     long l = lg(u);
    1941           56 :     GEN P = cgetg(l, t_VEC), b;
    1942          147 :     for (i = 1; i < l; i++)
    1943              :     {
    1944           91 :       b = liftselmerinit(gel(u,i), vnf, sqrtLS2, factLS2, badprimes, vcrt, pol);
    1945           91 :       gel(P,i) = liftselmer_cover(b, gel(u,i), LS2, pol, discF, K);
    1946              :     }
    1947           56 :     return P;
    1948              :   }
    1949          784 :   listpoints = vec_lengthen(help, dim); /* points on ell */
    1950          784 :   covers = zerovec(dim);
    1951         5789 :   for (i=1; i <= dim; i++)
    1952              :   {
    1953         5005 :     GEN b, P, expo, vec = vecsmall_ei(dim, i);
    1954         5005 :     if (Flm_Flc_invimage(newselmer, vec, 2)) continue;
    1955         2555 :     expo = Flm_Flc_mul(selmer, vec, 2);
    1956         2555 :     b = liftselmerinit(expo, vnf, sqrtLS2, factLS2, badprimes, vcrt, pol);
    1957         2555 :     P = liftselmer(b, expo, sbase, LS2, pol, discF, K, 1, &gel(covers,i));
    1958         2555 :     if (P)
    1959              :     {
    1960         1456 :       gel(listpoints, ++nbpoints) = P; /* new point on ell */
    1961         1456 :       gel(newselmer, nbpoints) = vec;
    1962         1456 :       setlg(newselmer, nbpoints+1);
    1963              :     }
    1964              :   }
    1965          784 :   if (nbpoints < dim)
    1966              :   {
    1967              :     long i, j;
    1968          315 :     GEN M = cgetg(dim+1, t_MAT), selker;
    1969          315 :     GEN D = mulii(muliu(absi(disc), 27*4096), powiu(K,6));
    1970          315 :     GEN FD = ZV_sort_uniq_shallow(shallowconcat1(mkvec2(mkcol3s(3,5,7), factdisc)));
    1971              : 
    1972         1974 :     for (i = 1; i <= dim; i++) gel(M,i) = cgetg(dim+1, t_COL);
    1973         1974 :     for (i = 1; i <= dim; i++)
    1974         9415 :       for (j = 1; j <= i; j++)
    1975              :       {
    1976              :         GEN Q;
    1977         7756 :         if (isintzero(gel(covers,i)))
    1978         3150 :           Q = gen_0;
    1979         4606 :         else if (i==j)
    1980         1057 :           Q = quartic_findunit(D, gel(covers,i));
    1981              :         else
    1982              :         {
    1983         3549 :           GEN e = Flv_add(gel(selmer,i), gel(selmer,j), 2);
    1984         3549 :           GEN b = liftselmerinit(e, vnf, sqrtLS2, factLS2, badprimes, vcrt, pol);
    1985         3549 :           Q = quartic_findunit(D, gel(liftselmer_cover(b, e, LS2, pol, discF, K),1));
    1986              :         }
    1987         7756 :         gmael(M,j,i) = gmael(M,i,j) = Q;
    1988              :       }
    1989          315 :     selker = F2m_to_Flm(F2m_ker(matcassels(FD, M)));
    1990          315 :     sha2 = dim - (lg(selker)-1);
    1991          315 :     dim = lg(selker)-1;
    1992         1133 :     for (t=1, u=1; nbpoints < dim && effort > 0; t++)
    1993              :     {
    1994          818 :       pari_sp btop = avma;
    1995              :       GEN expo, b, P, vec;
    1996         1091 :       do vec = Flm_Flc_mul(selker,random_Flv(dim, 2), 2);
    1997         1091 :       while (zv_equal0(vec) || Flm_Flc_invimage(newselmer, vec, 2));
    1998          818 :       expo = Flm_Flc_mul(selmer, vec, 2);
    1999          818 :       b = liftselmerinit(expo, vnf, sqrtLS2, factLS2, badprimes, vcrt, pol);
    2000          818 :       P = liftselmer(b, expo, sbase, LS2, pol, discF, K, u, NULL);
    2001          818 :       if (P)
    2002              :       {
    2003          329 :         gel(listpoints, ++nbpoints) = P;
    2004          329 :         gel(newselmer, nbpoints) = vec;
    2005          329 :         setlg(newselmer, nbpoints+1);
    2006          489 :       } else set_avma(btop);
    2007          818 :       if (t == dim) { t = 0; u++; effort--; }
    2008              :     }
    2009              :   }
    2010          784 :   setlg(listpoints, nbpoints+1);
    2011          784 :   mwrank = nbpoints - tors2;
    2012          784 :   if (odd(dim - nbpoints)) mwrank++;
    2013          784 :   listpoints = vecslice(listpoints,tors2+1, nbpoints);
    2014          784 :   listpoints = elltwistpoints(listpoints, K);
    2015          784 :   listpoints = vecellabs(ellQ_genreduce(ell_K, listpoints, NULL, prec));
    2016          784 :   gtoset_inplace(listpoints);
    2017          784 :   return mkvec4(utoi(mwrank), utoi(dim-tors2), utoi(sha2), listpoints);
    2018              : }
    2019              : 
    2020              : GEN
    2021           49 : ell2selmer_basis(GEN ell, GEN *cb, long prec)
    2022              : {
    2023           49 :   GEN E = ellminimalbmodel(ell, cb);
    2024           49 :   GEN S = ell2selmer(E, E, NULL, gen_1, makevbnf(E, prec), 0, 1, prec);
    2025           49 :   obj_free(E); return S;
    2026              : }
    2027              : 
    2028              : static void
    2029           98 : ellrank_get_nudur(GEN E, GEN F, GEN *nu, GEN *du, GEN *r)
    2030              : {
    2031           98 :   GEN ea2 = ell_get_a2(E), ea2t = ell_get_a2(F);
    2032           98 :   GEN ec4 = ell_get_c4(E), ec4t = ell_get_c4(F);
    2033           98 :   GEN ec6 = ell_get_c6(E), ec6t = ell_get_c6(F);
    2034              :   GEN N, D, d;
    2035           98 :   if (signe(ec4)==0)
    2036              :   {
    2037           21 :     if (!Z_ispowerall(mulii(ec6,sqri(ec6t)),3,&N))
    2038            7 :       pari_err_TYPE("ellrank",F);
    2039           14 :     D = ec6t;
    2040              :   }
    2041           77 :   else if (signe(ec6)==0)
    2042              :   {
    2043           21 :     if (!Z_issquareall(mulii(ec4,ec4t),&N))
    2044            7 :       pari_err_TYPE("ellrank",F);
    2045           14 :     D = ec4t;
    2046              :   }
    2047              :   else
    2048              :   {
    2049           56 :     GEN d46 = mulii(gcdii(ec4,ec4t),gcdii(ec6,ec6t));
    2050           56 :     N = diviiexact(mulii(ec6,ec4t),d46);
    2051           56 :     D = diviiexact(mulii(ec6t,ec4),d46);
    2052              :   }
    2053           84 :   d = gcdii(N, D);
    2054           84 :   *nu = diviiexact(N, d); *du = diviiexact(D, d);
    2055           84 :   *r  = diviuexact(subii(mulii(*nu,ea2t),mulii(*du,ea2)),3);
    2056           84 : }
    2057              : 
    2058              : static GEN
    2059          882 : ellrank_flag(GEN e, long effort, GEN help, long flag, long prec)
    2060              : {
    2061          882 :   pari_sp ltop = avma;
    2062              :   GEN urst, v, vbnf, eK;
    2063          882 :   GEN et = NULL, K = gen_1, r = NULL, urstK = NULL;
    2064          882 :   long newell = 0;
    2065              : 
    2066          882 :   if (lg(e)==3 && typ(e)==t_VEC) { et = gel(e,2); e = gel(e,1); }
    2067          882 :   if (lg(e)==4 && typ(e)==t_VEC)
    2068              :   {
    2069          126 :     vbnf = gel(e,3); urst = gel(e,2);
    2070          126 :     e = gel(e,1); checkell_Q(e);
    2071          126 :     if (!ell_is_integral(e)) pari_err_TYPE("ellrank [nonintegral model]",e);
    2072          119 :     if (signe(ell_get_a1(e))) pari_err_TYPE("ellrank [a1 != 0]", e);
    2073          112 :     if (signe(ell_get_a3(e))) pari_err_TYPE("ell2rank [a3 != 0]", e);
    2074              :   }
    2075              :   else
    2076              :   {
    2077          756 :     checkell_Q(e);
    2078          756 :     e = ellminimalbmodel(e, &urst);
    2079          756 :     newell = 1;
    2080          756 :     vbnf = makevbnf(e, prec);
    2081              :   }
    2082          861 :   if (et)
    2083              :   {
    2084              :     GEN nu, du;
    2085          105 :     checkell_Q(et);
    2086          105 :     if (!gequal(ell_get_j(et),ell_get_j(e))) pari_err_TYPE("ellrank",et);
    2087           98 :     et = ellminimalbmodel(et, &urst);
    2088           98 :     ellrank_get_nudur(e, et, &nu, &du, &r);
    2089           84 :     K = mulii(nu, du);
    2090           84 :     urstK =  mkvec4(ginv(nu), gdiv(negi(r),sqri(nu)), gen_0, gen_0);
    2091           84 :     urst = urst ? ellchangecompose(e,urst,urstK): urstK;
    2092              :   }
    2093          840 :   if (help && urst)
    2094           42 :     help = ellchangepoint(help, urst);
    2095          840 :   eK = elltwistequation(e, K);
    2096              :   /* help is a vector of rational points [x,y] satisfying K y^2 = pol(x) */
    2097              :   /* [Kx, K^2y] is on eK: Y^2 = K^3 pol(X/K)  */
    2098          840 :   if (help) check_oncurve(eK, help);
    2099          840 :   v = ell2selmer(e, eK, help, K, vbnf, effort, flag, prec);
    2100          840 :   if (flag==0)
    2101              :   {
    2102          784 :     if (urst) gel(v,4) = ellchangepointinv(gel(v,4), urst);
    2103              :   }
    2104              :   else
    2105              :   {
    2106           56 :     long i, l = lg(v);
    2107          147 :     for (i = 1; i < l; i++)
    2108           91 :       if (urst) gmael(v,i,2) = ellchangepointinv(gmael(v,i,2), urst);
    2109              :   }
    2110          840 :   if (newell) obj_free(e);
    2111          840 :   if (eK != e) obj_free(eK);
    2112          840 :   return gc_GEN(ltop, v);
    2113              : }
    2114              : 
    2115              : GEN
    2116          826 : ellrank(GEN e, long effort, GEN help, long prec)
    2117              : {
    2118          826 :   return ellrank_flag(e, effort, help, 0, prec);
    2119              : }
    2120              : 
    2121              : GEN
    2122           56 : ell2cover(GEN ell, long prec)
    2123              : {
    2124           56 :   return ellrank_flag(ell, 0, NULL, 2, prec);
    2125              : }
        

Generated by: LCOV version 2.0-1