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 - Qfb.c (source / functions) Coverage Total Hit
Test: PARI/GP v2.18.1 lcov report (development 31042-0fbe168e69) Lines: 91.3 % 1264 1154
Test Date: 2026-07-23 17:04:59 Functions: 93.2 % 146 136
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Copyright (C) 2000-2005  The PARI group.
       2              : 
       3              : This file is part of the PARI/GP package.
       4              : 
       5              : PARI/GP is free software; you can redistribute it and/or modify it under the
       6              : terms of the GNU General Public License as published by the Free Software
       7              : Foundation; either version 2 of the License, or (at your option) any later
       8              : version. It is distributed in the hope that it will be useful, but WITHOUT
       9              : ANY WARRANTY WHATSOEVER.
      10              : 
      11              : Check the License for details. You should have received a copy of it, along
      12              : with the package; see the file 'COPYING'. If not, write to the Free Software
      13              : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14              : #include "pari.h"
      15              : #include "paripriv.h"
      16              : /*******************************************************************/
      17              : /*                                                                 */
      18              : /*         QUADRATIC POLYNOMIAL ASSOCIATED TO A DISCRIMINANT       */
      19              : /*                                                                 */
      20              : /*******************************************************************/
      21              : 
      22              : void
      23       203877 : check_quaddisc(GEN x, long *s, long *pr, const char *f)
      24              : {
      25              :   long r;
      26       203877 :   if (typ(x) != t_INT) pari_err_TYPE(f,x);
      27       203863 :   *s = signe(x);
      28       203863 :   if (Z_issquare(x)) pari_err_DOMAIN(f,"issquare(disc)","=", gen_1,x);
      29       203863 :   r = mod4(x); if (*s < 0 && r) r = 4 - r;
      30       203863 :   if (r > 1) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);
      31       203849 :   if (pr) *pr = r;
      32       203849 : }
      33              : void
      34         6916 : check_quaddisc_real(GEN x, long *r, const char *f)
      35              : {
      36         6916 :   long sx; check_quaddisc(x, &sx, r, f);
      37         6916 :   if (sx < 0) pari_err_DOMAIN(f, "disc","<",gen_0,x);
      38         6916 : }
      39              : void
      40         2184 : check_quaddisc_imag(GEN x, long *r, const char *f)
      41              : {
      42         2184 :   long sx; check_quaddisc(x, &sx, r, f);
      43         2177 :   if (sx > 0) pari_err_DOMAIN(f, "disc",">",gen_0,x);
      44         2177 : }
      45              : 
      46              : /* X^2 + b X + c is the canonical quadratic t_POL of discriminant D.
      47              :  * Dodd is nonzero iff D is odd */
      48              : static void
      49      1021000 : quadpoly_bc(GEN D, long Dodd, GEN *b, GEN *c)
      50              : {
      51      1021000 :   if (Dodd)
      52              :   {
      53       921964 :     pari_sp av = avma;
      54       921964 :     *b = gen_m1;
      55       921964 :     *c = gc_INT(av, shifti(subui(1,D), -2));
      56              :   }
      57              :   else
      58              :   {
      59        99036 :     *b = gen_0;
      60        99036 :     *c = shifti(D,-2); togglesign(*c);
      61              :   }
      62      1021000 : }
      63              : /* X^2 - X - (D-1)/4 or X^2 - D/4 */
      64              : static GEN
      65       245665 : quadpoly_ii(GEN D, long Dmod4)
      66              : {
      67       245665 :   GEN b, c, y = cgetg(5,t_POL);
      68       245665 :   y[1] = evalsigne(1) | evalvarn(0);
      69       245665 :   quadpoly_bc(D, Dmod4, &b,&c);
      70       245665 :   gel(y,2) = c;
      71       245665 :   gel(y,3) = b;
      72       245665 :   gel(y,4) = gen_1; return y;
      73              : }
      74              : GEN
      75         2205 : quadpoly(GEN D)
      76              : {
      77              :   long s, Dmod4;
      78         2205 :   check_quaddisc(D, &s, &Dmod4, "quadpoly");
      79         2198 :   return quadpoly_ii(D, Dmod4);
      80              : }
      81              : GEN /* no checks */
      82       243467 : quadpoly_i(GEN D) { return quadpoly_ii(D, Mod4(D)); }
      83              : 
      84              : GEN
      85         1071 : quadpoly0(GEN x, long v)
      86              : {
      87         1071 :   GEN T = quadpoly(x);
      88         1064 :   if (v > 0) setvarn(T, v);
      89         1064 :   return T;
      90              : }
      91              : 
      92              : GEN
      93            0 : quadgen(GEN x)
      94            0 : { retmkquad(quadpoly(x), gen_0, gen_1); }
      95              : 
      96              : GEN
      97          623 : quadgen0(GEN x, long v)
      98              : {
      99          623 :   if (v==-1) v = fetch_user_var("w");
     100          623 :   retmkquad(quadpoly0(x, v), gen_0, gen_1);
     101              : }
     102              : 
     103              : /***********************************************************************/
     104              : /**                                                                   **/
     105              : /**                      BINARY QUADRATIC FORMS                       **/
     106              : /**                                                                   **/
     107              : /***********************************************************************/
     108              : static int
     109       814212 : is_qfi(GEN q) { return typ(q)==t_QFB && qfb_is_qfi(q); }
     110              : 
     111              : static GEN
     112      2131138 : check_qfbext(const char *fun, GEN x)
     113              : {
     114      2131138 :   long t = typ(x);
     115      2131138 :   if (t == t_QFB) return x;
     116          196 :   if (t == t_VEC && lg(x)==3)
     117              :   {
     118          196 :     GEN q = gel(x,1);
     119          196 :     if (!is_qfi(q) && typ(gel(x,2))==t_REAL) return q;
     120              :   }
     121            0 :   pari_err_TYPE(fun, x);
     122              :   return NULL;/* LCOV_EXCL_LINE */
     123              : }
     124              : 
     125              : static GEN
     126       142504 : qfb3(GEN x, GEN y, GEN z)
     127       142504 : { retmkqfb(icopy(x), icopy(y), icopy(z), qfb_disc3(x,y,z)); }
     128              : 
     129              : static int
     130     23783634 : qfb_equal(GEN x, GEN y)
     131              : {
     132     23783634 :   return equalii(gel(x,1),gel(y,1))
     133      1592913 :       && equalii(gel(x,2),gel(y,2))
     134     25376547 :       && equalii(gel(x,3),gel(y,3));
     135              : }
     136              : 
     137              : /* valid for t_QFB, qfr3, qfr5; shallow */
     138              : static GEN
     139      1042687 : qfb_inv(GEN x) {
     140      1042687 :   GEN z = shallowcopy(x);
     141      1042687 :   gel(z,2) = negi(gel(z,2));
     142      1042687 :   return z;
     143              : }
     144              : /* valid for t_QFB, GC clean */
     145              : static GEN
     146            7 : qfbinv(GEN x)
     147            7 : { retmkqfb(icopy(gel(x,1)),negi(gel(x,2)),icopy(gel(x,3)), icopy(gel(x,4))); }
     148              : 
     149              : GEN
     150        77259 : Qfb0(GEN a, GEN b, GEN c)
     151              : {
     152              :   GEN q, D;
     153        77259 :   if (!b)
     154              :   {
     155           49 :     if (c) pari_err_TYPE("Qfb",c);
     156           42 :     if (typ(a) == t_VEC && lg(a) == 4)
     157           21 :     { b = gel(a,2); c = gel(a,3); a = gel(a,1); }
     158           21 :     else if (typ(a) == t_POL && degpol(a) == 2)
     159            7 :     { b = gel(a,3); c = gel(a,2); a = gel(a,4); }
     160           14 :     else if (typ(a) == t_MAT && lg(a)==3 && lgcols(a)==3)
     161              :     {
     162            7 :       b = gadd(gcoeff(a,2,1), gcoeff(a,1,2));
     163            7 :       c = gcoeff(a,2,2); a = gcoeff(a,1,1);
     164              :     }
     165              :     else
     166            7 :       pari_err_TYPE("Qfb",a);
     167              :   }
     168        77210 :   else if (!c)
     169            7 :     pari_err_TYPE("Qfb",b);
     170        77238 :   if (typ(a)!=t_INT) pari_err_TYPE("Qfb",a);
     171        77231 :   if (typ(b)!=t_INT) pari_err_TYPE("Qfb",b);
     172        77231 :   if (typ(c)!=t_INT) pari_err_TYPE("Qfb",c);
     173        77231 :   q = qfb3(a, b, c); D = qfb_disc(q);
     174        77231 :   if (signe(D) < 0)
     175        42392 :   { if (signe(a) < 0) pari_err_IMPL("negative definite t_QFB"); }
     176        34839 :   else if (Z_issquare(D)) pari_err_DOMAIN("Qfb","issquare(disc)","=", gen_1,q);
     177        77224 :   return q;
     178              : }
     179              : 
     180              : /***********************************************************************/
     181              : /**                                                                   **/
     182              : /**                         Reduction                                 **/
     183              : /**                                                                   **/
     184              : /***********************************************************************/
     185              : 
     186              : /* assume a > 0. Write b = q*2a + r, with -a < r <= a */
     187              : static GEN
     188     16933943 : dvmdii_round(GEN b, GEN a, GEN *r)
     189              : {
     190     16933943 :   GEN a2 = shifti(a, 1), q = dvmdii(b, a2, r);
     191     16933943 :   if (signe(b) >= 0) {
     192      9305229 :     if (abscmpii(*r, a) > 0) { q = addiu(q, 1); *r = subii(*r, a2); }
     193              :   } else { /* r <= 0 */
     194      7628714 :     if (abscmpii(*r, a) >= 0){ q = subiu(q, 1); *r = addii(*r, a2); }
     195              :   }
     196     16933943 :   return q;
     197              : }
     198              : /* Assume 0 < a <= LONG_MAX. Ensure no overflow */
     199              : static long
     200    120856302 : dvmdsu_round(long b, ulong a, long *r)
     201              : {
     202    120856302 :   ulong a2 = a << 1, q, ub, ur;
     203    120856302 :   if (b >= 0) {
     204     76862111 :     ub = b;
     205     76862111 :     q = ub / a2;
     206     76862111 :     ur = ub % a2;
     207     76862111 :     if (ur > a) { ur -= a; q++; *r = (long)ur; *r -= (long)a; }
     208     26973712 :     else *r = (long)ur;
     209     76862111 :     return (long)q;
     210              :   } else { /* r <= 0 */
     211     43994191 :     ub = (ulong)-b; /* |b| */
     212     43994191 :     q = ub / a2;
     213     43994191 :     ur = ub % a2;
     214     43994191 :     if (ur >= a) { ur -= a; q++; *r = (long)ur; *r = (long)a - *r; }
     215     24199455 :     else *r = -(long)ur;
     216     43994191 :     return -(long)q;
     217              :   }
     218              : }
     219              : /* reduce b mod 2*a. Update b,c */
     220              : static void
     221      2776818 : REDB(GEN a, GEN *b, GEN *c)
     222              : {
     223      2776818 :   GEN r, q = dvmdii_round(*b, a, &r);
     224      2776818 :   if (!signe(q)) return;
     225      2707359 :   *c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));
     226      2707359 :   *b = r;
     227              : }
     228              : /* Assume a > 0. Reduce b mod 2*a. Update b,c */
     229              : static void
     230    120856302 : sREDB(ulong a, long *b, ulong *c)
     231              : {
     232              :   long r, q;
     233              :   ulong uz;
     234    131244105 :   if (a > LONG_MAX) return; /* b already reduced */
     235    120856302 :   q = dvmdsu_round(*b, a, &r);
     236    120856302 :   if (q == 0) return;
     237              :   /* Final (a,r,c2) satisfies |r| <= |b| hence c2 <= c, c2 = c - q*z,
     238              :    * where z = (b+r) / 2, representable as long, has the same sign as q. */
     239    110468499 :   if (*b < 0)
     240              :   { /* uz = -z >= 0, q < 0 */
     241     38740464 :     if (r >= 0) /* different signs=>no overflow, exact division */
     242     19864556 :       uz = (ulong)-((*b + r)>>1);
     243              :     else
     244              :     {
     245     18875908 :       ulong ub = (ulong)-*b, ur = (ulong)-r;
     246     18875908 :       uz = (ub + ur) >> 1;
     247              :     }
     248     38740464 :     *c -= (-q) * uz; /* c -= qz */
     249              :   }
     250              :   else
     251              :   { /* uz = z >= 0, q > 0 */
     252     71728035 :     if (r <= 0)
     253     49973388 :       uz = (*b + r)>>1;
     254              :     else
     255              :     {
     256     21754647 :       ulong ub = (ulong)*b, ur = (ulong)r;
     257     21754647 :       uz = ((ub + ur) >> 1);
     258              :     }
     259     71728035 :     *c -= q * uz; /* c -= qz */
     260              :   }
     261    110468499 :   *b = r;
     262              : }
     263              : static void
     264     14157125 : REDBU(GEN a, GEN *b, GEN *c, GEN u1, GEN *u2)
     265              : { /* REDB(a,b,c) */
     266     14157125 :   GEN r, q = dvmdii_round(*b, a, &r);
     267     14157125 :   *c = subii(*c, mulii(q, shifti(addii(*b, r),-1)));
     268     14157125 :   *b = r;
     269     14157125 :   *u2 = subii(*u2, mulii(q, u1));
     270     14157125 : }
     271              : 
     272              : /* q t_QFB, return reduced representative and set base change U in Sl2(Z) */
     273              : static GEN
     274      6784631 : qfi_redsl2_basecase(GEN q, GEN *U)
     275              : {
     276      6784631 :   pari_sp av = avma;
     277              :   GEN z, u1,u2,v1,v2,Q;
     278      6784631 :   GEN a = gel(q,1), b = gel(q,2), c = gel(q,3);
     279              :   long cmp;
     280      6784631 :   u1 = gen_1; u2 = gen_0;
     281      6784631 :   cmp = abscmpii(a, b);
     282      6784631 :   if (cmp < 0)
     283      2198892 :     REDBU(a,&b,&c, u1,&u2);
     284      4585739 :   else if (cmp == 0 && signe(b) < 0)
     285              :   { /* b = -a */
     286        11964 :     b = negi(b);
     287        11964 :     u2 = gen_1;
     288              :   }
     289              :   for(;;)
     290              :   {
     291     18742864 :     cmp = abscmpii(a, c); if (cmp <= 0) break;
     292     11958233 :     swap(a,c); b = negi(b);
     293     11958233 :     z = u1; u1 = u2; u2 = negi(z);
     294     11958233 :     REDBU(a,&b,&c, u1,&u2);
     295     11958233 :     if (gc_needed(av, 1)) {
     296            7 :       if (DEBUGMEM>1) pari_warn(warnmem, "qfbredsl2");
     297            7 :       (void)gc_all(av, 5, &a,&b,&c, &u1,&u2);
     298              :     }
     299              :   }
     300      6784631 :   if (cmp == 0 && signe(b) < 0)
     301              :   {
     302        17739 :     b = negi(b);
     303        17739 :     z = u1; u1 = u2; u2 = negi(z);
     304              :   }
     305              :   /* Let q = (A,B,C). q o [u1,u2; v1,v2] = Q implies
     306              :    * [v1,v2] = (1/C) [(b-B)/2 u1 - a u2, c u1 - (b+B)/2 u2] */
     307      6784631 :   z = shifti(subii(b, gel(q,2)), -1);
     308      6784631 :   v1 = subii(mulii(z, u1), mulii(a, u2)); v1 = diviiexact(v1, gel(q,3));
     309      6784631 :   z = subii(z, b);
     310      6784631 :   v2 = addii(mulii(z, u2), mulii(c, u1)); v2 = diviiexact(v2, gel(q,3));
     311      6784631 :   *U = mkmat2(mkcol2(u1,v1), mkcol2(u2,v2));
     312      6784631 :   Q = mkqfb(a,b,c,gel(q,4));
     313      6784631 :   return gc_all(av, 2, &Q, U);
     314              : }
     315              : 
     316              : static GEN
     317      1137401 : setq_b0(ulong a, ulong c, GEN D)
     318      1137401 : { retmkqfb(utoipos(a), gen_0, utoipos(c), icopy(D)); }
     319              : /* assume |sb| = 1 */
     320              : static GEN
     321     92687076 : setq(ulong a, ulong b, ulong c, long sb, GEN D)
     322     92687076 : { retmkqfb(utoipos(a), sb==1? utoipos(b): utoineg(b), utoipos(c), icopy(D)); }
     323              : /* 0 < a, c < 2^BIL, b = 0 */
     324              : static GEN
     325       982592 : qfi_red_1_b0(ulong a, ulong c, GEN D)
     326       982592 : { return (a <= c)? setq_b0(a, c, D): setq_b0(c, a, D); }
     327              : 
     328              : /* 0 < a, c < 2^BIL: single word affair */
     329              : static GEN
     330     93964981 : qfi_red_1(pari_sp av, GEN a, GEN b, GEN c, GEN D)
     331              : {
     332              :   ulong ua, ub, uc;
     333              :   long sb;
     334              :   for(;;)
     335       140504 :   { /* at most twice */
     336     93964981 :     long lb = lgefint(b); /* <= 3 after first loop */
     337     93964981 :     if (lb == 2) return qfi_red_1_b0(a[2],c[2], D);
     338     92982389 :     if (lb == 3 && uel(b,2) <= (ulong)LONG_MAX) break;
     339       140504 :     REDB(a,&b,&c);
     340       140504 :     if (uel(a,2) <= uel(c,2))
     341              :     { /* lg(b) <= 3 but may be too large for itos */
     342            0 :       long s = signe(b);
     343            0 :       set_avma(av);
     344            0 :       if (!s) return qfi_red_1_b0(a[2], c[2], D);
     345            0 :       if (a[2] == c[2]) s = 1;
     346            0 :       return setq(a[2], b[2], c[2], s, D);
     347              :     }
     348       140504 :     swap(a,c); b = negi(b);
     349              :   }
     350              :   /* b != 0 */
     351     92841885 :   set_avma(av);
     352     92841885 :   ua = a[2];
     353     92841885 :   ub = sb = b[2]; if (signe(b) < 0) sb = -sb;
     354     92841885 :   uc = c[2];
     355     92841885 :   if (ua < ub)
     356     35429936 :     sREDB(ua, &sb, &uc);
     357     57411949 :   else if (ua == ub && sb < 0) sb = (long)ub;
     358    178268251 :   while(ua > uc)
     359              :   {
     360     85426366 :     lswap(ua,uc); sb = -sb;
     361     85426366 :     sREDB(ua, &sb, &uc);
     362              :   }
     363     92841885 :   if (!sb) return setq_b0(ua, uc, D);
     364              :   else
     365              :   {
     366     92687076 :     long s = 1;
     367     92687076 :     if (sb < 0)
     368              :     {
     369     36763503 :       sb = -sb;
     370     36763503 :       if (ua != uc) s = -1;
     371              :     }
     372     92687076 :     return setq(ua, sb, uc, s, D);
     373              :   }
     374              : }
     375              : 
     376              : static GEN
     377            7 : qfi_rho(GEN x)
     378              : {
     379            7 :   pari_sp av = avma;
     380            7 :   GEN a = gel(x,1), b = gel(x,2), c = gel(x,3);
     381            7 :   int fl = abscmpii(a, c);
     382            7 :   if (fl <= 0)
     383              :   {
     384            7 :     int fg = abscmpii(a, b);
     385            7 :     if (fg >= 0)
     386              :     {
     387            7 :       x = gcopy(x);
     388            7 :       if ((!fl || !fg) && signe(gel(x,2)) < 0) setsigne(gel(x,2), 1);
     389            7 :       return x;
     390              :     }
     391              :   }
     392            0 :   swap(a,c); b = negi(b);
     393            0 :   REDB(a, &b, &c);
     394            0 :   return gc_GEN(av, mkqfb(a,b,c, qfb_disc(x)));
     395              : }
     396              : 
     397              : /* qfr3 / qfr5 */
     398              : 
     399              : /* t_QFB are unusable: D, sqrtD, isqrtD are recomputed all the time and the
     400              :  * logarithmic Shanks's distance is costly and hard to control.
     401              :  * qfr3 / qfr5 routines take a container of t_INTs (e.g a t_VEC) as argument,
     402              :  * at least 3 (resp. 5) components [it is a feature that they do not check the
     403              :  * precise type or length of the input]. They return a vector of length 3
     404              :  * (resp. 5). A qfr3 [a,b,c] contains the form coeffs, in a qfr5 [a,b,c, e,d]
     405              :  * the t_INT e is a binary exponent, d a t_REAL, coding the distance in
     406              :  * multiplicative form: the true distance is obtained from qfr5_dist.
     407              :  * All other qfr routines are obsolete (inefficient) wrappers */
     408              : 
     409              : /* static functions are not stack-clean. Unless mentionned otherwise, public
     410              :  * functions are. */
     411              : 
     412              : #define EMAX 22
     413              : static void
     414     10217928 : fix_expo(GEN x)
     415              : {
     416     10217928 :   if (expo(gel(x,5)) >= (1L << EMAX)) {
     417            0 :     gel(x,4) = addiu(gel(x,4), 1);
     418            0 :     shiftr_inplace(gel(x,5), - (1L << EMAX));
     419              :   }
     420     10217928 : }
     421              : 
     422              : /* (1/2) log (|d| * 2^{e * 2^EMAX}). Not stack clean if e != 0 */
     423              : GEN
     424       184688 : qfr5_dist(GEN e, GEN d, long prec)
     425              : {
     426       184688 :   GEN t = logr_abs(d);
     427       184688 :   if (signe(e)) {
     428            0 :     GEN u = mulir(e, mplog2(prec));
     429            0 :     shiftr_inplace(u, EMAX); t = addrr(t, u);
     430              :   }
     431       184688 :   shiftr_inplace(t, -1); return t;
     432              : }
     433              : 
     434              : /* cf rho_get_BC, with contfrac normalizations: applies rho^(-1) and
     435              :  * make sure a > 0 */
     436              : static GEN
     437          175 : rhoi_cf(GEN *A, GEN *B, GEN *C, GEN t)
     438              : {
     439          175 :   GEN u, q, a = *A, b = *B, c = *C;
     440          175 :   q = truedvmdii(addii(t, b), shifti(a,1), &u);
     441          175 :   *A = subii(c, mulii(q, subii(b, mulii(q,a))));
     442          175 :   *B = subii(u, t);
     443          175 :   *C = a;
     444          175 :   if (signe(*A) < 0) { *A = negi(*A); *B = negi(*B); *C = negi(*C); }
     445          175 :   return q;
     446              : }
     447              : static void
     448     14152587 : rho_get_BC(GEN *B, GEN *C, GEN a, GEN b, GEN c, struct qfr_data *S)
     449              : {
     450              :   GEN t, u, q;
     451     14152587 :   t = (abscmpii(S->isqrtD,c) >= 0)? S->isqrtD: absi_shallow(c);
     452     14152587 :   q = truedvmdii(addii(t, b), shifti(c,1), &u);
     453     14152587 :   *B = subii(t, u); /* t - ((t+b) % 2c) */
     454     14152587 :   *C = subii(a, mulii(q, subii(b, mulii(q,c))));
     455     14152587 : }
     456              : /* Not stack-clean */
     457              : GEN
     458      1139460 : qfr3_rho(GEN x, struct qfr_data *S)
     459              : {
     460      1139460 :   GEN B, C, a = gel(x,1), b = gel(x,2), c = gel(x,3);
     461      1139460 :   rho_get_BC(&B, &C, a, b, c, S);
     462      1139460 :   return mkvec3(c, B, C);
     463              : }
     464              : 
     465              : /* Not stack-clean */
     466              : GEN
     467      8486681 : qfr5_rho(GEN x, struct qfr_data *S)
     468              : {
     469      8486681 :   GEN B, C, a = gel(x,1), b = gel(x,2), c = gel(x,3), y;
     470      8486681 :   long sb = signe(b);
     471      8486681 :   rho_get_BC(&B, &C, a, b, c, S);
     472      8486681 :   y = mkvec5(c, B, C, gel(x,4), gel(x,5));
     473      8486681 :   if (sb) {
     474      8482698 :     GEN t = subii(sqri(b), S->D);
     475      8482698 :     if (sb < 0)
     476      2509500 :       t = divir(t, sqrr(subir(b,S->sqrtD)));
     477              :     else
     478      5973198 :       t = divri(sqrr(addir(b,S->sqrtD)), t);
     479              :     /* t = (b + sqrt(D)) / (b - sqrt(D)), evaluated stably */
     480      8482698 :     gel(y,5) = mulrr(t, gel(y,5)); fix_expo(y);
     481         3983 :   } else gel(y,5) = negr(gel(y,5));
     482      8486681 :   return y;
     483              : }
     484              : 
     485              : /* Not stack-clean */
     486              : GEN
     487       217728 : qfr_to_qfr5(GEN x, long prec)
     488       217728 : { return mkvec5(gel(x,1),gel(x,2),gel(x,3),gen_0,real_1(prec)); }
     489              : 
     490              : /* d0 = initial distance, x = [a,b,c, expo(d), d], d = exp(2*distance) */
     491              : GEN
     492          532 : qfr5_to_qfr(GEN x, GEN D, GEN d0)
     493              : {
     494          532 :   if (d0)
     495              :   {
     496          140 :     GEN n = gel(x,4), d = absr(gel(x,5));
     497          140 :     if (signe(n))
     498              :     {
     499            0 :       n = addis(shifti(n, EMAX), expo(d));
     500            0 :       setexpo(d, 0); d = logr_abs(d);
     501            0 :       if (signe(n)) d = addrr(d, mulir(n, mplog2(lg(d0))));
     502            0 :       shiftr_inplace(d, -1);
     503            0 :       d0 = addrr(d0, d);
     504              :     }
     505          140 :     else if (!gequal1(d)) /* avoid loss of precision */
     506              :     {
     507           91 :       d = logr_abs(d);
     508           91 :       shiftr_inplace(d, -1);
     509           91 :       d0 = addrr(d0, d);
     510              :     }
     511              :   }
     512          532 :   x = qfr3_to_qfr(x, D);
     513          532 :   return d0 ? mkvec2(x,d0): x;
     514              : }
     515              : 
     516              : /* Not stack-clean */
     517              : GEN
     518        31969 : qfr3_to_qfr(GEN x, GEN d) { retmkqfb(gel(x,1), gel(x,2), gel(x,3), d); }
     519              : 
     520              : static int
     521     17713057 : ab_isreduced(GEN a, GEN b, GEN isqrtD)
     522              : {
     523              :   GEN t;
     524     17713057 :   if (signe(b) <= 0 || abscmpii(b, isqrtD) > 0) return 0;
     525      5271685 :   t = addii_sign(isqrtD,1, shifti(a,1),-1); /* floor(sqrt(D)) - |2a| */
     526      1099055 :   return signe(t) < 0 ? abscmpii(b, t) >= 0
     527      6370740 :                       : abscmpii(b, t) > 0;
     528              : }
     529              : 
     530              : /* Not stack-clean */
     531              : GEN
     532      1952895 : qfr5_red(GEN x, struct qfr_data *S)
     533              : {
     534      1952895 :   pari_sp av = avma;
     535      8465338 :   while (!ab_isreduced(gel(x,1), gel(x,2), S->isqrtD))
     536              :   {
     537      6512443 :     x = qfr5_rho(x, S);
     538      6512443 :     if (gc_needed(av,2))
     539              :     {
     540            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"qfr5_red");
     541            0 :       x = gc_GEN(av, x);
     542              :     }
     543              :   }
     544      1952895 :   return x;
     545              : }
     546              : /* Not stack-clean */
     547              : GEN
     548      1172882 : qfr3_red(GEN x, struct qfr_data *S)
     549              : {
     550      1172882 :   pari_sp av = avma;
     551      1172882 :   GEN a = gel(x,1), b = gel(x,2), c = gel(x,3);
     552      5699328 :   while (!ab_isreduced(a, b, S->isqrtD))
     553              :   {
     554              :     GEN B, C;
     555      4526446 :     rho_get_BC(&B, &C, a, b, c, S);
     556      4526446 :     a = c; b = B; c = C;
     557      4526446 :     if (gc_needed(av,2))
     558              :     {
     559            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"qfr3_red");
     560            0 :       (void)gc_all(av, 3, &a, &b, &c);
     561              :     }
     562              :   }
     563      1172882 :   return mkvec3(a, b, c);
     564              : }
     565              : 
     566              : void
     567         2170 : qfr_data_init(GEN D, long prec, struct qfr_data *S)
     568              : {
     569         2170 :   S->D = D;
     570         2170 :   S->sqrtD = sqrtr(itor(S->D,prec));
     571         2170 :   S->isqrtD = truncr(S->sqrtD);
     572         2170 : }
     573              : 
     574              : static GEN
     575          140 : qfr5_init(GEN x, GEN d, struct qfr_data *S)
     576              : {
     577          140 :   long prec = realprec(d), l = -expo(d);
     578          140 :   if (l < BITS_IN_LONG) l = BITS_IN_LONG;
     579          140 :   prec = maxss(prec, nbits2prec(l));
     580          140 :   S->D = qfb_disc(x);
     581          140 :   x = qfr_to_qfr5(x,prec);
     582          140 :   if (!S->sqrtD) S->sqrtD = sqrtr(itor(S->D,prec));
     583            0 :   else if (typ(S->sqrtD) != t_REAL) pari_err_TYPE("qfr_init",S->sqrtD);
     584              : 
     585          140 :   if (!S->isqrtD)
     586              :   {
     587          126 :     pari_sp av=avma;
     588              :     long e;
     589          126 :     S->isqrtD = gcvtoi(S->sqrtD,&e);
     590          126 :     if (e>-2) { set_avma(av); S->isqrtD = sqrti(S->D); }
     591              :   }
     592           14 :   else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);
     593          140 :   return x;
     594              : }
     595              : static GEN
     596          420 : qfr3_init(GEN x, struct qfr_data *S)
     597              : {
     598          420 :   S->D = qfb_disc(x);
     599          420 :   if (!S->isqrtD) S->isqrtD = sqrti(S->D);
     600          294 :   else if (typ(S->isqrtD) != t_INT) pari_err_TYPE("qfr_init",S->isqrtD);
     601          420 :   return x;
     602              : }
     603              : 
     604              : #define qf_NOD  2
     605              : #define qf_STEP 1
     606              : 
     607              : static GEN
     608          476 : qfr_red_basecase_i(GEN x, long flag, GEN isqrtD, GEN sqrtD)
     609              : {
     610              :   struct qfr_data S;
     611          476 :   GEN d = NULL, y;
     612          476 :   if (typ(x)==t_VEC) { d = gel(x,2); x = gel(x,1); } else flag |= qf_NOD;
     613          476 :   S.sqrtD = sqrtD;
     614          476 :   S.isqrtD = isqrtD;
     615          476 :   y = (flag & qf_NOD)? qfr3_init(x, &S): qfr5_init(x, d, &S);
     616          476 :   switch(flag) {
     617           63 :     case 0:              y = qfr5_red(y,&S); break;
     618          371 :     case qf_NOD:         y = qfr3_red(y,&S); break;
     619           21 :     case qf_STEP:        y = qfr5_rho(y,&S); break;
     620           21 :     case qf_STEP|qf_NOD: y = qfr3_rho(y,&S); break;
     621            0 :     default: pari_err_FLAG("qfbred");
     622              :   }
     623          476 :   return qfr5_to_qfr(y, qfb_disc(x), d);
     624              : }
     625              : 
     626              : static void
     627     13379357 : qfr_rhosl2_i(GEN *pa, GEN *pb, GEN *pc, GEN *pu1, GEN *pu2, GEN *pv1,
     628              :              GEN *pv2, GEN rd)
     629              : {
     630     13379357 :   GEN C = mpabs_shallow(*pc), t = addii(*pb, gmax_shallow(rd,C));
     631     13379357 :   GEN r, q = truedvmdii(t, shifti(C,1), &r);
     632     13379357 :   GEN a = *pa, b = *pb, c = *pc;
     633     13379357 :   if (signe(c) < 0) togglesign(q);
     634     13379357 :   *pa = *pc;
     635     13379357 :   *pb = subii(t, addii(r, *pb));
     636     13379357 :   *pc = subii(a, mulii(q, subii(b, mulii(q,c))));
     637     13379357 :   r = *pu1; *pu1 = *pv1; *pv1 = subii(mulii(q, *pv1), r);
     638     13379357 :   r = *pu2; *pu2 = *pv2; *pv2 = subii(mulii(q, *pv2), r);
     639     13379357 : }
     640              : 
     641              : static GEN
     642     10810674 : qfr_rhosl2(GEN A, GEN rd)
     643              : {
     644     10810674 :   GEN V = gel(A,1), M = gel(A,2);
     645     10810674 :   GEN a = gel(V,1), b = gel(V,2), c = gel(V,3), d = qfb_disc(V);
     646     10810674 :   GEN u1 = gcoeff(M,1,1), v1 = gcoeff(M,1,2);
     647     10810674 :   GEN u2 = gcoeff(M,2,1), v2 = gcoeff(M,2,2);
     648     10810674 :   qfr_rhosl2_i(&a,&b,&c, &u1,&u2,&v1,&v2, rd);
     649     10810674 :   return mkvec2(mkqfb(a,b,c,d), mkmat22(u1,v1,u2,v2));
     650              : }
     651              : 
     652              : static GEN
     653       979701 : qfr_redsl2_basecase(GEN V, GEN rd)
     654              : {
     655       979701 :   pari_sp av = avma;
     656       979701 :   GEN u1 = gen_1, u2 = gen_0, v1 = gen_0, v2 = gen_1;
     657       979701 :   GEN a = gel(V,1), b = gel(V,2), c = gel(V,3), d = qfb_disc(V);
     658      3548384 :   while (!ab_isreduced(a,b,rd))
     659              :   {
     660      2568683 :     qfr_rhosl2_i(&a,&b,&c, &u1,&u2,&v1,&v2, rd);
     661      2568683 :     if (gc_needed(av, 1))
     662              :     {
     663            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"qfbredsl2");
     664            0 :       (void)gc_all(av, 7, &a,&b,&c,&u1,&u2,&v1,&v2);
     665              :     }
     666              :   }
     667       979701 :   return gc_GEN(av, mkvec2(mkqfb(a,b,c,d), mkmat22(u1,v1,u2,v2)));
     668              : }
     669              : 
     670              : /* fast reduction of qfb with positive coefficients, based on
     671              : Arnold Schoenhage, Fast reduction and composition of binary quadratic forms,
     672              : Proc. of Intern. Symp. on Symbolic and Algebraic Computation (Bonn) (S. M.
     673              : Watt, ed.), ACM Press, 1991, pp. 128-133.
     674              : <https://dl.acm.org/doi/pdf/10.1145/120694.120711>
     675              : With thanks to Keegan Ryan
     676              : BA20230927
     677              : */
     678              : 
     679              : /* pqfb: qf with positive coefficients */
     680              : 
     681              : static int
     682      5357816 : lti2n(GEN a, long m) { return signe(a) < 0 || expi(a) < m;}
     683              : 
     684              : static GEN
     685      2085300 : pqfbred_1(GEN Q, long m, GEN U)
     686              : {
     687      2085300 :   GEN a = gel(Q,1), b = gel(Q,2), c = gel(Q,3), d = gel(Q,4);
     688      2085300 :   if (abscmpii(a, c) < 0)
     689              :   {
     690              :     GEN t, at, r;
     691      1042455 :     GEN r2 = addii(shifti(a, m + 2), d);
     692      1042455 :     long e2 = expi(r2);
     693      1042455 :     r = int2n(signe(r2) < 0 || e2 < 2*m+2 ? m+1 : e2>>1);
     694      1042455 :     t = truedivii(subii(b, r), shifti(a,1));
     695      1042455 :     if (signe(t)==0) pari_err_BUG("pqfbred_1");
     696      1042455 :     at = mulii(a,t);
     697      1042455 :     c = addii(subii(c, mulii(b, t)), mulii(at, t));
     698      1042455 :     b = subii(b, shifti(at,1));
     699      1042455 :     gcoeff(U,1,2) = subii( gcoeff(U,1,2), mulii(gcoeff(U,1,1), t));
     700      1042455 :     gcoeff(U,2,2) = subii( gcoeff(U,2,2), mulii(gcoeff(U,2,1), t));
     701              :   } else
     702              :   {
     703              :     GEN t, ct, r;
     704      1042845 :     GEN r2 = addii(shifti(c, m + 2), d);
     705      1042845 :     long e2 = expi(r2);
     706      1042845 :     r = int2n(signe(r2) < 0 || e2 < 2*m+2 ? m+1 : e2>>1);
     707      1042845 :     t = truedivii(subii(b, r), shifti(c,1));
     708      1042845 :     if (signe(t)==0) pari_err_BUG("pqfbred_1");
     709      1042845 :     ct = mulii(c, t);
     710      1042845 :     a = addii(subii(a, mulii(b, t)), mulii(ct, t));
     711      1042845 :     b = subii(b, shifti(ct, 1));
     712      1042845 :     gcoeff(U,1,1) = subii(gcoeff(U,1,1), mulii(gcoeff(U,1,2), t));
     713      1042845 :     gcoeff(U,2,1) = subii(gcoeff(U,2,1), mulii(gcoeff(U,2,2), t));
     714              :   }
     715      2085300 :   return mkqfb(a,b,c,d);
     716              : }
     717              : 
     718              : static int
     719      2217733 : is_minimal(GEN Q, long m)
     720              : {
     721      2217733 :   pari_sp av = avma;
     722      2217733 :   GEN a = gel(Q,1), b = gel(Q,2), c = gel(Q,3);
     723      5357816 :   return gc_bool(av, lti2n(addii(subii(a,b), c), m)
     724      2091269 :                  || (lti2n(subii(b, shifti(a,1)), m+1)
     725      1048814 :                      && lti2n(subii(b, shifti(c,1)), m+1)));
     726              : }
     727              : 
     728              : static GEN
     729       131244 : pqfbred_iter_1(GEN Q, ulong m, GEN U)
     730              : {
     731       131244 :   pari_sp av = avma;
     732      2087424 :   while (!is_minimal(Q,m))
     733              :   {
     734      1956180 :     Q = pqfbred_1(Q, m, U);
     735      1956180 :     if (gc_needed(av, 1))
     736              :     {
     737            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"pqfbred_iter_1, lc = %ld", expi(gel(Q,3)));
     738            0 :       (void)gc_all(av, 3, &Q, &gel(U,1), &gel(U,2));
     739              :     }
     740              :   }
     741       131244 :   return Q;
     742              : }
     743              : 
     744              : static GEN
     745        65097 : pqfbred_basecase(GEN Q, ulong m, GEN *pt_U)
     746              : {
     747        65097 :   pari_sp av = avma;
     748        65097 :   GEN  U = matid(2);
     749        65097 :   Q = pqfbred_iter_1(Q, m, U);
     750        65097 :   *pt_U = U;
     751        65097 :   return gc_all(av, 2, &Q, pt_U);
     752              : }
     753              : 
     754              : static long
     755     99746689 : qfb_maxexpi(GEN Q)
     756     99746689 : { return 1+maxss(expi(gel(Q,1)), maxss(expi(gel(Q,2)), expi(gel(Q,3)))); }
     757              : 
     758              : /* use asymptotically fast reduction ? */
     759              : static int
     760     99420178 : qfi_red_fast(GEN Q)
     761              : {
     762     99420178 :   const long QFBRED_LIMIT = 9000;
     763     99420178 :   return 2*qfb_maxexpi(Q) - expi(gel(Q,4)) > QFBRED_LIMIT;
     764              : }
     765              : 
     766              : static long
     767       132294 : qfb_minexpi(GEN Q)
     768              : {
     769       132294 :   long m =  minss(expi(gel(Q,1)), minss(expi(gel(Q,2)), expi(gel(Q,3))));
     770       132294 :   return m < 0 ? 0: m;
     771              : }
     772              : 
     773              : GEN
     774        65308 : qfb3_SL2_apply(GEN q, GEN M)
     775              : {
     776        65308 :   GEN a = gel(q,1), b = gel(q,2), c = gel(q,3);
     777        65308 :   GEN x = gcoeff(M,1,1), y = gcoeff(M,2,1);
     778        65308 :   GEN z = gcoeff(M,1,2), t = gcoeff(M,2,2);
     779        65308 :   GEN by = mulii(b,y), bt = mulii(b,t), bz  = mulii(b,z);
     780        65308 :   GEN a2 = shifti(a,1), c2 = shifti(c,1);
     781              : 
     782        65308 :   GEN A1 = mulii(x, addii(mulii(a,x), by));
     783        65308 :   GEN A2 = mulii(c, sqri(y));
     784        65308 :   GEN B1 = mulii(x, addii(mulii(a2,z), bt));
     785        65308 :   GEN B2 = mulii(y, addii(mulii(c2,t), bz));
     786        65308 :   GEN C1 = mulii(z, addii(mulii(a,z), bt));
     787        65308 :   GEN C2 = mulii(c, sqri(t));
     788        65308 :   retmkvec3(addii(A1,A2), addii(B1,B2), addii(C1, C2));
     789              : }
     790              : 
     791              : static GEN
     792       131244 : pqfbred_rec(GEN Q, long m, GEN *pt_U)
     793              : {
     794       131244 :   pari_sp av = avma;
     795       131244 :   GEN U, Q0, Q1, QR, d = qfb_disc(Q);
     796       131244 :   long h, n = qfb_maxexpi(Q) - m;
     797       131244 :   int going_to_r8 = 0;
     798              : 
     799       131244 :   if (n < 170) return pqfbred_basecase(Q, m, pt_U);
     800        66147 :   if (qfb_minexpi(Q) <= m + 2) { U = matid(2); QR = Q; }
     801              :   else
     802              :   {
     803              :     long p, mm;
     804        66147 :     if (m <= n) { mm = m; p = 0; Q1 = Q; }
     805              :     else
     806              :     {
     807        65273 :       mm = n; p = m + 1 - n;
     808        65273 :       Q0 = mkvec3(remi2n(gel(Q,1),p), remi2n(gel(Q,2),p), remi2n(gel(Q,3),p));
     809        65273 :       Q1 = qfb3(shifti(gel(Q,1),-p), shifti(gel(Q,2),-p), shifti(gel(Q,3),-p));
     810              :     }
     811        66147 :     h = mm + (n>>1);
     812        66147 :     if (qfb_minexpi(Q1) <= h) { U = matid(2); QR = Q1; }
     813              :     else
     814        65940 :       QR = pqfbred_rec(Q1, h, &U);
     815       195267 :     while (qfb_maxexpi(QR) > h)
     816              :     {
     817       130309 :       if (is_minimal(QR, mm)) { going_to_r8 = 1; break; }
     818       129120 :       QR = pqfbred_1(QR, mm, U);
     819              :     }
     820        66147 :     if (!going_to_r8)
     821              :     {
     822              :       GEN V;
     823        64958 :       QR = pqfbred_rec(QR, mm, &V);
     824        64958 :       U = ZM2_mul(U,V);
     825              :     }
     826        66147 :     if (p > 0)
     827              :     {
     828        65273 :       GEN Q0U = qfb3_SL2_apply(Q0,U);
     829       130546 :       QR = mkqfb(addii(shifti(gel(QR,1), p), gel(Q0U,1)),
     830        65273 :                  addii(shifti(gel(QR,2), p), gel(Q0U,2)),
     831        65273 :                  addii(shifti(gel(QR,3), p), gel(Q0U,3)), d);
     832              :     }
     833              :   }
     834        66147 :   QR = pqfbred_iter_1(QR, m, U);
     835        66147 :   *pt_U = U; return gc_all(av, 2, &QR, pt_U);
     836              : }
     837              : 
     838              : static GEN
     839       209575 : qfr_redsl2(GEN Q, GEN isqrtD)
     840              : {
     841       209575 :   pari_sp av = avma;
     842       209575 :   if (!qfi_red_fast(Q))
     843       209575 :     return qfr_redsl2_basecase(Q, isqrtD);
     844              :   else
     845              :   {
     846            0 :     GEN a = gel(Q,1), b = gel(Q,2), c = gel(Q,3), d = gel(Q,4);
     847            0 :     GEN Qf, Qr, W, U, t = NULL;
     848            0 :     long sa = signe(a), sb;
     849            0 :     if (sa < 0) { a = negi(a); b = negi(b); c = negi(c); }
     850            0 :     if (signe(c) < 0)
     851              :     {
     852              :       GEN at;
     853            0 :       t  = addiu(truedivii(subii(isqrtD,b),shifti(a,1)),1);
     854            0 :       at = mulii(a,t);
     855            0 :       c = addii(subii(c, mulii(b, t)), mulii(at, t));
     856            0 :       b = subii(b, shifti(at,1));
     857              :     }
     858            0 :     sb = signe(b);
     859            0 :     Qr = pqfbred_rec(mkqfb(a, sb < 0 ? negi(b): b, c, d), 0, &U);
     860            0 :     if (sa < 0)
     861            0 :       Qr = mkqfb(negi(gel(Qr,1)), negi(gel(Qr,2)), negi(gel(Qr,3)), gel(Qr,4));
     862            0 :     if (sb < 0)
     863              :     {
     864            0 :       gcoeff(U,2,1) = negi(gcoeff(U,2,1));
     865            0 :       gcoeff(U,2,2) = negi(gcoeff(U,2,2));
     866              :     }
     867            0 :     if (t)
     868              :     {
     869            0 :       gcoeff(U,1,1) = subii( gcoeff(U,1,1), mulii(gcoeff(U,2,1), t));
     870            0 :       gcoeff(U,1,2) = subii( gcoeff(U,1,2), mulii(gcoeff(U,2,2), t));
     871              :     }
     872            0 :     W = qfr_redsl2_basecase(Qr, isqrtD);
     873            0 :     Qf = gel(W,1);
     874            0 :     U = ZM2_mul(U,gel(W,2));
     875            0 :     return gc_GEN(av, mkvec2(Qf,U));
     876              :   }
     877              : }
     878              : 
     879              : static GEN
     880      5194280 : qfi_redsl2(GEN Q)
     881              : {
     882      5194280 :   pari_sp av = avma;
     883              :   GEN Qt, U;
     884      5194280 :   if (!qfi_red_fast(Q))
     885      5193969 :     Qt = qfi_redsl2_basecase(Q, &U);
     886              :   else
     887              :   {
     888          311 :     long sb = signe(gel(Q,2));
     889              :     GEN W;
     890          311 :     if (sb < 0) Q = mkqfb(gel(Q,1), negi(gel(Q,2)), gel(Q,3), gel(Q,4));
     891          311 :     Q = pqfbred_rec(Q, 0, &U);
     892          311 :     Qt = qfi_redsl2_basecase(Q, &W);
     893          311 :     U = ZM2_mul(U,W);
     894          311 :     if (sb < 0)
     895              :     {
     896          173 :       gcoeff(U,2,1) = negi(gcoeff(U,2,1));
     897          173 :       gcoeff(U,2,2) = negi(gcoeff(U,2,2));
     898              :     }
     899              :   }
     900      5194280 :   return gc_GEN(av, mkvec2(Qt,U));
     901              : }
     902              : 
     903              : GEN
     904      4883969 : redimagsl2(GEN Q, GEN *U)
     905              : {
     906      4883969 :   GEN q = qfi_redsl2(Q);
     907      4883969 :   *U = gel(q,2); return gel(q,1);
     908              : }
     909              : 
     910              : GEN
     911       519893 : qfbredsl2(GEN q, GEN isD)
     912              : {
     913              :   pari_sp av;
     914       519893 :   if (typ(q) != t_QFB) pari_err_TYPE("qfbredsl2",q);
     915       519893 :   if (qfb_is_qfi(q))
     916              :   {
     917       310311 :     if (isD) pari_err_TYPE("qfbredsl2", isD);
     918       310311 :     return qfi_redsl2(q);
     919              :   }
     920       209582 :   av = avma;
     921       209582 :   if (!isD) isD = sqrti(qfb_disc(q));
     922       208068 :   else if (typ(isD) != t_INT) pari_err_TYPE("qfbredsl2",isD);
     923       209575 :   return gc_upto(av, qfr_redsl2(q, isD));
     924              : }
     925              : 
     926              : /* not gc-clean */
     927              : static GEN
     928          476 : qfr_red_i(GEN Q, long flag, GEN isqrtD, GEN sqrtD)
     929              : {
     930          476 :   if (typ(Q) == t_QFB && !(flag & qf_STEP) && qfi_red_fast(Q))
     931              :   {
     932           28 :     GEN U, a = gel(Q,1), b = gel(Q,2), c = gel(Q,3), d = gel(Q,4);
     933           28 :     long sa = signe(a);
     934           28 :     if (sa < 0) { a = negi(a); b = negi(b); c = negi(c); }
     935           28 :     if (signe(c) < 0)
     936              :     {
     937              :       GEN at, t;
     938           14 :       if (!isqrtD) isqrtD = sqrti(d);
     939           14 :       t  = addiu(truedivii(subii(isqrtD,b),shifti(a,1)),1);
     940           14 :       at = mulii(a,t);
     941           14 :       c = addii(subii(c, mulii(b, t)), mulii(at, t));
     942           14 :       b = subii(b, shifti(at,1));
     943              :     }
     944           28 :     Q = pqfbred_rec(mkqfb(a, absi_shallow(b), c, d), 0, &U);
     945           28 :     if (sa < 0)
     946            0 :       Q = mkqfb(negi(gel(Q,1)), negi(gel(Q,2)), negi(gel(Q,3)), gel(Q,4));
     947              :   }
     948          476 :   return qfr_red_basecase_i(Q, flag, isqrtD, sqrtD);
     949              : }
     950              : 
     951              : GEN
     952            7 : qfr_boundcf(GEN x, long n)
     953              : {
     954            7 :   pari_sp av = avma;
     955            7 :   GEN a = gel(x,1), b = gel(x,2), c = gel(x,3), d = gel(x,4), V;
     956            7 :   GEN t = sqrtint(d);
     957              :   long i;
     958            7 :   if (signe(a) < 0) { a = negi(a); c = negi(c); } else b = negi(b);
     959            7 :   V = cgetg(n+1, t_VEC);
     960          147 :   for (i = 1; i <= n; i++) gel(V,i) = rhoi_cf(&a, &b, &c, t);
     961            7 :   return gc_GEN(av, V);
     962              : }
     963              : 
     964              : GEN
     965            7 : qfr_cf(GEN x)
     966              : {
     967            7 :   pari_sp av = avma;
     968            7 :   GEN a = gel(x,1), b = gel(x,2), c = gel(x,3), d = gel(x,4);
     969            7 :   GEN t = sqrtint(d);
     970            7 :   GEN a0 = NULL, b0 = NULL, V, W;
     971            7 :   long i, l = 16;
     972            7 :   if (signe(a) < 0) { a = negi(a); c = negi(c); }
     973            7 :   else b = negi(b);
     974            7 :   V = cgetg(l+1, t_VEC);
     975            7 :   for (i = 1;;i++)
     976              :   {
     977            7 :     if (ab_isreduced(a, b, t)) break;
     978            0 :     gel(V,i) = rhoi_cf(&a, &b, &c, t);
     979            0 :     if (i==l) { l *= 2; V = vec_lengthen(V, l); }
     980              :   }
     981            7 :   setlg(V, i); l = 16;
     982            7 :   a0 = a; b0 = b;
     983            7 :   W = cgetg(l+1, t_VEC);
     984            7 :   for (i = 1;; i++)
     985              :   {
     986           35 :     gel(W,i) = rhoi_cf(&a, &b, &c, t);
     987           35 :     if (equalii(a,a0) && equalii(b,b0)) break;
     988           28 :     if (i==l) { l *= 2; W = vec_lengthen(W, l); }
     989              :   }
     990            7 :   setlg(W, i+1); return gc_GEN(av, mkvec2(V,W));
     991              : }
     992              : 
     993              : static GEN
     994           63 : qfr_red_av(pari_sp av, GEN x)
     995           63 : { return gc_GEN(av, qfr_red_i(x,0,NULL,NULL)); }
     996              : GEN
     997            0 : qfr_red(GEN x) { return qfr_red_av(avma, x); }
     998              : 
     999              : static GEN
    1000     94015952 : qfi_red_basecase_av(pari_sp av, GEN q)
    1001              : {
    1002     94015952 :   GEN a = gel(q,1), b = gel(q,2), c = gel(q,3), D = gel(q,4);
    1003     94015952 :   long cmp, lc = lgefint(c);
    1004              : 
    1005     94015952 :   if (lgefint(a) == 3 && lc == 3) return qfi_red_1(av, a, b, c, D);
    1006       911922 :   cmp = abscmpii(a, b);
    1007       911922 :   if (cmp < 0)
    1008       436234 :     REDB(a,&b,&c);
    1009       475688 :   else if (cmp == 0 && signe(b) < 0)
    1010           27 :     b = negi(b);
    1011              :   for(;;)
    1012              :   {
    1013      3112002 :     cmp = abscmpii(a, c); if (cmp <= 0) break;
    1014      2920527 :     lc = lgefint(a); /* lg(future c): we swap a & c next */
    1015      2920527 :     if (lc == 3) return qfi_red_1(av, a, b, c, D);
    1016      2200080 :     swap(a,c); b = negi(b); /* apply rho */
    1017      2200080 :     REDB(a,&b,&c);
    1018      2200080 :     if (gc_needed(av, 2))
    1019              :     {
    1020            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"qfi_red, lc = %ld", lc);
    1021            0 :       (void)gc_all(av, 3, &a,&b,&c);
    1022              :     }
    1023              :   }
    1024       191475 :   if (cmp == 0 && signe(b) < 0) b = negi(b);
    1025       191475 :   return gc_GEN(av, mkqfb(a, b, c, D));
    1026              : }
    1027              : static GEN
    1028     94015952 : qfi_red_av(pari_sp av, GEN Q)
    1029              : {
    1030     94015952 :   if (qfi_red_fast(Q))
    1031              :   {
    1032              :     GEN U;
    1033            7 :     if (signe(gel(Q,2)) < 0)
    1034            0 :       Q = mkqfb(gel(Q,1), negi(gel(Q,2)), gel(Q,3), gel(Q,4));
    1035            7 :     Q = pqfbred_rec(Q, 0, &U);
    1036              :   }
    1037     94015952 :   return qfi_red_basecase_av(av, Q);
    1038              : }
    1039              : 
    1040              : GEN
    1041     19339781 : qfi_red(GEN q) { return qfi_red_av(avma, q); }
    1042              : 
    1043              : GEN
    1044        94076 : qfbred0(GEN x, long flag, GEN isqrtD, GEN sqrtD)
    1045              : {
    1046              :   pari_sp av;
    1047        94076 :   GEN q = check_qfbext("qfbred",x);
    1048        94076 :   if (qfb_is_qfi(q)) return (flag & qf_STEP)? qfi_rho(x): qfi_red(x);
    1049          413 :   if (typ(x)==t_QFB) flag |= qf_NOD;
    1050           49 :   else               flag &= ~qf_NOD;
    1051          413 :   av = avma;
    1052          413 :   return gc_GEN(av, qfr_red_i(x,flag,isqrtD,sqrtD));
    1053              : }
    1054              : /* t_QFB */
    1055              : GEN
    1056     11761409 : qfbred_i(GEN x) { return qfb_is_qfi(x)? qfi_red(x): qfr_red(x); }
    1057              : GEN
    1058        92214 : qfbred(GEN x) { return qfbred0(x, 0, NULL, NULL); }
    1059              : /***********************************************************************/
    1060              : /**                                                                   **/
    1061              : /**                         Composition                               **/
    1062              : /**                                                                   **/
    1063              : /***********************************************************************/
    1064              : 
    1065              : static void
    1066     27236445 : qfb_sqr(GEN z, GEN x)
    1067              : {
    1068              :   GEN c, d1, x2, v1, v2, c3, m, p1, r;
    1069              : 
    1070     27236445 :   d1 = bezout(gel(x,2),gel(x,1),&x2, NULL); /* usually 1 */
    1071     27236445 :   c = gel(x,3);
    1072     27236445 :   m = mulii(c,x2);
    1073     27236445 :   if (equali1(d1))
    1074     20481639 :     v1 = v2 = gel(x,1);
    1075              :   else
    1076              :   {
    1077      6754806 :     v1 = diviiexact(gel(x,1),d1);
    1078      6754806 :     v2 = mulii(v1, gcdii(d1,c)); /* = v1 iff x primitive */
    1079      6754806 :     c = mulii(c, d1);
    1080              :   }
    1081     27236445 :   togglesign(m);
    1082     27236445 :   r = modii(m,v2);
    1083     27236445 :   p1 = mulii(r, v1);
    1084     27236445 :   c3 = addii(c, mulii(r,addii(gel(x,2),p1)));
    1085     27236445 :   gel(z,1) = mulii(v1,v2);
    1086     27236445 :   gel(z,2) = addii(gel(x,2), shifti(p1,1));
    1087     27236445 :   gel(z,3) = diviiexact(c3,v2);
    1088     27236445 : }
    1089              : /* z <- x * y */
    1090              : static void
    1091     76187759 : qfb_comp(GEN z, GEN x, GEN y)
    1092              : {
    1093              :   GEN n, c, d, y1, v1, v2, c3, m, p1, r;
    1094              : 
    1095     76187759 :   if (x == y) { qfb_sqr(z,x); return; }
    1096     49774279 :   n = shifti(subii(gel(y,2),gel(x,2)), -1);
    1097     49774279 :   v1 = gel(x,1);
    1098     49774279 :   v2 = gel(y,1);
    1099     49774279 :   c  = gel(y,3);
    1100     49774279 :   d = bezout(v2,v1,&y1,NULL);
    1101     49774279 :   if (equali1(d))
    1102     30861456 :     m = mulii(y1,n);
    1103              :   else
    1104              :   {
    1105     18912823 :     GEN s = subii(gel(y,2), n);
    1106     18912823 :     GEN x2, y2, d1 = bezout(s,d,&x2,&y2); /* x2 s + y2 (x1 v1 + y1 v2) = d1 */
    1107     18912823 :     if (!equali1(d1))
    1108              :     {
    1109      9049670 :       v1 = diviiexact(v1,d1);
    1110      9049670 :       v2 = diviiexact(v2,d1); /* gcd = 1 iff x or y primitive */
    1111      9049670 :       v1 = mulii(v1, gcdii(c,gcdii(gel(x,3),gcdii(d1,n))));
    1112      9049670 :       c = mulii(c, d1);
    1113              :     }
    1114     18912823 :     m = addii(mulii(mulii(y1,y2),n), mulii(gel(y,3),x2));
    1115              :   }
    1116     49774279 :   togglesign(m);
    1117     49774279 :   r = modii(m, v1);
    1118     49774279 :   p1 = mulii(r, v2);
    1119     49774279 :   c3 = addii(c, mulii(r,addii(gel(y,2),p1)));
    1120     49774279 :   gel(z,1) = mulii(v1,v2);
    1121     49774279 :   gel(z,2) = addii(gel(y,2), shifti(p1,1));
    1122     49774279 :   gel(z,3) = diviiexact(c3,v1);
    1123              : }
    1124              : 
    1125              : /* not meant to be efficient */
    1126              : static GEN
    1127           84 : qfb_comp_gen(GEN x, GEN y)
    1128              : {
    1129           84 :   GEN d1 = qfb_disc(x), d2 = qfb_disc(y);
    1130           84 :   GEN a1 = gel(x,1), b1 = gel(x,2), c1 = gel(x,3), n1;
    1131           84 :   GEN a2 = gel(y,1), b2 = gel(y,2), c2 = gel(y,3), n2;
    1132           84 :   GEN cx = content(x), cy = content(y), A, B, C, D, U, m, m2;
    1133              : 
    1134           84 :   if (!is_pm1(cx))
    1135              :   {
    1136           14 :     a1 = diviiexact(a1, cx); b1 = diviiexact(b1, cx);
    1137           14 :     c1 = diviiexact(c1, cx); d1 = diviiexact(d1, sqri(cx));
    1138              :   }
    1139           84 :   if (!is_pm1(cy))
    1140              :   {
    1141           28 :     a2 = diviiexact(a2, cy); c2 = diviiexact(c2, cy);
    1142           28 :     b2 = diviiexact(b2, cy); d2 = diviiexact(d2, sqri(cy));
    1143              :   }
    1144           84 :   D = gcdii(d1, d2); if (signe(d1) < 0) setsigne(D, -1);
    1145          133 :   if (!Z_issquareall(diviiexact(d1, D), &n1) ||
    1146           84 :       !Z_issquareall(diviiexact(d2, D), &n2)) return NULL;
    1147           49 :   A = mulii(a1, n2);
    1148           49 :   B = mulii(a2, n1);
    1149           49 :   C = shifti(addii(mulii(b1, n2), mulii(b2, n1)), -1);
    1150           49 :   U = ZV_extgcd(mkvec3(A, B, C));
    1151           49 :   m = gel(U,1); U = gmael(U,2,3);
    1152           49 :   A = mulii(diviiexact(mulii(a1,b2),m), gel(U,1));
    1153           49 :   B = mulii(diviiexact(mulii(a2,b1),m), gel(U,2));
    1154           49 :   C = addii(mulii(b1,b2), mulii(D, mulii(n1,n2)));
    1155           49 :   C = mulii(diviiexact(shifti(C,-1), m), gel(U,3));
    1156           49 :   B = addii(A, addii(B, C));
    1157           49 :   m2 = sqri(m);
    1158           49 :   A = diviiexact(mulii(a1, a2), m2);
    1159           49 :   C = diviiexact(shifti(subii(sqri(B),D), -2), A);
    1160           49 :   cx = mulii(cx, cy);
    1161           49 :   if (!is_pm1(cx))
    1162              :   {
    1163           14 :     A = mulii(A, cx); B = mulii(B, cx);
    1164           14 :     C = mulii(C, cx); D = mulii(D, sqri(cx));
    1165              :   }
    1166           49 :   return mkqfb(A, B, C, D);
    1167              : }
    1168              : 
    1169              : static GEN
    1170     73442691 : qficomp0(GEN x, GEN y, int raw)
    1171              : {
    1172     73442691 :   pari_sp av = avma;
    1173     73442691 :   GEN z = cgetg(5,t_QFB);
    1174     73442691 :   gel(z,4) = gel(x,4);
    1175     73442691 :   qfb_comp(z, x,y);
    1176     73442691 :   if (raw) return gc_GEN(av,z);
    1177     73440913 :   return qfi_red_av(av, z);
    1178              : }
    1179              : static GEN
    1180          441 : qfrcomp0(GEN x, GEN y, int raw)
    1181              : {
    1182          441 :   pari_sp av = avma;
    1183          441 :   GEN dx = NULL, dy = NULL;
    1184          441 :   GEN z = cgetg(5,t_QFB);
    1185          441 :   if (typ(x)==t_VEC) { dx = gel(x,2); x = gel(x,1); }
    1186          441 :   if (typ(y)==t_VEC) { dy = gel(y,2); y = gel(y,1); }
    1187          441 :   gel(z,4) = gel(x,4);
    1188          441 :   qfb_comp(z, x,y);
    1189          441 :   if (dx) z = mkvec2(z, dy? addrr(dx, dy): dx); else if (dy) z = mkvec2(z, dy);
    1190          441 :   if (raw) return gc_GEN(av, z);
    1191           28 :   return qfr_red_av(av, z);
    1192              : }
    1193              : /* same discriminant, no distance, no checks */
    1194              : GEN
    1195     38663194 : qfbcomp_i(GEN x, GEN y)
    1196     38663194 : { return qfb_is_qfi(x)? qficomp0(x,y,0): qfrcomp0(x,y,0); }
    1197              : GEN
    1198       138644 : qfbcomp(GEN x, GEN y)
    1199              : {
    1200       138644 :   GEN qx = check_qfbext("qfbcomp", x);
    1201       138644 :   GEN qy = check_qfbext("qfbcomp", y);
    1202       138644 :   if (!equalii(gel(qx,4),gel(qy,4)))
    1203              :   {
    1204           63 :     pari_sp av = avma;
    1205           63 :     GEN z = qfb_comp_gen(qx, qy);
    1206           63 :     if (typ(x) == t_VEC || typ(y) == t_VEC)
    1207            7 :       pari_err_IMPL("Shanks's distance in general composition");
    1208           56 :     if (!z) pari_err_OP("*",x,y);
    1209           21 :     return gc_upto(av, qfbred(z));
    1210              :   }
    1211       138581 :   return qfb_is_qfi(qx)? qficomp0(x,y,0): qfrcomp0(x,y,0);
    1212              : }
    1213              : /* same discriminant, no distance, no checks */
    1214              : GEN
    1215            0 : qfbcompraw_i(GEN x, GEN y)
    1216            0 : { return qfb_is_qfi(x)? qficomp0(x,y,1): qfrcomp0(x,y,1); }
    1217              : GEN
    1218         2198 : qfbcompraw(GEN x, GEN y)
    1219              : {
    1220         2198 :   GEN qx = check_qfbext("qfbcompraw", x);
    1221         2198 :   GEN qy = check_qfbext("qfbcompraw", y);
    1222         2198 :   if (!equalii(gel(qx,4),gel(qy,4)))
    1223              :   {
    1224           21 :     pari_sp av = avma;
    1225           21 :     GEN z = qfb_comp_gen(qx, qy);
    1226           21 :     if (typ(x) == t_VEC || typ(y) == t_VEC)
    1227            0 :       pari_err_IMPL("Shanks's distance in general composition");
    1228           21 :     if (!z) pari_err_OP("qfbcompraw",x,y);
    1229           21 :     return gc_GEN(av, z);
    1230              :   }
    1231         2177 :   if (!equalii(gel(qx,4),gel(qy,4))) pari_err_OP("qfbcompraw",x,y);
    1232         2177 :   return qfb_is_qfi(qx)? qficomp0(x,y,1): qfrcomp0(x,y,1);
    1233              : }
    1234              : 
    1235              : static GEN
    1236       822930 : qfisqr0(GEN x, long raw)
    1237              : {
    1238       822930 :   pari_sp av = avma;
    1239       822930 :   GEN z = cgetg(5,t_QFB);
    1240       822930 :   gel(z,4) = gel(x,4);
    1241       822930 :   qfb_sqr(z,x);
    1242       822930 :   if (raw) return gc_GEN(av,z);
    1243       822930 :   return qfi_red_av(av, z);
    1244              : }
    1245              : static GEN
    1246           35 : qfrsqr0(GEN x, long raw)
    1247              : {
    1248           35 :   pari_sp av = avma;
    1249           35 :   GEN dx = NULL, z = cgetg(5,t_QFB);
    1250           35 :   if (typ(x) == t_VEC) { dx = gel(x,2); x = gel(x,1); }
    1251           35 :   gel(z,4) = gel(x,4); qfb_sqr(z,x);
    1252           35 :   if (dx) z = mkvec2(z, shiftr(dx,1));
    1253           35 :   if (raw) return gc_GEN(av, z);
    1254           35 :   return qfr_red_av(av, z);
    1255              : }
    1256              : /* same discriminant, no distance, no checks */
    1257              : GEN
    1258       694003 : qfbsqr_i(GEN x)
    1259       694003 : { return qfb_is_qfi(x)? qfisqr0(x,0): qfrsqr0(x,0); }
    1260              : GEN
    1261       128962 : qfbsqr(GEN x)
    1262              : {
    1263       128962 :   GEN qx = check_qfbext("qfbsqr", x);
    1264       128962 :   return qfb_is_qfi(qx)? qfisqr0(x,0): qfrsqr0(x,0);
    1265              : }
    1266              : 
    1267              : static GEN
    1268         6867 : qfr_1_by_disc(GEN D)
    1269              : {
    1270              :   GEN y, r, s;
    1271         6867 :   check_quaddisc_real(D, NULL, "qfr_1_by_disc");
    1272         6867 :   y = cgetg(5,t_QFB);
    1273         6867 :   s = sqrtremi(D, &r); togglesign(r); /* s^2 - r = D */
    1274         6867 :   if (mpodd(r))
    1275              :   {
    1276         3535 :     s = subiu(s,1);
    1277         3535 :     r = subii(r, addiu(shifti(s, 1), 1));
    1278         3535 :     r = shifti(r, -2); set_avma((pari_sp)y); s = icopy(s);
    1279              :   }
    1280              :   else
    1281         3332 :   { r = shifti(r, -2); set_avma((pari_sp)s); }
    1282         6867 :   gel(y,1) = gen_1;
    1283         6867 :   gel(y,2) = s;
    1284         6867 :   gel(y,3) = icopy(r);
    1285         6867 :   gel(y,4) = icopy(D); return y;
    1286              : }
    1287              : 
    1288              : static GEN
    1289           35 : qfr_disc(GEN x)
    1290           35 : { return qfb_disc(typ(x)==t_VEC ? gel(x,1): x); }
    1291              : 
    1292              : static GEN
    1293           35 : qfr_1(GEN x)
    1294           35 : { return qfr_1_by_disc(qfr_disc(x)); }
    1295              : 
    1296              : static void
    1297            0 : qfr_1_fill(GEN y, struct qfr_data *S)
    1298              : {
    1299            0 :   pari_sp av = avma;
    1300            0 :   GEN y2 = S->isqrtD;
    1301            0 :   gel(y,1) = gen_1;
    1302            0 :   if (mod2(S->D) != mod2(y2)) y2 = subiu(y,1);
    1303            0 :   gel(y,2) = y2; av = avma;
    1304            0 :   gel(y,3) = gc_INT(av, shifti(subii(sqri(y2), S->D),-2));
    1305            0 : }
    1306              : static GEN
    1307            0 : qfr5_1(struct qfr_data *S, long prec)
    1308              : {
    1309            0 :   GEN y = cgetg(6, t_VEC);
    1310            0 :   qfr_1_fill(y, S);
    1311            0 :   gel(y,4) = gen_0;
    1312            0 :   gel(y,5) = real_1(prec); return y;
    1313              : }
    1314              : static GEN
    1315            0 : qfr3_1(struct qfr_data *S)
    1316              : {
    1317            0 :   GEN y = cgetg(4, t_VEC);
    1318            0 :   qfr_1_fill(y, S); return y;
    1319              : }
    1320              : 
    1321              : /* Assume D < 0 is the discriminant of a t_QFB */
    1322              : static GEN
    1323       775335 : qfi_1_by_disc(GEN D)
    1324              : {
    1325       775335 :   GEN b,c, y = cgetg(5,t_QFB);
    1326       775335 :   quadpoly_bc(D, mod2(D), &b,&c);
    1327       775335 :   if (b == gen_m1) b = gen_1;
    1328       775335 :   gel(y,1) = gen_1;
    1329       775335 :   gel(y,2) = b;
    1330       775335 :   gel(y,3) = c;
    1331       775335 :   gel(y,4) = icopy(D); return y;
    1332              : }
    1333              : static GEN
    1334       763241 : qfi_1(GEN x)
    1335              : {
    1336       763241 :   if (typ(x) != t_QFB) pari_err_TYPE("qfi_1",x);
    1337       763241 :   return qfi_1_by_disc(qfb_disc(x));
    1338              : }
    1339              : 
    1340              : GEN
    1341            0 : qfb_1(GEN x) { return qfb_is_qfi(x) ? qfi_1(x): qfr_1(x); }
    1342              : 
    1343              : static GEN
    1344      9607916 : _qfimul(void *E, GEN x, GEN y) { (void) E; return qficomp0(x,y,0); }
    1345              : static GEN
    1346     25031250 : _qfisqr(void *E, GEN x) { (void) E; return qficomp0(x,x,0); }
    1347              : static GEN
    1348            7 : _qfimulraw(void *E, GEN x, GEN y) { (void) E; return qficomp0(x,y,1); }
    1349              : static GEN
    1350            7 : _qfisqrraw(void *E, GEN x) { (void) E; return qficomp0(x,x,1); }
    1351              : 
    1352              : static GEN
    1353            7 : qfipowraw(GEN x, long n)
    1354              : {
    1355            7 :   pari_sp av = avma;
    1356              :   GEN y;
    1357            7 :   if (!n) return qfi_1(x);
    1358            7 :   if (n== 1) return gcopy(x);
    1359            7 :   if (n==-1) { x = gcopy(x); togglesign(gel(x,2)); return x; }
    1360            7 :   if (n < 0) x = qfb_inv(x);
    1361            7 :   y = gen_powu(x, labs(n), NULL, &_qfisqrraw, &_qfimulraw);
    1362            7 :   return gc_GEN(av,y);
    1363              : }
    1364              : 
    1365              : static GEN
    1366     12524650 : qfipow(GEN x, GEN n)
    1367              : {
    1368     12524650 :   pari_sp av = avma;
    1369              :   GEN y;
    1370     12524650 :   long s = signe(n);
    1371     12524650 :   if (!s) return qfi_1(x);
    1372     11761409 :   if (s < 0) x = qfb_inv(x);
    1373     11761409 :   y = gen_pow(qfbred_i(x), n, NULL, &_qfisqr, &_qfimul);
    1374     11761409 :   return gc_GEN(av,y);
    1375              : }
    1376              : 
    1377              : static long
    1378       412328 : parteucl(GEN L, GEN *d, GEN *v3, GEN *v, GEN *v2)
    1379              : {
    1380              :   long z;
    1381       412328 :   *v = gen_0; *v2 = gen_1;
    1382      4351417 :   for (z=0; abscmpii(*v3,L) > 0; z++)
    1383              :   {
    1384      3939089 :     GEN t3, t2 = subii(*v, mulii(truedvmdii(*d,*v3,&t3),*v2));
    1385      3939089 :     *v = *v2; *d = *v3; *v2 = t2; *v3 = t3;
    1386              :   }
    1387       412328 :   return z;
    1388              : }
    1389              : 
    1390              : /* composition: Shanks' NUCOMP & NUDUPL */
    1391              : /* L = floor((|d|/4)^(1/4)) */
    1392              : GEN
    1393       400722 : nucomp(GEN x, GEN y, GEN L)
    1394              : {
    1395       400722 :   pari_sp av = avma;
    1396              :   long z;
    1397              :   GEN a, a1, a2, b2, b, d, d1, g, n, p1, q1, q2, s, u, u1, v, v2, v3, Q;
    1398              : 
    1399       400722 :   if (x==y) return nudupl(x,L);
    1400       400680 :   if (!is_qfi(x)) pari_err_TYPE("nucomp",x);
    1401       400680 :   if (!is_qfi(y)) pari_err_TYPE("nucomp",y);
    1402              : 
    1403       400680 :   if (abscmpii(gel(x,1),gel(y,1)) < 0) swap(x, y);
    1404       400680 :   s = shifti(addii(gel(x,2),gel(y,2)), -1);
    1405       400680 :   n = subii(gel(y,2), s);
    1406       400680 :   a1 = gel(x,1);
    1407       400680 :   a2 = gel(y,1); d = bezout(a2,a1,&u,&v);
    1408       400680 :   if (equali1(d)) { a = negi(mulii(u,n)); d1 = d; }
    1409       163576 :   else if (dvdii(s,d)) /* d | s */
    1410              :   {
    1411        83503 :     a = negi(mulii(u,n)); d1 = d;
    1412        83503 :     a1 = diviiexact(a1, d1);
    1413        83503 :     a2 = diviiexact(a2, d1);
    1414        83503 :     s = diviiexact(s, d1);
    1415              :   }
    1416              :   else
    1417              :   {
    1418              :     GEN p2, l;
    1419        80073 :     d1 = bezout(s,d,&u1,NULL);
    1420        80073 :     if (!equali1(d1))
    1421              :     {
    1422         2044 :       a1 = diviiexact(a1,d1);
    1423         2044 :       a2 = diviiexact(a2,d1);
    1424         2044 :       s = diviiexact(s,d1);
    1425         2044 :       d = diviiexact(d,d1);
    1426              :     }
    1427        80073 :     p1 = remii(gel(x,3),d);
    1428        80073 :     p2 = remii(gel(y,3),d);
    1429        80073 :     l = modii(mulii(negi(u1), addii(mulii(u,p1),mulii(v,p2))), d);
    1430        80073 :     a = subii(mulii(l,diviiexact(a1,d)), mulii(u,diviiexact(n,d)));
    1431              :   }
    1432       400680 :   a = modii(a,a1); p1 = subii(a,a1); if (abscmpii(a,p1) > 0) a = p1;
    1433       400680 :   d = a1; v3 = a; z = parteucl(L, &d,&v3, &v,&v2);
    1434       400680 :   Q = cgetg(5,t_QFB);
    1435       400680 :   if (!z) {
    1436        37632 :     g = diviiexact(addii(mulii(v3,s),gel(y,3)), d);
    1437        37632 :     b = a2;
    1438        37632 :     b2 = gel(y,2);
    1439        37632 :     v2 = d1;
    1440        37632 :     gel(Q,1) = mulii(d,b);
    1441              :   } else {
    1442              :     GEN e, q3, q4;
    1443       363048 :     if (z&1) { v3 = negi(v3); v2 = negi(v2); }
    1444       363048 :     b = diviiexact(addii(mulii(a2,d), mulii(n,v)), a1);
    1445       363048 :     e = diviiexact(addii(mulii(s,d),mulii(gel(y,3),v)), a1);
    1446       363048 :     q3 = mulii(e,v2);
    1447       363048 :     q4 = subii(q3,s);
    1448       363048 :     b2 = addii(q3,q4);
    1449       363048 :     g = diviiexact(q4,v);
    1450       363048 :     if (!equali1(d1)) { v2 = mulii(d1,v2); v = mulii(d1,v); b2 = mulii(d1,b2); }
    1451       363048 :     gel(Q,1) = addii(mulii(d,b), mulii(e,v));
    1452              :   }
    1453       400680 :   q1 = mulii(b, v3);
    1454       400680 :   q2 = addii(q1,n);
    1455       400680 :   gel(Q,2) = addii(b2, z? addii(q1,q2): shifti(q1, 1));
    1456       400680 :   gel(Q,3) = addii(mulii(v3,diviiexact(q2,d)), mulii(g,v2));
    1457       400680 :   gel(Q,4) = gel(x,4);
    1458       400680 :   return qfi_red_av(av, Q);
    1459              : }
    1460              : 
    1461              : GEN
    1462        11648 : nudupl(GEN x, GEN L)
    1463              : {
    1464        11648 :   pari_sp av = avma;
    1465              :   long z;
    1466              :   GEN u, v, d, d1, p1, a, b, c, a2, b2, c2, Q, v2, v3, g;
    1467              : 
    1468        11648 :   if (!is_qfi(x)) pari_err_TYPE("nudupl",x);
    1469        11648 :   a = gel(x,1);
    1470        11648 :   b = gel(x,2);
    1471        11648 :   d1 = bezout(b,a, &u,NULL);
    1472        11648 :   if (!equali1(d1))
    1473              :   {
    1474         4620 :     a = diviiexact(a, d1);
    1475         4620 :     b = diviiexact(b, d1);
    1476              :   }
    1477        11648 :   c = modii(negi(mulii(u,gel(x,3))), a);
    1478        11648 :   p1 = subii(c,a); if (abscmpii(c,p1) > 0) c = p1;
    1479        11648 :   d = a; v3 = c; z = parteucl(L, &d,&v3, &v,&v2);
    1480        11648 :   a2 = sqri(d);
    1481        11648 :   c2 = sqri(v3);
    1482        11648 :   Q = cgetg(5,t_QFB);
    1483        11648 :   if (!z) {
    1484         1281 :     g = diviiexact(addii(mulii(v3,b),gel(x,3)), d);
    1485         1281 :     b2 = gel(x,2);
    1486         1281 :     v2 = d1;
    1487         1281 :     gel(Q,1) = a2;
    1488              :   } else {
    1489              :     GEN e;
    1490        10367 :     if (z&1) { v = negi(v); d = negi(d); }
    1491        10367 :     e = diviiexact(addii(mulii(gel(x,3),v), mulii(b,d)), a);
    1492        10367 :     g = diviiexact(subii(mulii(e,v2), b), v);
    1493        10367 :     b2 = addii(mulii(e,v2), mulii(v,g));
    1494        10367 :     if (!equali1(d1)) { b2 = mulii(d1,b2); v = mulii(d1,v); v2 = mulii(d1,v2); }
    1495        10367 :     gel(Q,1) = addii(a2, mulii(e,v));
    1496              :   }
    1497        11648 :   gel(Q,2) = addii(b2, subii(sqri(addii(d,v3)), addii(a2,c2)));
    1498        11648 :   gel(Q,3) = addii(c2, mulii(g,v2));
    1499        11648 :   gel(Q,4) = gel(x,4);
    1500        11648 :   return qfi_red_av(av, Q);
    1501              : }
    1502              : 
    1503              : static GEN
    1504         4739 : mul_nucomp(void *l, GEN x, GEN y) { return nucomp(x, y, (GEN)l); }
    1505              : static GEN
    1506        11606 : mul_nudupl(void *l, GEN x) { return nudupl(x, (GEN)l); }
    1507              : GEN
    1508         1008 : nupow(GEN x, GEN n, GEN L)
    1509              : {
    1510              :   pari_sp av;
    1511              :   GEN y, D;
    1512              : 
    1513         1008 :   if (typ(n) != t_INT) pari_err_TYPE("nupow",n);
    1514         1008 :   if (!is_qfi(x)) pari_err_TYPE("nupow",x);
    1515         1008 :   if (gequal1(n)) return gcopy(x);
    1516         1008 :   av = avma;
    1517         1008 :   D = qfb_disc(x);
    1518         1008 :   y = qfi_1_by_disc(D);
    1519         1008 :   if (!signe(n)) return y;
    1520          959 :   if (!L) L = sqrtnint(absi_shallow(D), 4);
    1521          959 :   y = gen_pow_i(x, n, (void*)L, &mul_nudupl, &mul_nucomp);
    1522          959 :   if (signe(n) < 0
    1523           35 :   && !absequalii(gel(y,1),gel(y,2))
    1524           35 :   && !absequalii(gel(y,1),gel(y,3))) togglesign(gel(y,2));
    1525          959 :   return gc_GEN(av, y);
    1526              : }
    1527              : 
    1528              : /* Not stack-clean */
    1529              : GEN
    1530      1735230 : qfr5_compraw(GEN x, GEN y)
    1531              : {
    1532      1735230 :   GEN z = cgetg(6,t_VEC); qfb_comp(z,x,y);
    1533      1735230 :   if (x == y)
    1534              :   {
    1535        34552 :     gel(z,4) = shifti(gel(x,4),1);
    1536        34552 :     gel(z,5) = sqrr(gel(x,5));
    1537              :   }
    1538              :   else
    1539              :   {
    1540      1700678 :     gel(z,4) = addii(gel(x,4),gel(y,4));
    1541      1700678 :     gel(z,5) = mulrr(gel(x,5),gel(y,5));
    1542              :   }
    1543      1735230 :   fix_expo(z); return z;
    1544              : }
    1545              : GEN
    1546      1735216 : qfr5_comp(GEN x, GEN y, struct qfr_data *S)
    1547      1735216 : { return qfr5_red(qfr5_compraw(x, y), S); }
    1548              : /* Not stack-clean */
    1549              : GEN
    1550      1009397 : qfr3_compraw(GEN x, GEN y)
    1551              : {
    1552      1009397 :   GEN z = cgetg(4,t_VEC); qfb_comp(z,x,y);
    1553      1009397 :   return z;
    1554              : }
    1555              : GEN
    1556      1009397 : qfr3_comp(GEN x, GEN y, struct qfr_data *S)
    1557      1009397 : { return qfr3_red(qfr3_compraw(x,y), S); }
    1558              : 
    1559              : /* m > 0. Not stack-clean */
    1560              : static GEN
    1561            7 : qfr5_powraw(GEN x, long m)
    1562              : {
    1563            7 :   GEN y = NULL;
    1564           14 :   for (; m; m >>= 1)
    1565              :   {
    1566           14 :     if (m&1) y = y? qfr5_compraw(y,x): x;
    1567           14 :     if (m == 1) break;
    1568            7 :     x = qfr5_compraw(x,x);
    1569              :   }
    1570            7 :   return y;
    1571              : }
    1572              : 
    1573              : /* return x^n. Not stack-clean */
    1574              : GEN
    1575           21 : qfr5_pow(GEN x, GEN n, struct qfr_data *S)
    1576              : {
    1577           21 :   GEN y = NULL;
    1578           21 :   long i, m, s = signe(n);
    1579           21 :   if (!s) return qfr5_1(S, lg(gel(x,5)));
    1580           21 :   if (s < 0) x = qfb_inv(x);
    1581           42 :   for (i=lgefint(n)-1; i>1; i--)
    1582              :   {
    1583           21 :     m = n[i];
    1584           56 :     for (; m; m>>=1)
    1585              :     {
    1586           56 :       if (m&1) y = y? qfr5_comp(y,x,S): x;
    1587           56 :       if (m == 1 && i == 2) break;
    1588           35 :       x = qfr5_comp(x,x,S);
    1589              :     }
    1590              :   }
    1591           21 :   return y;
    1592              : }
    1593              : /* m > 0; return x^m. Not stack-clean */
    1594              : static GEN
    1595            0 : qfr3_powraw(GEN x, long m)
    1596              : {
    1597            0 :   GEN y = NULL;
    1598            0 :   for (; m; m>>=1)
    1599              :   {
    1600            0 :     if (m&1) y = y? qfr3_compraw(y,x): x;
    1601            0 :     if (m == 1) break;
    1602            0 :     x = qfr3_compraw(x,x);
    1603              :   }
    1604            0 :   return y;
    1605              : }
    1606              : /* return x^n. Not stack-clean */
    1607              : GEN
    1608         4557 : qfr3_pow(GEN x, GEN n, struct qfr_data *S)
    1609              : {
    1610         4557 :   GEN y = NULL;
    1611         4557 :   long i, m, s = signe(n);
    1612         4557 :   if (!s) return qfr3_1(S);
    1613         4557 :   if (s < 0) x = qfb_inv(x);
    1614         9130 :   for (i=lgefint(n)-1; i>1; i--)
    1615              :   {
    1616         4573 :     m = n[i];
    1617         5312 :     for (; m; m>>=1)
    1618              :     {
    1619         5292 :       if (m&1) y = y? qfr3_comp(y,x,S): x;
    1620         5292 :       if (m == 1 && i == 2) break;
    1621          739 :       x = qfr3_comp(x,x,S);
    1622              :     }
    1623              :   }
    1624         4557 :   return y;
    1625              : }
    1626              : 
    1627              : static GEN
    1628            7 : qfrinvraw(GEN x)
    1629              : {
    1630            7 :   if (typ(x) == t_VEC) retmkvec2(qfbinv(gel(x,1)), negr(gel(x,2)));
    1631            7 :  return qfbinv(x);
    1632              : }
    1633              : static GEN
    1634           14 : qfrpowraw(GEN x, long n)
    1635              : {
    1636           14 :   struct qfr_data S = { NULL, NULL, NULL };
    1637           14 :   pari_sp av = avma;
    1638           14 :   if (n==1) return gcopy(x);
    1639           14 :   if (n==-1) return qfrinvraw(x);
    1640            7 :   if (typ(x)==t_QFB)
    1641              :   {
    1642            0 :     GEN D = qfb_disc(x);
    1643            0 :     if (!n) return qfr_1(x);
    1644            0 :     if (n < 0) { x = qfb_inv(x); n = -n; }
    1645            0 :     x = qfr3_powraw(x, n);
    1646            0 :     x = qfr3_to_qfr(x, D);
    1647              :   }
    1648              :   else
    1649              :   {
    1650            7 :     GEN d0 = gel(x,2);
    1651            7 :     x = gel(x,1);
    1652            7 :     if (!n) retmkvec2(qfr_1(x), real_0(precision(d0)));
    1653            7 :     if (n < 0) { x = qfb_inv(x); n = -n; }
    1654            7 :     x = qfr5_init(x, d0, &S);
    1655            7 :     if (labs(n) != 1) x = qfr5_powraw(x, n);
    1656            7 :     x = qfr5_to_qfr(x, S.D, mulrs(d0,n));
    1657              :   }
    1658            7 :   return gc_GEN(av, x);
    1659              : }
    1660              : static GEN
    1661          112 : qfrpow(GEN x, GEN n)
    1662              : {
    1663          112 :   struct qfr_data S = { NULL, NULL, NULL };
    1664          112 :   long s = signe(n);
    1665          112 :   pari_sp av = avma;
    1666          112 :   if (typ(x)==t_QFB)
    1667              :   {
    1668           42 :     if (!s) return qfr_1(x);
    1669           28 :     if (s < 0) x = qfb_inv(x);
    1670           28 :     x = qfr3_init(x, &S);
    1671           28 :     x = is_pm1(n)? qfr3_red(x, &S): qfr3_pow(x, n, &S);
    1672           28 :     x = qfr3_to_qfr(x, S.D);
    1673              :   }
    1674              :   else
    1675              :   {
    1676           70 :     GEN d0 = gel(x,2);
    1677           70 :     x = gel(x,1);
    1678           70 :     if (!s) retmkvec2(qfr_1(x), real_0(precision(d0)));
    1679           49 :     if (s < 0) x = qfb_inv(x);
    1680           49 :     x = qfr5_init(x, d0, &S);
    1681           49 :     x = is_pm1(n)? qfr5_red(x, &S): qfr5_pow(x, n, &S);
    1682           49 :     x = qfr5_to_qfr(x, S.D, mulri(d0,n));
    1683              :   }
    1684           77 :   return gc_GEN(av, x);
    1685              : }
    1686              : GEN
    1687           21 : qfbpowraw(GEN x, long n)
    1688              : {
    1689           21 :   GEN q = check_qfbext("qfbpowraw",x);
    1690           21 :   return qfb_is_qfi(q)? qfipowraw(x,n): qfrpowraw(x,n);
    1691              : }
    1692              : /* same discriminant, no distance, no checks */
    1693              : GEN
    1694     10898367 : qfbpow_i(GEN x, GEN n) { return qfb_is_qfi(x)? qfipow(x,n): qfrpow(x,n); }
    1695              : GEN
    1696      1626395 : qfbpow(GEN x, GEN n)
    1697              : {
    1698      1626395 :   GEN q = check_qfbext("qfbpow",x);
    1699      1626395 :   return qfb_is_qfi(q)? qfipow(x,n): qfrpow(x,n);
    1700              : }
    1701              : GEN
    1702      1472324 : qfbpows(GEN x, long n)
    1703              : {
    1704      1472324 :   long N[] = { evaltyp(t_INT) | _evallg(3), 0, 0};
    1705      1472324 :   affsi(n, N); return qfbpow(x, N);
    1706              : }
    1707              : 
    1708              : /* Prime forms attached to prime ideals of degree 1 */
    1709              : 
    1710              : /* assume x != 0 a t_INT, p > 0
    1711              :  * Return a t_QFB, but discriminant sign is not checked: can be used for
    1712              :  * real forms as well */
    1713              : GEN
    1714     15083067 : primeform_u(GEN x, ulong p)
    1715              : {
    1716     15083067 :   GEN c, y = cgetg(5, t_QFB);
    1717     15083067 :   pari_sp av = avma;
    1718              :   ulong b;
    1719              :   long s;
    1720              : 
    1721     15083067 :   s = mod8(x); if (signe(x) < 0 && s) s = 8-s;
    1722              :   /* 2 or 3 mod 4 */
    1723     15083067 :   if (s & 2) pari_err_DOMAIN("primeform", "disc % 4", ">",gen_1, x);
    1724     15083060 :   if (p == 2) {
    1725      4296174 :     switch(s) {
    1726       642754 :       case 0: b = 0; break;
    1727      3301766 :       case 1: b = 1; break;
    1728       351654 :       case 4: b = 2; break;
    1729            0 :       default: pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );
    1730            0 :                b = 0; /* -Wall */
    1731              :     }
    1732      4296174 :     c = shifti(subsi(s,x), -3);
    1733              :   } else {
    1734     10786886 :     b = Fl_sqrt(umodiu(x,p), p);
    1735     10786886 :     if (b == ~0UL) pari_err_SQRTN("primeform", mkintmod(x,utoi(p)) );
    1736              :     /* mod(b) != mod2(x) ? */
    1737     10786886 :     if ((b ^ s) & 1) b = p - b;
    1738     10786886 :     c = diviuexact(shifti(subii(sqru(b), x), -2), p);
    1739              :   }
    1740     15083060 :   gel(y,3) = gc_INT(av, c);
    1741     15083060 :   gel(y,4) = icopy(x);
    1742     15083060 :   gel(y,2) = utoi(b);
    1743     15083060 :   gel(y,1) = utoipos(p); return y;
    1744              : }
    1745              : 
    1746              : /* special case: p = 1 return unit form */
    1747              : GEN
    1748       135595 : primeform(GEN x, GEN p)
    1749              : {
    1750       135595 :   const char *f = "primeform";
    1751              :   pari_sp av;
    1752       135595 :   long s, sx = signe(x), sp = signe(p);
    1753              :   GEN y, b, absp;
    1754              : 
    1755       135595 :   if (typ(x) != t_INT) pari_err_TYPE(f,x);
    1756       135595 :   if (typ(p) != t_INT) pari_err_TYPE(f,p);
    1757       135595 :   if (!sp) pari_err_DOMAIN(f,"p","=",gen_0,p);
    1758       135595 :   if (!sx) pari_err_DOMAIN(f,"D","=",gen_0,x);
    1759       135595 :   if (lgefint(p) == 3)
    1760              :   {
    1761       135581 :     ulong pp = p[2];
    1762       135581 :     if (pp == 1) {
    1763        17918 :       if (sx < 0) {
    1764              :         long r;
    1765        11086 :         if (sp < 0) pari_err_IMPL("negative definite t_QFB");
    1766        11086 :         r = mod4(x);
    1767        11086 :         if (r && r != 3) pari_err_DOMAIN(f,"disc % 4",">", gen_1,x);
    1768        11086 :         return qfi_1_by_disc(x);
    1769              :       }
    1770         6832 :       y = qfr_1_by_disc(x);
    1771         6832 :       if (sp < 0) { gel(y,1) = gen_m1; togglesign(gel(y,3)); }
    1772         6832 :       return y;
    1773              :     }
    1774       117663 :     y = primeform_u(x, pp);
    1775       117656 :     if (sx < 0) {
    1776        89957 :       if (sp < 0) pari_err_IMPL("negative definite t_QFB");
    1777        89957 :       return y;
    1778              :     }
    1779        27699 :     if (sp < 0) { togglesign(gel(y,1)); togglesign(gel(y,3)); }
    1780        27699 :     return gcopy( qfr3_to_qfr(y, x) );
    1781              :   }
    1782           14 :   s = mod8(x);
    1783           14 :   if (sx < 0)
    1784              :   {
    1785            7 :     if (sp < 0) pari_err_IMPL("negative definite t_QFB");
    1786            7 :     if (s) s = 8-s;
    1787              :   }
    1788           14 :   y = cgetg(5, t_QFB);
    1789              :   /* 2 or 3 mod 4 */
    1790           14 :   if (s & 2) pari_err_DOMAIN(f, "disc % 4", ">",gen_1, x);
    1791           14 :   absp = absi_shallow(p); av = avma;
    1792           14 :   b = Fp_sqrt(x, absp); if (!b) pari_err_SQRTN(f, mkintmod(x,absp));
    1793           14 :   s &= 1; /* s = x mod 2 */
    1794              :   /* mod(b) != mod2(x) ? [Warning: we may have b == 0] */
    1795           14 :   if ((!signe(b) && s) || mod2(b) != s) b = gc_INT(av, subii(absp,b));
    1796              : 
    1797           14 :   av = avma;
    1798           14 :   gel(y,3) = gc_INT(av, diviiexact(shifti(subii(sqri(b), x), -2), p));
    1799           14 :   gel(y,4) = icopy(x);
    1800           14 :   gel(y,2) = b;
    1801           14 :   gel(y,1) = icopy(p);
    1802           14 :   return y;
    1803              : }
    1804              : 
    1805              : static GEN
    1806      2620772 : normforms(GEN D, GEN fa)
    1807              : {
    1808              :   long i, j, k, lB, aN, sa;
    1809              :   GEN a, L, V, B, N, N2;
    1810      2620772 :   int D_odd = mpodd(D);
    1811      2620772 :   a = typ(fa) == t_INT ? fa: typ(fa) == t_VEC? gel(fa,1): factorback(fa);
    1812      2620772 :   sa = signe(a);
    1813      2620772 :   if (sa==0 || (signe(D)<0 && sa<0)) return NULL;
    1814      1203972 :   V = D_odd? Zn_quad_roots(fa, gen_1, shifti(subsi(1, D), -2))
    1815      2551766 :            : Zn_quad_roots(fa, gen_0, negi(shifti(D, -2)));
    1816      2551766 :   if (!V) return NULL;
    1817       511966 :   N = gel(V,1); B = gel(V,2); lB = lg(B);
    1818       511966 :   N2 = shifti(N,1);
    1819       511966 :   aN = itou(diviiexact(a, N)); /* |a|/N */
    1820       511966 :   L = cgetg((lB-1)*aN+1, t_VEC);
    1821      2360568 :   for (k = 1, i = 1; i < lB; i++)
    1822              :   {
    1823      1848602 :     GEN b = shifti(gel(B,i), 1), c, C;
    1824      1848602 :     if (D_odd) b = addiu(b, 1);
    1825      1848602 :     c = diviiexact(shifti(subii(sqri(b), D), -2), a);
    1826      1848602 :     for (j = 0;; b = addii(b, N2))
    1827              :     {
    1828      2216676 :       gel(L, k++) = mkqfb(a, b, c, D);
    1829      2216676 :       if (++j == aN) break;
    1830       368074 :       C = addii(b, N); if (aN > 1) C = diviuexact(C, aN);
    1831       368074 :       c = sa > 0? addii(c, C): subii(c, C);
    1832              :     }
    1833              :   }
    1834       511966 :   return L;
    1835              : }
    1836              : 
    1837              : /* Let M and N in SL2(Z), return (N*M^-1)[,1] */
    1838              : static GEN
    1839       344323 : SL2_div_mul_e1(GEN N, GEN M)
    1840              : {
    1841       344323 :   GEN b = gcoeff(M,2,1), d = gcoeff(M,2,2);
    1842       344323 :   GEN A = mulii(gcoeff(N,1,1), d), B = mulii(gcoeff(N,1,2), b);
    1843       344323 :   GEN C = mulii(gcoeff(N,2,1), d), D = mulii(gcoeff(N,2,2), b);
    1844       344323 :   retmkvec2(subii(A,B), subii(C,D));
    1845              : }
    1846              : static GEN
    1847      1445682 : qfisolve_normform(GEN Q, GEN P)
    1848              : {
    1849      1445682 :   GEN a = gel(Q,1), N = gel(Q,2);
    1850      1445682 :   GEN M, b = qfi_redsl2_basecase(P, &M);
    1851      1445682 :   if (!qfb_equal(a,b)) return NULL;
    1852       102130 :   return SL2_div_mul_e1(N,M);
    1853              : }
    1854              : 
    1855              : /* Test equality modulo GL2 of two reduced forms */
    1856              : static int
    1857        61068 : GL2_qfb_equal(GEN a, GEN b)
    1858              : {
    1859        61068 :   return equalii(gel(a,1),gel(b,1))
    1860        11361 :    && absequalii(gel(a,2),gel(b,2))
    1861        72429 :    &&    equalii(gel(a,3),gel(b,3));
    1862              : }
    1863              : 
    1864              : /* Q(u,v) = p; if s < 0 return that solution; else the set of all solutions */
    1865              : static GEN
    1866        48083 : allsols(GEN Q, long s, GEN u, GEN v)
    1867              : {
    1868        48083 :   GEN w = mkvec2(u, v), b;
    1869        48083 :   if (signe(v) < 0) { u = negi(u); v = negi(v); } /* normalize for v >= 0 */
    1870        48083 :   w = mkvec2(u, v); if (s < 0) return w;
    1871        41447 :   if (!s) return mkvec(w);
    1872        39018 :   b = gel(Q,2); /* sum of the 2 solutions (if they exist) is -bv / a */
    1873        39018 :   if (signe(b))
    1874              :   { /* something to check */
    1875              :     GEN r, t;
    1876        13433 :     t = dvmdii(mulii(b, v), gel(Q,1), &r);
    1877        13433 :     if (signe(r)) return mkvec(w);
    1878         1820 :     u = addii(u, t);
    1879              :   }
    1880        27405 :   return mkvec2(w, mkvec2(negi(u), v));
    1881              : }
    1882              : static GEN
    1883       223125 : qfisolvep_all(GEN Q, GEN p, long all)
    1884              : {
    1885       223125 :   GEN R, U, V, M, N, x, q, D = qfb_disc(Q);
    1886       223125 :   long s = kronecker(D, p);
    1887              : 
    1888       223125 :   if (s < 0) return NULL;
    1889       127050 :   if (!all) s = -1; /* to indicate we want a single solution */
    1890              :   /* Solutions iff a class of maximal ideal above p is the class of Q;
    1891              :    * Two solutions iff (s > 0 and the class has order > 2), else one */
    1892       127050 :   if (!signe(gel(Q,2)))
    1893              :   { /* if principal form, use faster cornacchia */
    1894        43729 :     GEN a = gel(Q,1), c = gel(Q,3);
    1895        43729 :     if (equali1(a))
    1896              :     {
    1897        38255 :       if (!cornacchia(c, p, &M,&N)) return NULL;
    1898        33768 :       return allsols(Q, s, M, N);
    1899              :     }
    1900         5474 :     if (equali1(c))
    1901              :     {
    1902         5194 :       if (!cornacchia(a, p, &M,&N)) return NULL;
    1903          721 :       return allsols(Q, s, N, M);
    1904              :     }
    1905              :   }
    1906        83601 :   R = qfi_redsl2_basecase(Q, &U);
    1907        83601 :   if (equali1(gel(R,1)))
    1908              :   { /* principal form */
    1909        22533 :     if (!signe(gel(R,2)))
    1910              :     {
    1911         4396 :       if (!cornacchia(gel(R,3), p, &M,&N)) return NULL;
    1912          812 :       x = mkvec2(M,N);
    1913              :     }
    1914              :     else
    1915              :     { /* x^2 + xy + ((1-D)/4)y^2 = p <==> (2x + y)^2 - D y^2 = 4p */
    1916        18137 :       if (!cornacchia2(negi(D), p, &M, &N)) return NULL;
    1917         2331 :       x = subii(M,N); if (mpodd(x)) return NULL;
    1918         2331 :       x = mkvec2(shifti(x,-1), N);
    1919              :     }
    1920         3143 :     x = ZM_ZC_mul(U, x); x[0] = evaltyp(t_VEC) | _evallg(3); /* transpose */
    1921         3143 :     return allsols(Q, s, gel(x,1), gel(x,2));
    1922              :   }
    1923        61068 :   q = qfi_redsl2_basecase(primeform(D, p), &V);
    1924        61068 :   if (!GL2_qfb_equal(R,q)) return NULL;
    1925        10451 :   if (signe(gel(R,2)) != signe(gel(q,2))) gcoeff(V,2,1) = negi(gcoeff(V,2,1));
    1926        10451 :   x = SL2_div_mul_e1(U,V); return allsols(Q, s, gel(x,1), gel(x,2));
    1927              : }
    1928              : GEN
    1929            0 : qfisolvep(GEN Q, GEN p)
    1930              : {
    1931            0 :   pari_sp av = avma;
    1932            0 :   GEN x = qfisolvep_all(Q, p, 0);
    1933            0 :   return x? gc_GEN(av, x): gc_const(av, gen_0);
    1934              : }
    1935              : 
    1936              : static GEN
    1937       770126 : qfrsolve_normform(GEN N, GEN Ps, GEN rd)
    1938              : {
    1939       770126 :   pari_sp av = avma, btop;
    1940       770126 :   GEN M = N, P = qfr_redsl2_basecase(Ps, rd), Q = P;
    1941              : 
    1942       770126 :   btop = avma;
    1943              :   for(;;)
    1944              :   {
    1945      5840681 :     if (qfb_equal(gel(M,1), gel(P,1)))
    1946       154084 :       return gc_upto(av, SL2_div_mul_e1(gel(M,2),gel(P,2)));
    1947      5686597 :     if (qfb_equal(gel(N,1), gel(Q,1)))
    1948        77658 :       return gc_upto(av, SL2_div_mul_e1(gel(N,2),gel(Q,2)));
    1949      5608939 :     M = qfr_rhosl2(M, rd);
    1950      5608939 :     if (qfb_equal(gel(M,1), gel(N,1))) return gc_NULL(av);
    1951      5201735 :     Q = qfr_rhosl2(Q, rd);
    1952      5201735 :     if (qfb_equal(gel(P,1), gel(Q,1))) return gc_NULL(av);
    1953      5070555 :     if (gc_needed(btop, 1)) (void)gc_all(btop, 2, &M, &Q);
    1954              :   }
    1955              : }
    1956              : 
    1957              : GEN
    1958            0 : qfrsolvep(GEN Q, GEN p)
    1959              : {
    1960            0 :   pari_sp av = avma;
    1961            0 :   GEN N, x, rd, d = qfb_disc(Q);
    1962              : 
    1963            0 :   if (kronecker(d, p) < 0) return gc_const(av, gen_0);
    1964            0 :   rd = sqrti(d);
    1965            0 :   N = qfr_redsl2(Q, rd);
    1966            0 :   x = qfrsolve_normform(N, primeform(d, p), rd);
    1967            0 :   return x? gc_upto(av, x): gc_const(av, gen_0);
    1968              : }
    1969              : 
    1970              : static GEN
    1971      1863022 : known_prime(GEN v)
    1972              : {
    1973      1863022 :   GEN p, e, fa = check_arith_all(v, "qfbsolve");
    1974      1863022 :   if (!fa) return BPSW_psp(v)? v: NULL;
    1975        42154 :   if (lg(gel(fa,1)) != 2) return NULL;
    1976        29428 :   p = gcoeff(fa,1,1);
    1977        29428 :   e = gcoeff(fa,1,2);
    1978        29428 :   return (equali1(e) && !is_pm1(p) && signe(p) > 0)? p: NULL;
    1979              : }
    1980              : static GEN
    1981      2215808 : qfsolve_normform(GEN Q, GEN f, GEN rd)
    1982      2215808 : { return rd? qfrsolve_normform(Q, f, rd): qfisolve_normform(Q, f); }
    1983              : static GEN
    1984      2843897 : qfbsolve_primitive_i(GEN Q, GEN rd, GEN *Qr, GEN fa, long all)
    1985              : {
    1986              :   GEN x, W, F, p;
    1987              :   long i, j, l;
    1988      2843897 :   if (!rd && (p = known_prime(fa))) return qfisolvep_all(Q, p, all);
    1989      2620772 :   F = normforms(qfb_disc(Q), fa);
    1990      2620772 :   if (!F) return NULL;
    1991       511966 :   if (!*Qr) *Qr = qfbredsl2(Q, rd);
    1992       511966 :   l = lg(F); W = all? cgetg(l, t_VEC): NULL;
    1993      2727263 :   for (j = i = 1; i < l; i++)
    1994      2215808 :     if ((x = qfsolve_normform(*Qr, gel(F,i), rd)))
    1995              :     {
    1996       333872 :       if (!all) return x;
    1997       333361 :       gel(W,j++) = x;
    1998              :     }
    1999       511455 :   if (j == 1) return NULL;
    2000       127456 :   setlg(W,j); return lexsort(W);
    2001              : }
    2002              : 
    2003              : static GEN
    2004      2838598 : qfb_initrd(GEN Q) { GEN d = qfb_disc(Q); return signe(d) > 0? sqrti(d): NULL; }
    2005              : static GEN
    2006      2828371 : qfbsolve_primitive(GEN Q, GEN fa, long all)
    2007              : {
    2008      2828371 :   GEN x, Qr = NULL, rdQ = qfb_initrd(Q);
    2009      2828371 :   x = qfbsolve_primitive_i(Q, rdQ, &Qr, fa, all);
    2010      2828371 :   if (!x) return cgetg(1, t_VEC);
    2011       174832 :   return x;
    2012              : }
    2013              : 
    2014              : /* f / g^2 */
    2015              : static GEN
    2016         5299 : famat_divsqr(GEN f, GEN g)
    2017         5299 : { return famat_reduce(famat_div_shallow(f, famat_pows_shallow(g,2))); }
    2018              : static GEN
    2019        10227 : qfbsolve_all(GEN Q, GEN n, long all)
    2020              : {
    2021        10227 :   GEN W, Qr = NULL, fa = factorint(n, 0), rdQ = qfb_initrd(Q);
    2022        10227 :   GEN D = divisors_factored(mkmat2(gel(fa,1), gshift(gel(fa,2),-1)));
    2023        10227 :   long i, j, l = lg(D);
    2024        10227 :   W = all? cgetg(l, t_VEC): NULL;
    2025        25151 :   for (i = j = 1; i < l; i++)
    2026              :   {
    2027        15526 :     GEN w, d = gel(D,i), FA = i == 1? fa: famat_divsqr(fa, gel(d,2));
    2028        15526 :     if ((w = qfbsolve_primitive_i(Q, rdQ, &Qr, FA, all)))
    2029              :     {
    2030         1218 :       if (i != 1) w = RgV_Rg_mul(w, gel(d,1));
    2031         1218 :       if (!all) return w;
    2032          616 :       gel(W,j++) = w;
    2033              :     }
    2034              :   }
    2035         9625 :   if (j == 1) return cgetg(1, t_VEC);
    2036          525 :   setlg(W,j); return lexsort(shallowconcat1(W));
    2037              : }
    2038              : 
    2039              : GEN
    2040      2838605 : qfbsolve(GEN Q, GEN n, long fl)
    2041              : {
    2042      2838605 :   pari_sp av = avma;
    2043      2838605 :   if (typ(Q) != t_QFB) pari_err_TYPE("qfbsolve",Q);
    2044      2838605 :   if (fl < 0 || fl > 3) pari_err_FLAG("qfbsolve");
    2045      5666969 :   return gc_GEN(av, (fl & 2)? qfbsolve_all(Q, n, fl & 1)
    2046      2828371 :                             : qfbsolve_primitive(Q, n, fl & 1));
    2047              : }
    2048              : 
    2049              : /* 1 if there exists x,y such that x^2 + dy^2 = p, 0 otherwise;
    2050              :  * Assume d > 0 and p is prime */
    2051              : long
    2052        55328 : cornacchia(GEN d, GEN p, GEN *px, GEN *py)
    2053              : {
    2054        55328 :   pari_sp av = avma;
    2055              :   GEN b, c, r;
    2056              : 
    2057        55328 :   *px = *py = gen_0;
    2058        55328 :   b = subii(p, d);
    2059        55328 :   if (signe(b) < 0) return gc_long(av,0);
    2060        55118 :   if (signe(b) == 0) { *py = gen_1; return gc_long(av,1); }
    2061        55111 :   b = Fp_sqrt(b, p); /* sqrt(-d) */
    2062        55111 :   if (!b) return gc_long(av,0);
    2063        51380 :   b = gmael(halfgcdii(p, b), 2, 2);
    2064        51380 :   c = dvmdii(subii(p, sqri(b)), d, &r);
    2065        51380 :   if (r != gen_0 || !Z_issquareall(c, &c)) return gc_long(av,0);
    2066        35532 :   set_avma(av);
    2067        35532 :   *px = icopy(b);
    2068        35532 :   *py = icopy(c); return 1;
    2069              : }
    2070              : 
    2071              : static GEN
    2072      2595837 : lastqi(GEN Q)
    2073              : {
    2074      2595837 :   GEN s = gcoeff(Q,1,1), q = gcoeff(Q,1,2), p = absi_shallow(gcoeff(Q,2,2));
    2075      2595837 :   if (!signe(q)) return gen_0;
    2076      2595648 :   if (!signe(s)) return p;
    2077      2588048 :   if (is_pm1(q)) return subiu(p,1);
    2078      2588048 :   return divii(p, absi_shallow(q));
    2079              : }
    2080              : 
    2081              : static long
    2082      2595851 : cornacchia2_i(long av, GEN d, GEN p, GEN b, GEN px4, GEN *px, GEN *py)
    2083              : {
    2084              :   GEN M, Q, V, c, r, b2;
    2085      2595851 :   if (!signe(b)) { /* d = p,2p,3p,4p */
    2086           14 :     set_avma(av);
    2087           14 :     if (absequalii(d, px4)){ *py = gen_1; return 1; }
    2088           14 :     if (absequalii(d, p))  { *py = gen_2; return 1; }
    2089            0 :     return 0;
    2090              :   }
    2091      2595837 :   if (mod2(b) != mod2(d)) b = subii(p,b);
    2092      2595837 :   M = halfgcdii(shifti(p,1), b); Q = gel(M,1); V = gel(M, 2);
    2093      2595837 :   b = addii(mulii(gel(V,1), lastqi(Q)), gel(V,2));
    2094      2595837 :   b2 = sqri(b);
    2095      2595837 :   if (cmpii(b2,px4) > 0)
    2096              :   {
    2097      2585851 :     b = gel(V,1); b2 = sqri(b);
    2098      2585851 :     if (cmpii(b2,px4) > 0) { b = gel(V,2); b2 = sqri(b); }
    2099              :   }
    2100      2595837 :   c = dvmdii(subii(px4, b2), d, &r);
    2101      2595837 :   if (r != gen_0 || !Z_issquareall(c, &c)) return gc_long(av,0);
    2102      2555027 :   set_avma(av);
    2103      2555027 :   *px = icopy(b);
    2104      2555027 :   *py = icopy(c); return 1;
    2105              : }
    2106              : 
    2107              : /* 1 if there exists x,y such that x^2 + dy^2 = 4p, 0 otherwise;
    2108              :  * Assume d > 0 is congruent to 0 or 3 mod 4 and p is prime */
    2109              : long
    2110      2561278 : cornacchia2(GEN d, GEN p, GEN *px, GEN *py)
    2111              : {
    2112      2561278 :   pari_sp av = avma;
    2113      2561278 :   GEN b, p4 = shifti(p,2);
    2114              : 
    2115      2561278 :   *px = *py = gen_0;
    2116      2561278 :   if (abscmpii(p4, d) < 0) return gc_long(av,0);
    2117      2560459 :   if (absequaliu(p, 2))
    2118              :   {
    2119            7 :     set_avma(av);
    2120            7 :     switch (itou_or_0(d)) {
    2121            0 :       case 4: *px = gen_2; break;
    2122            0 :       case 7: *px = gen_1; break;
    2123            7 :       default: return 0;
    2124              :     }
    2125            0 :     *py = gen_1; return 1;
    2126              :   }
    2127      2560452 :   b = Fp_sqrt(negi(d), p);
    2128      2560452 :   if (!b) return gc_long(av,0);
    2129      2560368 :   return cornacchia2_i(av, d, p, b, p4, px, py);
    2130              : }
    2131              : 
    2132              : /* 1 if there exists x,y such that x^2 + dy^2 = 4p [p prime], 0 otherwise */
    2133              : long
    2134        35483 : cornacchia2_sqrt(GEN d, GEN p, GEN b, GEN *px, GEN *py)
    2135              : {
    2136        35483 :   pari_sp av = avma;
    2137        35483 :   GEN p4 = shifti(p,2);
    2138        35483 :   *px = *py = gen_0;
    2139        35483 :   if (abscmpii(p4, d) < 0) return gc_long(av,0);
    2140        35483 :   return cornacchia2_i(av, d, p, b, p4, px, py);
    2141              : }
    2142              : 
    2143              : GEN
    2144         7630 : qfbcornacchia(GEN d, GEN p)
    2145              : {
    2146         7630 :   pari_sp av = avma;
    2147              :   GEN x, y;
    2148         7630 :   if (typ(d) != t_INT || signe(d) <= 0) pari_err_TYPE("qfbcornacchia", d);
    2149         7630 :   if (typ(p) != t_INT || cmpiu(p, 2) < 0) pari_err_TYPE("qfbcornacchia", p);
    2150         7630 :   if (mod4(p)? cornacchia(d, p, &x, &y): cornacchia2(d, shifti(p, -2), &x, &y))
    2151          287 :     return gc_GEN(av, mkvec2(x, y));
    2152         7343 :   retgc_const(av, cgetg(1, t_VEC));
    2153              : }
        

Generated by: LCOV version 2.0-1