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 - Flx.c (source / functions) Coverage Total Hit
Test: PARI/GP v2.18.1 lcov report (development 31042-0fbe168e69) Lines: 86.6 % 2953 2556
Test Date: 2026-07-23 17:04:59 Functions: 82.2 % 370 304
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Copyright (C) 2004  The PARI group.
       2              : 
       3              : This file is part of the PARI/GP package.
       4              : 
       5              : PARI/GP is free software; you can redistribute it and/or modify it under the
       6              : terms of the GNU General Public License as published by the Free Software
       7              : Foundation; either version 2 of the License, or (at your option) any later
       8              : version. It is distributed in the hope that it will be useful, but WITHOUT
       9              : ANY WARRANTY WHATSOEVER.
      10              : 
      11              : Check the License for details. You should have received a copy of it, along
      12              : with the package; see the file 'COPYING'. If not, write to the Free Software
      13              : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14              : 
      15              : #include "pari.h"
      16              : #include "paripriv.h"
      17              : 
      18              : /* Not so fast arithmetic with polynomials with small coefficients. */
      19              : 
      20              : static GEN
      21   1021568730 : get_Flx_red(GEN T, GEN *B)
      22              : {
      23   1021568730 :   if (typ(T)!=t_VEC) { *B=NULL; return T; }
      24       730292 :   *B = gel(T,1); return gel(T,2);
      25              : }
      26              : 
      27              : /***********************************************************************/
      28              : /**                              Flx                                  **/
      29              : /***********************************************************************/
      30              : /* Flx objects are defined as follows:
      31              :  * Let l an ulong. An Flx is a t_VECSMALL:
      32              :  * x[0] = codeword
      33              :  * x[1] = evalvarn(variable number)  (signe is not stored).
      34              :  * x[2] = a_0 x[3] = a_1, etc. with 0 <= a_i < l
      35              :  *
      36              :  * signe(x) is not valid. Use degpol(x)>0 instead. */
      37              : /***********************************************************************/
      38              : /**                      Conversion from Flx                          **/
      39              : /***********************************************************************/
      40              : 
      41              : GEN
      42     38523117 : Flx_to_ZX(GEN z)
      43              : {
      44     38523117 :   long i, l = lg(z);
      45     38523117 :   GEN x = cgetg(l,t_POL);
      46    249320491 :   for (i=2; i<l; i++) gel(x,i) = utoi(z[i]);
      47     38523117 :   x[1] = evalsigne(l-2!=0)| z[1]; return x;
      48              : }
      49              : 
      50              : GEN
      51        71583 : Flx_to_FlxX(GEN z, long sv)
      52              : {
      53        71583 :   long i, l = lg(z);
      54        71583 :   GEN x = cgetg(l,t_POL);
      55       278765 :   for (i=2; i<l; i++) gel(x,i) = Fl_to_Flx(z[i], sv);
      56        71583 :   x[1] = evalsigne(l-2!=0)| z[1]; return x;
      57              : }
      58              : 
      59              : /* same as Flx_to_ZX, in place */
      60              : GEN
      61     36056471 : Flx_to_ZX_inplace(GEN z)
      62              : {
      63     36056471 :   long i, l = lg(z);
      64    225787691 :   for (i=2; i<l; i++) gel(z,i) = utoi(z[i]);
      65     36056471 :   settyp(z, t_POL); z[1]=evalsigne(l-2!=0)|z[1]; return z;
      66              : }
      67              : 
      68              : /*Flx_to_Flv=zx_to_zv*/
      69              : GEN
      70     67382463 : Flx_to_Flv(GEN x, long N)
      71              : {
      72     67382463 :   GEN z = cgetg(N+1,t_VECSMALL);
      73     67382463 :   long i, l = lg(x)-1;
      74     67382463 :   x++;
      75    725804634 :   for (i=1; i<l ; i++) z[i]=x[i];
      76    336777511 :   for (   ; i<=N; i++) z[i]=0;
      77     67382463 :   return z;
      78              : }
      79              : 
      80              : /*Flv_to_Flx=zv_to_zx*/
      81              : GEN
      82     26076776 : Flv_to_Flx(GEN x, long sv)
      83              : {
      84     26076776 :   long i, l=lg(x)+1;
      85     26076776 :   GEN z = cgetg(l,t_VECSMALL); z[1]=sv;
      86     26076776 :   x--;
      87    288934372 :   for (i=2; i<l ; i++) z[i]=x[i];
      88     26076776 :   return Flx_renormalize(z,l);
      89              : }
      90              : 
      91              : /*Flm_to_FlxV=zm_to_zxV*/
      92              : GEN
      93         2772 : Flm_to_FlxV(GEN x, long sv)
      94         7455 : { pari_APPLY_type(t_VEC, Flv_to_Flx(gel(x,i), sv)) }
      95              : 
      96              : /*FlxC_to_ZXC=zxC_to_ZXC*/
      97              : GEN
      98       104095 : FlxC_to_ZXC(GEN x)
      99       527845 : { pari_APPLY_type(t_COL, Flx_to_ZX(gel(x,i))) }
     100              : 
     101              : /*FlxC_to_ZXC=zxV_to_ZXV*/
     102              : GEN
     103       610763 : FlxV_to_ZXV(GEN x)
     104      2472894 : { pari_APPLY_type(t_VEC, Flx_to_ZX(gel(x,i))) }
     105              : 
     106              : void
     107      3036638 : FlxV_to_ZXV_inplace(GEN v)
     108              : {
     109              :   long i;
     110      8064529 :   for(i=1;i<lg(v);i++) gel(v,i)= Flx_to_ZX(gel(v,i));
     111      3036638 : }
     112              : 
     113              : /*FlxM_to_ZXM=zxM_to_ZXM*/
     114              : GEN
     115         2485 : FlxM_to_ZXM(GEN x)
     116         8351 : { pari_APPLY_same(FlxC_to_ZXC(gel(x,i))) }
     117              : 
     118              : GEN
     119       410249 : FlxV_to_FlxX(GEN x, long v)
     120              : {
     121       410249 :   long i, l = lg(x)+1;
     122       410249 :   GEN z = cgetg(l,t_POL); z[1] = evalvarn(v);
     123       410249 :   x--;
     124      5925058 :   for (i=2; i<l ; i++) gel(z,i) = gel(x,i);
     125       410249 :   return FlxX_renormalize(z,l);
     126              : }
     127              : 
     128              : GEN
     129            0 : FlxM_to_FlxXV(GEN x, long v)
     130            0 : { pari_APPLY_type(t_COL, FlxV_to_FlxX(gel(x,i), v)) }
     131              : 
     132              : GEN
     133            0 : FlxM_Flx_add_shallow(GEN x, GEN y, ulong p)
     134              : {
     135            0 :   long l = lg(x), i, j;
     136            0 :   GEN z = cgetg(l,t_MAT);
     137              : 
     138            0 :   if (l==1) return z;
     139            0 :   if (l != lgcols(x)) pari_err_OP( "+", x, y);
     140            0 :   for (i=1; i<l; i++)
     141              :   {
     142            0 :     GEN zi = cgetg(l,t_COL), xi = gel(x,i);
     143            0 :     gel(z,i) = zi;
     144            0 :     for (j=1; j<l; j++) gel(zi,j) = gel(xi,j);
     145            0 :     gel(zi,i) = Flx_add(gel(zi,i), y, p);
     146              :   }
     147            0 :   return z;
     148              : }
     149              : 
     150              : /***********************************************************************/
     151              : /**                      Conversion to Flx                            **/
     152              : /***********************************************************************/
     153              : /* Take an integer and return a scalar polynomial mod p,  with evalvarn=vs */
     154              : GEN
     155     22911559 : Fl_to_Flx(ulong x, long sv) { return x? mkvecsmall2(sv, x): pol0_Flx(sv); }
     156              : 
     157              : /* a X^d */
     158              : GEN
     159       947471 : monomial_Flx(ulong a, long d, long vs)
     160              : {
     161              :   GEN P;
     162       947471 :   if (a==0) return pol0_Flx(vs);
     163       947471 :   P = const_vecsmall(d+2, 0);
     164       947471 :   P[1] = vs; P[d+2] = a; return P;
     165              : }
     166              : 
     167              : GEN
     168      7605485 : Z_to_Flx(GEN x, ulong p, long sv)
     169              : {
     170      7605485 :   long u = umodiu(x,p);
     171      7605485 :   return u? mkvecsmall2(sv, u): pol0_Flx(sv);
     172              : }
     173              : 
     174              : /* return x[0 .. dx] mod p as t_VECSMALL. Assume x a t_POL*/
     175              : GEN
     176    170264758 : ZX_to_Flx(GEN x, ulong p)
     177              : {
     178    170264758 :   long i, lx = lg(x);
     179    170264758 :   GEN a = cgetg(lx, t_VECSMALL);
     180    170264758 :   a[1]=((ulong)x[1])&VARNBITS;
     181   1119913808 :   for (i=2; i<lx; i++) a[i] = umodiu(gel(x,i), p);
     182    170264758 :   return Flx_renormalize(a,lx);
     183              : }
     184              : 
     185              : /* return x[0 .. dx] mod p as t_VECSMALL. Assume x a t_POL*/
     186              : GEN
     187      6493059 : zx_to_Flx(GEN x, ulong p)
     188              : {
     189      6493059 :   long i, lx = lg(x);
     190      6493059 :   GEN a = cgetg(lx, t_VECSMALL);
     191      6493059 :   a[1] = x[1];
     192     19973331 :   for (i=2; i<lx; i++) uel(a,i) = umodsu(x[i], p);
     193      6493059 :   return Flx_renormalize(a,lx);
     194              : }
     195              : 
     196              : ulong
     197     80743024 : Rg_to_Fl(GEN x, ulong p)
     198              : {
     199     80743024 :   switch(typ(x))
     200              :   {
     201     56000066 :     case t_INT: return umodiu(x, p);
     202       480241 :     case t_FRAC: {
     203       480241 :       ulong z = umodiu(gel(x,1), p);
     204       480241 :       if (!z) return 0;
     205       470416 :       return Fl_div(z, umodiu(gel(x,2), p), p);
     206              :     }
     207       205973 :     case t_PADIC: return padic_to_Fl(x, p);
     208     24056744 :     case t_INTMOD: {
     209     24056744 :       GEN q = gel(x,1), a = gel(x,2);
     210     24056744 :       if (absequaliu(q, p)) return itou(a);
     211            0 :       if (!dvdiu(q,p)) pari_err_MODULUS("Rg_to_Fl", q, utoipos(p));
     212            0 :       return umodiu(a, p);
     213              :     }
     214            0 :     default: pari_err_TYPE("Rg_to_Fl",x);
     215              :       return 0; /* LCOV_EXCL_LINE */
     216              :   }
     217              : }
     218              : 
     219              : ulong
     220      1716462 : Rg_to_F2(GEN x)
     221              : {
     222      1716462 :   switch(typ(x))
     223              :   {
     224       281252 :     case t_INT: return mpodd(x);
     225            0 :     case t_FRAC:
     226            0 :       if (!mpodd(gel(x,2))) (void)Fl_inv(0,2); /* error */
     227            0 :       return mpodd(gel(x,1));
     228            0 :     case t_PADIC:
     229            0 :       if (!absequaliu(padic_p(x),2)) pari_err_OP("",x, mkintmodu(1,2));
     230            0 :       if (valp(x) < 0) (void)Fl_inv(0,2);
     231            0 :       return valp(x) & 1;
     232      1435210 :     case t_INTMOD: {
     233      1435210 :       GEN q = gel(x,1), a = gel(x,2);
     234      1435210 :       if (mpodd(q)) pari_err_MODULUS("Rg_to_F2", q, gen_2);
     235      1435210 :       return mpodd(a);
     236              :     }
     237            0 :     default: pari_err_TYPE("Rg_to_F2",x);
     238              :       return 0; /* LCOV_EXCL_LINE */
     239              :   }
     240              : }
     241              : 
     242              : GEN
     243      2228650 : RgX_to_Flx(GEN x, ulong p)
     244              : {
     245      2228650 :   long i, lx = lg(x);
     246      2228650 :   GEN a = cgetg(lx, t_VECSMALL);
     247      2228650 :   a[1]=((ulong)x[1])&VARNBITS;
     248     19903308 :   for (i=2; i<lx; i++) a[i] = Rg_to_Fl(gel(x,i), p);
     249      2228650 :   return Flx_renormalize(a,lx);
     250              : }
     251              : 
     252              : GEN
     253           14 : RgXV_to_FlxV(GEN x, ulong p)
     254          602 : { pari_APPLY_type(t_VEC, RgX_to_Flx(gel(x,i), p)) }
     255              : 
     256              : /* If x is a POLMOD, assume modulus is a multiple of T. */
     257              : GEN
     258      3532252 : Rg_to_Flxq(GEN x, GEN T, ulong p)
     259              : {
     260      3532252 :   long ta, tx = typ(x), v = get_Flx_var(T);
     261              :   ulong pi;
     262              :   GEN a, b;
     263      3532252 :   if (is_const_t(tx))
     264              :   {
     265      3268818 :     if (tx == t_FFELT) return FF_to_Flxq(x);
     266      2665792 :     return Fl_to_Flx(Rg_to_Fl(x, p), v);
     267              :   }
     268       263434 :   switch(tx)
     269              :   {
     270         8576 :     case t_POLMOD:
     271         8576 :       b = gel(x,1);
     272         8576 :       a = gel(x,2); ta = typ(a);
     273         8576 :       if (is_const_t(ta)) return Fl_to_Flx(Rg_to_Fl(a, p), v);
     274         8422 :       b = RgX_to_Flx(b, p); if (b[1] != v) break;
     275         8422 :       a = RgX_to_Flx(a, p); if (Flx_equal(b,T)) return a;
     276            0 :       pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
     277            0 :       if (lgpol(Flx_rem_pre(b,T,p,pi))==0) return Flx_rem_pre(a, T, p, pi);
     278            0 :       break;
     279       254858 :     case t_POL:
     280       254858 :       x = RgX_to_Flx(x,p);
     281       254858 :       if (x[1] != v) break;
     282       254858 :       return Flx_rem(x, T, p);
     283            0 :     case t_RFRAC:
     284            0 :       a = Rg_to_Flxq(gel(x,1), T,p);
     285            0 :       b = Rg_to_Flxq(gel(x,2), T,p);
     286            0 :       return Flxq_div(a,b, T,p);
     287              :   }
     288            0 :   pari_err_TYPE("Rg_to_Flxq",x);
     289              :   return NULL; /* LCOV_EXCL_LINE */
     290              : }
     291              : 
     292              : /***********************************************************************/
     293              : /**                   Basic operation on Flx                          **/
     294              : /***********************************************************************/
     295              : /* = zx_renormalize. Similar to normalizepol, in place */
     296              : GEN
     297   2234725994 : Flx_renormalize(GEN /*in place*/ x, long lx)
     298              : {
     299              :   long i;
     300   2551706390 :   for (i = lx-1; i>1; i--)
     301   2436219990 :     if (x[i]) break;
     302   2234725994 :   stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
     303   2234725994 :   setlg(x, i+1); return x;
     304              : }
     305              : 
     306              : GEN
     307      1889643 : Flx_red(GEN z, ulong p)
     308              : {
     309      1889643 :   long i, l = lg(z);
     310      1889643 :   GEN x = cgetg(l, t_VECSMALL);
     311      1889643 :   x[1] = z[1];
     312     34189034 :   for (i=2; i<l; i++) x[i] = uel(z,i)%p;
     313      1889643 :   return Flx_renormalize(x,l);
     314              : }
     315              : 
     316              : int
     317     26518102 : Flx_equal(GEN V, GEN W)
     318              : {
     319     26518102 :   long l = lg(V);
     320     26518102 :   if (lg(W) != l) return 0;
     321     27172615 :   while (--l > 1) /* do not compare variables, V[1] */
     322     26272282 :     if (V[l] != W[l]) return 0;
     323       900333 :   return 1;
     324              : }
     325              : 
     326              : GEN
     327      2666500 : random_Flx(long d1, long vs, ulong p)
     328              : {
     329      2666500 :   long i, d = d1+2;
     330      2666500 :   GEN y = cgetg(d,t_VECSMALL); y[1] = vs;
     331     18456866 :   for (i=2; i<d; i++) y[i] = random_Fl(p);
     332      2666500 :   return Flx_renormalize(y,d);
     333              : }
     334              : 
     335              : static GEN
     336      7734676 : Flx_addspec(GEN x, GEN y, ulong p, long lx, long ly)
     337              : {
     338              :   long i,lz;
     339              :   GEN z;
     340              : 
     341      7734676 :   if (ly>lx) swapspec(x,y, lx,ly);
     342      7734676 :   lz = lx+2; z = cgetg(lz, t_VECSMALL);
     343    115374589 :   for (i=0; i<ly; i++) z[i+2] = Fl_add(x[i], y[i], p);
     344     97551794 :   for (   ; i<lx; i++) z[i+2] = x[i];
     345      7734676 :   z[1] = 0; return Flx_renormalize(z, lz);
     346              : }
     347              : 
     348              : GEN
     349     77210845 : Flx_add(GEN x, GEN y, ulong p)
     350              : {
     351              :   long i,lz;
     352              :   GEN z;
     353     77210845 :   long lx=lg(x);
     354     77210845 :   long ly=lg(y);
     355     77210845 :   if (ly>lx) swapspec(x,y, lx,ly);
     356     77210845 :   lz = lx; z = cgetg(lz, t_VECSMALL); z[1]=x[1];
     357    652632542 :   for (i=2; i<ly; i++) z[i] = Fl_add(x[i], y[i], p);
     358    170790417 :   for (   ; i<lx; i++) z[i] = x[i];
     359     77210845 :   return Flx_renormalize(z, lz);
     360              : }
     361              : 
     362              : GEN
     363      9950583 : Flx_Fl_add(GEN y, ulong x, ulong p)
     364              : {
     365              :   GEN z;
     366              :   long lz, i;
     367      9950583 :   if (!lgpol(y))
     368       230308 :     return Fl_to_Flx(x,y[1]);
     369      9720275 :   lz=lg(y);
     370      9720275 :   z=cgetg(lz,t_VECSMALL);
     371      9720275 :   z[1]=y[1];
     372      9720275 :   z[2] = Fl_add(y[2],x,p);
     373     47174900 :   for(i=3;i<lz;i++)
     374     37454625 :     z[i] = y[i];
     375      9720275 :   if (lz==3) z = Flx_renormalize(z,lz);
     376      9720275 :   return z;
     377              : }
     378              : 
     379              : static GEN
     380       941456 : Flx_subspec(GEN x, GEN y, ulong p, long lx, long ly)
     381              : {
     382              :   long i,lz;
     383              :   GEN z;
     384              : 
     385       941456 :   if (ly <= lx)
     386              :   {
     387       941456 :     lz = lx+2; z = cgetg(lz, t_VECSMALL);
     388     58430209 :     for (i=0; i<ly; i++) z[i+2] = Fl_sub(x[i],y[i],p);
     389      1503744 :     for (   ; i<lx; i++) z[i+2] = x[i];
     390              :   }
     391              :   else
     392              :   {
     393            0 :     lz = ly+2; z = cgetg(lz, t_VECSMALL);
     394            0 :     for (i=0; i<lx; i++) z[i+2] = Fl_sub(x[i],y[i],p);
     395            0 :     for (   ; i<ly; i++) z[i+2] = Fl_neg(y[i],p);
     396              :   }
     397       941456 :   z[1] = 0; return Flx_renormalize(z, lz);
     398              : }
     399              : 
     400              : GEN
     401    141562515 : Flx_sub(GEN x, GEN y, ulong p)
     402              : {
     403    141562515 :   long i,lz,lx = lg(x), ly = lg(y);
     404              :   GEN z;
     405              : 
     406    141562515 :   if (ly <= lx)
     407              :   {
     408     93223102 :     lz = lx; z = cgetg(lz, t_VECSMALL);
     409    449508767 :     for (i=2; i<ly; i++) z[i] = Fl_sub(x[i],y[i],p);
     410    183495546 :     for (   ; i<lx; i++) z[i] = x[i];
     411              :   }
     412              :   else
     413              :   {
     414     48339413 :     lz = ly; z = cgetg(lz, t_VECSMALL);
     415    247193136 :     for (i=2; i<lx; i++) z[i] = Fl_sub(x[i],y[i],p);
     416    227941817 :     for (   ; i<ly; i++) z[i] = y[i]? (long)(p - y[i]): y[i];
     417              :   }
     418    141562515 :   z[1]=x[1]; return Flx_renormalize(z, lz);
     419              : }
     420              : 
     421              : GEN
     422       151980 : Flx_Fl_sub(GEN y, ulong x, ulong p)
     423              : {
     424              :   GEN z;
     425       151980 :   long lz = lg(y), i;
     426       151980 :   if (lz==2)
     427          513 :     return Fl_to_Flx(Fl_neg(x, p),y[1]);
     428       151467 :   z = cgetg(lz, t_VECSMALL);
     429       151467 :   z[1] = y[1];
     430       151467 :   z[2] = Fl_sub(uel(y,2), x, p);
     431       754576 :   for(i=3; i<lz; i++)
     432       603109 :     z[i] = y[i];
     433       151467 :   if (lz==3) z = Flx_renormalize(z,lz);
     434       151467 :   return z;
     435              : }
     436              : 
     437              : static GEN
     438      4269121 : Flx_negspec(GEN x, ulong p, long l)
     439              : {
     440              :   long i;
     441      4269121 :   GEN z = cgetg(l+2, t_VECSMALL) + 2;
     442     27892039 :   for (i=0; i<l; i++) z[i] = Fl_neg(x[i], p);
     443      4269121 :   return z-2;
     444              : }
     445              : 
     446              : GEN
     447      4269121 : Flx_neg(GEN x, ulong p)
     448              : {
     449      4269121 :   GEN z = Flx_negspec(x+2, p, lgpol(x));
     450      4269121 :   z[1] = x[1];
     451      4269121 :   return z;
     452              : }
     453              : 
     454              : GEN
     455      1892852 : Flx_neg_inplace(GEN x, ulong p)
     456              : {
     457      1892852 :   long i, l = lg(x);
     458     56576231 :   for (i=2; i<l; i++)
     459     54683379 :     if (x[i]) x[i] = p - x[i];
     460      1892852 :   return x;
     461              : }
     462              : 
     463              : GEN
     464      1182839 : Flx_double(GEN y, ulong p)
     465              : {
     466              :   long i, l;
     467      1182839 :   GEN z = cgetg_copy(y, &l); z[1] = y[1];
     468      9628919 :   for(i=2; i<l; i++) z[i] = Fl_double(y[i], p);
     469      1182839 :   return Flx_renormalize(z, l);
     470              : }
     471              : GEN
     472       551631 : Flx_triple(GEN y, ulong p)
     473              : {
     474              :   long i, l;
     475       551631 :   GEN z = cgetg_copy(y, &l); z[1] = y[1];
     476      3850338 :   for(i=2; i<l; i++) z[i] = Fl_triple(y[i], p);
     477       551631 :   return Flx_renormalize(z, l);
     478              : }
     479              : 
     480              : GEN
     481     20389790 : Flx_Fl_mul_pre(GEN y, ulong x, ulong p, ulong pi)
     482              : {
     483              :   GEN z;
     484              :   long i, l;
     485     20389790 :   if (!x) return pol0_Flx(y[1]);
     486     19358748 :   z = cgetg_copy(y, &l); z[1] = y[1];
     487     19358748 :   if (pi==0)
     488              :   {
     489     17070650 :     if (HIGHWORD(x | p))
     490            0 :       for(i=2; i<l; i++) z[i] = Fl_mul(uel(y,i), x, p);
     491              :     else
     492    101611291 :       for(i=2; i<l; i++) z[i] = (uel(y,i) * x) % p;
     493              :   } else
     494     18792113 :       for(i=2; i<l; i++) z[i] = Fl_mul_pre(uel(y,i), x, p, pi);
     495     19358748 :   return Flx_renormalize(z, l);
     496              : }
     497              : 
     498              : GEN
     499      9481826 : Flx_Fl_mul(GEN x, ulong y, ulong p)
     500      9481826 : { return Flx_Fl_mul_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
     501              : 
     502              : GEN
     503            0 : Flx_convol(GEN x, GEN y, ulong p)
     504              : {
     505            0 :   long lx = lg(x), ly = lg(y), i;
     506              :   GEN z;
     507            0 :   if (lx < ly) swapspec(x,y, lx,ly);
     508            0 :   z = cgetg(ly,t_VECSMALL); z[1] = x[1];
     509            0 :   for (i=2; i<ly; i++) uel(z,i) = Fl_mul(uel(x,i),uel(y,i), p);
     510            0 :   return Flx_renormalize(z, ly);
     511              : }
     512              : 
     513              : GEN
     514     11913433 : Flx_Fl_mul_to_monic(GEN y, ulong x, ulong p)
     515              : {
     516              :   GEN z;
     517              :   long i, l;
     518     11913433 :   z = cgetg_copy(y, &l); z[1] = y[1];
     519     11913433 :   if (HIGHWORD(x | p))
     520      5414797 :     for(i=2; i<l-1; i++) z[i] = Fl_mul(y[i], x, p);
     521              :   else
     522     27076274 :     for(i=2; i<l-1; i++) z[i] = (y[i] * x) % p;
     523     11913433 :   z[l-1] = 1; return z;
     524              : }
     525              : 
     526              : /* Return a*x^n if n>=0 and a\x^(-n) if n<0 */
     527              : GEN
     528     27608471 : Flx_shift(GEN a, long n)
     529              : {
     530     27608471 :   long i, l = lg(a);
     531              :   GEN  b;
     532     27608471 :   if (l==2 || !n) return Flx_copy(a);
     533     27261085 :   if (l+n<=2) return pol0_Flx(a[1]);
     534     27043537 :   b = cgetg(l+n, t_VECSMALL);
     535     27043537 :   b[1] = a[1];
     536     27043537 :   if (n < 0)
     537     75586465 :     for (i=2-n; i<l; i++) b[i+n] = a[i];
     538              :   else
     539              :   {
     540     53253451 :     for (i=0; i<n; i++) b[2+i] = 0;
     541    154488862 :     for (i=2; i<l; i++) b[i+n] = a[i];
     542              :   }
     543     27043537 :   return b;
     544              : }
     545              : 
     546              : GEN
     547     67778867 : Flx_normalize(GEN z, ulong p)
     548              : {
     549     67778867 :   long l = lg(z)-1;
     550     67778867 :   ulong p1 = z[l]; /* leading term */
     551     67778867 :   if (p1 == 1) return z;
     552     11913433 :   return Flx_Fl_mul_to_monic(z, Fl_inv(p1,p), p);
     553              : }
     554              : 
     555              : /* return (x * X^d) + y. Assume d > 0, shallow if x == 0*/
     556              : static GEN
     557      3970806 : Flx_addshift(GEN x, GEN y, ulong p, long d)
     558              : {
     559      3970806 :   GEN xd,yd,zd = (GEN)avma;
     560      3970806 :   long a,lz,ny = lgpol(y), nx = lgpol(x);
     561      3970806 :   long vs = x[1];
     562      3970806 :   if (nx == 0) return y;
     563      3968963 :   x += 2; y += 2; a = ny-d;
     564      3968963 :   if (a <= 0)
     565              :   {
     566        85113 :     lz = (a>nx)? ny+2: nx+d+2;
     567        85113 :     (void)new_chunk(lz); xd = x+nx; yd = y+ny;
     568      1733797 :     while (xd > x) *--zd = *--xd;
     569        85113 :     x = zd + a;
     570       165659 :     while (zd > x) *--zd = 0;
     571              :   }
     572              :   else
     573              :   {
     574      3883850 :     xd = new_chunk(d); yd = y+d;
     575      3883850 :     x = Flx_addspec(x,yd,p, nx,a);
     576      3883850 :     lz = (a>nx)? ny+2: lg(x)+d;
     577    143661019 :     x += 2; while (xd > x) *--zd = *--xd;
     578              :   }
     579     65216718 :   while (yd > y) *--zd = *--yd;
     580      3968963 :   *--zd = vs;
     581      3968963 :   *--zd = evaltyp(t_VECSMALL) | evallg(lz); return zd;
     582              : }
     583              : 
     584              : /* shift polynomial + GC; do not set evalvarn*/
     585              : static GEN
     586    649261259 : Flx_shiftip(pari_sp av, GEN x, long v)
     587              : {
     588    649261259 :   long i, lx = lg(x), ly;
     589              :   GEN y;
     590    649261259 :   if (!v || lx==2) return gc_leaf(av, x);
     591    177214786 :   ly = lx + v; /* result length */
     592    177214786 :   (void)new_chunk(ly); /* check that result fits */
     593    177214786 :   x += lx; y = (GEN)av;
     594   1283482635 :   for (i = 2; i<lx; i++) *--y = *--x;
     595    711793554 :   for (i = 0; i< v; i++) *--y = 0;
     596    177214786 :   y -= 2; y[0] = evaltyp(t_VECSMALL) | evallg(ly);
     597    177214786 :   return gc_const((pari_sp)y, y);
     598              : }
     599              : 
     600              : static long
     601   2389611307 : get_Fl_threshold(ulong p, long mul, long mul2)
     602              : {
     603   2389611307 :   return SMALL_ULONG(p) ? mul: mul2;
     604              : }
     605              : 
     606              : #define BITS_IN_QUARTULONG (BITS_IN_HALFULONG >> 1)
     607              : #define QUARTMASK ((1UL<<BITS_IN_QUARTULONG)-1UL)
     608              : #define LLQUARTWORD(x) ((x) & QUARTMASK)
     609              : #define HLQUARTWORD(x) (((x) >> BITS_IN_QUARTULONG) & QUARTMASK)
     610              : #define LHQUARTWORD(x) (((x) >> (2*BITS_IN_QUARTULONG)) & QUARTMASK)
     611              : #define HHQUARTWORD(x) (((x) >> (3*BITS_IN_QUARTULONG)) & QUARTMASK)
     612              : INLINE long
     613      8971924 : maxbitcoeffpol(ulong p, long n)
     614              : {
     615      8971924 :   GEN z = muliu(sqru(p - 1), n);
     616      8971924 :   long b = expi(z) + 1;
     617              :   /* only do expensive bit-packing if it saves at least 1 limb */
     618      8971924 :   if (b <= BITS_IN_QUARTULONG)
     619              :   {
     620       856941 :     if (nbits2nlong(n*b) == (n + 3)>>2)
     621       110872 :       b = BITS_IN_QUARTULONG;
     622              :   }
     623      8114983 :   else if (b <= BITS_IN_HALFULONG)
     624              :   {
     625      1696703 :     if (nbits2nlong(n*b) == (n + 1)>>1)
     626         5682 :       b = BITS_IN_HALFULONG;
     627              :   }
     628              :   else
     629              :   {
     630      6418280 :     long l = lgefint(z) - 2;
     631      6418280 :     if (nbits2nlong(n*b) == n*l)
     632       360226 :       b = l*BITS_IN_LONG;
     633              :   }
     634      8971924 :   return b;
     635              : }
     636              : 
     637              : INLINE ulong
     638   3482393121 : Flx_mullimb_ok(GEN x, GEN y, ulong p, long a, long b)
     639              : { /* Assume OK_ULONG*/
     640   3482393121 :   ulong p1 = 0;
     641              :   long i;
     642  16559987766 :   for (i=a; i<b; i++)
     643  13077594645 :     if (y[i])
     644              :     {
     645  10996458190 :       p1 += y[i] * x[-i];
     646  10996458190 :       if (p1 & HIGHBIT) p1 %= p;
     647              :     }
     648   3482393121 :   return p1 % p;
     649              : }
     650              : 
     651              : INLINE ulong
     652   1230369909 : Flx_mullimb(GEN x, GEN y, ulong p, ulong pi, long a, long b)
     653              : {
     654   1230369909 :   ulong p1 = 0;
     655              :   long i;
     656   3938517383 :   for (i=a; i<b; i++)
     657   2708147474 :     if (y[i])
     658   2665531050 :       p1 = Fl_addmul_pre(p1, y[i], x[-i], p, pi);
     659   1230369909 :   return p1;
     660              : }
     661              : 
     662              : /* assume nx >= ny > 0 */
     663              : static GEN
     664    356430863 : Flx_mulspec_basecase(GEN x, GEN y, ulong p, ulong pi, long nx, long ny)
     665              : {
     666              :   long i,lz,nz;
     667              :   GEN z;
     668              : 
     669    356430863 :   lz = nx+ny+1; nz = lz-2;
     670    356430863 :   z = cgetg(lz, t_VECSMALL) + 2; /* x:y:z [i] = term of degree i */
     671    356430863 :   if (!pi)
     672              :   {
     673   1180783471 :     for (i=0; i<ny; i++)z[i] = Flx_mullimb_ok(x+i,y,p,0,i+1);
     674    740130501 :     for (  ; i<nx; i++) z[i] = Flx_mullimb_ok(x+i,y,p,0,ny);
     675    919048167 :     for (  ; i<nz; i++) z[i] = Flx_mullimb_ok(x+i,y,p,i-nx+1,ny);
     676              :   }
     677              :   else
     678              :   {
     679    336567207 :     for (i=0; i<ny; i++)z[i] = Flx_mullimb(x+i,y,p,pi,0,i+1);
     680    230316949 :     for (  ; i<nx; i++) z[i] = Flx_mullimb(x+i,y,p,pi,0,ny);
     681    241871648 :     for (  ; i<nz; i++) z[i] = Flx_mullimb(x+i,y,p,pi,i-nx+1,ny);
     682              :   }
     683    356430863 :   z -= 2; return Flx_renormalize(z, lz);
     684              : }
     685              : 
     686              : static GEN
     687        14449 : int_to_Flx(GEN z, ulong p)
     688              : {
     689        14449 :   long i, l = lgefint(z);
     690        14449 :   GEN x = cgetg(l, t_VECSMALL);
     691      1236849 :   for (i=2; i<l; i++) x[i] = uel(z,i)%p;
     692        14449 :   return Flx_renormalize(x, l);
     693              : }
     694              : 
     695              : INLINE GEN
     696        11437 : Flx_mulspec_mulii(GEN a, GEN b, ulong p, long na, long nb)
     697              : {
     698        11437 :   GEN z=muliispec(a,b,na,nb);
     699        11437 :   return int_to_Flx(z,p);
     700              : }
     701              : 
     702              : static GEN
     703       556176 : Flx_to_int_halfspec(GEN a, long na)
     704              : {
     705              :   long j;
     706       556176 :   long n = (na+1)>>1UL;
     707       556176 :   GEN V = cgetipos(2+n);
     708              :   GEN w;
     709      1513393 :   for (w = int_LSW(V), j=0; j+1<na; j+=2, w=int_nextW(w))
     710       957217 :     *w = a[j]|(a[j+1]<<BITS_IN_HALFULONG);
     711       556176 :   if (j<na)
     712       407228 :     *w = a[j];
     713       556176 :   return V;
     714              : }
     715              : 
     716              : static GEN
     717       733977 : int_to_Flx_half(GEN z, ulong p)
     718              : {
     719              :   long i;
     720       733977 :   long lx = (lgefint(z)-2)*2+2;
     721       733977 :   GEN w, x = cgetg(lx, t_VECSMALL);
     722      2293472 :   for (w = int_LSW(z), i=2; i<lx; i+=2, w=int_nextW(w))
     723              :   {
     724      1559495 :     x[i]   = LOWWORD((ulong)*w)%p;
     725      1559495 :     x[i+1] = HIGHWORD((ulong)*w)%p;
     726              :   }
     727       733977 :   return Flx_renormalize(x, lx);
     728              : }
     729              : 
     730              : static GEN
     731         5543 : Flx_mulspec_halfmulii(GEN a, GEN b, ulong p, long na, long nb)
     732              : {
     733         5543 :   GEN A = Flx_to_int_halfspec(a,na);
     734         5543 :   GEN B = Flx_to_int_halfspec(b,nb);
     735         5543 :   GEN z = mulii(A,B);
     736         5543 :   return int_to_Flx_half(z,p);
     737              : }
     738              : 
     739              : static GEN
     740       211232 : Flx_to_int_quartspec(GEN a, long na)
     741              : {
     742              :   long j;
     743       211232 :   long n = (na+3)>>2UL;
     744       211232 :   GEN V = cgetipos(2+n);
     745              :   GEN w;
     746      4625177 :   for (w = int_LSW(V), j=0; j+3<na; j+=4, w=int_nextW(w))
     747      4413945 :     *w = a[j]|(a[j+1]<<BITS_IN_QUARTULONG)|(a[j+2]<<(2*BITS_IN_QUARTULONG))|(a[j+3]<<(3*BITS_IN_QUARTULONG));
     748       211232 :   switch (na-j)
     749              :   {
     750       118717 :   case 3:
     751       118717 :     *w = a[j]|(a[j+1]<<BITS_IN_QUARTULONG)|(a[j+2]<<(2*BITS_IN_QUARTULONG));
     752       118717 :     break;
     753        35407 :   case 2:
     754        35407 :     *w = a[j]|(a[j+1]<<BITS_IN_QUARTULONG);
     755        35407 :     break;
     756        28553 :   case 1:
     757        28553 :     *w = a[j];
     758        28553 :     break;
     759        28555 :   case 0:
     760        28555 :     break;
     761              :   }
     762       211232 :   return V;
     763              : }
     764              : 
     765              : static GEN
     766       110872 : int_to_Flx_quart(GEN z, ulong p)
     767              : {
     768              :   long i;
     769       110872 :   long lx = (lgefint(z)-2)*4+2;
     770       110872 :   GEN w, x = cgetg(lx, t_VECSMALL);
     771      5127515 :   for (w = int_LSW(z), i=2; i<lx; i+=4, w=int_nextW(w))
     772              :   {
     773      5016643 :     x[i]   = LLQUARTWORD((ulong)*w)%p;
     774      5016643 :     x[i+1] = HLQUARTWORD((ulong)*w)%p;
     775      5016643 :     x[i+2] = LHQUARTWORD((ulong)*w)%p;
     776      5016643 :     x[i+3] = HHQUARTWORD((ulong)*w)%p;
     777              :   }
     778       110872 :   return Flx_renormalize(x, lx);
     779              : }
     780              : 
     781              : static GEN
     782       100360 : Flx_mulspec_quartmulii(GEN a, GEN b, ulong p, long na, long nb)
     783              : {
     784       100360 :   GEN A = Flx_to_int_quartspec(a,na);
     785       100360 :   GEN B = Flx_to_int_quartspec(b,nb);
     786       100360 :   GEN z = mulii(A,B);
     787       100360 :   return int_to_Flx_quart(z,p);
     788              : }
     789              : 
     790              : /*Eval x in 2^(k*BIL) in linear time, k==2 or 3*/
     791              : static GEN
     792       681479 : Flx_eval2BILspec(GEN x, long k, long l)
     793              : {
     794       681479 :   long i, lz = k*l, ki;
     795       681479 :   GEN pz = cgetipos(2+lz);
     796     19498113 :   for (i=0; i < lz; i++)
     797     18816634 :     *int_W(pz,i) = 0UL;
     798     10089796 :   for (i=0, ki=0; i<l; i++, ki+=k)
     799      9408317 :     *int_W(pz,ki) = x[i];
     800       681479 :   return int_normalize(pz,0);
     801              : }
     802              : 
     803              : static GEN
     804       348707 : Z_mod2BIL_Flx_2(GEN x, long d, ulong p)
     805              : {
     806       348707 :   long i, offset, lm = lgefint(x)-2, l = d+3;
     807       348707 :   ulong pi = get_Fl_red(p);
     808       348707 :   GEN pol = cgetg(l, t_VECSMALL);
     809       348707 :   pol[1] = 0;
     810      9536550 :   for (i=0, offset=0; offset+1 < lm; i++, offset += 2)
     811      9187843 :     pol[i+2] = remll_pre(*int_W(x,offset+1), *int_W(x,offset), p, pi);
     812       348707 :   if (offset < lm)
     813       273562 :     pol[i+2] = (*int_W(x,offset)) % p;
     814       348707 :   return Flx_renormalize(pol,l);
     815              : }
     816              : 
     817              : static GEN
     818            0 : Z_mod2BIL_Flx_3(GEN x, long d, ulong p)
     819              : {
     820            0 :   long i, offset, lm = lgefint(x)-2, l = d+3;
     821            0 :   ulong pi = get_Fl_red(p);
     822            0 :   GEN pol = cgetg(l, t_VECSMALL);
     823            0 :   pol[1] = 0;
     824            0 :   for (i=0, offset=0; offset+2 < lm; i++, offset += 3)
     825            0 :     pol[i+2] = remlll_pre(*int_W(x,offset+2), *int_W(x,offset+1),
     826            0 :                           *int_W(x,offset), p, pi);
     827            0 :   if (offset+1 < lm)
     828            0 :     pol[i+2] = remll_pre(*int_W(x,offset+1), *int_W(x,offset), p, pi);
     829            0 :   else if (offset < lm)
     830            0 :     pol[i+2] = (*int_W(x,offset)) % p;
     831            0 :   return Flx_renormalize(pol,l);
     832              : }
     833              : 
     834              : static GEN
     835       345777 : Z_mod2BIL_Flx(GEN x, long bs, long d, ulong p)
     836              : {
     837       345777 :   return bs==2 ? Z_mod2BIL_Flx_2(x, d, p): Z_mod2BIL_Flx_3(x, d, p);
     838              : }
     839              : 
     840              : static GEN
     841       332313 : Flx_mulspec_mulii_inflate(GEN x, GEN y, long N, ulong p, long nx, long ny)
     842              : {
     843       332313 :   pari_sp av = avma;
     844       332313 :   GEN z = mulii(Flx_eval2BILspec(x,N,nx), Flx_eval2BILspec(y,N,ny));
     845       332313 :   return gc_upto(av, Z_mod2BIL_Flx(z, N, nx+ny-2, p));
     846              : }
     847              : 
     848              : static GEN
     849     22387649 : kron_pack_Flx_spec_bits(GEN x, long b, long l) {
     850              :   GEN y;
     851              :   long i;
     852     22387649 :   if (l == 0)
     853      3794548 :     return gen_0;
     854     18593101 :   y = cgetg(l + 1, t_VECSMALL);
     855    894011326 :   for(i = 1; i <= l; i++)
     856    875418225 :     y[i] = x[l - i];
     857     18593101 :   return nv_fromdigits_2k(y, b);
     858              : }
     859              : 
     860              : /* assume b < BITS_IN_LONG */
     861              : static GEN
     862      6446145 : kron_unpack_Flx_bits_narrow(GEN z, long b, ulong p) {
     863      6446145 :   GEN v = binary_2k_nv(z, b), x;
     864      6446145 :   long i, l = lg(v) + 1;
     865      6446145 :   x = cgetg(l, t_VECSMALL);
     866    687499108 :   for (i = 2; i < l; i++)
     867    681052963 :     x[i] = v[l - i] % p;
     868      6446145 :   return Flx_renormalize(x, l);
     869              : }
     870              : 
     871              : static GEN
     872      5979729 : kron_unpack_Flx_bits_wide(GEN z, long b, ulong p, ulong pi) {
     873      5979729 :   GEN v = binary_2k(z, b), x, y;
     874      5979729 :   long i, l = lg(v) + 1, ly;
     875      5979729 :   x = cgetg(l, t_VECSMALL);
     876    253053653 :   for (i = 2; i < l; i++) {
     877    247073924 :     y = gel(v, l - i);
     878    247073924 :     ly = lgefint(y);
     879    247073924 :     switch (ly) {
     880      6290663 :     case 2: x[i] = 0; break;
     881     31496598 :     case 3: x[i] = *int_W_lg(y, 0, ly) % p; break;
     882    193253268 :     case 4: x[i] = remll_pre(*int_W_lg(y, 1, ly), *int_W_lg(y, 0, ly), p, pi); break;
     883     32066790 :     case 5: x[i] = remlll_pre(*int_W_lg(y, 2, ly), *int_W_lg(y, 1, ly),
     884     16033395 :                               *int_W_lg(y, 0, ly), p, pi); break;
     885            0 :     default: x[i] = umodiu(gel(v, l - i), p);
     886              :     }
     887              :   }
     888      5979729 :   return Flx_renormalize(x, l);
     889              : }
     890              : 
     891              : static GEN
     892      7768105 : Flx_mulspec_Kronecker(GEN A, GEN B, long b, ulong p, long lA, long lB)
     893              : {
     894              :   GEN C, D;
     895      7768105 :   pari_sp av = avma;
     896      7768105 :   A =  kron_pack_Flx_spec_bits(A, b, lA);
     897      7768105 :   B =  kron_pack_Flx_spec_bits(B, b, lB);
     898      7768105 :   C = gc_INT(av, mulii(A, B));
     899      7768105 :   if (b < BITS_IN_LONG)
     900      2204096 :     D =  kron_unpack_Flx_bits_narrow(C, b, p);
     901              :   else
     902              :   {
     903      5564009 :     ulong pi = get_Fl_red(p);
     904      5564009 :     D = kron_unpack_Flx_bits_wide(C, b, p, pi);
     905              :   }
     906      7768105 :   return D;
     907              : }
     908              : 
     909              : static GEN
     910       727039 : Flx_sqrspec_Kronecker(GEN A, long b, ulong p, long lA)
     911              : {
     912              :   GEN C, D;
     913       727039 :   A =  kron_pack_Flx_spec_bits(A, b, lA);
     914       727039 :   C = sqri(A);
     915       727039 :   if (b < BITS_IN_LONG)
     916       478671 :     D =  kron_unpack_Flx_bits_narrow(C, b, p);
     917              :   else
     918              :   {
     919       248368 :     ulong pi = get_Fl_red(p);
     920       248368 :     D = kron_unpack_Flx_bits_wide(C, b, p, pi);
     921              :   }
     922       727039 :   return D;
     923              : }
     924              : 
     925              : /* fast product (Karatsuba) of polynomials a,b. These are not real GENs, a+2,
     926              :  * b+2 were sent instead. na, nb = number of terms of a, b.
     927              :  * Only c, c0, c1, c2 are genuine GEN.
     928              :  */
     929              : static GEN
     930    399610311 : Flx_mulspec(GEN a, GEN b, ulong p, ulong pi, long na, long nb)
     931              : {
     932              :   GEN a0,c,c0;
     933    399610311 :   long n0, n0a, i, v = 0;
     934              :   pari_sp av;
     935              : 
     936    506397598 :   while (na && !a[0]) { a++; na--; v++; }
     937    587153769 :   while (nb && !b[0]) { b++; nb--; v++; }
     938    399610311 :   if (na < nb) swapspec(a,b, na,nb);
     939    399610311 :   if (!nb) return pol0_Flx(0);
     940              : 
     941    366596312 :   av = avma;
     942    366596312 :   if (nb >= get_Fl_threshold(p, Flx_MUL_MULII_LIMIT, Flx_MUL2_MULII_LIMIT))
     943              :   {
     944      8217758 :     long m = maxbitcoeffpol(p,nb);
     945      8217758 :     switch (m)
     946              :     {
     947       100360 :     case BITS_IN_QUARTULONG:
     948       100360 :       return Flx_shiftip(av,Flx_mulspec_quartmulii(a,b,p,na,nb), v);
     949         5543 :     case BITS_IN_HALFULONG:
     950         5543 :       return Flx_shiftip(av,Flx_mulspec_halfmulii(a,b,p,na,nb), v);
     951        11437 :     case BITS_IN_LONG:
     952        11437 :       return Flx_shiftip(av,Flx_mulspec_mulii(a,b,p,na,nb), v);
     953       332313 :     case 2*BITS_IN_LONG:
     954       332313 :       return Flx_shiftip(av,Flx_mulspec_mulii_inflate(a,b,2,p,na,nb), v);
     955            0 :     case 3*BITS_IN_LONG:
     956            0 :       return Flx_shiftip(av,Flx_mulspec_mulii_inflate(a,b,3,p,na,nb), v);
     957      7768105 :     default:
     958      7768105 :       return Flx_shiftip(av,Flx_mulspec_Kronecker(a,b,m,p,na,nb), v);
     959              :     }
     960              :   }
     961    358378554 :   if (nb < get_Fl_threshold(p, Flx_MUL_KARATSUBA_LIMIT, Flx_MUL2_KARATSUBA_LIMIT))
     962    356430863 :     return Flx_shiftip(av,Flx_mulspec_basecase(a,b,p,pi,na,nb), v);
     963      1947691 :   i=(na>>1); n0=na-i; na=i;
     964      1947691 :   a0=a+n0; n0a=n0;
     965      2764332 :   while (n0a && !a[n0a-1]) n0a--;
     966              : 
     967      1947691 :   if (nb > n0)
     968              :   {
     969              :     GEN b0,c1,c2;
     970              :     long n0b;
     971              : 
     972      1892852 :     nb -= n0; b0 = b+n0; n0b = n0;
     973      3023871 :     while (n0b && !b[n0b-1]) n0b--;
     974      1892852 :     c =  Flx_mulspec(a,b,p,pi,n0a,n0b);
     975      1892852 :     c0 = Flx_mulspec(a0,b0,p,pi,na,nb);
     976              : 
     977      1892852 :     c2 = Flx_addspec(a0,a,p,na,n0a);
     978      1892852 :     c1 = Flx_addspec(b0,b,p,nb,n0b);
     979              : 
     980      1892852 :     c1 = Flx_mul_pre(c1,c2,p,pi);
     981      1892852 :     c2 = Flx_add(c0,c,p);
     982              : 
     983      1892852 :     c2 = Flx_neg_inplace(c2,p);
     984      1892852 :     c2 = Flx_add(c1,c2,p);
     985      1892852 :     c0 = Flx_addshift(c0,c2, p, n0);
     986              :   }
     987              :   else
     988              :   {
     989        54839 :     c  = Flx_mulspec(a,b,p,pi,n0a,nb);
     990        54839 :     c0 = Flx_mulspec(a0,b,p,pi,na,nb);
     991              :   }
     992      1947691 :   c0 = Flx_addshift(c0,c,p,n0);
     993      1947691 :   return Flx_shiftip(av,c0, v);
     994              : }
     995              : 
     996              : GEN
     997    393575947 : Flx_mul_pre(GEN x, GEN y, ulong p, ulong pi)
     998              : {
     999    393575947 :   GEN z = Flx_mulspec(x+2,y+2,p, pi, lgpol(x),lgpol(y));
    1000    393575947 :   z[1] = x[1]; return z;
    1001              : }
    1002              : GEN
    1003     26819185 : Flx_mul(GEN x, GEN y, ulong p)
    1004     26819185 : { return Flx_mul_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    1005              : 
    1006              : static GEN
    1007    281845640 : Flx_sqrspec_basecase(GEN x, ulong p, ulong pi, long nx)
    1008              : {
    1009              :   long i, lz, nz;
    1010              :   ulong p1;
    1011              :   GEN z;
    1012              : 
    1013    281845640 :   if (!nx) return pol0_Flx(0);
    1014    281845640 :   lz = (nx << 1) + 1, nz = lz-2;
    1015    281845640 :   z = cgetg(lz, t_VECSMALL) + 2;
    1016    281845640 :   if (!pi)
    1017              :   {
    1018    217398404 :     z[0] = x[0]*x[0]%p;
    1019    931216851 :     for (i=1; i<nx; i++)
    1020              :     {
    1021    713818447 :       p1 = Flx_mullimb_ok(x+i,x,p,0, (i+1)>>1);
    1022    713818447 :       p1 <<= 1;
    1023    713818447 :       if ((i&1) == 0) p1 += x[i>>1] * x[i>>1];
    1024    713818447 :       z[i] = p1 % p;
    1025              :     }
    1026    931216851 :     for (  ; i<nz; i++)
    1027              :     {
    1028    713818447 :       p1 = Flx_mullimb_ok(x+i,x,p,i-nx+1, (i+1)>>1);
    1029    713818447 :       p1 <<= 1;
    1030    713818447 :       if ((i&1) == 0) p1 += x[i>>1] * x[i>>1];
    1031    713818447 :       z[i] = p1 % p;
    1032              :     }
    1033              :   }
    1034              :   else
    1035              :   {
    1036     64447236 :     z[0] = Fl_sqr_pre(x[0], p, pi);
    1037    417297627 :     for (i=1; i<nx; i++)
    1038              :     {
    1039    352850391 :       p1 = Flx_mullimb(x+i,x,p,pi,0, (i+1)>>1);
    1040    352850391 :       p1 = Fl_add(p1, p1, p);
    1041    352850391 :       if ((i&1) == 0) p1 = Fl_add(p1, Fl_sqr_pre(x[i>>1], p, pi), p);
    1042    352850391 :       z[i] = p1;
    1043              :     }
    1044    417297627 :     for (  ; i<nz; i++)
    1045              :     {
    1046    352850391 :       p1 = Flx_mullimb(x+i,x,p,pi,i-nx+1, (i+1)>>1);
    1047    352850391 :       p1 = Fl_add(p1, p1, p);
    1048    352850391 :       if ((i&1) == 0) p1 = Fl_add(p1, Fl_sqr_pre(x[i>>1], p, pi), p);
    1049    352850391 :       z[i] = p1;
    1050              :     }
    1051              :   }
    1052    281845640 :   z -= 2; return Flx_renormalize(z, lz);
    1053              : }
    1054              : 
    1055              : static GEN
    1056         3012 : Flx_sqrspec_sqri(GEN a, ulong p, long na)
    1057              : {
    1058         3012 :   GEN z=sqrispec(a,na);
    1059         3012 :   return int_to_Flx(z,p);
    1060              : }
    1061              : 
    1062              : static GEN
    1063          139 : Flx_sqrspec_halfsqri(GEN a, ulong p, long na)
    1064              : {
    1065          139 :   GEN z = sqri(Flx_to_int_halfspec(a,na));
    1066          139 :   return int_to_Flx_half(z,p);
    1067              : }
    1068              : 
    1069              : static GEN
    1070        10512 : Flx_sqrspec_quartsqri(GEN a, ulong p, long na)
    1071              : {
    1072        10512 :   GEN z = sqri(Flx_to_int_quartspec(a,na));
    1073        10512 :   return int_to_Flx_quart(z,p);
    1074              : }
    1075              : 
    1076              : static GEN
    1077        13464 : Flx_sqrspec_sqri_inflate(GEN x, long N, ulong p, long nx)
    1078              : {
    1079        13464 :   pari_sp av = avma;
    1080        13464 :   GEN  z = sqri(Flx_eval2BILspec(x,N,nx));
    1081        13464 :   return gc_upto(av, Z_mod2BIL_Flx(z, N, (nx-1)*2, p));
    1082              : }
    1083              : 
    1084              : static GEN
    1085    282911709 : Flx_sqrspec(GEN a, ulong p, ulong pi, long na)
    1086              : {
    1087              :   GEN a0, c, c0;
    1088    282911709 :   long n0, n0a, i, v = 0, m;
    1089              :   pari_sp av;
    1090              : 
    1091    404947111 :   while (na && !a[0]) { a++; na--; v += 2; }
    1092    282911709 :   if (!na) return pol0_Flx(0);
    1093              : 
    1094    282664947 :   av = avma;
    1095    282664947 :   if (na >= get_Fl_threshold(p, Flx_SQR_SQRI_LIMIT, Flx_SQR2_SQRI_LIMIT))
    1096              :   {
    1097       754166 :     m = maxbitcoeffpol(p,na);
    1098       754166 :     switch(m)
    1099              :     {
    1100        10512 :     case BITS_IN_QUARTULONG:
    1101        10512 :       return Flx_shiftip(av, Flx_sqrspec_quartsqri(a,p,na), v);
    1102          139 :     case BITS_IN_HALFULONG:
    1103          139 :       return Flx_shiftip(av, Flx_sqrspec_halfsqri(a,p,na), v);
    1104         3012 :     case BITS_IN_LONG:
    1105         3012 :       return Flx_shiftip(av, Flx_sqrspec_sqri(a,p,na), v);
    1106        13464 :     case 2*BITS_IN_LONG:
    1107        13464 :       return Flx_shiftip(av, Flx_sqrspec_sqri_inflate(a,2,p,na), v);
    1108            0 :     case 3*BITS_IN_LONG:
    1109            0 :       return Flx_shiftip(av, Flx_sqrspec_sqri_inflate(a,3,p,na), v);
    1110       727039 :     default:
    1111       727039 :       return Flx_shiftip(av, Flx_sqrspec_Kronecker(a,m,p,na), v);
    1112              :     }
    1113              :   }
    1114    281910781 :   if (na < get_Fl_threshold(p, Flx_SQR_KARATSUBA_LIMIT, Flx_SQR2_KARATSUBA_LIMIT))
    1115    281845640 :     return Flx_shiftip(av, Flx_sqrspec_basecase(a,p,pi,na), v);
    1116        65141 :   i=(na>>1); n0=na-i; na=i;
    1117        65141 :   a0=a+n0; n0a=n0;
    1118        80228 :   while (n0a && !a[n0a-1]) n0a--;
    1119              : 
    1120        65141 :   c = Flx_sqrspec(a,p,pi,n0a);
    1121        65141 :   c0= Flx_sqrspec(a0,p,pi,na);
    1122        65141 :   if (p == 2) n0 *= 2;
    1123              :   else
    1124              :   {
    1125        65122 :     GEN c1, t = Flx_addspec(a0,a,p,na,n0a);
    1126        65122 :     t = Flx_sqr_pre(t,p,pi);
    1127        65122 :     c1= Flx_add(c0,c, p);
    1128        65122 :     c1= Flx_sub(t, c1, p);
    1129        65122 :     c0 = Flx_addshift(c0,c1,p,n0);
    1130              :   }
    1131        65141 :   c0 = Flx_addshift(c0,c,p,n0);
    1132        65141 :   return Flx_shiftip(av,c0,v);
    1133              : }
    1134              : 
    1135              : GEN
    1136    282781427 : Flx_sqr_pre(GEN x, ulong p, ulong pi)
    1137              : {
    1138    282781427 :   GEN z = Flx_sqrspec(x+2,p, pi, lgpol(x));
    1139    282781427 :   z[1] = x[1]; return z;
    1140              : }
    1141              : GEN
    1142       394177 : Flx_sqr(GEN x, ulong p)
    1143       394177 : { return Flx_sqr_pre(x, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    1144              : 
    1145              : GEN
    1146         4020 : Flx_powu_pre(GEN x, ulong n, ulong p, ulong pi)
    1147              : {
    1148         4020 :   GEN y = pol1_Flx(x[1]), z;
    1149              :   ulong m;
    1150         4020 :   if (n == 0) return y;
    1151         4020 :   m = n; z = x;
    1152              :   for (;;)
    1153              :   {
    1154        13037 :     if (m&1UL) y = Flx_mul_pre(y,z, p, pi);
    1155        13037 :     m >>= 1; if (!m) return y;
    1156         9017 :     z = Flx_sqr_pre(z, p, pi);
    1157              :   }
    1158              : }
    1159              : GEN
    1160            0 : Flx_powu(GEN x, ulong n, ulong p)
    1161              : {
    1162            0 :   if (n == 0) return pol1_Flx(x[1]);
    1163            0 :   return Flx_powu_pre(x, n, p, SMALL_ULONG(p)? 0: get_Fl_red(p));
    1164              : }
    1165              : 
    1166              : GEN
    1167        13965 : Flx_halve(GEN y, ulong p)
    1168              : {
    1169              :   GEN z;
    1170              :   long i, l;
    1171        13965 :   z = cgetg_copy(y, &l); z[1] = y[1];
    1172        57625 :   for(i=2; i<l; i++) uel(z,i) = Fl_halve(uel(y,i), p);
    1173        13965 :   return z;
    1174              : }
    1175              : 
    1176              : static GEN
    1177      7414319 : Flx_recipspec(GEN x, long l, long n)
    1178              : {
    1179              :   long i;
    1180      7414319 :   GEN z=cgetg(n+2,t_VECSMALL)+2;
    1181    124654067 :   for(i=0; i<l; i++)
    1182    117239748 :     z[n-i-1] = x[i];
    1183     16161185 :   for(   ; i<n; i++)
    1184      8746866 :     z[n-i-1] = 0;
    1185      7414319 :   return Flx_renormalize(z-2,n+2);
    1186              : }
    1187              : 
    1188              : GEN
    1189            0 : Flx_recip(GEN x)
    1190              : {
    1191            0 :   GEN z=Flx_recipspec(x+2,lgpol(x),lgpol(x));
    1192            0 :   z[1]=x[1];
    1193            0 :   return z;
    1194              : }
    1195              : 
    1196              : /* Return P(x * h) */
    1197              : GEN
    1198            0 : Flx_unscale(GEN P, ulong h, ulong p)
    1199              : {
    1200              :   long i, l;
    1201            0 :   ulong hi = 1UL;
    1202            0 :   GEN Q = cgetg_copy(P, &l);
    1203            0 :   Q[1] = P[1];
    1204            0 :   if (l == 2) return Q;
    1205            0 :   uel(Q,2) = uel(P,2);
    1206            0 :   for (i=3; i<l; i++)
    1207              :   {
    1208            0 :     hi = Fl_mul(hi, h, p);
    1209            0 :     uel(Q,i) = Fl_mul(uel(P,i), hi, p);
    1210              :   }
    1211            0 :   return Q;
    1212              : }
    1213              : /* Return h^degpol(P) P(x / h) */
    1214              : GEN
    1215         1117 : Flx_rescale(GEN P, ulong h, ulong p)
    1216              : {
    1217         1117 :   long i, l = lg(P);
    1218         1117 :   GEN Q = cgetg(l,t_VECSMALL);
    1219         1117 :   ulong hi = h;
    1220         1117 :   Q[l-1] = P[l-1];
    1221        12538 :   for (i=l-2; i>=2; i--)
    1222              :   {
    1223        12538 :     Q[i] = Fl_mul(P[i], hi, p);
    1224        12538 :     if (i == 2) break;
    1225        11421 :     hi = Fl_mul(hi,h, p);
    1226              :   }
    1227         1117 :   Q[1] = P[1]; return Q;
    1228              : }
    1229              : 
    1230              : /* x/polrecip(P)+O(x^n); allow pi = 0 */
    1231              : static GEN
    1232       134967 : Flx_invBarrett_basecase(GEN T, ulong p, ulong pi)
    1233              : {
    1234       134967 :   long i, l=lg(T)-1, lr=l-1, k;
    1235       134967 :   GEN r=cgetg(lr,t_VECSMALL); r[1] = T[1];
    1236       134967 :   r[2] = 1;
    1237       134967 :   if (!pi)
    1238       784007 :     for (i=3;i<lr;i++)
    1239              :     {
    1240       776645 :       ulong u = uel(T, l-i+2);
    1241     46900361 :       for (k=3; k<i; k++)
    1242     46123716 :         { u += uel(T,l-i+k) * uel(r, k); if (u & HIGHBIT) u %= p; }
    1243       776645 :       r[i] = Fl_neg(u % p, p);
    1244              :     }
    1245              :   else
    1246      2122222 :     for (i=3;i<lr;i++)
    1247              :     {
    1248      1994617 :       ulong u = Fl_neg(uel(T,l-i+2), p);
    1249     59830442 :       for (k=3; k<i; k++)
    1250              :       {
    1251     57835825 :         ulong t = Fl_neg(uel(T,l-i+k), p);
    1252     57835825 :         u = Fl_addmul_pre(u, t, uel(r,k), p, pi);
    1253              :       }
    1254      1994617 :       r[i] = u;
    1255              :     }
    1256       134967 :   return Flx_renormalize(r,lr);
    1257              : }
    1258              : 
    1259              : /* Return new lgpol */
    1260              : static long
    1261      2236844 : Flx_lgrenormalizespec(GEN x, long lx)
    1262              : {
    1263              :   long i;
    1264      7711508 :   for (i = lx-1; i>=0; i--)
    1265      7710602 :     if (x[i]) break;
    1266      2236844 :   return i+1;
    1267              : }
    1268              : /* allow pi = 0 */
    1269              : static GEN
    1270        24044 : Flx_invBarrett_Newton(GEN T, ulong p, ulong pi)
    1271              : {
    1272        24044 :   long nold, lx, lz, lq, l = degpol(T), lQ;
    1273        24044 :   GEN q, y, z, x = zero_zv(l+1) + 2;
    1274        24044 :   ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
    1275              :   pari_sp av;
    1276              : 
    1277        24044 :   y = T+2;
    1278        24044 :   q = Flx_recipspec(y,l+1,l+1); lQ = lgpol(q); q+=2;
    1279        24044 :   av = avma;
    1280              :   /* We work on _spec_ Flx's, all the l[xzq12] below are lgpol's */
    1281              : 
    1282              :   /* initialize */
    1283        24044 :   x[0] = Fl_inv(q[0], p);
    1284        24044 :   if (lQ>1 && q[1])
    1285         5861 :   {
    1286         5861 :     ulong u = q[1];
    1287         5861 :     if (x[0] != 1) u = Fl_mul(u, Fl_sqr(x[0],p), p);
    1288         5861 :     x[1] = p - u; lx = 2;
    1289              :   }
    1290              :   else
    1291        18183 :     lx = 1;
    1292        24044 :   nold = 1;
    1293       166034 :   for (; mask > 1; set_avma(av))
    1294              :   { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
    1295       141990 :     long i, lnew, nnew = nold << 1;
    1296              : 
    1297       141990 :     if (mask & 1) nnew--;
    1298       141990 :     mask >>= 1;
    1299              : 
    1300       141990 :     lnew = nnew + 1;
    1301       141990 :     lq = Flx_lgrenormalizespec(q, minss(lQ, lnew));
    1302       141990 :     z = Flx_mulspec(x, q, p, pi, lx, lq); /* FIXME: high product */
    1303       141990 :     lz = lgpol(z); if (lz > lnew) lz = lnew;
    1304       141990 :     z += 2;
    1305              :     /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
    1306       327752 :     for (i = nold; i < lz; i++) if (z[i]) break;
    1307       141990 :     nold = nnew;
    1308       141990 :     if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
    1309              : 
    1310              :     /* z + i represents (x*q - 1) / t^i */
    1311       106094 :     lz = Flx_lgrenormalizespec (z+i, lz-i);
    1312       106094 :     z = Flx_mulspec(x, z+i, p, pi, lx, lz); /* FIXME: low product */
    1313       106094 :     lz = lgpol(z); z += 2;
    1314       106094 :     if (lz > lnew-i) lz = Flx_lgrenormalizespec(z, lnew-i);
    1315              : 
    1316       106094 :     lx = lz+ i;
    1317       106094 :     y  = x + i; /* x -= z * t^i, in place */
    1318      1106928 :     for (i = 0; i < lz; i++) y[i] = Fl_neg(z[i], p);
    1319              :   }
    1320        24044 :   x -= 2; setlg(x, lx + 2); x[1] = T[1];
    1321        24044 :   return x;
    1322              : }
    1323              : 
    1324              : /* allow pi = 0 */
    1325              : static GEN
    1326       160314 : Flx_invBarrett_pre(GEN T, ulong p, ulong pi)
    1327              : {
    1328       160314 :   pari_sp ltop = avma;
    1329       160314 :   long l = lgpol(T);
    1330              :   GEN r;
    1331       160314 :   if (l < 3) return pol0_Flx(T[1]);
    1332       159011 :   if (l < get_Fl_threshold(p, Flx_INVBARRETT_LIMIT, Flx_INVBARRETT2_LIMIT))
    1333              :   {
    1334       134967 :     ulong c = T[l+1];
    1335       134967 :     if (c != 1)
    1336              :     {
    1337        98118 :       ulong ci = Fl_inv(c,p);
    1338        98118 :       T = Flx_Fl_mul_pre(T, ci, p, pi);
    1339        98118 :       r = Flx_invBarrett_basecase(T, p, pi);
    1340        98118 :       r = Flx_Fl_mul_pre(r, ci, p, pi);
    1341              :     }
    1342              :     else
    1343        36849 :       r = Flx_invBarrett_basecase(T, p, pi);
    1344              :   }
    1345              :   else
    1346        24044 :     r = Flx_invBarrett_Newton(T, p, pi);
    1347       159011 :   return gc_leaf(ltop, r);
    1348              : }
    1349              : GEN
    1350            0 : Flx_invBarrett(GEN T, ulong p)
    1351            0 : { return Flx_invBarrett_pre(T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    1352              : 
    1353              : /* allow pi = 0 */
    1354              : GEN
    1355     98338446 : Flx_get_red_pre(GEN T, ulong p, ulong pi)
    1356              : {
    1357     98338446 :   if (typ(T)!=t_VECSMALL
    1358     98301254 :     || lgpol(T) < get_Fl_threshold(p, Flx_BARRETT_LIMIT,
    1359              :                                        Flx_BARRETT2_LIMIT))
    1360     98330417 :     return T;
    1361         8029 :   retmkvec2(Flx_invBarrett_pre(T, p, pi),T);
    1362              : }
    1363              : GEN
    1364     14550265 : Flx_get_red(GEN T, ulong p)
    1365              : {
    1366     14550265 :   if (typ(T)!=t_VECSMALL
    1367     14550158 :     || lgpol(T) < get_Fl_threshold(p, Flx_BARRETT_LIMIT,
    1368              :                                        Flx_BARRETT2_LIMIT))
    1369     14544871 :     return T;
    1370         5394 :   retmkvec2(Flx_invBarrett_pre(T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)),T);
    1371              : }
    1372              : 
    1373              : /* separate from Flx_divrem for maximal speed. */
    1374              : static GEN
    1375    815529340 : Flx_rem_basecase(GEN x, GEN y, ulong p, ulong pi)
    1376              : {
    1377              :   pari_sp av;
    1378              :   GEN z, c;
    1379              :   long dx,dy,dy1,dz,i,j;
    1380              :   ulong p1,inv;
    1381    815529340 :   long vs=x[1];
    1382              : 
    1383    815529340 :   dy = degpol(y); if (!dy) return pol0_Flx(x[1]);
    1384    779390587 :   dx = degpol(x);
    1385    779390587 :   dz = dx-dy; if (dz < 0) return Flx_copy(x);
    1386    779390587 :   x += 2; y += 2;
    1387    779390587 :   inv = y[dy];
    1388    779390587 :   if (inv != 1UL) inv = Fl_inv(inv,p);
    1389    932516058 :   for (dy1=dy-1; dy1>=0 && !y[dy1]; dy1--);
    1390              : 
    1391    779390581 :   c = cgetg(dy+3, t_VECSMALL); c[1]=vs; c += 2; av=avma;
    1392    779390581 :   z = cgetg(dz+3, t_VECSMALL); z[1]=vs; z += 2;
    1393              : 
    1394    779390581 :   if (!pi)
    1395              :   {
    1396    494986694 :     z[dz] = (inv*x[dx]) % p;
    1397   1842765221 :     for (i=dx-1; i>=dy; --i)
    1398              :     {
    1399   1347778527 :       p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1400  10684676435 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1401              :       {
    1402   9336897908 :         p1 += z[j]*y[i-j];
    1403   9336897908 :         if (p1 & HIGHBIT) p1 %= p;
    1404              :       }
    1405   1347778527 :       p1 %= p;
    1406   1347778527 :       z[i-dy] = p1? ((p - p1)*inv) % p: 0;
    1407              :     }
    1408   3389567223 :     for (i=0; i<dy; i++)
    1409              :     {
    1410   2894580529 :       p1 = z[0]*y[i];
    1411  14866059210 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1412              :       {
    1413  11971478681 :         p1 += z[j]*y[i-j];
    1414  11971478681 :         if (p1 & HIGHBIT) p1 %= p;
    1415              :       }
    1416   2894580529 :       c[i] = Fl_sub(x[i], p1%p, p);
    1417              :     }
    1418              :   }
    1419              :   else
    1420              :   {
    1421    284403887 :     z[dz] = Fl_mul_pre(inv, x[dx], p, pi);
    1422    873565007 :     for (i=dx-1; i>=dy; --i)
    1423              :     {
    1424    589161120 :       p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1425   2496183719 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1426   1907022599 :         p1 = Fl_addmul_pre(p1, z[j], y[i - j], p, pi);
    1427    589161120 :       z[i-dy] = p1? Fl_mul_pre(p - p1, inv, p, pi): 0;
    1428              :     }
    1429   2094520114 :     for (i=0; i<dy; i++)
    1430              :     {
    1431   1810116227 :       p1 = Fl_mul_pre(z[0],y[i],p,pi);
    1432   4919012910 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1433   3108896683 :         p1 = Fl_addmul_pre(p1, z[j], y[i - j], p, pi);
    1434   1810116227 :       c[i] = Fl_sub(x[i], p1, p);
    1435              :     }
    1436              :   }
    1437    951075641 :   i = dy-1; while (i>=0 && !c[i]) i--;
    1438    779390581 :   set_avma(av); return Flx_renormalize(c-2, i+3);
    1439              : }
    1440              : 
    1441              : /* as FpX_divrem but working only on ulong types.
    1442              :  * if relevant, *pr is the last object on stack */
    1443              : static GEN
    1444     61532362 : Flx_divrem_basecase(GEN x, GEN y, ulong p, ulong pi, GEN *pr)
    1445              : {
    1446              :   GEN z,q,c;
    1447              :   long dx,dy,dy1,dz,i,j;
    1448              :   ulong p1,inv;
    1449     61532362 :   long sv=x[1];
    1450              : 
    1451     61532362 :   dy = degpol(y);
    1452     61532362 :   if (dy<0) pari_err_INV("Flx_divrem",y);
    1453     61532362 :   if (pr == ONLY_REM) return Flx_rem_basecase(x, y, p, pi);
    1454     61531961 :   if (!dy)
    1455              :   {
    1456      7141049 :     if (pr && pr != ONLY_DIVIDES) *pr = pol0_Flx(sv);
    1457      7141049 :     if (y[2] == 1UL) return Flx_copy(x);
    1458      5127147 :     return Flx_Fl_mul_pre(x, Fl_inv(y[2], p), p, pi);
    1459              :   }
    1460     54390912 :   dx = degpol(x);
    1461     54390912 :   dz = dx-dy;
    1462     54390912 :   if (dz < 0)
    1463              :   {
    1464      1062385 :     q = pol0_Flx(sv);
    1465      1062385 :     if (pr && pr != ONLY_DIVIDES) *pr = Flx_copy(x);
    1466      1062385 :     return q;
    1467              :   }
    1468     53328527 :   x += 2;
    1469     53328527 :   y += 2;
    1470     53328527 :   z = cgetg(dz + 3, t_VECSMALL); z[1] = sv; z += 2;
    1471     53328527 :   inv = uel(y, dy);
    1472     53328527 :   if (inv != 1UL) inv = Fl_inv(inv,p);
    1473     78687355 :   for (dy1=dy-1; dy1>=0 && !y[dy1]; dy1--);
    1474              : 
    1475     53328527 :   if (SMALL_ULONG(p))
    1476              :   {
    1477     51433320 :     z[dz] = (inv*x[dx]) % p;
    1478    131170505 :     for (i=dx-1; i>=dy; --i)
    1479              :     {
    1480     79737185 :       p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1481    262072122 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1482              :       {
    1483    182334937 :         p1 += z[j]*y[i-j];
    1484    182334937 :         if (p1 & HIGHBIT) p1 %= p;
    1485              :       }
    1486     79737185 :       p1 %= p;
    1487     79737185 :       z[i-dy] = p1? (long) ((p - p1)*inv) % p: 0;
    1488              :     }
    1489              :   }
    1490              :   else
    1491              :   {
    1492      1895207 :     z[dz] = Fl_mul(inv, x[dx], p);
    1493      9302516 :     for (i=dx-1; i>=dy; --i)
    1494              :     { /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1495      7407309 :       p1 = p - uel(x,i);
    1496     26553917 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1497     19146608 :         p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
    1498      7407309 :       z[i-dy] = p1? Fl_mul(p - p1, inv, p): 0;
    1499              :     }
    1500              :   }
    1501     53328527 :   q = Flx_renormalize(z-2, dz+3);
    1502     53328527 :   if (!pr) return q;
    1503              : 
    1504     25701686 :   c = cgetg(dy + 3, t_VECSMALL); c[1] = sv; c += 2;
    1505     25701686 :   if (SMALL_ULONG(p))
    1506              :   {
    1507    213643893 :     for (i=0; i<dy; i++)
    1508              :     {
    1509    189592259 :       p1 = (ulong)z[0]*y[i];
    1510    453086646 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1511              :       {
    1512    263494387 :         p1 += (ulong)z[j]*y[i-j];
    1513    263494387 :         if (p1 & HIGHBIT) p1 %= p;
    1514              :       }
    1515    189592259 :       c[i] = Fl_sub(x[i], p1%p, p);
    1516              :     }
    1517              :   }
    1518              :   else
    1519              :   {
    1520     16113377 :     for (i=0; i<dy; i++)
    1521              :     {
    1522     14463325 :       p1 = Fl_mul(z[0],y[i],p);
    1523     50363696 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1524     35900371 :         p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
    1525     14463325 :       c[i] = Fl_sub(x[i], p1, p);
    1526              :     }
    1527              :   }
    1528     34883555 :   i=dy-1; while (i>=0 && !c[i]) i--;
    1529     25701686 :   c = Flx_renormalize(c-2, i+3);
    1530     25701686 :   if (pr == ONLY_DIVIDES)
    1531          455 :   { if (lg(c) != 2) return NULL; }
    1532              :   else
    1533     25701231 :     *pr = c;
    1534     25701539 :   return q;
    1535              : }
    1536              : 
    1537              : /* Compute x mod T where 2 <= degpol(T) <= l+1 <= 2*(degpol(T)-1)
    1538              :  * and mg is the Barrett inverse of T. */
    1539              : static GEN
    1540       949442 : Flx_divrem_Barrettspec(GEN x, long l, GEN mg, GEN T, ulong p, ulong pi, GEN *pr)
    1541              : {
    1542              :   GEN q, r;
    1543       949442 :   long lt = degpol(T); /*We discard the leading term*/
    1544              :   long ld, lm, lT, lmg;
    1545       949442 :   ld = l-lt;
    1546       949442 :   lm = minss(ld, lgpol(mg));
    1547       949442 :   lT  = Flx_lgrenormalizespec(T+2,lt);
    1548       949442 :   lmg = Flx_lgrenormalizespec(mg+2,lm);
    1549       949442 :   q = Flx_recipspec(x+lt,ld,ld);               /* q = rec(x)      lz<=ld*/
    1550       949442 :   q = Flx_mulspec(q+2,mg+2,p,pi,lgpol(q),lmg); /* q = rec(x) * mg lz<=ld+lm*/
    1551       949442 :   q = Flx_recipspec(q+2,minss(ld,lgpol(q)),ld);/* q = rec (rec(x) * mg) lz<=ld*/
    1552       949442 :   if (!pr) return q;
    1553       941456 :   r = Flx_mulspec(q+2,T+2,p,pi,lgpol(q),lT);   /* r = q*pol      lz<=ld+lt*/
    1554       941456 :   r = Flx_subspec(x,r+2,p,lt,minss(lt,lgpol(r)));/* r = x - q*pol lz<=lt */
    1555       941456 :   if (pr == ONLY_REM) return r;
    1556       442136 :   *pr = r; return q;
    1557              : }
    1558              : 
    1559              : static GEN
    1560       635049 : Flx_divrem_Barrett(GEN x, GEN mg, GEN T, ulong p, ulong pi, GEN *pr)
    1561              : {
    1562       635049 :   GEN q = NULL, r = Flx_copy(x);
    1563       635049 :   long l = lgpol(x), lt = degpol(T), lm = 2*lt-1, v = T[1];
    1564              :   long i;
    1565       635049 :   if (l <= lt)
    1566              :   {
    1567            0 :     if (pr == ONLY_REM) return Flx_copy(x);
    1568            0 :     if (pr == ONLY_DIVIDES) return lgpol(x)? NULL: pol0_Flx(v);
    1569            0 :     if (pr) *pr = Flx_copy(x);
    1570            0 :     return pol0_Flx(v);
    1571              :   }
    1572       635049 :   if (lt <= 1)
    1573         1303 :     return Flx_divrem_basecase(x,T,p,pi,pr);
    1574       633746 :   if (pr != ONLY_REM && l>lm)
    1575        29291 :   { q = zero_zv(l-lt+1); q[1] = T[1]; }
    1576       951087 :   while (l>lm)
    1577              :   {
    1578       317341 :     GEN zr, zq = Flx_divrem_Barrettspec(r+2+l-lm,lm,mg,T,p,pi,&zr);
    1579       317341 :     long lz = lgpol(zr);
    1580       317341 :     if (pr != ONLY_REM)
    1581              :     {
    1582        65542 :       long lq = lgpol(zq);
    1583       947971 :       for(i=0; i<lq; i++) q[2+l-lm+i] = zq[2+i];
    1584              :     }
    1585      4617241 :     for(i=0; i<lz; i++)   r[2+l-lm+i] = zr[2+i];
    1586       317341 :     l = l-lm+lz;
    1587              :   }
    1588       633746 :   if (pr == ONLY_REM)
    1589              :   {
    1590       499363 :     if (l > lt)
    1591       499320 :       r = Flx_divrem_Barrettspec(r+2,l,mg,T,p,pi,ONLY_REM);
    1592              :     else
    1593           43 :       r = Flx_renormalize(r, l+2);
    1594       499363 :     r[1] = v; return r;
    1595              :   }
    1596       134383 :   if (l > lt)
    1597              :   {
    1598       132781 :     GEN zq = Flx_divrem_Barrettspec(r+2,l,mg,T,p,pi, pr ? &r: NULL);
    1599       132781 :     if (!q) q = zq;
    1600              :     else
    1601              :     {
    1602        27689 :       long lq = lgpol(zq);
    1603       166223 :       for(i=0; i<lq; i++) q[2+i] = zq[2+i];
    1604              :     }
    1605              :   }
    1606         1602 :   else if (pr)
    1607         1550 :     r = Flx_renormalize(r, l+2);
    1608       134383 :   q[1] = v; q = Flx_renormalize(q, lg(q));
    1609       134383 :   if (pr == ONLY_DIVIDES) return lgpol(r)? NULL: q;
    1610       134383 :   if (pr) { r[1] = v; *pr = r; }
    1611       134383 :   return q;
    1612              : }
    1613              : 
    1614              : /* allow pi = 0 (SMALL_ULONG) */
    1615              : GEN
    1616     78297209 : Flx_divrem_pre(GEN x, GEN T, ulong p, ulong pi, GEN *pr)
    1617              : {
    1618              :   GEN B, y;
    1619              :   long dy, dx, d;
    1620     78297209 :   if (pr==ONLY_REM) return Flx_rem_pre(x, T, p, pi);
    1621     61666344 :   y = get_Flx_red(T, &B);
    1622     61666344 :   dy = degpol(y); dx = degpol(x); d = dx-dy;
    1623     61666344 :   if (!B && d+3 < get_Fl_threshold(p, Flx_DIVREM_BARRETT_LIMIT,Flx_DIVREM2_BARRETT_LIMIT))
    1624     61531059 :     return Flx_divrem_basecase(x,y,p,pi,pr);
    1625              :   else
    1626              :   {
    1627       135285 :     pari_sp av = avma;
    1628       135285 :     GEN mg = B? B: Flx_invBarrett_pre(y, p, pi);
    1629       135285 :     GEN q1 = Flx_divrem_Barrett(x,mg,y,p,pi,pr);
    1630       135285 :     if (!q1) return gc_NULL(av);
    1631       135285 :     if (!pr || pr==ONLY_DIVIDES) return gc_leaf(av, q1);
    1632       126692 :     return gc_all(av, 2, &q1, pr);
    1633              :   }
    1634              : }
    1635              : GEN
    1636     29837359 : Flx_divrem(GEN x, GEN T, ulong p, GEN *pr)
    1637     29837359 : { return Flx_divrem_pre(x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p), pr); }
    1638              : 
    1639              : GEN
    1640    958143912 : Flx_rem_pre(GEN x, GEN T, ulong p, ulong pi)
    1641              : {
    1642    958143912 :   GEN B, y = get_Flx_red(T, &B);
    1643    958143912 :   long d = degpol(x) - degpol(y);
    1644    958143912 :   if (d < 0) return Flx_copy(x);
    1645    816028703 :   if (!B && d+3 < get_Fl_threshold(p, Flx_REM_BARRETT_LIMIT,Flx_REM2_BARRETT_LIMIT))
    1646    815528939 :     return Flx_rem_basecase(x,y,p, pi);
    1647              :   else
    1648              :   {
    1649       499764 :     pari_sp av=avma;
    1650       499764 :     GEN mg = B ? B: Flx_invBarrett_pre(y, p, pi);
    1651       499764 :     GEN r  = Flx_divrem_Barrett(x, mg, y, p, pi, ONLY_REM);
    1652       499764 :     return gc_leaf(av, r);
    1653              :   }
    1654              : }
    1655              : GEN
    1656     42360736 : Flx_rem(GEN x, GEN T, ulong p)
    1657     42360736 : { return Flx_rem_pre(x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    1658              : 
    1659              : /* reduce T mod (X^n - 1, p). Shallow function */
    1660              : GEN
    1661      4920861 : Flx_mod_Xnm1(GEN T, ulong n, ulong p)
    1662              : {
    1663      4920861 :   long i, j, L = lg(T), l = n+2;
    1664              :   GEN S;
    1665      4920861 :   if (L <= l || n & ~LGBITS) return T;
    1666         3583 :   S = cgetg(l, t_VECSMALL);
    1667         3583 :   S[1] = T[1];
    1668        15896 :   for (i = 2; i < l; i++) S[i] = T[i];
    1669        10029 :   for (j = 2; i < L; i++) {
    1670         6446 :     S[j] = Fl_add(S[j], T[i], p);
    1671         6446 :     if (++j == l) j = 2;
    1672              :   }
    1673         3583 :   return Flx_renormalize(S, l);
    1674              : }
    1675              : /* reduce T mod (X^n + 1, p). Shallow function */
    1676              : GEN
    1677        31667 : Flx_mod_Xn1(GEN T, ulong n, ulong p)
    1678              : {
    1679        31667 :   long i, j, L = lg(T), l = n+2, s = -1;
    1680              :   GEN S;
    1681        31667 :   if (L <= l || n & ~LGBITS) return T;
    1682         2794 :   S = cgetg(l, t_VECSMALL);
    1683         2794 :   S[1] = T[1];
    1684        12831 :   for (i = 2; i < l; i++) S[i] = T[i];
    1685         7541 :   for (j = 2; i < L; i++) {
    1686         4747 :     S[j] = s==-1 ? Fl_sub(S[j], T[i], p): Fl_add(S[j], T[i], p);
    1687         4747 :     if (++j == l) { j = 2; s = -s; }
    1688              :   }
    1689         2794 :   return Flx_renormalize(S, l);
    1690              : }
    1691              : 
    1692              : struct _Flxq {
    1693              :   GEN aut, T;
    1694              :   ulong p, pi;
    1695              : };
    1696              : /* allow pi = 0 */
    1697              : static void
    1698     70189208 : set_Flxq_pre(struct _Flxq *D, GEN T, ulong p, ulong pi)
    1699              : {
    1700     70189208 :   D->p = p;
    1701     70189208 :   D->pi = pi;
    1702     70189208 :   D->T = Flx_get_red_pre(T, p, pi);
    1703     70189208 : }
    1704              : static void
    1705        68811 : set_Flxq(struct _Flxq *D, GEN T, ulong p)
    1706        68811 : { set_Flxq_pre(D, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    1707              : 
    1708              : struct _Flx {
    1709              :   long v;
    1710              :   ulong p, pi;
    1711              : };
    1712              : 
    1713              : static GEN
    1714            0 : _Flx_divrem(void * E, GEN x, GEN y, GEN *r)
    1715              : {
    1716            0 :   struct _Flx *D = (struct _Flx*) E;
    1717            0 :   return Flx_divrem_pre(x, y, D->p, D->pi, r);
    1718              : }
    1719              : static GEN
    1720            0 : _Flx_add(void * E, GEN x, GEN y) {
    1721            0 :   struct _Flx *D = (struct _Flx*) E;
    1722            0 :   return Flx_add(x, y, D->p);
    1723              : }
    1724              : static GEN
    1725            0 : _Flx_sub(void * E, GEN x, GEN y) {
    1726            0 :   struct _Flx *D = (struct _Flx*) E;
    1727            0 :   return Flx_sub(x, y, D->p);
    1728              : }
    1729              : static GEN
    1730     13263924 : _Flx_mul(void *E, GEN x, GEN y) {
    1731     13263924 :   struct _Flx *D = (struct _Flx*) E;
    1732     13263924 :   return Flx_mul_pre(x, y, D->p, D->pi);
    1733              : }
    1734              : static GEN
    1735            0 : _Flx_sqr(void *E, GEN x) {
    1736            0 :   struct _Flx *D = (struct _Flx*) E;
    1737            0 :   return Flx_sqr_pre(x, D->p, D->pi);
    1738              : }
    1739              : 
    1740              : static struct bb_ring Flx_ring = { _Flx_add,_Flx_mul,_Flx_sqr };
    1741              : 
    1742              : GEN
    1743            0 : Flx_digits(GEN x, GEN T, ulong p)
    1744              : {
    1745              :   struct _Flx D;
    1746            0 :   long d = get_Flx_degree(T), n = (lgpol(x)+d-1)/d;
    1747            0 :   D.p = p; D.pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    1748            0 :   return gen_digits(x,T,n,(void *)&D, &Flx_ring, _Flx_divrem);
    1749              : }
    1750              : 
    1751              : GEN
    1752            0 : FlxV_Flx_fromdigits(GEN x, GEN T, ulong p)
    1753              : {
    1754              :   struct _Flx D;
    1755            0 :   D.p = p; D.pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    1756            0 :   return gen_fromdigits(x,T,(void *)&D, &Flx_ring);
    1757              : }
    1758              : 
    1759              : long
    1760      4584270 : Flx_val(GEN x)
    1761              : {
    1762      4584270 :   long i, l=lg(x);
    1763      4584270 :   if (l==2)  return LONG_MAX;
    1764      4593060 :   for (i=2; i<l && x[i]==0; i++) /*empty*/;
    1765      4584270 :   return i-2;
    1766              : }
    1767              : long
    1768     31189832 : Flx_valrem(GEN x, GEN *Z)
    1769              : {
    1770     31189832 :   long v, i, l=lg(x);
    1771              :   GEN y;
    1772     31189832 :   if (l==2) { *Z = Flx_copy(x); return LONG_MAX; }
    1773     33370936 :   for (i=2; i<l && x[i]==0; i++) /*empty*/;
    1774     31189832 :   v = i-2;
    1775     31189832 :   if (v == 0) { *Z = x; return 0; }
    1776      1025466 :   l -= v;
    1777      1025466 :   y = cgetg(l, t_VECSMALL); y[1] = x[1];
    1778      2638577 :   for (i=2; i<l; i++) y[i] = x[i+v];
    1779      1025466 :   *Z = y; return v;
    1780              : }
    1781              : 
    1782              : GEN
    1783     23061986 : Flx_deriv(GEN z, ulong p)
    1784              : {
    1785     23061986 :   long i,l = lg(z)-1;
    1786              :   GEN x;
    1787     23061986 :   if (l < 2) l = 2;
    1788     23061986 :   x = cgetg(l, t_VECSMALL); x[1] = z[1]; z++;
    1789     23061986 :   if (HIGHWORD(l | p))
    1790     63204159 :     for (i=2; i<l; i++) x[i] = Fl_mul((ulong)i-1, z[i], p);
    1791              :   else
    1792     88502149 :     for (i=2; i<l; i++) x[i] = ((i-1) * z[i]) % p;
    1793     23061986 :   return Flx_renormalize(x,l);
    1794              : }
    1795              : 
    1796              : static GEN
    1797       423966 : Flx_integXn(GEN x, long n, ulong p)
    1798              : {
    1799       423966 :   long i, lx = lg(x);
    1800              :   GEN y;
    1801       423966 :   if (lx == 2) return Flx_copy(x);
    1802       414152 :   y = cgetg(lx, t_VECSMALL); y[1] = x[1];
    1803      2106983 :   for (i=2; i<lx; i++)
    1804              :   {
    1805      1692831 :     ulong xi = uel(x,i);
    1806      1692831 :     if (xi == 0)
    1807        13352 :       uel(y,i) = 0;
    1808              :     else
    1809              :     {
    1810      1679479 :       ulong j = n+i-1;
    1811      1679479 :       ulong d = ugcd(j, xi);
    1812      1679479 :       if (d==1)
    1813      1023392 :         uel(y,i) = Fl_div(xi, j, p);
    1814              :       else
    1815       656087 :         uel(y,i) = Fl_div(xi/d, j/d, p);
    1816              :     }
    1817              :   }
    1818       414152 :   return Flx_renormalize(y, lx);;
    1819              : }
    1820              : 
    1821              : GEN
    1822            0 : Flx_integ(GEN x, ulong p)
    1823              : {
    1824            0 :   long i, lx = lg(x);
    1825              :   GEN y;
    1826            0 :   if (lx == 2) return Flx_copy(x);
    1827            0 :   y = cgetg(lx+1, t_VECSMALL); y[1] = x[1];
    1828            0 :   uel(y,2) = 0;
    1829            0 :   for (i=3; i<=lx; i++)
    1830            0 :     uel(y,i) = uel(x,i-1) ? Fl_div(uel(x,i-1), (i-2)%p, p): 0UL;
    1831            0 :   return Flx_renormalize(y, lx+1);;
    1832              : }
    1833              : 
    1834              : /* assume p prime */
    1835              : GEN
    1836       660758 : Flx_diff1(GEN P, ulong p)
    1837              : {
    1838       660758 :   return Flx_sub(Flx_translate1(P, p), P, p);
    1839              : }
    1840              : 
    1841              : GEN
    1842       421892 : Flx_deflate(GEN x0, long d)
    1843              : {
    1844              :   GEN z, y, x;
    1845       421892 :   long i,id, dy, dx = degpol(x0);
    1846       421892 :   if (d == 1 || dx <= 0) return Flx_copy(x0);
    1847       358220 :   dy = dx/d;
    1848       358220 :   y = cgetg(dy+3, t_VECSMALL); y[1] = x0[1];
    1849       358220 :   z = y + 2;
    1850       358220 :   x = x0+ 2;
    1851      1164920 :   for (i=id=0; i<=dy; i++,id+=d) z[i] = x[id];
    1852       358220 :   return y;
    1853              : }
    1854              : 
    1855              : GEN
    1856       161027 : Flx_inflate(GEN x0, long d)
    1857              : {
    1858       161027 :   long i, id, dy, dx = degpol(x0);
    1859       161027 :   GEN x = x0 + 2, z, y;
    1860       161027 :   if (dx <= 0) return Flx_copy(x0);
    1861       159965 :   dy = dx*d;
    1862       159965 :   y = cgetg(dy+3, t_VECSMALL); y[1] = x0[1];
    1863       159965 :   z = y + 2;
    1864      8999283 :   for (i=0; i<=dy; i++) z[i] = 0;
    1865      4380153 :   for (i=id=0; i<=dx; i++,id+=d) z[id] = x[i];
    1866       159965 :   return y;
    1867              : }
    1868              : 
    1869              : /* write p(X) = a_0(X^k) + X*a_1(X^k) + ... + X^(k-1)*a_{k-1}(X^k) */
    1870              : GEN
    1871       149183 : Flx_splitting(GEN p, long k)
    1872              : {
    1873       149183 :   long n = degpol(p), v = p[1], m, i, j, l;
    1874              :   GEN r;
    1875              : 
    1876       149183 :   m = n/k;
    1877       149183 :   r = cgetg(k+1,t_VEC);
    1878       707319 :   for(i=1; i<=k; i++)
    1879              :   {
    1880       558136 :     gel(r,i) = cgetg(m+3, t_VECSMALL);
    1881       558136 :     mael(r,i,1) = v;
    1882              :   }
    1883      4596150 :   for (j=1, i=0, l=2; i<=n; i++)
    1884              :   {
    1885      4446967 :     mael(r,j,l) = p[2+i];
    1886      4446967 :     if (j==k) { j=1; l++; } else j++;
    1887              :   }
    1888       707319 :   for(i=1; i<=k; i++)
    1889       558136 :     gel(r,i) = Flx_renormalize(gel(r,i),i<j?l+1:l);
    1890       149183 :   return r;
    1891              : }
    1892              : 
    1893              : /* ux + vy */
    1894              : static GEN
    1895       360140 : Flx_addmulmul(GEN u, GEN v, GEN x, GEN y, ulong p, ulong pi)
    1896       360140 : { return Flx_add(Flx_mul_pre(u,x, p,pi), Flx_mul_pre(v,y, p,pi), p); }
    1897              : 
    1898              : static GEN
    1899        26703 : FlxM_Flx_mul2(GEN M, GEN x, GEN y, ulong p, ulong pi)
    1900              : {
    1901        26703 :   GEN res = cgetg(3, t_COL);
    1902        26703 :   gel(res, 1) = Flx_addmulmul(gcoeff(M,1,1), gcoeff(M,1,2), x, y, p, pi);
    1903        26703 :   gel(res, 2) = Flx_addmulmul(gcoeff(M,2,1), gcoeff(M,2,2), x, y, p, pi);
    1904        26703 :   return res;
    1905              : }
    1906              : 
    1907              : #if 0
    1908              : static GEN
    1909              : FlxM_mul2_old(GEN M, GEN N, ulong p)
    1910              : {
    1911              :   GEN res = cgetg(3, t_MAT);
    1912              :   gel(res, 1) = FlxM_Flx_mul2(M,gcoeff(N,1,1),gcoeff(N,2,1),p);
    1913              :   gel(res, 2) = FlxM_Flx_mul2(M,gcoeff(N,1,2),gcoeff(N,2,2),p);
    1914              :   return res;
    1915              : }
    1916              : #endif
    1917              : /* A,B are 2x2 matrices, Flx entries. Return A x B using Strassen 7M formula */
    1918              : static GEN
    1919         7297 : FlxM_mul2(GEN A, GEN B, ulong p, ulong pi)
    1920              : {
    1921         7297 :   GEN A11=gcoeff(A,1,1),A12=gcoeff(A,1,2), B11=gcoeff(B,1,1),B12=gcoeff(B,1,2);
    1922         7297 :   GEN A21=gcoeff(A,2,1),A22=gcoeff(A,2,2), B21=gcoeff(B,2,1),B22=gcoeff(B,2,2);
    1923         7297 :   GEN M1 = Flx_mul_pre(Flx_add(A11,A22, p), Flx_add(B11,B22, p), p, pi);
    1924         7297 :   GEN M2 = Flx_mul_pre(Flx_add(A21,A22, p), B11, p, pi);
    1925         7297 :   GEN M3 = Flx_mul_pre(A11, Flx_sub(B12,B22, p), p, pi);
    1926         7297 :   GEN M4 = Flx_mul_pre(A22, Flx_sub(B21,B11, p), p, pi);
    1927         7297 :   GEN M5 = Flx_mul_pre(Flx_add(A11,A12, p), B22, p, pi);
    1928         7297 :   GEN M6 = Flx_mul_pre(Flx_sub(A21,A11, p), Flx_add(B11,B12, p), p, pi);
    1929         7297 :   GEN M7 = Flx_mul_pre(Flx_sub(A12,A22, p), Flx_add(B21,B22, p), p, pi);
    1930         7297 :   GEN T1 = Flx_add(M1,M4, p), T2 = Flx_sub(M7,M5, p);
    1931         7297 :   GEN T3 = Flx_sub(M1,M2, p), T4 = Flx_add(M3,M6, p);
    1932         7297 :   retmkmat22(Flx_add(T1,T2, p), Flx_add(M3,M5, p),
    1933              :              Flx_add(M2,M4, p), Flx_add(T3,T4, p));
    1934              : }
    1935              : 
    1936              : /* Return [0,1;1,-q]*M */
    1937              : static GEN
    1938         7125 : Flx_FlxM_qmul(GEN q, GEN M, ulong p, ulong pi)
    1939              : {
    1940         7125 :   GEN u = Flx_mul_pre(gcoeff(M,2,1), q, p, pi);
    1941         7125 :   GEN v = Flx_mul_pre(gcoeff(M,2,2), q, p, pi);
    1942         7125 :   retmkmat22(gcoeff(M,2,1), gcoeff(M,2,2),
    1943              :     Flx_sub(gcoeff(M,1,1), u, p), Flx_sub(gcoeff(M,1,2), v, p));
    1944              : }
    1945              : 
    1946              : static GEN
    1947          955 : matid2_FlxM(long v)
    1948          955 : { retmkmat22(pol1_Flx(v),pol0_Flx(v),pol0_Flx(v),pol1_Flx(v)); }
    1949              : 
    1950              : static GEN
    1951           13 : matJ2_FlxM(long v)
    1952           13 : { retmkmat22(pol0_Flx(v),pol1_Flx(v),pol1_Flx(v),pol0_Flx(v)); }
    1953              : 
    1954              : struct Flx_res
    1955              : {
    1956              :    ulong res, lc;
    1957              :    long deg0, deg1, off;
    1958              : };
    1959              : 
    1960              : INLINE void
    1961         9405 : Flx_halfres_update_pre(long da, long db, long dr, ulong p, ulong pi, struct Flx_res *res)
    1962              : {
    1963         9405 :   if (dr >= 0)
    1964              :   {
    1965         9405 :     if (res->lc != 1)
    1966              :     {
    1967         7596 :       if (pi)
    1968              :       {
    1969         3127 :         res->lc  = Fl_powu_pre(res->lc, da - dr, p, pi);
    1970         3127 :         res->res = Fl_mul_pre(res->res, res->lc, p, pi);
    1971              :       } else
    1972              :       {
    1973         4469 :         res->lc  = Fl_powu(res->lc, da - dr, p);
    1974         4469 :         res->res = Fl_mul(res->res, res->lc, p);
    1975              :       }
    1976              :     }
    1977         9405 :     if (both_odd(da + res->off, db + res->off))
    1978           63 :       res->res = Fl_neg(res->res, p);
    1979              :   } else
    1980              :   {
    1981            0 :     if (db == 0)
    1982              :     {
    1983            0 :       if (res->lc != 1)
    1984              :       {
    1985            0 :         if (pi)
    1986              :         {
    1987            0 :           res->lc  = Fl_powu_pre(res->lc, da, p, pi);
    1988            0 :           res->res = Fl_mul_pre(res->res, res->lc, p, pi);
    1989              :         } else
    1990              :         {
    1991            0 :           res->lc  = Fl_powu(res->lc, da, p);
    1992            0 :           res->res = Fl_mul(res->res, res->lc, p);
    1993              :         }
    1994              :       }
    1995              :     } else
    1996            0 :       res->res = 0;
    1997              :   }
    1998         9405 : }
    1999              : 
    2000              : static GEN
    2001      1099347 : Flx_halfres_basecase(GEN a, GEN b, ulong p, ulong pi, GEN *pa, GEN *pb, struct Flx_res *res)
    2002              : {
    2003      1099347 :   pari_sp av = avma;
    2004              :   GEN u, u1, v, v1, M;
    2005      1099347 :   long vx = a[1], n = lgpol(a)>>1;
    2006      1099347 :   u1 = v = pol0_Flx(vx);
    2007      1099347 :   u = v1 = pol1_Flx(vx);
    2008      6393020 :   while (lgpol(b)>n)
    2009              :   {
    2010              :     GEN r, q;
    2011      5293673 :     q = Flx_divrem_pre(a,b,p,pi, &r);
    2012      5293673 :     if (res)
    2013              :     {
    2014         8362 :       long da = degpol(a), db=degpol(b), dr = degpol(r);
    2015         8362 :       res->lc = b[db+2];
    2016         8362 :       if (dr >= n)
    2017         7133 :         Flx_halfres_update_pre(da, db, dr, p, pi, res);
    2018              :       else
    2019              :       {
    2020         1229 :         res->deg0 = da;
    2021         1229 :         res->deg1 = db;
    2022              :       }
    2023              :     }
    2024      5293673 :     a = b; b = r; swap(u,u1); swap(v,v1);
    2025      5293673 :     u1 = Flx_sub(u1, Flx_mul(u, q, p), p);
    2026      5293673 :     v1 = Flx_sub(v1, Flx_mul(v, q, p), p);
    2027      5293673 :     if (gc_needed(av,2))
    2028              :     {
    2029            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_halfgcd (d = %ld)",degpol(b));
    2030            0 :       (void)gc_all(av,6, &a,&b,&u1,&v1,&u,&v);
    2031              :     }
    2032              :   }
    2033      1099347 :   M = mkmat22(u,v,u1,v1); *pa = a; *pb = b;
    2034      1099347 :   return gc_all(av,3, &M, pa, pb);
    2035              : }
    2036              : 
    2037              : static GEN Flx_halfres_i(GEN x, GEN y, ulong p, ulong pi, GEN *a, GEN *b, struct Flx_res *res);
    2038              : 
    2039              : static GEN
    2040        20513 : Flx_halfres_split(GEN x, GEN y, ulong p, ulong pi, GEN *a, GEN *b, struct Flx_res *res)
    2041              : {
    2042        20513 :   pari_sp av = avma;
    2043              :   GEN R, S, T, V1, V2;
    2044              :   GEN x1, y1, r, q;
    2045        20513 :   long l = lgpol(x), n = l>>1, k;
    2046        20513 :   if (lgpol(y) <= n)
    2047          915 :     { *a = Flx_copy(x); *b = Flx_copy(y); return matid2_FlxM(x[1]); }
    2048        19598 :   if (res)
    2049              :   {
    2050         3263 :      res->lc = Flx_lead(y);
    2051         3263 :      res->deg0 -= n;
    2052         3263 :      res->deg1 -= n;
    2053         3263 :      res->off += n;
    2054              :   }
    2055        19598 :   R = Flx_halfres_i(Flx_shift(x,-n),Flx_shift(y,-n),p,pi,a,b,res);
    2056        19598 :   if (res)
    2057              :   {
    2058         3263 :     res->off -= n;
    2059         3263 :     res->deg0 += n;
    2060         3263 :     res->deg1 += n;
    2061              :   }
    2062        19598 :   V1 = FlxM_Flx_mul2(R, Flxn_red(x,n), Flxn_red(y,n), p, pi);
    2063        19598 :   x1 = Flx_add(Flx_shift(*a,n), gel(V1,1), p);
    2064        19598 :   y1 = Flx_add(Flx_shift(*b,n), gel(V1,2), p);
    2065        19598 :   if (lgpol(y1) <= n)
    2066        12493 :     { *a = x1; *b = y1; return gc_all(av, 3, &R, a, b); }
    2067         7105 :   k = 2*n-degpol(y1);
    2068         7105 :   q = Flx_divrem_pre(x1, y1, p, pi, &r);
    2069         7105 :   if (res)
    2070              :   {
    2071         1043 :     long dx1 = degpol(x1), dy1 = degpol(y1), dr = degpol(r);
    2072         1043 :     if (dy1 < degpol(y))
    2073          185 :       Flx_halfres_update_pre(res->deg0, res->deg1, dy1, p, pi, res);
    2074         1043 :     res->lc = uel(y1, dy1+2);
    2075         1043 :     res->deg0 = dx1;
    2076         1043 :     res->deg1 = dy1;
    2077         1043 :     if (dr >= n)
    2078              :     {
    2079         1043 :       Flx_halfres_update_pre(dx1, dy1, dr, p, pi, res);
    2080         1043 :       res->deg0 = dy1;
    2081         1043 :       res->deg1 = dr;
    2082              :     }
    2083         1043 :     res->deg0 -= k;
    2084         1043 :     res->deg1 -= k;
    2085         1043 :     res->off += k;
    2086              :   }
    2087         7105 :   S = Flx_halfres_i(Flx_shift(y1,-k), Flx_shift(r,-k), p, pi, a, b, res);
    2088         7105 :   if (res)
    2089              :   {
    2090         1043 :     res->deg0 += k;
    2091         1043 :     res->deg1 += k;
    2092         1043 :     res->off -= k;
    2093              :   }
    2094         7105 :   T = FlxM_mul2(S, Flx_FlxM_qmul(q, R, p,pi), p, pi);
    2095         7105 :   V2 = FlxM_Flx_mul2(S, Flxn_red(y1,k), Flxn_red(r,k), p, pi);
    2096         7105 :   *a = Flx_add(Flx_shift(*a,k), gel(V2,1), p);
    2097         7105 :   *b = Flx_add(Flx_shift(*b,k), gel(V2,2), p);
    2098         7105 :   return gc_all(av, 3, &T, a, b);
    2099              : }
    2100              : 
    2101              : static GEN
    2102      1119860 : Flx_halfres_i(GEN x, GEN y, ulong p, ulong pi, GEN *a, GEN *b, struct Flx_res *res)
    2103              : {
    2104      1119860 :   if (lgpol(x) < get_Fl_threshold(p, Flx_HALFGCD_LIMIT, Flx_HALFGCD2_LIMIT))
    2105      1099347 :     return Flx_halfres_basecase(x, y, p, pi, a, b, res);
    2106        20513 :   return Flx_halfres_split(x, y, p, pi, a, b, res);
    2107              : }
    2108              : 
    2109              : static GEN
    2110      1092113 : Flx_halfgcd_all_i(GEN x, GEN y, ulong p, ulong pi, GEN *pa, GEN *pb)
    2111              : {
    2112              :   GEN a, b, R;
    2113      1092113 :   R = Flx_halfres_i(x, y, p, pi, &a, &b, NULL);
    2114      1092113 :   if (pa) *pa = a;
    2115      1092113 :   if (pb) *pb = b;
    2116      1092113 :   return R;
    2117              : }
    2118              : 
    2119              : /* Return M in GL_2(Fl[X]) such that:
    2120              : if [a',b']~=M*[a,b]~ then degpol(a')>= (lgpol(a)>>1) >degpol(b')
    2121              : */
    2122              : 
    2123              : GEN
    2124      1092113 : Flx_halfgcd_all_pre(GEN x, GEN y, ulong p, ulong pi, GEN *a, GEN *b)
    2125              : {
    2126              :   pari_sp av;
    2127              :   GEN R, q, r;
    2128      1092113 :   long lx = lgpol(x), ly = lgpol(y);
    2129      1092113 :   if (!lx)
    2130              :   {
    2131            0 :     if (a) *a = Flx_copy(y);
    2132            0 :     if (b) *b = Flx_copy(x);
    2133            0 :     return matJ2_FlxM(x[1]);
    2134              :   }
    2135      1092113 :   if (ly < lx) return Flx_halfgcd_all_i(x, y, p, pi, a, b);
    2136         8512 :   av = avma;
    2137         8512 :   q = Flx_divrem(y,x,p,&r);
    2138         8512 :   R = Flx_halfgcd_all_i(x, r, p, pi, a, b);
    2139         8512 :   gcoeff(R,1,1) = Flx_sub(gcoeff(R,1,1), Flx_mul_pre(q,gcoeff(R,1,2), p,pi), p);
    2140         8512 :   gcoeff(R,2,1) = Flx_sub(gcoeff(R,2,1), Flx_mul_pre(q,gcoeff(R,2,2), p,pi), p);
    2141         8512 :   return !a && b ? gc_all(av, 2, &R, b): gc_all(av, 1+!!a+!!b, &R, a, b);
    2142              : }
    2143              : 
    2144              : GEN
    2145          154 : Flx_halfgcd_all(GEN x, GEN y, ulong p, GEN *a, GEN *b)
    2146          154 : { return Flx_halfgcd_all_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p), a, b); }
    2147              : 
    2148              : GEN
    2149       879237 : Flx_halfgcd_pre(GEN x, GEN y, ulong p, ulong pi)
    2150       879237 : { return Flx_halfgcd_all_pre(x, y, p, pi, NULL, NULL); }
    2151              : 
    2152              : GEN
    2153            0 : Flx_halfgcd(GEN x, GEN y, ulong p)
    2154            0 : { return Flx_halfgcd_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2155              : 
    2156              : /*Do not garbage collect*/
    2157              : static GEN
    2158     87735564 : Flx_gcd_basecase(GEN a, GEN b, ulong p, ulong pi)
    2159              : {
    2160     87735564 :   pari_sp av = avma;
    2161     87735564 :   ulong iter = 0;
    2162     87735564 :   if (lg(b) > lg(a)) swap(a, b);
    2163    301756128 :   while (lgpol(b))
    2164              :   {
    2165    214020570 :     GEN c = Flx_rem_pre(a,b,p,pi);
    2166    214020564 :     iter++; a = b; b = c;
    2167    214020564 :     if (gc_needed(av,2))
    2168              :     {
    2169            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_gcd (d = %ld)",degpol(c));
    2170            0 :       (void)gc_all(av,2, &a,&b);
    2171              :     }
    2172              :   }
    2173     87735558 :   return iter < 2 ? Flx_copy(a) : a;
    2174              : }
    2175              : 
    2176              : GEN
    2177     89494437 : Flx_gcd_pre(GEN x, GEN y, ulong p, ulong pi)
    2178              : {
    2179     89494437 :   pari_sp av = avma;
    2180              :   long lim;
    2181     89494437 :   if (!lgpol(x)) return Flx_copy(y);
    2182     87735564 :   lim = get_Fl_threshold(p, Flx_GCD_LIMIT, Flx_GCD2_LIMIT);
    2183     87735885 :   while (lgpol(y) >= lim)
    2184              :   {
    2185          321 :     if (lgpol(y)<=(lgpol(x)>>1))
    2186              :     {
    2187            0 :       GEN r = Flx_rem_pre(x, y, p, pi);
    2188            0 :       x = y; y = r;
    2189              :     }
    2190          321 :     (void) Flx_halfgcd_all_pre(x, y, p, pi, &x, &y);
    2191          321 :     if (gc_needed(av,2))
    2192              :     {
    2193            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_gcd (y = %ld)",degpol(y));
    2194            0 :       (void)gc_all(av,2,&x,&y);
    2195              :     }
    2196              :   }
    2197     87735564 :   return gc_leaf(av, Flx_gcd_basecase(x,y,p,pi));
    2198              : }
    2199              : GEN
    2200     36623665 : Flx_gcd(GEN x, GEN y, ulong p)
    2201     36623665 : { return Flx_gcd_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2202              : 
    2203              : int
    2204      9229580 : Flx_is_squarefree(GEN z, ulong p)
    2205              : {
    2206      9229580 :   pari_sp av = avma;
    2207      9229580 :   GEN d = Flx_gcd(z, Flx_deriv(z,p), p);
    2208      9229580 :   return gc_bool(av, degpol(d) == 0);
    2209              : }
    2210              : 
    2211              : static long
    2212       127014 : Flx_is_smooth_squarefree(GEN f, long r, ulong p, ulong pi)
    2213              : {
    2214       127014 :   pari_sp av = avma;
    2215              :   long i;
    2216       127014 :   GEN sx = polx_Flx(f[1]), a = sx;
    2217       127014 :   for(i=1;;i++)
    2218              :   {
    2219       536410 :     if (degpol(f)<=r) return gc_long(av,1);
    2220       515617 :     a = Flxq_powu_pre(Flx_rem_pre(a,f,p,pi), p, f, p, pi);
    2221       515617 :     if (Flx_equal(a, sx)) return gc_long(av,1);
    2222       511155 :     if (i==r) return gc_long(av,0);
    2223       409396 :     f = Flx_div_pre(f, Flx_gcd_pre(Flx_sub(a,sx,p),f,p,pi),p,pi);
    2224              :   }
    2225              : }
    2226              : 
    2227              : static long
    2228         8208 : Flx_is_l_pow(GEN x, ulong p)
    2229              : {
    2230         8208 :   ulong i, lx = lgpol(x);
    2231        16396 :   for (i=1; i<lx; i++)
    2232        14711 :     if (x[i+2] && i%p) return 0;
    2233         1685 :   return 1;
    2234              : }
    2235              : 
    2236              : int
    2237       127014 : Flx_is_smooth_pre(GEN g, long r, ulong p, ulong pi)
    2238              : {
    2239              :   while (1)
    2240         8208 :   {
    2241       127014 :     GEN f = Flx_gcd_pre(g, Flx_deriv(g, p), p, pi);
    2242       127014 :     if (!Flx_is_smooth_squarefree(Flx_div_pre(g, f, p, pi), r, p, pi))
    2243       101759 :       return 0;
    2244        25255 :     if (degpol(f)==0) return 1;
    2245         8208 :     g = Flx_is_l_pow(f,p) ? Flx_deflate(f, p): f;
    2246              :   }
    2247              : }
    2248              : int
    2249        74256 : Flx_is_smooth(GEN g, long r, ulong p)
    2250        74256 : { return Flx_is_smooth_pre(g, r, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2251              : 
    2252              : static GEN
    2253      6255875 : Flx_extgcd_basecase(GEN a, GEN b, ulong p, ulong pi, GEN *ptu, GEN *ptv)
    2254              : {
    2255      6255875 :   pari_sp av=avma;
    2256              :   GEN u,v,u1,v1;
    2257      6255875 :   long vx = a[1];
    2258      6255875 :   v = pol0_Flx(vx); v1 = pol1_Flx(vx);
    2259      6255875 :   if (ptu) { u = pol1_Flx(vx); u1 = pol0_Flx(vx); }
    2260     27596328 :   while (lgpol(b))
    2261              :   {
    2262     21340453 :     GEN r, q = Flx_divrem_pre(a,b,p,pi, &r);
    2263     21340453 :     a = b; b = r;
    2264     21340453 :     if (ptu)
    2265              :     {
    2266      2201089 :       swap(u,u1);
    2267      2201089 :       u1 = Flx_sub(u1, Flx_mul_pre(u, q, p, pi), p);
    2268              :     }
    2269     21340453 :     swap(v,v1);
    2270     21340453 :     v1 = Flx_sub(v1, Flx_mul_pre(v, q, p, pi), p);
    2271     21340453 :     if (gc_needed(av,2))
    2272              :     {
    2273            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_extgcd (d = %ld)",degpol(a));
    2274            0 :       (void)gc_all(av,ptu ? 6: 4, &a,&b,&v,&v1,&u,&u1);
    2275              :     }
    2276              :   }
    2277      6255875 :   if (ptu) *ptu = u;
    2278      6255875 :   *ptv = v;
    2279      6255875 :   return a;
    2280              : }
    2281              : 
    2282              : static GEN
    2283       122234 : Flx_extgcd_halfgcd(GEN x, GEN y, ulong p, ulong pi, GEN *ptu, GEN *ptv)
    2284              : {
    2285              :   GEN u, v;
    2286       122234 :   long lim = get_Fl_threshold(p, Flx_EXTGCD_LIMIT, Flx_EXTGCD2_LIMIT);
    2287       122234 :   GEN V = cgetg(expu(lgpol(y))+2,t_VEC);
    2288       122234 :   long i, n = 0, vs = x[1];
    2289       333319 :   while (lgpol(y) >= lim)
    2290              :   {
    2291       211085 :     if (lgpol(y)<=(lgpol(x)>>1))
    2292              :     {
    2293           26 :       GEN r, q = Flx_divrem_pre(x, y, p, pi, &r);
    2294           26 :       x = y; y = r;
    2295           26 :       gel(V,++n) = mkmat22(pol0_Flx(vs),pol1_Flx(vs),pol1_Flx(vs),Flx_neg(q,p));
    2296              :     } else
    2297       211059 :       gel(V,++n) = Flx_halfgcd_all_pre(x, y, p, pi, &x, &y);
    2298              :   }
    2299       122234 :   y = Flx_extgcd_basecase(x,y,p,pi,&u,&v);
    2300       211085 :   for (i = n; i>1; i--)
    2301              :   {
    2302        88851 :     GEN R = gel(V,i);
    2303        88851 :     GEN u1 = Flx_addmulmul(u, v, gcoeff(R,1,1), gcoeff(R,2,1), p, pi);
    2304        88851 :     GEN v1 = Flx_addmulmul(u, v, gcoeff(R,1,2), gcoeff(R,2,2), p, pi);
    2305        88851 :     u = u1; v = v1;
    2306              :   }
    2307              :   {
    2308       122234 :     GEN R = gel(V,1);
    2309       122234 :     if (ptu)
    2310         6692 :       *ptu = Flx_addmulmul(u, v, gcoeff(R,1,1), gcoeff(R,2,1), p, pi);
    2311       122234 :     *ptv   = Flx_addmulmul(u, v, gcoeff(R,1,2), gcoeff(R,2,2), p, pi);
    2312              :   }
    2313       122234 :   return y;
    2314              : }
    2315              : 
    2316              : /* x and y in Z[X], return lift(gcd(x mod p, y mod p)). Set u and v st
    2317              :  * ux + vy = gcd (mod p) */
    2318              : GEN
    2319      6255875 : Flx_extgcd_pre(GEN x, GEN y, ulong p, ulong pi, GEN *ptu, GEN *ptv)
    2320              : {
    2321      6255875 :   pari_sp av = avma;
    2322              :   GEN d;
    2323      6255875 :   long lim = get_Fl_threshold(p, Flx_EXTGCD_LIMIT, Flx_EXTGCD2_LIMIT);
    2324      6255875 :   if (lgpol(y) >= lim)
    2325       122234 :     d = Flx_extgcd_halfgcd(x, y, p, pi, ptu, ptv);
    2326              :   else
    2327      6133641 :     d = Flx_extgcd_basecase(x, y, p, pi, ptu, ptv);
    2328      6255875 :   return gc_all(av, ptu?3:2, &d, ptv, ptu);
    2329              : }
    2330              : GEN
    2331       861529 : Flx_extgcd(GEN x, GEN y, ulong p, GEN *ptu, GEN *ptv)
    2332       861529 : { return Flx_extgcd_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p), ptu, ptv); }
    2333              : 
    2334              : static GEN
    2335         1044 : Flx_halfres_pre(GEN x, GEN y, ulong p, ulong pi, GEN *a, GEN *b, ulong *r)
    2336              : {
    2337              :   struct Flx_res res;
    2338              :   GEN R;
    2339              :   long dB;
    2340              : 
    2341         1044 :   res.res  = *r;
    2342         1044 :   res.lc   = Flx_lead(y);
    2343         1044 :   res.deg0 = degpol(x);
    2344         1044 :   res.deg1 = degpol(y);
    2345         1044 :   res.off = 0;
    2346         1044 :   R = Flx_halfres_i(x, y, p, pi, a, b, &res);
    2347         1044 :   dB = degpol(*b);
    2348         1044 :   if (dB < degpol(y))
    2349         1044 :     Flx_halfres_update_pre(res.deg0, res.deg1, dB, p, pi, &res);
    2350         1044 :   *r = res.res;
    2351         1044 :   return R;
    2352              : }
    2353              : 
    2354              : static ulong
    2355     14609815 : Flx_resultant_basecase_pre(GEN a, GEN b, ulong p, ulong pi)
    2356              : {
    2357              :   pari_sp av;
    2358              :   long da,db,dc;
    2359     14609815 :   ulong lb, res = 1UL;
    2360              :   GEN c;
    2361              : 
    2362     14609815 :   da = degpol(a);
    2363     14609815 :   db = degpol(b);
    2364     14609815 :   if (db > da)
    2365              :   {
    2366            0 :     swapspec(a,b, da,db);
    2367            0 :     if (both_odd(da,db)) res = p-res;
    2368              :   }
    2369     14609815 :   else if (!da) return 1; /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
    2370     14609815 :   av = avma;
    2371    120050461 :   while (db)
    2372              :   {
    2373    105446786 :     lb = b[db+2];
    2374    105446786 :     c = Flx_rem_pre(a,b, p,pi);
    2375    105446786 :     a = b; b = c; dc = degpol(c);
    2376    105446786 :     if (dc < 0) return gc_long(av,0);
    2377              : 
    2378    105440646 :     if (both_odd(da,db)) res = p - res;
    2379    105440646 :     if (lb != 1) res = Fl_mul(res, Fl_powu_pre(lb, da - dc, p, pi), p);
    2380    105440646 :     if (gc_needed(av,2))
    2381              :     {
    2382            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_resultant (da = %ld)",da);
    2383            0 :       (void)gc_all(av,2, &a,&b);
    2384              :     }
    2385    105440646 :     da = db; /* = degpol(a) */
    2386    105440646 :     db = dc; /* = degpol(b) */
    2387              :   }
    2388     14603675 :   return gc_ulong(av, Fl_mul(res, Fl_powu_pre(b[2], da, p, pi), p));
    2389              : }
    2390              : 
    2391              : ulong
    2392     14611257 : Flx_resultant_pre(GEN x, GEN y, ulong p, ulong pi)
    2393              : {
    2394     14611257 :   pari_sp av = avma;
    2395              :   long lim;
    2396     14611257 :   ulong res = 1;
    2397     14611257 :   long dx = degpol(x), dy = degpol(y);
    2398     14611257 :   if (dx < 0 || dy < 0) return 0;
    2399     14609815 :   if (dx < dy)
    2400              :   {
    2401      1045004 :     swap(x,y);
    2402      1045004 :     if (both_odd(dx, dy))
    2403         1906 :       res = Fl_neg(res, p);
    2404              :   }
    2405     14609815 :   lim = get_Fl_threshold(p, Flx_GCD_LIMIT, Flx_GCD2_LIMIT);
    2406     14610667 :   while (lgpol(y) >= lim)
    2407              :   {
    2408          852 :     if (lgpol(y)<=(lgpol(x)>>1))
    2409              :     {
    2410            0 :       GEN r = Flx_rem_pre(x, y, p, pi);
    2411            0 :       long dx = degpol(x), dy = degpol(y), dr = degpol(r);
    2412            0 :       ulong ly = y[dy+2];
    2413            0 :       if (ly != 1) res = Fl_mul(res, Fl_powu_pre(ly, dx - dr, p, pi), p);
    2414            0 :       if (both_odd(dx, dy))
    2415            0 :         res = Fl_neg(res, p);
    2416            0 :       x = y; y = r;
    2417              :     }
    2418          852 :     (void) Flx_halfres_pre(x, y, p, pi, &x, &y, &res);
    2419          852 :     if (gc_needed(av,2))
    2420              :     {
    2421            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_res (y = %ld)",degpol(y));
    2422            0 :       (void)gc_all(av,2,&x,&y);
    2423              :     }
    2424              :   }
    2425     14609815 :   return gc_ulong(av, Fl_mul(res, Flx_resultant_basecase_pre(x, y, p, pi), p));
    2426              : }
    2427              : 
    2428              : ulong
    2429      4738003 : Flx_resultant(GEN a, GEN b, ulong p)
    2430      4738003 : { return Flx_resultant_pre(a, b, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2431              : 
    2432              : /* If resultant is 0, *ptU and *ptV are not set */
    2433              : static ulong
    2434           53 : Flx_extresultant_basecase(GEN a, GEN b, ulong p, ulong pi, GEN *ptU, GEN *ptV)
    2435              : {
    2436           53 :   GEN z,q,u,v, x = a, y = b;
    2437           53 :   ulong lb, res = 1UL;
    2438           53 :   pari_sp av = avma;
    2439              :   long dx, dy, dz;
    2440           53 :   long vs = a[1];
    2441              : 
    2442           53 :   u = pol0_Flx(vs);
    2443           53 :   v = pol1_Flx(vs); /* v = 1 */
    2444           53 :   dx = degpol(x);
    2445           53 :   dy = degpol(y);
    2446          764 :   while (dy)
    2447              :   { /* b u = x (a), b v = y (a) */
    2448          711 :     lb = y[dy+2];
    2449          711 :     q = Flx_divrem_pre(x,y, p, pi, &z);
    2450          711 :     x = y; y = z; /* (x,y) = (y, x - q y) */
    2451          711 :     dz = degpol(z); if (dz < 0) return gc_ulong(av,0);
    2452          711 :     z = Flx_sub(u, Flx_mul_pre(q,v, p, pi), p);
    2453          711 :     u = v; v = z; /* (u,v) = (v, u - q v) */
    2454              : 
    2455          711 :     if (both_odd(dx,dy)) res = p - res;
    2456          711 :     if (lb != 1) res = Fl_mul(res, Fl_powu_pre(lb, dx-dz, p, pi), p);
    2457          711 :     dx = dy; /* = degpol(x) */
    2458          711 :     dy = dz; /* = degpol(y) */
    2459              :   }
    2460           53 :   res = Fl_mul(res, Fl_powu_pre(y[2], dx, p, pi), p);
    2461           53 :   lb = Fl_mul(res, Fl_inv(y[2],p), p);
    2462           53 :   v = gc_leaf(av, Flx_Fl_mul_pre(v, lb, p, pi));
    2463           53 :   av = avma;
    2464           53 :   u = Flx_sub(Fl_to_Flx(res,vs), Flx_mul_pre(b,v,p,pi), p);
    2465           53 :   u = gc_leaf(av, Flx_div_pre(u,a,p,pi)); /* = (res - b v) / a */
    2466           53 :   *ptU = u;
    2467           53 :   *ptV = v; return res;
    2468              : }
    2469              : 
    2470              : ulong
    2471           53 : Flx_extresultant_pre(GEN x, GEN y, ulong p, ulong pi, GEN *ptU, GEN *ptV)
    2472              : {
    2473           53 :   pari_sp av=avma;
    2474              :   GEN u, v, R;
    2475           53 :   long lim = get_Fl_threshold(p, Flx_EXTGCD_LIMIT, Flx_EXTGCD2_LIMIT);
    2476           53 :   ulong res = 1, res1;
    2477           53 :   long dx = degpol(x), dy = degpol(y);
    2478           53 :   if (dy > dx)
    2479              :   {
    2480           13 :     swap(x,y); lswap(dx,dy);
    2481           13 :     if (both_odd(dx,dy)) res = p-res;
    2482           13 :     R = matJ2_FlxM(x[1]);
    2483           40 :   } else R = matid2_FlxM(x[1]);
    2484           53 :   if (dy < 0) return 0;
    2485          245 :   while (lgpol(y) >= lim)
    2486              :   {
    2487              :     GEN M;
    2488          192 :     if (lgpol(y)<=(lgpol(x)>>1))
    2489              :     {
    2490           20 :       GEN r, q = Flx_divrem_pre(x, y, p, pi, &r);
    2491           20 :       long dx = degpol(x), dy = degpol(y), dr = degpol(r);
    2492           20 :       ulong ly = y[dy+2];
    2493           20 :       if (ly != 1) res = Fl_mul(res, Fl_powu_pre(ly, dx - dr, p, pi), p);
    2494           20 :       if (both_odd(dx, dy))
    2495            0 :         res = Fl_neg(res, p);
    2496           20 :       x = y; y = r;
    2497           20 :       R = Flx_FlxM_qmul(q, R, p,pi);
    2498              :     }
    2499          192 :     M = Flx_halfres_pre(x, y, p, pi, &x, &y, &res);
    2500          192 :     if (!res) return gc_ulong(av, 0);
    2501          192 :     R = FlxM_mul2(M, R, p, pi);
    2502          192 :     (void)gc_all(av,3,&x,&y,&R);
    2503              :   }
    2504           53 :   res1 = Flx_extresultant_basecase(x,y,p,pi,&u,&v);
    2505           53 :   if (!res1) return gc_ulong(av, 0);
    2506           53 :   *ptU = Flx_Fl_mul_pre(Flx_addmulmul(u, v, gcoeff(R,1,1), gcoeff(R,2,1), p, pi), res, p, pi);
    2507           53 :   *ptV = Flx_Fl_mul_pre(Flx_addmulmul(u, v, gcoeff(R,1,2), gcoeff(R,2,2), p, pi), res, p, pi);
    2508           53 :   (void)gc_all(av, 2, ptU, ptV);
    2509           53 :   return Fl_mul(res1,res,p);
    2510              : }
    2511              : 
    2512              : ulong
    2513           53 : Flx_extresultant(GEN a, GEN b, ulong p, GEN *ptU, GEN *ptV)
    2514           53 : { return Flx_extresultant_pre(a, b, p, SMALL_ULONG(p)? 0: get_Fl_red(p), ptU, ptV); }
    2515              : 
    2516              : /* allow pi = 0 (SMALL_ULONG) */
    2517              : ulong
    2518     49647471 : Flx_eval_powers_pre(GEN x, GEN y, ulong p, ulong pi)
    2519              : {
    2520     49647471 :   ulong l0, l1, h0, h1, v1,  i = 1, lx = lg(x)-1;
    2521              : 
    2522     49647471 :   if (lx == 1) return 0;
    2523     46599599 :   x++;
    2524     46599599 :   if (pi)
    2525              :   {
    2526              :     LOCAL_OVERFLOW;
    2527              :     LOCAL_HIREMAINDER;
    2528     46528689 :     l1 = mulll(uel(x,i), uel(y,i)); h1 = hiremainder; v1 = 0;
    2529    117316938 :     while (++i < lx)
    2530              :     {
    2531     70788249 :       l0 = mulll(uel(x,i), uel(y,i)); h0 = hiremainder;
    2532     70788249 :       l1 = addll(l0, l1); h1 = addllx(h0, h1); v1 += overflow;
    2533              :     }
    2534        81325 :     return v1? remlll_pre(v1, h1, l1, p, pi)
    2535     46610014 :              : remll_pre(h1, l1, p, pi);
    2536              :   }
    2537              :   else
    2538              :   {
    2539        70910 :     l1 = x[i] * y[i];
    2540     30946658 :     while (++i < lx) { l1 += x[i] * y[i]; if (l1 & HIGHBIT) l1 %= p; }
    2541        70910 :     return l1 % p;
    2542              :   }
    2543              : }
    2544              : 
    2545              : /* allow pi = 0 (SMALL_ULONG) */
    2546              : ulong
    2547    136438260 : Flx_eval_pre(GEN x, ulong y, ulong p, ulong pi)
    2548              : {
    2549    136438260 :   long i, n = degpol(x);
    2550              :   ulong t;
    2551    136438260 :   if (n <= 0) return n? 0: x[2];
    2552     39669967 :   if (n > 15)
    2553              :   {
    2554       180213 :     pari_sp av = avma;
    2555       180213 :     GEN v = Fl_powers_pre(y, n, p, pi);
    2556       180213 :     return gc_ulong(av, Flx_eval_powers_pre(x, v, p, pi));
    2557              :   }
    2558     39489754 :   i = n+2; t = x[i];
    2559     39489754 :   if (pi)
    2560              :   {
    2561    137110086 :     for (i--; i>=2; i--) t = Fl_addmul_pre(uel(x, i), t, y, p, pi);
    2562     38270447 :     return t;
    2563              :   }
    2564      2895297 :   for (i--; i>=2; i--) t = (t * y + x[i]) % p;
    2565      1219307 :   return t %= p;
    2566              : }
    2567              : ulong
    2568     20507460 : Flx_eval(GEN x, ulong y, ulong p)
    2569     20507460 : { return Flx_eval_pre(x, y, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2570              : 
    2571              : ulong
    2572         3719 : Flv_prod_pre(GEN x, ulong p, ulong pi)
    2573              : {
    2574         3719 :   pari_sp ltop = avma;
    2575              :   GEN v;
    2576         3719 :   long i,k,lx = lg(x);
    2577         3719 :   if (lx == 1) return 1UL;
    2578         3719 :   if (lx == 2) return uel(x,1);
    2579         3152 :   v = cgetg(1+(lx << 1), t_VECSMALL);
    2580         3152 :   k = 1;
    2581        26952 :   for (i=1; i<lx-1; i+=2)
    2582        23800 :     uel(v,k++) = Fl_mul_pre(uel(x,i), uel(x,i+1), p, pi);
    2583         3152 :   if (i < lx) uel(v,k++) = uel(x,i);
    2584        13043 :   while (k > 2)
    2585              :   {
    2586         9891 :     lx = k; k = 1;
    2587        33691 :     for (i=1; i<lx-1; i+=2)
    2588        23800 :       uel(v,k++) = Fl_mul_pre(uel(v,i), uel(v,i+1), p, pi);
    2589         9891 :     if (i < lx) uel(v,k++) = uel(v,i);
    2590              :   }
    2591         3152 :   return gc_ulong(ltop, uel(v,1));
    2592              : }
    2593              : 
    2594              : ulong
    2595            0 : Flv_prod(GEN v, ulong p)
    2596              : {
    2597            0 :   return Flv_prod_pre(v, p, get_Fl_red(p));
    2598              : }
    2599              : 
    2600              : GEN
    2601            0 : FlxV_prod(GEN V, ulong p)
    2602              : {
    2603              :   struct _Flx D;
    2604            0 :   D.p = p; D.pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2605            0 :   return gen_product(V, (void *)&D, &_Flx_mul);
    2606              : }
    2607              : 
    2608              : static GEN
    2609            0 : _Flx_pow(void* E, GEN x, GEN y)
    2610            0 : { struct _Flx *D = (struct _Flx *)E; return Flx_powu(x, itou(y), D->p); }
    2611              : static GEN
    2612            0 : _Flx_one(void *E)
    2613            0 : { struct _Flx *D = (struct _Flx *)E; return pol1_Flx(D->v); }
    2614              : 
    2615              : GEN
    2616            0 : FlxV_factorback(GEN f, GEN e, ulong p, long v)
    2617              : {
    2618              :   struct _Flx D;
    2619            0 :   D.p = p; D.v = v;
    2620            0 :   D.pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2621            0 :   return gen_factorback(f, e, (void *)&D, &_Flx_mul, &_Flx_pow, &_Flx_one);
    2622              : }
    2623              : 
    2624              : /* compute prod (x - a[i]) */
    2625              : GEN
    2626       904279 : Flv_roots_to_pol(GEN a, ulong p, long vs)
    2627              : {
    2628              :   struct _Flx D;
    2629       904279 :   long i,k,lx = lg(a);
    2630              :   GEN p1;
    2631       904279 :   if (lx == 1) return pol1_Flx(vs);
    2632       904279 :   p1 = cgetg(lx, t_VEC);
    2633     15013125 :   for (k=1,i=1; i<lx-1; i+=2)
    2634     14108846 :     gel(p1,k++) = mkvecsmall4(vs, Fl_mul(a[i], a[i+1], p),
    2635     14108846 :                               Fl_neg(Fl_add(a[i],a[i+1],p),p), 1);
    2636       904279 :   if (i < lx)
    2637        59357 :     gel(p1,k++) = mkvecsmall3(vs, Fl_neg(a[i],p), 1);
    2638       904279 :   D.p = p; D.pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2639       904279 :   setlg(p1, k); return gen_product(p1, (void *)&D, _Flx_mul);
    2640              : }
    2641              : 
    2642              : /* set v[i] = w[i]^{-1}; may be called with w = v, suitable for "large" p */
    2643              : INLINE void
    2644     21753054 : Flv_inv_pre_indir(GEN w, GEN v, ulong p, ulong pi)
    2645              : {
    2646     21753054 :   pari_sp av = avma;
    2647     21753054 :   long n = lg(w), i;
    2648              :   ulong u;
    2649              :   GEN c;
    2650              : 
    2651     21753054 :   if (n == 1) return;
    2652     21753054 :   c = cgetg(n, t_VECSMALL); c[1] = w[1];
    2653     93673980 :   for (i = 2; i < n; ++i) c[i] = Fl_mul_pre(w[i], c[i-1], p, pi);
    2654     21753054 :   i = n-1; u = Fl_inv(c[i], p);
    2655     93673980 :   for ( ; i > 1; --i)
    2656              :   {
    2657     71920926 :     ulong t = Fl_mul_pre(u, c[i-1], p, pi);
    2658     71920926 :     u = Fl_mul_pre(u, w[i], p, pi); v[i] = t;
    2659              :   }
    2660     21753054 :   v[1] = u; set_avma(av);
    2661              : }
    2662              : 
    2663              : void
    2664     20037772 : Flv_inv_pre_inplace(GEN v, ulong p, ulong pi) { Flv_inv_pre_indir(v,v, p, pi); }
    2665              : 
    2666              : GEN
    2667         9646 : Flv_inv_pre(GEN w, ulong p, ulong pi)
    2668         9646 : { GEN v = cgetg(lg(w), t_VECSMALL); Flv_inv_pre_indir(w, v, p, pi); return v; }
    2669              : 
    2670              : /* set v[i] = w[i]^{-1}; may be called with w = v, suitable for SMALL_ULONG p */
    2671              : INLINE void
    2672        52847 : Flv_inv_indir(GEN w, GEN v, ulong p)
    2673              : {
    2674        52847 :   pari_sp av = avma;
    2675        52847 :   long n = lg(w), i;
    2676              :   ulong u;
    2677              :   GEN c;
    2678              : 
    2679        52847 :   if (n == 1) return;
    2680        52847 :   c = cgetg(n, t_VECSMALL); c[1] = w[1];
    2681      1827626 :   for (i = 2; i < n; ++i) c[i] = Fl_mul(w[i], c[i-1], p);
    2682        52847 :   i = n-1; u = Fl_inv(c[i], p);
    2683      1827626 :   for ( ; i > 1; --i)
    2684              :   {
    2685      1774779 :     ulong t = Fl_mul(u, c[i-1], p);
    2686      1774779 :     u = Fl_mul(u, w[i], p); v[i] = t;
    2687              :   }
    2688        52847 :   v[1] = u; set_avma(av);
    2689              : }
    2690              : static void
    2691      1758483 : Flv_inv_i(GEN v, GEN w, ulong p)
    2692              : {
    2693      1758483 :   if (SMALL_ULONG(p)) Flv_inv_indir(w, v, p);
    2694      1705636 :   else Flv_inv_pre_indir(w, v, p, get_Fl_red(p));
    2695      1758483 : }
    2696              : void
    2697        12017 : Flv_inv_inplace(GEN v, ulong p) { Flv_inv_i(v, v, p); }
    2698              : GEN
    2699      1746466 : Flv_inv(GEN w, ulong p)
    2700      1746466 : { GEN v = cgetg(lg(w), t_VECSMALL); Flv_inv_i(v, w, p); return v; }
    2701              : 
    2702              : GEN
    2703     40744193 : Flx_div_by_X_x(GEN a, ulong x, ulong p, ulong *rem)
    2704              : {
    2705     40744193 :   long l = lg(a), i;
    2706              :   GEN a0, z0, z;
    2707     40744193 :   if (l <= 3)
    2708              :   {
    2709            0 :     if (rem) *rem = l == 2? 0: a[2];
    2710            0 :     return zero_Flx(a[1]);
    2711              :   }
    2712     40744193 :   z = cgetg(l-1,t_VECSMALL); z[1] = a[1];
    2713     40744193 :   a0 = a + l-1;
    2714     40744193 :   z0 = z + l-2; *z0 = *a0--;
    2715     40744193 :   if (SMALL_ULONG(p))
    2716              :   {
    2717     94815787 :     for (i=l-3; i>1; i--) /* z[i] = (a[i+1] + x*z[i+1]) % p */
    2718              :     {
    2719     68944366 :       ulong t = (*a0-- + x *  *z0--) % p;
    2720     68944366 :       *z0 = (long)t;
    2721              :     }
    2722     25871421 :     if (rem) *rem = (*a0 + x *  *z0) % p;
    2723              :   }
    2724              :   else
    2725              :   {
    2726     56099130 :     for (i=l-3; i>1; i--)
    2727              :     {
    2728     41226358 :       ulong t = Fl_add((ulong)*a0--, Fl_mul(x, *z0--, p), p);
    2729     41226358 :       *z0 = (long)t;
    2730              :     }
    2731     14872772 :     if (rem) *rem = Fl_add((ulong)*a0, Fl_mul(x, *z0, p), p);
    2732              :   }
    2733     40744193 :   return z;
    2734              : }
    2735              : 
    2736              : /* xa, ya = t_VECSMALL */
    2737              : static GEN
    2738      1747672 : Flv_producttree(GEN xa, GEN s, ulong p, ulong pi, long vs)
    2739              : {
    2740      1747672 :   long n = lg(xa)-1;
    2741      1747672 :   long m = n==1 ? 1: expu(n-1)+1;
    2742      1747672 :   long i, j, k, ls = lg(s);
    2743      1747672 :   GEN T = cgetg(m+1, t_VEC);
    2744      1747672 :   GEN t = cgetg(ls, t_VEC);
    2745     11664058 :   for (j=1, k=1; j<ls; k+=s[j++])
    2746      9916386 :     gel(t, j) = s[j] == 1 ?
    2747      9916386 :              mkvecsmall3(vs, Fl_neg(xa[k], p), 1):
    2748      3308194 :              mkvecsmall4(vs, Fl_mul(xa[k], xa[k+1], p),
    2749      3308194 :                  Fl_neg(Fl_add(xa[k],xa[k+1],p),p), 1);
    2750      1747672 :   gel(T,1) = t;
    2751      4780837 :   for (i=2; i<=m; i++)
    2752              :   {
    2753      3033165 :     GEN u = gel(T, i-1);
    2754      3033165 :     long n = lg(u)-1;
    2755      3033165 :     GEN t = cgetg(((n+1)>>1)+1, t_VEC);
    2756     11201879 :     for (j=1, k=1; k<n; j++, k+=2)
    2757      8168714 :       gel(t, j) = Flx_mul_pre(gel(u, k), gel(u, k+1), p, pi);
    2758      3033165 :     gel(T, i) = t;
    2759              :   }
    2760      1747672 :   return T;
    2761              : }
    2762              : 
    2763              : static GEN
    2764      1788391 : Flx_Flv_multieval_tree(GEN P, GEN xa, GEN T, ulong p, ulong pi)
    2765              : {
    2766              :   long i,j,k;
    2767      1788391 :   long m = lg(T)-1;
    2768      1788391 :   GEN R = cgetg(lg(xa), t_VECSMALL);
    2769      1788391 :   GEN Tp = cgetg(m+1, t_VEC), t;
    2770      1788391 :   gel(Tp, m) = mkvec(P);
    2771      5009016 :   for (i=m-1; i>=1; i--)
    2772              :   {
    2773      3220625 :     GEN u = gel(T, i), v = gel(Tp, i+1);
    2774      3220625 :     long n = lg(u)-1;
    2775      3220625 :     t = cgetg(n+1, t_VEC);
    2776     12433928 :     for (j=1, k=1; k<n; j++, k+=2)
    2777              :     {
    2778      9213303 :       gel(t, k)   = Flx_rem_pre(gel(v, j), gel(u, k), p, pi);
    2779      9213303 :       gel(t, k+1) = Flx_rem_pre(gel(v, j), gel(u, k+1), p, pi);
    2780              :     }
    2781      3220625 :     gel(Tp, i) = t;
    2782              :   }
    2783              :   {
    2784      1788391 :     GEN u = gel(T, i+1), v = gel(Tp, i+1);
    2785      1788391 :     long n = lg(u)-1;
    2786     12790085 :     for (j=1, k=1; j<=n; j++)
    2787              :     {
    2788     11001694 :       long c, d = degpol(gel(u,j));
    2789     25572570 :       for (c=1; c<=d; c++, k++) R[k] = Flx_eval_pre(gel(v, j), xa[k], p, pi);
    2790              :     }
    2791      1788391 :     return gc_const((pari_sp)R, R);
    2792              :   }
    2793              : }
    2794              : 
    2795              : static GEN
    2796      2648470 : FlvV_polint_tree(GEN T, GEN R, GEN s, GEN xa, GEN ya, ulong p, ulong pi, long vs)
    2797              : {
    2798      2648470 :   pari_sp av = avma;
    2799      2648470 :   long m = lg(T)-1;
    2800      2648470 :   long i, j, k, ls = lg(s);
    2801      2648470 :   GEN Tp = cgetg(m+1, t_VEC);
    2802      2648470 :   GEN t = cgetg(ls, t_VEC);
    2803     32778212 :   for (j=1, k=1; j<ls; k+=s[j++])
    2804     30129742 :     if (s[j]==2)
    2805              :     {
    2806     10539845 :       ulong a = Fl_mul(ya[k], R[k], p);
    2807     10539845 :       ulong b = Fl_mul(ya[k+1], R[k+1], p);
    2808     10539845 :       gel(t, j) = mkvecsmall3(vs, Fl_neg(Fl_add(Fl_mul(xa[k], b, p ),
    2809     10539845 :                   Fl_mul(xa[k+1], a, p), p), p), Fl_add(a, b, p));
    2810     10539845 :       gel(t, j) = Flx_renormalize(gel(t, j), 4);
    2811              :     }
    2812              :     else
    2813     19589897 :       gel(t, j) = Fl_to_Flx(Fl_mul(ya[k], R[k], p), vs);
    2814      2648470 :   gel(Tp, 1) = t;
    2815      9633648 :   for (i=2; i<=m; i++)
    2816              :   {
    2817      6985178 :     GEN u = gel(T, i-1);
    2818      6985178 :     GEN t = cgetg(lg(gel(T,i)), t_VEC);
    2819      6985178 :     GEN v = gel(Tp, i-1);
    2820      6985178 :     long n = lg(v)-1;
    2821     34466450 :     for (j=1, k=1; k<n; j++, k+=2)
    2822     27481272 :       gel(t, j) = Flx_add(Flx_mul_pre(gel(u, k), gel(v, k+1), p, pi),
    2823     27481272 :                           Flx_mul_pre(gel(u, k+1), gel(v, k), p, pi), p);
    2824      6985178 :     gel(Tp, i) = t;
    2825              :   }
    2826      2648470 :   return gc_leaf(av, gmael(Tp,m,1));
    2827              : }
    2828              : 
    2829              : GEN
    2830            0 : Flx_Flv_multieval(GEN P, GEN xa, ulong p)
    2831              : {
    2832            0 :   pari_sp av = avma;
    2833            0 :   GEN s = producttree_scheme(lg(xa)-1);
    2834            0 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2835            0 :   GEN T = Flv_producttree(xa, s, p, pi, P[1]);
    2836            0 :   return gc_leaf(av, Flx_Flv_multieval_tree(P, xa, T, p, pi));
    2837              : }
    2838              : 
    2839              : static GEN
    2840         2478 : FlxV_Flv_multieval_tree(GEN x, GEN xa, GEN T, ulong p, ulong pi)
    2841        45675 : { pari_APPLY_same(Flx_Flv_multieval_tree(gel(x,i), xa, T, p, pi)) }
    2842              : 
    2843              : GEN
    2844         2478 : FlxV_Flv_multieval(GEN P, GEN xa, ulong p)
    2845              : {
    2846         2478 :   pari_sp av = avma;
    2847         2478 :   GEN s = producttree_scheme(lg(xa)-1);
    2848         2478 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2849         2478 :   GEN T = Flv_producttree(xa, s, p, pi, P[1]);
    2850         2478 :   return gc_upto(av, FlxV_Flv_multieval_tree(P, xa, T, p, pi));
    2851              : }
    2852              : 
    2853              : GEN
    2854      1485852 : Flv_polint(GEN xa, GEN ya, ulong p, long vs)
    2855              : {
    2856      1485852 :   pari_sp av = avma;
    2857      1485852 :   GEN s = producttree_scheme(lg(xa)-1);
    2858      1485852 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2859      1485852 :   GEN T = Flv_producttree(xa, s, p, pi, vs);
    2860      1485852 :   long m = lg(T)-1;
    2861      1485852 :   GEN P = Flx_deriv(gmael(T, m, 1), p);
    2862      1485852 :   GEN R = Flv_inv(Flx_Flv_multieval_tree(P, xa, T, p, pi), p);
    2863      1485852 :   return gc_leaf(av, FlvV_polint_tree(T, R, s, xa, ya, p, pi, vs));
    2864              : }
    2865              : 
    2866              : GEN
    2867       105897 : Flv_Flm_polint(GEN xa, GEN ya, ulong p, long vs)
    2868              : {
    2869       105897 :   pari_sp av = avma;
    2870       105897 :   GEN s = producttree_scheme(lg(xa)-1);
    2871       105897 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2872       105897 :   GEN T = Flv_producttree(xa, s, p, pi, vs);
    2873       105897 :   long i, m = lg(T)-1, l = lg(ya)-1;
    2874       105897 :   GEN P = Flx_deriv(gmael(T, m, 1), p);
    2875       105897 :   GEN R = Flv_inv(Flx_Flv_multieval_tree(P, xa, T, p, pi), p);
    2876       105897 :   GEN M = cgetg(l+1, t_VEC);
    2877      1268515 :   for (i=1; i<=l; i++)
    2878      1162618 :     gel(M,i) = FlvV_polint_tree(T, R, s, xa, gel(ya,i), p, pi, vs);
    2879       105897 :   return gc_upto(av, M);
    2880              : }
    2881              : 
    2882              : GEN
    2883       153445 : Flv_invVandermonde(GEN L, ulong den, ulong p)
    2884              : {
    2885       153445 :   pari_sp av = avma;
    2886       153445 :   long i, n = lg(L);
    2887              :   GEN M, R;
    2888       153445 :   GEN s = producttree_scheme(n-1);
    2889       153445 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    2890       153445 :   GEN tree = Flv_producttree(L, s, p, pi, 0);
    2891       153445 :   long m = lg(tree)-1;
    2892       153445 :   GEN T = gmael(tree, m, 1);
    2893       153445 :   R = Flv_inv(Flx_Flv_multieval_tree(Flx_deriv(T, p), L, tree, p, pi), p);
    2894       153445 :   if (den!=1) R = Flv_Fl_mul(R, den, p);
    2895       153445 :   M = cgetg(n, t_MAT);
    2896       603585 :   for (i = 1; i < n; i++)
    2897              :   {
    2898       450140 :     GEN P = Flx_Fl_mul(Flx_div_by_X_x(T, uel(L,i), p, NULL), uel(R,i), p);
    2899       450140 :     gel(M,i) = Flx_to_Flv(P, n-1);
    2900              :   }
    2901       153445 :   return gc_GEN(av, M);
    2902              : }
    2903              : 
    2904              : /***********************************************************************/
    2905              : /**                               Flxq                                **/
    2906              : /***********************************************************************/
    2907              : /* Flxq objects are Flx modulo another Flx called q. */
    2908              : 
    2909              : /* Product of y and x in Z/pZ[X]/(T), as t_VECSMALL. */
    2910              : GEN
    2911    194806362 : Flxq_mul_pre(GEN x,GEN y,GEN T,ulong p,ulong pi)
    2912    194806362 : { return Flx_rem_pre(Flx_mul_pre(x,y,p,pi),T,p,pi); }
    2913              : GEN
    2914     18090061 : Flxq_mul(GEN x,GEN y,GEN T,ulong p)
    2915     18090061 : { return Flxq_mul_pre(x,y,T,p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2916              : 
    2917              : GEN
    2918    281870995 : Flxq_sqr_pre(GEN x,GEN T,ulong p,ulong pi)
    2919    281870995 : { return Flx_rem_pre(Flx_sqr_pre(x, p,pi), T, p,pi); }
    2920              : /* Square of y in Z/pZ[X]/(T), as t_VECSMALL. */
    2921              : GEN
    2922      2760620 : Flxq_sqr(GEN x,GEN T,ulong p)
    2923      2760620 : { return Flxq_sqr_pre(x,T,p,SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2924              : 
    2925              : static GEN
    2926      1549491 : _Flxq_red(void *E, GEN x)
    2927      1549491 : { struct _Flxq *s = (struct _Flxq *)E;
    2928      1549491 :   return Flx_rem_pre(x, s->T, s->p, s->pi); }
    2929              : static GEN
    2930       389834 : _Flxq_add(void *E, GEN x, GEN y)
    2931       389834 : { struct _Flxq *s = (struct _Flxq *)E;
    2932       389834 :   return Flx_add(x,y,s->p); }
    2933              : static GEN
    2934            0 : _Flxq_sub(void *E, GEN x, GEN y)
    2935            0 : { struct _Flxq *s = (struct _Flxq *)E;
    2936            0 :   return Flx_sub(x,y,s->p); }
    2937              : static GEN
    2938    275780571 : _Flxq_sqr(void *data, GEN x)
    2939              : {
    2940    275780571 :   struct _Flxq *D = (struct _Flxq*)data;
    2941    275780571 :   return Flxq_sqr_pre(x, D->T, D->p, D->pi);
    2942              : }
    2943              : static GEN
    2944    149810385 : _Flxq_mul(void *data, GEN x, GEN y)
    2945              : {
    2946    149810385 :   struct _Flxq *D = (struct _Flxq*)data;
    2947    149810385 :   return Flxq_mul_pre(x,y, D->T, D->p, D->pi);
    2948              : }
    2949              : static GEN
    2950     22642779 : _Flxq_one(void *data)
    2951              : {
    2952     22642779 :   struct _Flxq *D = (struct _Flxq*)data;
    2953     22642779 :   return pol1_Flx(get_Flx_var(D->T));
    2954              : }
    2955              : static GEN
    2956            0 : _Flxq_zero(void *data)
    2957              : {
    2958            0 :   struct _Flxq *D = (struct _Flxq*)data;
    2959            0 :   return pol0_Flx(get_Flx_var(D->T));
    2960              : }
    2961              : static GEN
    2962     23260288 : _Flxq_powu_i(struct _Flxq *D, GEN x, ulong n)
    2963     23260288 : { return gen_powu_i(x, n, (void*)D, &_Flxq_sqr, &_Flxq_mul); }
    2964              : static GEN
    2965           68 : _Flxq_powu(struct _Flxq *D, GEN x, ulong n)
    2966           68 : { pari_sp av = avma; return gc_leaf(av, _Flxq_powu_i(D, x, n)); }
    2967              : /* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
    2968              : GEN
    2969     24517634 : Flxq_powu_pre(GEN x, ulong n, GEN T, ulong p, ulong pi)
    2970              : {
    2971              :   pari_sp av;
    2972              :   struct _Flxq D;
    2973     24517634 :   switch(n)
    2974              :   {
    2975            0 :     case 0: return pol1_Flx(get_Flx_var(T));
    2976       279256 :     case 1: return Flx_copy(x);
    2977       978158 :     case 2: return Flxq_sqr_pre(x, T, p, pi);
    2978              :   }
    2979     23260220 :   av = avma; set_Flxq_pre(&D, T, p, pi);
    2980     23260220 :   return gc_leaf(av, _Flxq_powu_i(&D, x, n));
    2981              : }
    2982              : GEN
    2983       500641 : Flxq_powu(GEN x, ulong n, GEN T, ulong p)
    2984       500641 : { return Flxq_powu_pre(x, n, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    2985              : 
    2986              : /* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
    2987              : GEN
    2988     23678551 : Flxq_pow_pre(GEN x, GEN n, GEN T, ulong p, ulong pi)
    2989              : {
    2990     23678551 :   pari_sp av = avma;
    2991              :   struct _Flxq D;
    2992              :   GEN y;
    2993     23678551 :   long s = signe(n);
    2994     23678551 :   if (!s) return pol1_Flx(get_Flx_var(T));
    2995     23600839 :   if (s < 0) x = Flxq_inv_pre(x,T,p,pi);
    2996     23600839 :   if (is_pm1(n)) return s < 0 ? x : Flx_copy(x);
    2997     23079305 :   set_Flxq_pre(&D, T, p, pi);
    2998     23079305 :   y = gen_pow_i(x, n, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
    2999     23079305 :   return gc_leaf(av, y);
    3000              : }
    3001              : GEN
    3002       934136 : Flxq_pow(GEN x, GEN n, GEN T, ulong p)
    3003       934136 : { return Flxq_pow_pre(x, n, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3004              : 
    3005              : GEN
    3006           28 : Flxq_pow_init_pre(GEN x, GEN n, long k, GEN T, ulong p, ulong pi)
    3007              : {
    3008           28 :   struct _Flxq D; set_Flxq_pre(&D, T, p, pi);
    3009           28 :   return gen_pow_init(x, n, k, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
    3010              : }
    3011              : GEN
    3012            0 : Flxq_pow_init(GEN x, GEN n, long k, GEN T, ulong p)
    3013            0 : { return Flxq_pow_init_pre(x, n, k, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3014              : 
    3015              : GEN
    3016         4393 : Flxq_pow_table_pre(GEN R, GEN n, GEN T, ulong p, ulong pi)
    3017              : {
    3018         4393 :   struct _Flxq D; set_Flxq_pre(&D, T, p, pi);
    3019         4393 :   return gen_pow_table(R, n, (void*)&D, &_Flxq_one, &_Flxq_mul);
    3020              : }
    3021              : GEN
    3022            0 : Flxq_pow_table(GEN R, GEN n, GEN T, ulong p)
    3023            0 : { return Flxq_pow_table_pre(R, n, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3024              : 
    3025              : /* Inverse of x in Z/lZ[X]/(T) or NULL if inverse doesn't exist
    3026              :  * not stack clean. */
    3027              : GEN
    3028      5394346 : Flxq_invsafe_pre(GEN x, GEN T, ulong p, ulong pi)
    3029              : {
    3030      5394346 :   GEN V, z = Flx_extgcd_pre(get_Flx_mod(T), x, p, pi, NULL, &V);
    3031              :   ulong iz;
    3032      5394346 :   if (degpol(z)) return NULL;
    3033      5393682 :   iz = Fl_inv(uel(z,2), p);
    3034      5393682 :   return Flx_Fl_mul_pre(V, iz, p, pi);
    3035              : }
    3036              : GEN
    3037       671082 : Flxq_invsafe(GEN x, GEN T, ulong p)
    3038       671082 : { return Flxq_invsafe_pre(x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3039              : 
    3040              : GEN
    3041      4240179 : Flxq_inv_pre(GEN x, GEN T, ulong p, ulong pi)
    3042              : {
    3043      4240179 :   pari_sp av=avma;
    3044      4240179 :   GEN U = Flxq_invsafe_pre(x, T, p, pi);
    3045      4240179 :   if (!U) pari_err_INV("Flxq_inv",Flx_to_ZX(x));
    3046      4240151 :   return gc_leaf(av, U);
    3047              : }
    3048              : GEN
    3049       335914 : Flxq_inv(GEN x, GEN T, ulong p)
    3050       335914 : { return Flxq_inv_pre(x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3051              : 
    3052              : GEN
    3053      2323066 : Flxq_div_pre(GEN x, GEN y, GEN T, ulong p, ulong pi)
    3054              : {
    3055      2323066 :   pari_sp av = avma;
    3056      2323066 :   return gc_leaf(av, Flxq_mul_pre(x,Flxq_inv_pre(y,T,p,pi),T,p,pi));
    3057              : }
    3058              : GEN
    3059      1170208 : Flxq_div(GEN x, GEN y, GEN T, ulong p)
    3060      1170208 : { return Flxq_div_pre(x, y, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3061              : 
    3062              : GEN
    3063     22638386 : Flxq_powers_pre(GEN x, long l, GEN T, ulong p, ulong pi)
    3064              : {
    3065              :   struct _Flxq D;
    3066     22638386 :   long d = degpol(x), dT = get_Flx_degree(T);
    3067     22638386 :   if (d >= dT) { x = Flx_rem_pre(x, T, p, pi); d = degpol(x); }
    3068     22638386 :   set_Flxq_pre(&D, T, p, pi);
    3069     22638386 :   return gen_powers(x, l, 2*d>=dT, (void*)&D, &_Flxq_sqr, &_Flxq_mul, &_Flxq_one);
    3070              : }
    3071              : GEN
    3072       232435 : Flxq_powers(GEN x, long l, GEN T, ulong p)
    3073       232435 : { return Flxq_powers_pre(x, l, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3074              : 
    3075              : GEN
    3076       171249 : Flxq_matrix_pow_pre(GEN y, long n, long m, GEN P, ulong l, ulong li)
    3077       171249 : { return FlxV_to_Flm(Flxq_powers_pre(y,m-1,P,l,li),n); }
    3078              : GEN
    3079          399 : Flxq_matrix_pow(GEN y, long n, long m, GEN P, ulong l)
    3080          399 : { return Flxq_matrix_pow_pre(y, n, m, P, l, SMALL_ULONG(l)? 0: get_Fl_red(l)); }
    3081              : 
    3082              : GEN
    3083     14056189 : Flx_Frobenius_pre(GEN T, ulong p, ulong pi)
    3084     14056189 : { return Flxq_powu_pre(polx_Flx(get_Flx_var(T)), p, T, p, pi); }
    3085              : GEN
    3086        87438 : Flx_Frobenius(GEN T, ulong p)
    3087        87438 : { return Flx_Frobenius_pre(T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3088              : 
    3089              : GEN
    3090        86900 : Flx_matFrobenius_pre(GEN T, ulong p, ulong pi)
    3091              : {
    3092        86900 :   long n = get_Flx_degree(T);
    3093        86900 :   return Flxq_matrix_pow_pre(Flx_Frobenius_pre(T, p, pi), n, n, T, p, pi);
    3094              : }
    3095              : GEN
    3096            0 : Flx_matFrobenius(GEN T, ulong p)
    3097            0 : { return Flx_matFrobenius_pre(T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3098              : 
    3099              : static GEN
    3100     13198433 : Flx_blocks_Flm(GEN P, long n, long m)
    3101              : {
    3102     13198433 :   GEN z = cgetg(m+1,t_MAT);
    3103     13198433 :   long i,j, k=2, l = lg(P);
    3104     37914241 :   for(i=1; i<=m; i++)
    3105              :   {
    3106     24715808 :     GEN zi = cgetg(n+1,t_VECSMALL);
    3107     24715808 :     gel(z,i) = zi;
    3108    114182971 :     for(j=1; j<=n; j++)
    3109     89467163 :       uel(zi, j) = k==l ? 0 : uel(P,k++);
    3110              :   }
    3111     13198433 :   return z;
    3112              : }
    3113              : 
    3114              : GEN
    3115       529859 : Flx_blocks(GEN P, long n, long m)
    3116              : {
    3117       529859 :   GEN z = cgetg(m+1,t_VEC);
    3118       529859 :   long i,j, k=2, l = lg(P);
    3119      1589577 :   for(i=1; i<=m; i++)
    3120              :   {
    3121      1059718 :     GEN zi = cgetg(n+2,t_VECSMALL);
    3122      1059718 :     zi[1] = P[1];
    3123      1059718 :     gel(z,i) = zi;
    3124      7196204 :     for(j=2; j<n+2; j++)
    3125      6136486 :       uel(zi, j) = k==l ? 0 : uel(P,k++);
    3126      1059718 :     zi = Flx_renormalize(zi, n+2);
    3127              :   }
    3128       529859 :   return z;
    3129              : }
    3130              : 
    3131              : static GEN
    3132     13198433 : FlxV_to_Flm_lg(GEN x, long m, long n)
    3133              : {
    3134              :   long i;
    3135     13198433 :   GEN y = cgetg(n+1, t_MAT);
    3136     62344001 :   for (i=1; i<=n; i++) gel(y,i) = Flx_to_Flv(gel(x,i), m);
    3137     13198433 :   return y;
    3138              : }
    3139              : 
    3140              : /* allow pi = 0 (SMALL_ULONG) */
    3141              : GEN
    3142     13454003 : Flx_FlxqV_eval_pre(GEN Q, GEN x, GEN T, ulong p, ulong pi)
    3143              : {
    3144     13454003 :   pari_sp btop, av = avma;
    3145     13454003 :   long sv = get_Flx_var(T), m = get_Flx_degree(T);
    3146     13454003 :   long i, l = lg(x)-1, lQ = lgpol(Q), n,  d;
    3147              :   GEN A, B, C, S, g;
    3148     13454003 :   if (lQ == 0) return pol0_Flx(sv);
    3149     13198433 :   if (lQ <= l)
    3150              :   {
    3151      6538402 :     n = l;
    3152      6538402 :     d = 1;
    3153              :   }
    3154              :   else
    3155              :   {
    3156      6660031 :     n = l-1;
    3157      6660031 :     d = (lQ+n-1)/n;
    3158              :   }
    3159     13198433 :   A = FlxV_to_Flm_lg(x, m, n);
    3160     13198433 :   B = Flx_blocks_Flm(Q, n, d);
    3161     13198433 :   C = gc_upto(av, Flm_mul(A, B, p));
    3162     13198433 :   g = gel(x, l);
    3163     13198433 :   if (pi && SMALL_ULONG(p)) pi = 0;
    3164     13198433 :   T = Flx_get_red_pre(T, p, pi);
    3165     13198433 :   btop = avma;
    3166     13198433 :   S = Flv_to_Flx(gel(C, d), sv);
    3167     24715808 :   for (i = d-1; i>0; i--)
    3168              :   {
    3169     11517375 :     S = Flx_add(Flxq_mul_pre(S, g, T, p, pi), Flv_to_Flx(gel(C,i), sv), p);
    3170     11517375 :     if (gc_needed(btop,1))
    3171            0 :       S = gc_leaf(btop, S);
    3172              :   }
    3173     13198433 :   return gc_leaf(av, S);
    3174              : }
    3175              : GEN
    3176         5124 : Flx_FlxqV_eval(GEN Q, GEN x, GEN T, ulong p)
    3177         5124 : { return Flx_FlxqV_eval_pre(Q, x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3178              : 
    3179              : /* allow pi = 0 (SMALL_ULONG) */
    3180              : GEN
    3181      2533419 : Flx_Flxq_eval_pre(GEN Q, GEN x, GEN T, ulong p, ulong pi)
    3182              : {
    3183      2533419 :   pari_sp av = avma;
    3184              :   GEN z, V;
    3185      2533419 :   long d = degpol(Q), rtd;
    3186      2533419 :   if (d < 0) return pol0_Flx(get_Flx_var(T));
    3187      2533328 :   rtd = (long) sqrt((double)d);
    3188      2533328 :   T = Flx_get_red_pre(T, p, pi);
    3189      2533328 :   V = Flxq_powers_pre(x, rtd, T, p, pi);
    3190      2533328 :   z = Flx_FlxqV_eval_pre(Q, V, T, p, pi);
    3191      2533328 :   return gc_upto(av, z);
    3192              : }
    3193              : GEN
    3194       804900 : Flx_Flxq_eval(GEN Q, GEN x, GEN T, ulong p)
    3195       804900 : { return Flx_Flxq_eval_pre(Q, x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3196              : 
    3197              : /* allow pi = 0 (SMALL_ULONG) */
    3198              : GEN
    3199            0 : FlxC_FlxqV_eval_pre(GEN x, GEN v, GEN T, ulong p, ulong pi)
    3200            0 : { pari_APPLY_type(t_COL, Flx_FlxqV_eval_pre(gel(x,i), v, T, p, pi)) }
    3201              : GEN
    3202            0 : FlxC_FlxqV_eval(GEN x, GEN v, GEN T, ulong p)
    3203            0 : { return FlxC_FlxqV_eval_pre(x, v, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3204              : 
    3205              : /* allow pi = 0 (SMALL_ULONG) */
    3206              : GEN
    3207            0 : FlxC_Flxq_eval_pre(GEN x, GEN F, GEN T, ulong p, ulong pi)
    3208              : {
    3209            0 :   long d = brent_kung_optpow(get_Flx_degree(T)-1,lg(x)-1,1);
    3210            0 :   GEN Fp = Flxq_powers_pre(F, d, T, p, pi);
    3211            0 :   return FlxC_FlxqV_eval_pre(x, Fp, T, p, pi);
    3212              : }
    3213              : GEN
    3214            0 : FlxC_Flxq_eval(GEN x, GEN F, GEN T, ulong p)
    3215            0 : { return FlxC_Flxq_eval_pre(x, F, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3216              : 
    3217              : struct bb_algebra Flxq_algebra = { _Flxq_red, _Flxq_add, _Flxq_sub,
    3218              :                      _Flxq_mul, _Flxq_sqr, _Flxq_one, _Flxq_zero};
    3219              : 
    3220              : const struct bb_algebra *
    3221            0 : get_Flxq_algebra(void **E, GEN T, ulong p)
    3222              : {
    3223            0 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    3224            0 :   GEN z = new_chunk(sizeof(struct _Flxq));
    3225            0 :   struct _Flxq *e = (struct _Flxq *) z;
    3226            0 :   e->T = Flx_get_red(T, p);
    3227            0 :   e->p  = p;
    3228            0 :   e->pi = pi; *E = (void*)e;
    3229            0 :   return &Flxq_algebra;
    3230              : }
    3231              : 
    3232              : static GEN
    3233            0 : _Flx_red(void *E, GEN x)
    3234            0 : { (void) E; return x; }
    3235              : static GEN
    3236            0 : _Flx_zero(void *E)
    3237            0 : { struct _Flx *D = (struct _Flx *)E; return pol0_Flx(D->v); }
    3238              : 
    3239              : static struct bb_algebra Flx_algebra = { _Flx_red, _Flx_add, _Flx_sub,
    3240              :        _Flx_mul, _Flx_sqr, _Flx_one, _Flx_zero };
    3241              : 
    3242              : const struct bb_algebra *
    3243            0 : get_Flx_algebra(void **E, ulong p, long v)
    3244              : {
    3245            0 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    3246            0 :   GEN z = new_chunk(sizeof(struct _Flx));
    3247            0 :   struct _Flx *e = (struct _Flx *) z;
    3248            0 :   e->p  = p; e->pi = pi; e->v = v;
    3249            0 :   *E = (void*)e; return &Flx_algebra;
    3250              : }
    3251              : 
    3252              : static GEN
    3253        77454 : Flxq_autpow_sqr(void *E, GEN x)
    3254              : {
    3255        77454 :   struct _Flxq *D = (struct _Flxq*)E;
    3256        77454 :   return Flx_Flxq_eval_pre(x, x, D->T, D->p, D->pi);
    3257              : }
    3258              : static GEN
    3259        46259 : Flxq_autpow_msqr(void *E, GEN x)
    3260              : {
    3261        46259 :   struct _Flxq *D = (struct _Flxq*)E;
    3262        46259 :   return Flx_FlxqV_eval_pre(Flxq_autpow_sqr(E, x), D->aut, D->T, D->p, D->pi);
    3263              : }
    3264              : 
    3265              : GEN
    3266        98538 : Flxq_autpow_pre(GEN x, ulong n, GEN T, ulong p, ulong pi)
    3267              : {
    3268        98538 :   pari_sp av = avma;
    3269              :   struct _Flxq D;
    3270              :   long d;
    3271        98538 :   if (n==0) return Flx_rem_pre(polx_Flx(x[1]), T, p, pi);
    3272        98531 :   if (n==1) return Flx_rem_pre(x, T, p, pi);
    3273        61508 :   set_Flxq_pre(&D, T, p, pi);
    3274        61508 :   d = brent_kung_optpow(get_Flx_degree(T), hammingu(n)-1, 1);
    3275        61508 :   D.aut = Flxq_powers_pre(x, d, T, p, D.pi);
    3276        61508 :   x = gen_powu_fold_i(x,n,(void*)&D,Flxq_autpow_sqr,Flxq_autpow_msqr);
    3277        61508 :   return gc_GEN(av, x);
    3278              : }
    3279              : GEN
    3280            7 : Flxq_autpow(GEN x, ulong n, GEN T, ulong p)
    3281            7 : { return Flxq_autpow_pre(x, n, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3282              : 
    3283              : GEN
    3284         1667 : Flxq_autpowers(GEN x, ulong l, GEN T, ulong p)
    3285              : {
    3286         1667 :   long d, vT = get_Flx_var(T), dT = get_Flx_degree(T);
    3287              :   ulong i, pi;
    3288         1667 :   pari_sp av = avma;
    3289         1667 :   GEN xp, V = cgetg(l+2,t_VEC);
    3290         1667 :   gel(V,1) = polx_Flx(vT); if (l==0) return V;
    3291         1667 :   gel(V,2) = gcopy(x); if (l==1) return V;
    3292         1667 :   pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    3293         1667 :   T = Flx_get_red_pre(T, p, pi);
    3294         1667 :   d = brent_kung_optpow(dT-1, l-1, 1);
    3295         1667 :   xp = Flxq_powers_pre(x, d, T, p, pi);
    3296         6998 :   for(i = 3; i < l+2; i++)
    3297         5331 :     gel(V,i) = Flx_FlxqV_eval_pre(gel(V,i-1), xp, T, p, pi);
    3298         1667 :   return gc_GEN(av, V);
    3299              : }
    3300              : 
    3301              : static GEN
    3302       112033 : Flxq_autsum_mul(void *E, GEN x, GEN y)
    3303              : {
    3304       112033 :   struct _Flxq *D = (struct _Flxq*)E;
    3305       112033 :   GEN T = D->T;
    3306       112033 :   ulong p = D->p, pi = D->pi;
    3307       112033 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    3308       112033 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    3309       112033 :   ulong d = brent_kung_optpow(maxss(degpol(phi1),degpol(a1)),2,1);
    3310       112033 :   GEN V2 = Flxq_powers_pre(phi2, d, T, p, pi);
    3311       112033 :   GEN phi3 = Flx_FlxqV_eval_pre(phi1, V2, T, p, pi);
    3312       112033 :   GEN aphi = Flx_FlxqV_eval_pre(a1, V2, T, p, pi);
    3313       112033 :   GEN a3 = Flxq_mul_pre(aphi, a2, T, p, pi);
    3314       112033 :   return mkvec2(phi3, a3);
    3315              : }
    3316              : static GEN
    3317       104860 : Flxq_autsum_sqr(void *E, GEN x)
    3318       104860 : { return Flxq_autsum_mul(E, x, x); }
    3319              : 
    3320              : static GEN
    3321        98539 : Flxq_autsum_pre(GEN x, ulong n, GEN T, ulong p, ulong pi)
    3322              : {
    3323        98539 :   pari_sp av = avma;
    3324        98539 :   struct _Flxq D; set_Flxq_pre(&D, T, p, pi);
    3325        98539 :   x = gen_powu_i(x,n,(void*)&D,Flxq_autsum_sqr,Flxq_autsum_mul);
    3326        98539 :   return gc_GEN(av, x);
    3327              : }
    3328              : GEN
    3329            0 : Flxq_autsum(GEN x, ulong n, GEN T, ulong p)
    3330            0 : { return Flxq_autsum_pre(x, n, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3331              : 
    3332              : static GEN
    3333       799629 : Flxq_auttrace_mul(void *E, GEN x, GEN y)
    3334              : {
    3335       799629 :   struct _Flxq *D = (struct _Flxq*)E;
    3336       799629 :   GEN T = D->T;
    3337       799629 :   ulong p = D->p, pi = D->pi;
    3338       799629 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    3339       799629 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    3340       799629 :   ulong d = brent_kung_optpow(maxss(degpol(phi1),degpol(a1)),2,1);
    3341       799629 :   GEN V1 = Flxq_powers_pre(phi1, d, T, p, pi);
    3342       799629 :   GEN phi3 = Flx_FlxqV_eval_pre(phi2, V1, T, p, pi);
    3343       799629 :   GEN aphi = Flx_FlxqV_eval_pre(a2, V1, T, p, pi);
    3344       799629 :   GEN a3 = Flx_add(a1, aphi, p);
    3345       799629 :   return mkvec2(phi3, a3);
    3346              : }
    3347              : 
    3348              : static GEN
    3349       668191 : Flxq_auttrace_sqr(void *E, GEN x)
    3350       668191 : { return Flxq_auttrace_mul(E, x, x); }
    3351              : 
    3352              : GEN
    3353       978018 : Flxq_auttrace_pre(GEN x, ulong n, GEN T, ulong p, ulong pi)
    3354              : {
    3355       978018 :   pari_sp av = avma;
    3356              :   struct _Flxq D;
    3357       978018 :   set_Flxq_pre(&D, T, p, pi);
    3358       978018 :   x = gen_powu_i(x,n,(void*)&D,Flxq_auttrace_sqr,Flxq_auttrace_mul);
    3359       978018 :   return gc_GEN(av, x);
    3360              : }
    3361              : GEN
    3362            0 : Flxq_auttrace(GEN x, ulong n, GEN T, ulong p)
    3363            0 : { return Flxq_auttrace_pre(x, n, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3364              : 
    3365              : static long
    3366       393685 : bounded_order(ulong p, GEN b, long k)
    3367              : {
    3368       393685 :   GEN a = modii(utoipos(p), b);
    3369              :   long i;
    3370       805183 :   for(i = 1; i < k; i++)
    3371              :   {
    3372       510658 :     if (equali1(a)) return i;
    3373       411498 :     a = modii(muliu(a,p),b);
    3374              :   }
    3375       294525 :   return 0;
    3376              : }
    3377              : 
    3378              : /* n = (p^d-a)\b
    3379              :  * b = bb*p^vb
    3380              :  * p^k = 1 [bb]
    3381              :  * d = m*k+r+vb
    3382              :  * u = (p^k-1)/bb;
    3383              :  * v = (p^(r+vb)-a)/b;
    3384              :  * w = (p^(m*k)-1)/(p^k-1)
    3385              :  * n = p^r*w*u+v
    3386              :  * w*u = p^vb*(p^(m*k)-1)/b
    3387              :  * n = p^(r+vb)*(p^(m*k)-1)/b+(p^(r+vb)-a)/b */
    3388              : static GEN
    3389     22629129 : Flxq_pow_Frobenius(GEN x, GEN n, GEN aut, GEN T, ulong p, ulong pi)
    3390              : {
    3391     22629129 :   pari_sp av=avma;
    3392     22629129 :   long d = get_Flx_degree(T);
    3393     22629129 :   GEN an = absi_shallow(n), z, q;
    3394     22629129 :   if (abscmpiu(an,p)<0 || cmpis(an,d)<=0) return Flxq_pow_pre(x, n, T, p, pi);
    3395       394020 :   q = powuu(p, d);
    3396       394020 :   if (dvdii(q, n))
    3397              :   {
    3398          315 :     long vn = logint(an, utoipos(p));
    3399          315 :     GEN autvn = vn==1 ? aut: Flxq_autpow_pre(aut,vn,T,p,pi);
    3400          315 :     z = Flx_Flxq_eval_pre(x,autvn,T,p,pi);
    3401              :   } else
    3402              :   {
    3403       393705 :     GEN b = diviiround(q, an), a = subii(q, mulii(an,b));
    3404              :     GEN bb, u, v, autk;
    3405       393705 :     long vb = Z_lvalrem(b,p,&bb);
    3406       393705 :     long m, r, k = is_pm1(bb)? 1: bounded_order(p,bb,d);
    3407       393705 :     if (!k || d-vb < k) return Flxq_pow_pre(x,n, T,p,pi);
    3408        99173 :     m = (d-vb)/k; r = (d-vb)%k;
    3409        99173 :     u = diviiexact(subiu(powuu(p,k),1),bb);
    3410        99173 :     v = diviiexact(subii(powuu(p,r+vb),a),b);
    3411        99173 :     autk = k==1 ? aut: Flxq_autpow_pre(aut,k,T,p,pi);
    3412        99173 :     if (r)
    3413              :     {
    3414          448 :       GEN autr = r==1 ? aut: Flxq_autpow_pre(aut,r,T,p,pi);
    3415          448 :       z = Flx_Flxq_eval_pre(x,autr,T,p,pi);
    3416        98725 :     } else z = x;
    3417        99173 :     if (m > 1) z = gel(Flxq_autsum_pre(mkvec2(autk, z), m, T, p, pi), 2);
    3418        99173 :     if (!is_pm1(u)) z = Flxq_pow_pre(z, u, T, p, pi);
    3419        99173 :     if (signe(v)) z = Flxq_mul_pre(z, Flxq_pow_pre(x, v, T, p, pi), T, p, pi);
    3420              :   }
    3421        99488 :   return gc_upto(av,signe(n)>0 ? z : Flxq_inv_pre(z,T,p,pi));
    3422              : }
    3423              : 
    3424              : static GEN
    3425     22621711 : _Flxq_pow(void *data, GEN x, GEN n)
    3426              : {
    3427     22621711 :   struct _Flxq *D = (struct _Flxq*)data;
    3428     22621711 :   return Flxq_pow_Frobenius(x, n, D->aut, D->T, D->p, D->pi);
    3429              : }
    3430              : 
    3431              : static GEN
    3432         6291 : _Flxq_rand(void *data)
    3433              : {
    3434         6291 :   pari_sp av=avma;
    3435         6291 :   struct _Flxq *D = (struct _Flxq*)data;
    3436              :   GEN z;
    3437              :   do
    3438              :   {
    3439         6313 :     set_avma(av);
    3440         6313 :     z = random_Flx(get_Flx_degree(D->T),get_Flx_var(D->T),D->p);
    3441         6313 :   } while (lgpol(z)==0);
    3442         6291 :   return z;
    3443              : }
    3444              : 
    3445              : /* discrete log in FpXQ for a in Fp^*, g in FpXQ^* of order ord */
    3446              : static GEN
    3447        35414 : Fl_Flxq_log(ulong a, GEN g, GEN o, GEN T, ulong p)
    3448              : {
    3449        35414 :   pari_sp av = avma;
    3450              :   GEN q,n_q,ord,ordp, op;
    3451              : 
    3452        35414 :   if (a == 1UL) return gen_0;
    3453              :   /* p > 2 */
    3454              : 
    3455        35414 :   ordp = utoi(p - 1);
    3456        35414 :   ord  = get_arith_Z(o);
    3457        35414 :   if (!ord) ord = T? subiu(powuu(p, get_FpX_degree(T)), 1): ordp;
    3458        35414 :   if (a == p - 1) /* -1 */
    3459         7746 :     return gc_INT(av, shifti(ord,-1));
    3460        27668 :   ordp = gcdii(ordp, ord);
    3461        27668 :   op = typ(o)==t_MAT ? famat_Z_gcd(o, ordp) : ordp;
    3462              : 
    3463        27668 :   q = NULL;
    3464        27668 :   if (T)
    3465              :   { /* we want < g > = Fp^* */
    3466        27668 :     if (!equalii(ord,ordp)) {
    3467        11906 :       q = diviiexact(ord,ordp);
    3468        11906 :       g = Flxq_pow(g,q,T,p);
    3469              :     }
    3470              :   }
    3471        27668 :   n_q = Fp_log(utoi(a), utoipos(uel(g,2)), op, utoipos(p));
    3472        27668 :   if (lg(n_q)==1) return gc_leaf(av, n_q);
    3473        27668 :   if (q) n_q = mulii(q, n_q);
    3474        27668 :   return gc_INT(av, n_q);
    3475              : }
    3476              : 
    3477              : static GEN
    3478       519247 : Flxq_easylog(void* E, GEN a, GEN g, GEN ord)
    3479              : {
    3480       519247 :   struct _Flxq *f = (struct _Flxq *)E;
    3481       519247 :   GEN T = f->T;
    3482       519247 :   ulong p = f->p;
    3483       519247 :   long d = get_Flx_degree(T);
    3484       519247 :   if (Flx_equal1(a)) return gen_0;
    3485       359591 :   if (Flx_equal(a,g)) return gen_1;
    3486       174476 :   if (!degpol(a))
    3487        35414 :     return Fl_Flxq_log(uel(a,2), g, ord, T, p);
    3488       139062 :   if (typ(ord)!=t_INT || d <= 4 || d == 6 || abscmpiu(ord,1UL<<27)<0)
    3489       139034 :     return NULL;
    3490           28 :   return Flxq_log_index(a, g, ord, T, p);
    3491              : }
    3492              : 
    3493              : static const struct bb_group Flxq_star={_Flxq_mul,_Flxq_pow,_Flxq_rand,hash_GEN,Flx_equal,Flx_equal1,Flxq_easylog};
    3494              : 
    3495              : const struct bb_group *
    3496       283184 : get_Flxq_star(void **E, GEN T, ulong p)
    3497              : {
    3498       283184 :   struct _Flxq *e = (struct _Flxq *) stack_malloc(sizeof(struct _Flxq));
    3499       283184 :   e->T = T; e->p  = p; e->pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    3500       283184 :   e->aut =  Flx_Frobenius_pre(T, p, e->pi);
    3501       283184 :   *E = (void*)e; return &Flxq_star;
    3502              : }
    3503              : 
    3504              : GEN
    3505        97052 : Flxq_order(GEN a, GEN ord, GEN T, ulong p)
    3506              : {
    3507              :   void *E;
    3508        97052 :   const struct bb_group *S = get_Flxq_star(&E,T,p);
    3509        97052 :   return gen_order(a,ord,E,S);
    3510              : }
    3511              : 
    3512              : GEN
    3513       164217 : Flxq_log(GEN a, GEN g, GEN ord, GEN T, ulong p)
    3514              : {
    3515              :   void *E;
    3516       164217 :   pari_sp av = avma;
    3517       164217 :   const struct bb_group *S = get_Flxq_star(&E,T,p);
    3518       164217 :   GEN v = get_arith_ZZM(ord), F = gmael(v,2,1);
    3519       164217 :   if (lg(F) > 1 && Flxq_log_use_index(veclast(F), T, p))
    3520        24283 :     v = mkvec2(gel(v, 1), ZM_famat_limit(gel(v, 2), int2n(27)));
    3521       164217 :   return gc_leaf(av, gen_PH_log(a, g, v, E, S));
    3522              : }
    3523              : 
    3524              : static GEN
    3525       295304 : Flxq_sumautsum_sqr(void *E, GEN xzd)
    3526              : {
    3527       295304 :   struct _Flxq *D = (struct _Flxq*)E;
    3528       295304 :   pari_sp av = avma;
    3529              :   GEN xi, zeta, delta, xi2, zeta2, delta2, temp, xipow;
    3530       295304 :   GEN T = D->T;
    3531       295304 :   ulong d, p = D-> p, pi = D->pi;
    3532       295304 :   xi = gel(xzd, 1); zeta = gel(xzd, 2); delta = gel(xzd, 3);
    3533              : 
    3534       295304 :   d = brent_kung_optpow(get_Flx_degree(T)-1,3,1);
    3535       295304 :   xipow = Flxq_powers_pre(xi, d, T, p, pi);
    3536              : 
    3537       295304 :   xi2 = Flx_FlxqV_eval_pre(xi, xipow, T, p, pi);
    3538       295304 :   zeta2 = Flxq_mul_pre(zeta, Flx_FlxqV_eval_pre(zeta,  xipow, T, p, pi), T, p, pi);
    3539       295304 :   temp  = Flxq_mul_pre(zeta, Flx_FlxqV_eval_pre(delta, xipow, T, p, pi), T, p, pi);
    3540       295304 :   delta2 = Flx_add(delta, temp, p);
    3541       295304 :   return gc_GEN(av, mkvec3(xi2, zeta2, delta2));
    3542              : }
    3543              : 
    3544              : static GEN
    3545        39750 : Flxq_sumautsum_msqr(void *E, GEN xzd)
    3546              : {
    3547        39750 :   struct _Flxq *D = (struct _Flxq*)E;
    3548        39750 :   pari_sp av = avma;
    3549              :   GEN xii, zetai, deltai, xzd2;
    3550        39750 :   GEN T = D->T, xi0pow = gel(D->aut, 1), zeta0 = gel(D->aut, 2);
    3551        39750 :   ulong p = D-> p, pi = D->pi;
    3552        39750 :   xzd2 = Flxq_sumautsum_sqr(E, xzd);
    3553        39750 :   xii = Flx_FlxqV_eval_pre(gel(xzd2, 1), xi0pow, T, p, pi);
    3554        39750 :   zetai = Flxq_mul_pre(zeta0, Flx_FlxqV_eval_pre(gel(xzd2, 2), xi0pow, T, p, pi), T, p, pi);
    3555        39750 :   deltai = Flx_add(gel(xzd2, 3), zetai, p);
    3556              : 
    3557        39750 :   return gc_GEN(av, mkvec3(xii, zetai, deltai));
    3558              : }
    3559              : 
    3560              : /*returns a + a^(1+s) + a^(1+s+2s) + ... + a^(1+s+...+is)
    3561              :   where ax = [a,s] with s an automorphism */
    3562              : static GEN
    3563       209819 : Flxq_sumautsum_pre(GEN ax, long i, GEN T, ulong p, ulong pi) {
    3564       209819 :   pari_sp av = avma;
    3565              :   GEN a, xi, zeta, vec, res;
    3566              :   struct _Flxq D;
    3567              :   ulong d;
    3568       209819 :   D.T = Flx_get_red(T, p); D.p = p; D.pi = pi;
    3569       209819 :   a = gel(ax, 1); xi = gel(ax,2);
    3570       209819 :   d = brent_kung_optpow(get_Flx_degree(T)-1,2*(hammingu(i)-1),1);
    3571       209819 :   zeta = Flx_Flxq_eval_pre(a, xi, T, p, pi);
    3572       209819 :   D.aut = mkvec2(Flxq_powers_pre(xi, d, T, p, pi), zeta);
    3573              : 
    3574       209819 :   vec = gen_powu_fold(mkvec3(xi, zeta, zeta), i, (void *)&D, Flxq_sumautsum_sqr, Flxq_sumautsum_msqr);
    3575       209819 :   res = Flxq_mul_pre(a, Flx_add(pol1_Flx(get_Flx_var(T)), gel(vec, 3), p), T, p, pi);
    3576              : 
    3577       209819 :   return gc_GEN(av, res);
    3578              : }
    3579              : 
    3580              : /*algorithm from
    3581              : Doliskani, J., & Schost, E. (2014).
    3582              : Taking roots over high extensions of finite fields
    3583              : https://arxiv.org/abs/1110.4350
    3584              : */
    3585              : static GEN
    3586        37674 : Flxq_sqrtl_spec_pre(GEN z, GEN n, GEN T, ulong p, ulong pi, GEN *zetan)
    3587              : {
    3588        37674 :   pari_sp av = avma;
    3589              :   GEN psn, c, b, new_z, beta, x, y, w, ax, g, zeta;
    3590        37674 :   long s, l, v = get_Flx_var(T), d = get_Flx_degree(T);
    3591              :   ulong zeta2, beta2;
    3592        37674 :   s = itos(Fp_order(utoi(p), stoi(d), n));
    3593        37674 :   if(s >= d || d % s != 0)
    3594            0 :     pari_err(e_MISC, "expected p's order mod n to divide the degree of T");
    3595        37674 :   l = d/s;
    3596        37674 :   if (!lgpol(z)) return pol0_Flx(get_Flx_var(T));
    3597        37674 :   T = Flx_get_red(T, p);
    3598        37674 :   ax = mkvec2(NULL, Flxq_autpow_pre(Flx_Frobenius_pre(T,p,pi), s, T, p,pi));
    3599        37674 :   psn = diviiexact(subiu(powuu(p, s), 1), n);
    3600              :   do {
    3601        41682 :     do c = random_Flx(d, v, p); while (!lgpol(c));
    3602        41154 :     new_z = Flxq_mul_pre(z, Flxq_pow_pre(c, n, T, p,pi), T, p,pi);
    3603        41154 :     gel(ax,1) = Flxq_pow_pre(new_z, psn, T, p,pi);
    3604              : 
    3605              :     /*If l == 2, b has to be 1 + a^((p^s-1)/n)*/
    3606        41154 :     if(l == 2) y = gel(ax, 1);
    3607         3258 :     else y = Flxq_sumautsum_pre(ax, l-2, T, p, pi);
    3608        41154 :     b = Flx_Fl_add(y, 1, p);
    3609        41154 :   } while (!lgpol(b));
    3610              : 
    3611        37674 :   x = Flxq_mul_pre(new_z, Flxq_pow_pre(b, n, T, p,pi), T, p,pi);
    3612        37674 :   if(s == 1) {
    3613        36519 :     if (degpol(x) > 0) return gc_NULL(av);
    3614        36482 :     beta2 = Fl_sqrtn(Flx_constant(x), umodiu(n, p), p, &zeta2);
    3615        36482 :     if (beta2==~0UL) return gc_NULL(av);
    3616        36482 :     if(zetan) *zetan = monomial_Flx(zeta2, 0, get_Flx_var(T));
    3617        36482 :     w = Flx_Fl_mul(Flxq_inv_pre(Flxq_mul_pre(b, c, T, p,pi), T, p,pi), beta2, p);
    3618        36482 :     (void)gc_all(av, zetan? 2: 1, &w, zetan);
    3619        36482 :     return w;
    3620              :   }
    3621         1155 :   g = Flxq_minpoly(x, T, p);
    3622         1155 :   if (degpol(g) > s) return gc_NULL(av);
    3623         1155 :   beta = Flxq_sqrtn(polx_Flx(get_Flx_var(T)), n, g, p, &zeta);
    3624         1155 :   if (!beta) return gc_NULL(av);
    3625              : 
    3626         1155 :   if(zetan) *zetan = Flx_Flxq_eval(zeta, x, T, p);
    3627         1155 :   beta = Flx_Flxq_eval(beta, x, T, p);
    3628         1155 :   w = Flxq_mul_pre(Flxq_inv_pre(Flxq_mul_pre(b, c, T, p,pi), T, p,pi), beta, T, p,pi);
    3629         1155 :   (void)gc_all(av, zetan? 2: 1, &w, zetan);
    3630         1155 :   return w;
    3631              : }
    3632              : 
    3633              : static GEN
    3634        21915 : Flxq_sqrtn_spec_pre(GEN a, GEN n, GEN T, ulong p, ulong pi, GEN q, GEN *zetan)
    3635              : {
    3636        21915 :   pari_sp ltop = avma;
    3637              :   GEN z, m, u1, u2;
    3638              :   int is_1;
    3639        21915 :   if (is_pm1(n))
    3640              :   {
    3641         1925 :     if (zetan) *zetan = pol1_Flx(get_Flx_var(T));
    3642         1925 :     return signe(n) < 0? Flxq_inv_pre(a, T, p,pi): gcopy(a);
    3643              :   }
    3644        19990 :   is_1 = gequal1(a);
    3645        19990 :   if (is_1 && !zetan) return gcopy(a);
    3646        19990 :   z = pol1_Flx(get_Flx_var(T));
    3647        19990 :   m = bezout(n,q,&u1,&u2);
    3648        19990 :   if (!is_pm1(m))
    3649              :   {
    3650        19990 :     GEN F = Z_factor(m);
    3651        19990 :     long i, j, j2 = 0; /* -Wall */
    3652              :     GEN y, l;
    3653        19990 :     pari_sp av1 = avma;
    3654        40069 :     for (i = nbrows(F); i; i--)
    3655              :     {
    3656        20116 :       l = gcoeff(F,i,1);
    3657        20116 :       j = itos(gcoeff(F,i,2));
    3658        20116 :       if(zetan) {
    3659          111 :         a = Flxq_sqrtl_spec_pre(a,l,T,p,pi,&y);
    3660          148 :         if (!a) return gc_NULL(ltop);
    3661          111 :         j--;
    3662          111 :         j2 = j;
    3663              :       }
    3664        20116 :       if (!is_1 && j > 0) {
    3665              :         do
    3666              :         {
    3667        37451 :           a = Flxq_sqrtl_spec_pre(a,l,T,p,pi,NULL);
    3668        37451 :           if (!a) return gc_NULL(ltop);
    3669        37414 :         } while (--j);
    3670              :       }
    3671              :       /*This is below finding a's root,
    3672              :       so we don't spend time doing this, if a is not n-th root*/
    3673        20079 :       if(zetan) {
    3674          223 :         for(; j2>0; j2--) y = Flxq_sqrtl_spec_pre(y, l, T, p,pi,NULL);
    3675          111 :         z = Flxq_mul_pre(z, y, T, p,pi);
    3676              :       }
    3677        20079 :       if (gc_needed(ltop,1))
    3678              :       { /* n can have lots of prime factors*/
    3679            0 :         if(DEBUGMEM>1) pari_warn(warnmem,"Flxq_sqrtn_spec");
    3680            0 :         (void)gc_all(av1, zetan? 2: 1, &a, &z);
    3681              :       }
    3682              :     }
    3683              :   }
    3684              : 
    3685        19953 :   if (!equalii(m, n))
    3686          434 :     a = Flxq_pow_pre(a,modii(u1,q), T, p,pi);
    3687        19953 :   if (zetan)
    3688              :   {
    3689          111 :     *zetan = z;
    3690          111 :     (void)gc_all(ltop,2,&a,zetan);
    3691              :   }
    3692              :   else /* is_1 is 0: a was modified above -> gc_upto valid */
    3693        19842 :     a = gc_upto(ltop, a);
    3694        19953 :   return a;
    3695              : }
    3696              : 
    3697              : GEN
    3698        23128 : Flxq_sqrtn(GEN a, GEN n, GEN T, ulong p, GEN *zeta)
    3699              : {
    3700        23128 :   if (!lgpol(a))
    3701              :   {
    3702            7 :     if (signe(n) < 0) pari_err_INV("Flxq_sqrtn",a);
    3703            0 :     if (zeta)
    3704            0 :       *zeta=pol1_Flx(get_Flx_var(T));
    3705            0 :     return pol0_Flx(get_Flx_var(T));
    3706              :   }
    3707        23121 :   else if(p == 2) {
    3708         1206 :     pari_sp av = avma;
    3709              :     GEN z;
    3710         1206 :     z = F2xq_sqrtn(Flx_to_F2x(a), n, Flx_to_F2x(get_FpX_mod(T)), zeta);
    3711         1206 :     if (!z) return NULL;
    3712         1206 :     z = F2x_to_Flx(z);
    3713         1206 :     if (!zeta) return gc_leaf(av, z);
    3714            0 :     *zeta=F2x_to_Flx(*zeta);
    3715            0 :     return gc_all(av, 2, &z,zeta);
    3716              :   }
    3717              :   else
    3718              :   {
    3719              :     void *E;
    3720        21915 :     pari_sp av = avma;
    3721        21915 :     const struct bb_group *S = get_Flxq_star(&E,T,p);
    3722        21915 :     GEN o = subiu(powuu(p,get_Flx_degree(T)), 1);
    3723              :     GEN m, u1, u2, l, zeta2, F, n2, z;
    3724        21915 :     long i, s, pi, d = get_Flx_degree(T);
    3725        21915 :     pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    3726        21915 :     m = bezout(n,o,&u1,&u2);
    3727        21915 :     F = Z_factor(m);
    3728        46462 :     for (i = nbrows(F); i; i--)
    3729              :     {
    3730        24547 :       l = gcoeff(F,i,1);
    3731        24547 :       s = itos(Fp_order(utoi(p), subiu(l, 1), l));
    3732              :       /*Flxq_sqrtn_spec only works if d > s and s | d
    3733              :       for those factors of m we use Flxq_sqrtn_spec
    3734              :       for the other factor we stay with gen_Shanks_sqrtn*/
    3735        24547 :       if(d <= s || d % s != 0) {
    3736         4431 :         gcoeff(F,i,2) = gen_0;
    3737              :       }
    3738        20116 :       else gcoeff(F,i,2) = stoi(Z_pval(n,l));
    3739              :     }
    3740        21915 :     F = factorback(F);
    3741        21915 :     z = Flxq_sqrtn_spec_pre(a,F,T, p,pi,o,zeta);
    3742        21915 :     if(!z) return gc_NULL(av);
    3743        21878 :     n2 = diviiexact(n, F);
    3744        21878 :     if(!gequal1(n2)) {
    3745         5012 :       if(zeta) zeta2 = gcopy(*zeta);
    3746         5012 :       z = gen_Shanks_sqrtn(z, n2, o, zeta, E, S);
    3747         5012 :       if (!z) return gc_NULL(av);
    3748         5012 :       if(zeta) *zeta = Flxq_mul_pre(*zeta, zeta2, T, p,pi);
    3749              :     }
    3750        21878 :     return gc_all(av, zeta?2:1, &z, zeta);
    3751              :   }
    3752              : }
    3753              : 
    3754              : GEN
    3755       230608 : Flxq_sqrt_pre(GEN z, GEN T, ulong p, ulong pi)
    3756              : {
    3757       230608 :   pari_sp av = avma;
    3758              :   long d;
    3759       230608 :   if (p==2)
    3760              :   {
    3761            0 :     GEN r = F2xq_sqrt(Flx_to_F2x(z), Flx_to_F2x(get_Flx_mod(T)));
    3762            0 :     return gc_upto(av, F2x_to_Flx(r));
    3763              :   }
    3764       230608 :   d = get_Flx_degree(T);
    3765       230608 :   if (d==2)
    3766              :   {
    3767        65964 :     GEN P = get_Flx_mod(T), s;
    3768        65964 :     ulong c = uel(P,2), b = uel(P,3), a = uel(P,4);
    3769        65964 :     ulong y = degpol(z)<1 ? 0: uel(z,3);
    3770        65964 :     if (a==1 && b==0)
    3771        15289 :     {
    3772        16090 :       ulong x = degpol(z)<1 ? Flx_constant(z): uel(z,2);
    3773        16090 :       GEN r = Fl2_sqrt_pre(mkvecsmall2(x, y), Fl_neg(c, p), p, pi);
    3774        16090 :       if (!r) return gc_NULL(av);
    3775        15289 :       s = mkvecsmall3(P[1], uel(r,1), uel(r,2));
    3776              :     }
    3777              :     else
    3778              :     {
    3779        49874 :       ulong b2 = Fl_halve(b, p), t = Fl_div(b2, a, p);
    3780        49874 :       ulong D = Fl_sub(Fl_sqr(b2, p), Fl_mul(a, c, p), p);
    3781        49874 :       ulong x = degpol(z)<1 ? Flx_constant(z): Fl_sub(uel(z,2), Fl_mul(uel(z,3), t, p), p);
    3782        49874 :       GEN r = Fl2_sqrt_pre(mkvecsmall2(x, y), D, p, pi);
    3783        49874 :       if (!r) return gc_NULL(av);
    3784        47480 :       s = mkvecsmall3(P[1], Fl_add(uel(r,1), Fl_mul(uel(r,2),t,p), p), uel(r,2));
    3785              :     }
    3786        62769 :     return gc_leaf(av, Flx_renormalize(s, 4));
    3787              :   }
    3788       164644 :   if (lgpol(z)<=1 && odd(d))
    3789              :   {
    3790        11832 :     pari_sp av = avma;
    3791        11832 :     ulong s = Fl_sqrt(Flx_constant(z), p);
    3792        11832 :     if (s==~0UL) return gc_NULL(av);
    3793        11818 :     return gc_GEN(av, Fl_to_Flx(s, get_Flx_var(T)));
    3794              :   } else
    3795              :   {
    3796              :     GEN c, b, new_z, x, y, w, ax;
    3797              :     ulong p2, beta;
    3798       152812 :     long v = get_Flx_var(T);
    3799       152812 :     if (!lgpol(z)) return pol0_Flx(v);
    3800       152279 :     T = Flx_get_red_pre(T, p, pi);
    3801       152279 :     ax = mkvec2(NULL, Flx_Frobenius_pre(T, p, pi));
    3802       152279 :     p2 = p >> 1; /* (p-1) / 2 */
    3803              :     do {
    3804       207178 :       do c = random_Flx(d, v, p); while (!lgpol(c));
    3805              : 
    3806       206561 :       new_z = Flxq_mul_pre(z, Flxq_sqr_pre(c, T, p, pi), T, p, pi);
    3807       206561 :       gel(ax, 1) = Flxq_powu_pre(new_z, p2, T, p, pi);
    3808       206561 :       y = Flxq_sumautsum_pre(ax, d-2, T, p, pi); /* d > 2 */
    3809       206561 :       b = Flx_Fl_add(y, 1UL, p);
    3810       206561 :     } while (!lgpol(b));
    3811              : 
    3812       152279 :     x = Flxq_mul_pre(new_z, Flxq_sqr_pre(b, T, p, pi), T, p, pi);
    3813       152279 :     if (degpol(x) > 0) return gc_NULL(av);
    3814       145237 :     beta = Fl_sqrt_pre(Flx_constant(x), p, pi);
    3815       145237 :     if (beta==~0UL) return gc_NULL(av);
    3816       145237 :     w = Flx_Fl_mul(Flxq_inv_pre(Flxq_mul_pre(b, c, T,p,pi), T,p,pi), beta, p);
    3817       145237 :     return gc_GEN(av, w);
    3818              :   }
    3819              : }
    3820              : 
    3821              : GEN
    3822       230608 : Flxq_sqrt(GEN a, GEN T, ulong p)
    3823       230608 : { return Flxq_sqrt_pre(a, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3824              : 
    3825              : /* assume T irreducible mod p */
    3826              : int
    3827       402057 : Flxq_issquare(GEN x, GEN T, ulong p)
    3828              : {
    3829       402057 :   if (lgpol(x) == 0 || p == 2) return 1;
    3830       395457 :   return krouu(Flxq_norm(x,T,p), p) == 1;
    3831              : }
    3832              : 
    3833              : /* assume T irreducible mod p */
    3834              : int
    3835            0 : Flxq_is2npower(GEN x, long n, GEN T, ulong p)
    3836              : {
    3837              :   pari_sp av;
    3838              :   GEN m;
    3839            0 :   if (n==1) return Flxq_issquare(x, T, p);
    3840            0 :   if (lgpol(x) == 0 || p == 2) return 1;
    3841            0 :   av = avma;
    3842            0 :   m = shifti(subiu(powuu(p, get_Flx_degree(T)), 1), -n);
    3843            0 :   return gc_bool(av, Flx_equal1(Flxq_pow(x, m, T, p)));
    3844              : }
    3845              : 
    3846              : GEN
    3847       114702 : Flxq_lroot_fast_pre(GEN a, GEN sqx, GEN T, ulong p, ulong pi)
    3848              : {
    3849       114702 :   pari_sp av=avma;
    3850       114702 :   GEN A = Flx_splitting(a,p);
    3851       114702 :   return gc_leaf(av, FlxqV_dotproduct_pre(A,sqx,T,p,pi));
    3852              : }
    3853              : GEN
    3854            0 : Flxq_lroot_fast(GEN a, GEN sqx, GEN T, ulong p)
    3855            0 : { return Flxq_lroot_fast_pre(a, sqx, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3856              : 
    3857              : GEN
    3858        54306 : Flxq_lroot_pre(GEN a, GEN T, ulong p, ulong pi)
    3859              : {
    3860        54306 :   pari_sp av=avma;
    3861        54306 :   long n = get_Flx_degree(T), d = degpol(a);
    3862              :   GEN sqx, V;
    3863        54306 :   if (n==1 || d==-1) return leafcopy(a);
    3864        54180 :   if (n==2) return Flxq_powu_pre(a, p, T, p, pi);
    3865        54180 :   sqx = Flxq_autpow_pre(Flx_Frobenius_pre(T, p, pi), n-1, T, p, pi);
    3866        54180 :   if (d==1 && a[2]==0 && a[3]==1) return gc_leaf(av, sqx);
    3867        29071 :   if ((ulong) d>=p)
    3868              :   {
    3869            0 :     V = Flxq_powers_pre(sqx,p-1,T,p,pi);
    3870            0 :     return gc_leaf(av, Flxq_lroot_fast_pre(a,V,T,p,pi));
    3871              :   } else
    3872        29071 :     return gc_leaf(av, Flx_Flxq_eval_pre(a,sqx,T,p,pi));
    3873              : }
    3874              : GEN
    3875            0 : Flxq_lroot(GEN a, GEN T, ulong p)
    3876            0 : { return Flxq_lroot_pre(a, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3877              : 
    3878              : ulong
    3879       440660 : Flxq_norm(GEN x, GEN TB, ulong p)
    3880              : {
    3881       440660 :   GEN T = get_Flx_mod(TB);
    3882       440660 :   ulong y = Flx_resultant(T, x, p), L = Flx_lead(T);
    3883       440660 :   if (L==1 || lgpol(x)==0) return y;
    3884            0 :   return Fl_div(y, Fl_powu(L, (ulong)degpol(x), p), p);
    3885              : }
    3886              : 
    3887              : ulong
    3888         4434 : Flxq_trace(GEN x, GEN TB, ulong p)
    3889              : {
    3890         4434 :   pari_sp av = avma;
    3891              :   ulong t;
    3892         4434 :   GEN T = get_Flx_mod(TB);
    3893         4434 :   long n = degpol(T)-1;
    3894         4434 :   GEN z = Flxq_mul(x, Flx_deriv(T, p), TB, p);
    3895         4434 :   t = degpol(z)<n ? 0 : Fl_div(z[2+n],T[3+n],p);
    3896         4434 :   return gc_ulong(av, t);
    3897              : }
    3898              : 
    3899              : /*x must be reduced*/
    3900              : GEN
    3901         3625 : Flxq_charpoly(GEN x, GEN TB, ulong p)
    3902              : {
    3903         3625 :   pari_sp ltop=avma;
    3904         3625 :   GEN T = get_Flx_mod(TB);
    3905         3625 :   long vs = evalvarn(fetch_var());
    3906         3625 :   GEN xm1 = deg1pol_shallow(pol1_Flx(x[1]),Flx_neg(x,p),vs);
    3907         3625 :   GEN r = Flx_FlxY_resultant(T, xm1, p);
    3908         3625 :   r[1] = x[1];
    3909         3625 :   (void)delete_var(); return gc_upto(ltop, r);
    3910              : }
    3911              : 
    3912              : /* Computing minimal polynomial :                         */
    3913              : /* cf Shoup 'Efficient Computation of Minimal Polynomials */
    3914              : /*          in Algebraic Extensions of Finite Fields'     */
    3915              : 
    3916              : /* Let v a linear form, return the linear form z->v(tau*z)
    3917              :    that is, v*(M_tau) */
    3918              : 
    3919              : static GEN
    3920      1758474 : Flxq_transmul_init(GEN tau, GEN T, ulong p, ulong pi)
    3921              : {
    3922              :   GEN bht;
    3923      1758474 :   GEN h, Tp = get_Flx_red(T, &h);
    3924      1758474 :   long n = degpol(Tp), vT = Tp[1];
    3925      1758474 :   GEN ft = Flx_recipspec(Tp+2, n+1, n+1);
    3926      1758474 :   GEN bt = Flx_recipspec(tau+2, lgpol(tau), n);
    3927      1758474 :   ft[1] = vT; bt[1] = vT;
    3928      1758474 :   if (h)
    3929         2702 :     bht = Flxn_mul_pre(bt, h, n-1, p, pi);
    3930              :   else
    3931              :   {
    3932      1755772 :     GEN bh = Flx_div_pre(Flx_shift(tau, n-1), T, p, pi);
    3933      1755772 :     bht = Flx_recipspec(bh+2, lgpol(bh), n-1);
    3934      1755772 :     bht[1] = vT;
    3935              :   }
    3936      1758474 :   return mkvec3(bt, bht, ft);
    3937              : }
    3938              : 
    3939              : static GEN
    3940      4244902 : Flxq_transmul(GEN tau, GEN a, long n, ulong p, ulong pi)
    3941              : {
    3942      4244902 :   pari_sp ltop = avma;
    3943              :   GEN t1, t2, t3, vec;
    3944      4244902 :   GEN bt = gel(tau, 1), bht = gel(tau, 2), ft = gel(tau, 3);
    3945      4244902 :   if (lgpol(a)==0) return pol0_Flx(a[1]);
    3946      4213754 :   t2  = Flx_shift(Flx_mul_pre(bt, a, p, pi),1-n);
    3947      4213754 :   if (lgpol(bht)==0) return gc_leaf(ltop, t2);
    3948      3180586 :   t1  = Flx_shift(Flx_mul_pre(ft, a, p, pi),-n);
    3949      3180586 :   t3  = Flxn_mul_pre(t1, bht, n-1, p, pi);
    3950      3180586 :   vec = Flx_sub(t2, Flx_shift(t3, 1), p);
    3951      3180586 :   return gc_leaf(ltop, vec);
    3952              : }
    3953              : 
    3954              : GEN
    3955       815535 : Flxq_minpoly_pre(GEN x, GEN T, ulong p, ulong pi)
    3956              : {
    3957       815535 :   pari_sp ltop = avma;
    3958       815535 :   long vT = get_Flx_var(T), n = get_Flx_degree(T);
    3959              :   GEN v_x;
    3960       815535 :   GEN g = pol1_Flx(vT), tau = pol1_Flx(vT);
    3961       815535 :   T = Flx_get_red_pre(T, p, pi);
    3962       815535 :   v_x = Flxq_powers_pre(x, usqrt(2*n), T, p, pi);
    3963      1694772 :   while (lgpol(tau) != 0)
    3964              :   {
    3965              :     long i, j, m, k1;
    3966              :     GEN M, v, tr, g_prime, c;
    3967       879237 :     if (degpol(g) == n) { tau = pol1_Flx(vT); g = pol1_Flx(vT); }
    3968       879237 :     v = random_Flx(n, vT, p);
    3969       879237 :     tr = Flxq_transmul_init(tau, T, p, pi);
    3970       879237 :     v = Flxq_transmul(tr, v, n, p, pi);
    3971       879237 :     m = 2*(n-degpol(g));
    3972       879237 :     k1 = usqrt(m);
    3973       879237 :     tr = Flxq_transmul_init(gel(v_x,k1+1), T, p, pi);
    3974       879237 :     c = cgetg(m+2,t_VECSMALL);
    3975       879237 :     c[1] = vT;
    3976      4244902 :     for (i=0; i<m; i+=k1)
    3977              :     {
    3978      3365665 :       long mj = minss(m-i, k1);
    3979     13109541 :       for (j=0; j<mj; j++)
    3980      9743876 :         uel(c,m+1-(i+j)) = Flx_dotproduct_pre(v, gel(v_x,j+1), p, pi);
    3981      3365665 :       v = Flxq_transmul(tr, v, n, p, pi);
    3982              :     }
    3983       879237 :     c = Flx_renormalize(c, m+2);
    3984              :     /* now c contains <v,x^i>, i = 0..m-1  */
    3985       879237 :     M = Flx_halfgcd_pre(monomial_Flx(1, m, vT), c, p, pi);
    3986       879237 :     g_prime = gmael(M, 2, 2);
    3987       879237 :     if (degpol(g_prime) < 1) continue;
    3988       866729 :     g = Flx_mul_pre(g, g_prime, p, pi);
    3989       866729 :     tau = Flxq_mul_pre(tau, Flx_FlxqV_eval_pre(g_prime, v_x, T,p,pi), T,p,pi);
    3990              :   }
    3991       815535 :   g = Flx_normalize(g,p);
    3992       815535 :   return gc_leaf(ltop,g);
    3993              : }
    3994              : GEN
    3995        45978 : Flxq_minpoly(GEN x, GEN T, ulong p)
    3996        45978 : { return Flxq_minpoly_pre(x, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    3997              : 
    3998              : GEN
    3999           20 : Flxq_conjvec(GEN x, GEN T, ulong p)
    4000              : {
    4001           20 :   long i, l = 1+get_Flx_degree(T);
    4002           20 :   GEN z = cgetg(l,t_COL);
    4003           20 :   struct _Flxq D; set_Flxq(&D, T, p);
    4004           20 :   gel(z,1) = Flx_copy(x);
    4005           88 :   for (i=2; i<l; i++) gel(z,i) = _Flxq_powu(&D, gel(z,i-1), p);
    4006           20 :   return z;
    4007              : }
    4008              : 
    4009              : GEN
    4010         7201 : gener_Flxq(GEN T, ulong p, GEN *po)
    4011              : {
    4012         7201 :   long i, j, vT = get_Flx_var(T), f = get_Flx_degree(T);
    4013              :   ulong p_1, pi;
    4014              :   GEN g, L, L2, o, q, F;
    4015              :   pari_sp av0, av;
    4016              : 
    4017         7201 :   if (f == 1) {
    4018              :     GEN fa;
    4019           28 :     o = utoipos(p-1);
    4020           28 :     fa = Z_factor(o);
    4021           28 :     L = gel(fa,1);
    4022           28 :     L = vecslice(L, 2, lg(L)-1); /* remove 2 for efficiency */
    4023           28 :     g = Fl_to_Flx(pgener_Fl_local(p, vec_to_vecsmall(L)), vT);
    4024           28 :     if (po) *po = mkvec2(o, fa);
    4025           28 :     return g;
    4026              :   }
    4027              : 
    4028         7173 :   av0 = avma; p_1 = p - 1;
    4029         7173 :   q = diviuexact(subiu(powuu(p,f), 1), p_1);
    4030              : 
    4031         7173 :   L = cgetg(1, t_VECSMALL);
    4032         7173 :   if (p > 3)
    4033              :   {
    4034         2371 :     ulong t = p_1 >> vals(p_1);
    4035         2371 :     GEN P = gel(factoru(t), 1);
    4036         2371 :     L = cgetg_copy(P, &i);
    4037         3787 :     while (--i) L[i] = p_1 / P[i];
    4038              :   }
    4039         7173 :   o = factor_pn_1(utoipos(p),f);
    4040         7173 :   L2 = leafcopy( gel(o, 1) );
    4041        19212 :   for (i = j = 1; i < lg(L2); i++)
    4042              :   {
    4043        12039 :     if (umodui(p_1, gel(L2,i)) == 0) continue;
    4044         6488 :     gel(L2,j++) = diviiexact(q, gel(L2,i));
    4045              :   }
    4046         7173 :   setlg(L2, j); pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    4047         7173 :   F = Flx_Frobenius_pre(T, p, pi);
    4048        17716 :   for (av = avma;; set_avma(av))
    4049        10543 :   {
    4050              :     GEN tt;
    4051        17716 :     g = random_Flx(f, vT, p);
    4052        17716 :     if (degpol(g) < 1) continue;
    4053        12102 :     if (p == 2) tt = g;
    4054              :     else
    4055              :     {
    4056         8903 :       ulong t = Flxq_norm(g, T, p);
    4057         8903 :       if (t == 1 || !is_gener_Fl(t, p, p_1, L)) continue;
    4058         4781 :       tt = Flxq_powu_pre(g, p_1>>1, T, p, pi);
    4059              :     }
    4060        14591 :     for (i = 1; i < j; i++)
    4061              :     {
    4062         7418 :       GEN a = Flxq_pow_Frobenius(tt, gel(L2,i), F, T, p, pi);
    4063         7418 :       if (!degpol(a) && uel(a,2) == p_1) break;
    4064              :     }
    4065         7980 :     if (i == j) break;
    4066              :   }
    4067         7173 :   if (!po)
    4068              :   {
    4069          187 :     set_avma((pari_sp)g);
    4070          187 :     g = gc_leaf(av0, g);
    4071              :   }
    4072              :   else {
    4073         6986 :     *po = mkvec2(subiu(powuu(p,f), 1), o);
    4074         6986 :     (void)gc_all(av0, 2, &g, po);
    4075              :   }
    4076         7173 :   return g;
    4077              : }
    4078              : 
    4079              : static GEN
    4080       366572 : _Flxq_neg(void *E, GEN x)
    4081       366572 : { struct _Flxq *s = (struct _Flxq *)E;
    4082       366572 :   return Flx_neg(x,s->p); }
    4083              : 
    4084              : static GEN
    4085      1460401 : _Flxq_rmul(void *E, GEN x, GEN y)
    4086      1460401 : { struct _Flxq *s = (struct _Flxq *)E;
    4087      1460401 :   return Flx_mul_pre(x,y,s->p,s->pi); }
    4088              : 
    4089              : static GEN
    4090         9460 : _Flxq_inv(void *E, GEN x)
    4091         9460 : { struct _Flxq *s = (struct _Flxq *)E;
    4092         9460 :   return Flxq_inv(x,s->T,s->p); }
    4093              : 
    4094              : static int
    4095        69139 : _Flxq_equal0(GEN x) { return lgpol(x)==0; }
    4096              : 
    4097              : static GEN
    4098         6567 : _Flxq_s(void *E, long x)
    4099         6567 : { struct _Flxq *s = (struct _Flxq *)E;
    4100         6567 :   ulong u = x<0 ? s->p+x: (ulong)x;
    4101         6567 :   return Fl_to_Flx(u, get_Flx_var(s->T));
    4102              : }
    4103              : 
    4104              : static const struct bb_field Flxq_field={_Flxq_red,_Flxq_add,_Flxq_rmul,_Flxq_neg,
    4105              :                                          _Flxq_inv,_Flxq_equal0,_Flxq_s};
    4106              : 
    4107        68791 : const struct bb_field *get_Flxq_field(void **E, GEN T, ulong p)
    4108              : {
    4109        68791 :   GEN z = new_chunk(sizeof(struct _Flxq));
    4110        68791 :   set_Flxq((struct _Flxq *)z, T, p); *E = (void*)z; return &Flxq_field;
    4111              : }
    4112              : 
    4113              : /***********************************************************************/
    4114              : /**                               Flxn                                **/
    4115              : /***********************************************************************/
    4116              : 
    4117              : GEN
    4118        54830 : Flx_invLaplace(GEN x, ulong p)
    4119              : {
    4120        54830 :   long i, d = degpol(x);
    4121              :   ulong t;
    4122              :   GEN y;
    4123        54830 :   if (d <= 1) return Flx_copy(x);
    4124        54830 :   t = Fl_inv(factorial_Fl(d, p), p);
    4125        54830 :   y = cgetg(d+3, t_VECSMALL);
    4126        54830 :   y[1] = x[1];
    4127      1343364 :   for (i=d; i>=2; i--)
    4128              :   {
    4129      1288534 :     uel(y,i+2) = Fl_mul(uel(x,i+2), t, p);
    4130      1288534 :     t = Fl_mul(t, i, p);
    4131              :   }
    4132        54830 :   uel(y,3) = uel(x,3);
    4133        54830 :   uel(y,2) = uel(x,2);
    4134        54830 :   return y;
    4135              : }
    4136              : 
    4137              : GEN
    4138        27566 : Flx_Laplace(GEN x, ulong p)
    4139              : {
    4140        27566 :   long i, d = degpol(x);
    4141        27566 :   ulong t = 1;
    4142              :   GEN y;
    4143        27566 :   if (d <= 1) return Flx_copy(x);
    4144        27566 :   y = cgetg(d+3, t_VECSMALL);
    4145        27566 :   y[1] = x[1];
    4146        27566 :   uel(y,2) = uel(x,2);
    4147        27566 :   uel(y,3) = uel(x,3);
    4148       766234 :   for (i=2; i<=d; i++)
    4149              :   {
    4150       738668 :     t = Fl_mul(t, i%p, p);
    4151       738668 :     uel(y,i+2) = Fl_mul(uel(x,i+2), t, p);
    4152              :   }
    4153        27566 :   return y;
    4154              : }
    4155              : 
    4156              : GEN
    4157      6416437 : Flxn_red(GEN a, long n)
    4158              : {
    4159      6416437 :   long i, L, l = lg(a);
    4160              :   GEN  b;
    4161      6416437 :   if (l == 2 || !n) return zero_Flx(a[1]);
    4162      6018958 :   L = n+2; if (L > l) L = l;
    4163      6018958 :   b = cgetg(L, t_VECSMALL); b[1] = a[1];
    4164     62479605 :   for (i=2; i<L; i++) b[i] = a[i];
    4165      6018958 :   return Flx_renormalize(b,L);
    4166              : }
    4167              : 
    4168              : GEN
    4169      5222780 : Flxn_mul_pre(GEN a, GEN b, long n, ulong p, ulong pi)
    4170      5222780 : { return Flxn_red(Flx_mul_pre(a, b, p, pi), n); }
    4171              : GEN
    4172        76054 : Flxn_mul(GEN a, GEN b, long n, ulong p)
    4173        76054 : { return Flxn_mul_pre(a, b, n, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    4174              : 
    4175              : GEN
    4176            0 : Flxn_sqr_pre(GEN a, long n, ulong p, ulong pi)
    4177            0 : { return Flxn_red(Flx_sqr_pre(a, p, pi), n); }
    4178              : GEN
    4179            0 : Flxn_sqr(GEN a, long n, ulong p)
    4180            0 : { return Flxn_sqr_pre(a, n, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    4181              : 
    4182              : /* (f*g) \/ x^n */
    4183              : static GEN
    4184       953825 : Flx_mulhigh_i(GEN f, GEN g, long n, ulong p, ulong pi)
    4185       953825 : { return Flx_shift(Flx_mul_pre(f, g, p, pi),-n); }
    4186              : 
    4187              : static GEN
    4188       529859 : Flxn_mulhigh(GEN f, GEN g, long n2, long n, ulong p, ulong pi)
    4189              : {
    4190       529859 :   GEN F = Flx_blocks(f, n2, 2), fl = gel(F,1), fh = gel(F,2);
    4191       529859 :   return Flx_add(Flx_mulhigh_i(fl, g, n2, p, pi),
    4192              :                  Flxn_mul_pre(fh, g, n - n2, p, pi), p);
    4193              : }
    4194              : 
    4195              : /* g==NULL -> assume g==1 */
    4196              : GEN
    4197        56825 : Flxn_div_pre(GEN g, GEN f, long e, ulong p, ulong pi)
    4198              : {
    4199        56825 :   pari_sp av = avma, av2;
    4200              :   ulong mask;
    4201              :   GEN W;
    4202        56825 :   long n = 1;
    4203        56825 :   if (lg(f) <= 2) pari_err_INV("Flxn_inv",f);
    4204        56825 :   W = Fl_to_Flx(Fl_inv(uel(f,2),p), f[1]);
    4205        56825 :   mask = quadratic_prec_mask(e);
    4206        56825 :   av2 = avma;
    4207       272333 :   for (;mask>1;)
    4208              :   {
    4209              :     GEN u, fr;
    4210       215508 :     long n2 = n;
    4211       215508 :     n<<=1; if (mask & 1) n--;
    4212       215508 :     mask >>= 1;
    4213       215508 :     fr = Flxn_red(f, n);
    4214       215508 :     if (mask>1 || !g)
    4215              :     {
    4216       159720 :       u = Flxn_mul_pre(W, Flxn_mulhigh(fr, W, n2, n, p, pi), n-n2, p, pi);
    4217       159720 :       W = Flx_sub(W, Flx_shift(u, n2), p);
    4218              :     } else
    4219              :     {
    4220        55788 :       GEN y = Flxn_mul_pre(g, W, n, p, pi), yt =  Flxn_red(y, n-n2);
    4221        55788 :       u = Flxn_mul_pre(yt, Flxn_mulhigh(fr,  W, n2, n, p, pi), n-n2, p, pi);
    4222        55788 :       W = Flx_sub(y, Flx_shift(u, n2), p);
    4223              :     }
    4224       215508 :     if (gc_needed(av2,2))
    4225              :     {
    4226            0 :       if(DEBUGMEM>1) pari_warn(warnmem,"Flxn_div, e = %ld", n);
    4227            0 :       W = gc_upto(av2, W);
    4228              :     }
    4229              :   }
    4230        56825 :   return gc_upto(av, W);
    4231              : }
    4232              : GEN
    4233        56825 : Flxn_div(GEN g, GEN f, long e, ulong p)
    4234        56825 : { return Flxn_div_pre(g, f, e, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    4235              : 
    4236              : GEN
    4237         1037 : Flxn_inv(GEN f, long e, ulong p)
    4238         1037 : { return Flxn_div(NULL, f, e, p); }
    4239              : 
    4240              : GEN
    4241       109615 : Flxn_expint(GEN h, long e, ulong p)
    4242              : {
    4243       109615 :   pari_sp av = avma, av2;
    4244       109615 :   long v = h[1], n=1;
    4245       109615 :   GEN f = pol1_Flx(v), g = pol1_Flx(v);
    4246       109615 :   ulong mask = quadratic_prec_mask(e), pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    4247       109615 :   av2 = avma;
    4248       423966 :   for (;mask>1;)
    4249              :   {
    4250              :     GEN u, w;
    4251       423966 :     long n2 = n;
    4252       423966 :     n<<=1; if (mask & 1) n--;
    4253       423966 :     mask >>= 1;
    4254       423966 :     u = Flxn_mul_pre(g, Flx_mulhigh_i(f, Flxn_red(h, n2-1), n2-1, p,pi), n-n2, p,pi);
    4255       423966 :     u = Flx_add(u, Flx_shift(Flxn_red(h, n-1), 1-n2), p);
    4256       423966 :     w = Flxn_mul_pre(f, Flx_integXn(u, n2-1, p), n-n2, p, pi);
    4257       423966 :     f = Flx_add(f, Flx_shift(w, n2), p);
    4258       423966 :     if (mask<=1) break;
    4259       314351 :     u = Flxn_mul_pre(g, Flxn_mulhigh(f, g, n2, n, p, pi), n-n2, p, pi);
    4260       314351 :     g = Flx_sub(g, Flx_shift(u, n2), p);
    4261       314351 :     if (gc_needed(av2,2))
    4262              :     {
    4263            0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flxn_exp, e = %ld", n);
    4264            0 :       (void)gc_all(av2, 2, &f, &g);
    4265              :     }
    4266              :   }
    4267       109615 :   return gc_upto(av, f);
    4268              : }
    4269              : 
    4270              : GEN
    4271            0 : Flxn_exp(GEN h, long e, ulong p)
    4272              : {
    4273            0 :   if (degpol(h)<1 || uel(h,2)!=0)
    4274            0 :     pari_err_DOMAIN("Flxn_exp","valuation", "<", gen_1, h);
    4275            0 :   return Flxn_expint(Flx_deriv(h, p), e, p);
    4276              : }
    4277              : 
    4278              : INLINE GEN
    4279       218671 : Flxn_recip(GEN x, long n)
    4280              : {
    4281       218671 :   GEN z=Flx_recipspec(x+2,lgpol(x),n);
    4282       218671 :   z[1]=x[1];
    4283       218671 :   return z;
    4284              : }
    4285              : 
    4286              : GEN
    4287        54528 : Flx_Newton(GEN P, long n, ulong p)
    4288              : {
    4289        54528 :   pari_sp av = avma;
    4290        54528 :   long d = degpol(P);
    4291        54528 :   GEN dP = Flxn_recip(Flx_deriv(P, p), d);
    4292        54528 :   GEN Q = Flxn_div(dP, Flxn_recip(P, d+1), n, p);
    4293        54528 :   return gc_leaf(av, Q);
    4294              : }
    4295              : 
    4296              : GEN
    4297       109615 : Flx_fromNewton(GEN P, ulong p)
    4298              : {
    4299       109615 :   pari_sp av = avma;
    4300       109615 :   ulong n = Flx_constant(P)+1;
    4301       109615 :   GEN z = Flx_neg(Flx_shift(P, -1), p);
    4302       109615 :   GEN Q = Flxn_recip(Flxn_expint(z, n, p), n);
    4303       109615 :   return gc_leaf(av, Q);
    4304              : }
    4305              : 
    4306              : static void
    4307        12521 : init_invlaplace(long d, ulong p, GEN *pt_P, GEN *pt_V)
    4308              : {
    4309              :   long i;
    4310              :   ulong e;
    4311        12521 :   GEN P = cgetg(d+1, t_VECSMALL);
    4312        12521 :   GEN V = cgetg(d+1, t_VECSMALL);
    4313      1400396 :   for (i=1, e=1; i<=d; i++, e++)
    4314              :   {
    4315      1387875 :     if (e==p)
    4316              :     {
    4317       459279 :       e = 0;
    4318       459279 :       V[i] = u_lvalrem(i, p, &uel(P,i));
    4319              :     } else
    4320              :     {
    4321       928596 :       V[i] = 0; uel(P,i) = i;
    4322              :     }
    4323              :   }
    4324        12521 :   *pt_P = P; *pt_V = V;
    4325        12521 : }
    4326              : 
    4327              : /* return p^val * FpX_invLaplace(1+x+...x^(n-1), q), with q a power of p and
    4328              :  * val large enough to compensate for the power of p in the factorials */
    4329              : 
    4330              : static GEN
    4331          504 : ZpX_invLaplace_init(long n, GEN q, ulong p, long v, long sv)
    4332              : {
    4333          504 :   pari_sp av = avma;
    4334          504 :   long i, d = n-1, w;
    4335              :   GEN y, W, E, t;
    4336          504 :   init_invlaplace(d, p, &E, &W);
    4337          504 :   t = Fp_inv(FpV_prod(Flv_to_ZV(E), q), q);
    4338          504 :   w = zv_sum(W);
    4339          504 :   if (v > w) t = Fp_mul(t, powuu(p, v-w), q);
    4340          504 :   y = cgetg(d+3,t_POL);
    4341          504 :   y[1] = evalsigne(1) | sv;
    4342        32697 :   for (i=d; i>=1; i--)
    4343              :   {
    4344        32193 :     gel(y,i+2) = t;
    4345        32193 :     t = Fp_mulu(t, uel(E,i), q);
    4346        32193 :     if (uel(W,i)) t = Fp_mul(t, powuu(p, uel(W,i)), q);
    4347              :   }
    4348          504 :   gel(y,2) = t;
    4349          504 :   return gc_GEN(av, ZX_renormalize(y, d+3));
    4350              : }
    4351              : 
    4352              : GEN
    4353        27768 : Flx_composedsum(GEN P, GEN Q, ulong p)
    4354              : {
    4355        27768 :   pari_sp av = avma;
    4356        27768 :   long n = 1 + degpol(P)*degpol(Q);
    4357        27768 :   ulong lead = Fl_mul(Fl_powu(Flx_lead(P), degpol(Q), p),
    4358        27768 :                       Fl_powu(Flx_lead(Q), degpol(P), p), p);
    4359              :   GEN R;
    4360        27768 :   if (p >= (ulong)n)
    4361              :   {
    4362        27264 :     GEN Pl = Flx_invLaplace(Flx_Newton(P,n,p), p);
    4363        27264 :     GEN Ql = Flx_invLaplace(Flx_Newton(Q,n,p), p);
    4364        27264 :     GEN L  = Flx_Laplace(Flxn_mul(Pl, Ql, n, p), p);
    4365        27264 :     R = Flx_fromNewton(L, p);
    4366              :   } else
    4367              :   {
    4368          504 :     long v = factorial_lval(n-1, p);
    4369          504 :     long w = 1 + ulogint(n-1, p);
    4370          504 :     GEN pv = powuu(p, v);
    4371          504 :     GEN qf = powuu(p, w), q = mulii(pv, qf), q2 = mulii(q, pv);
    4372          504 :     GEN iL = ZpX_invLaplace_init(n, q, p, v, P[1]);
    4373          504 :     GEN Pl = FpX_convol(iL, FpX_Newton(Flx_to_ZX(P), n, qf), q);
    4374          504 :     GEN Ql = FpX_convol(iL, FpX_Newton(Flx_to_ZX(Q), n, qf), q);
    4375          504 :     GEN Ln = ZX_Z_divexact(FpXn_mul(Pl, Ql, n, q2), pv);
    4376          504 :     GEN L  = ZX_Z_divexact(FpX_Laplace(Ln, q), pv);
    4377          504 :     R = ZX_to_Flx(FpX_fromNewton(L, qf), p);
    4378              :   }
    4379        27768 :   return gc_leaf(av, Flx_Fl_mul(R, lead, p));
    4380              : }
    4381              : 
    4382              : static GEN
    4383         3896 : _Flx_composedsum(void *E, GEN a, GEN b)
    4384         3896 : { return Flx_composedsum(a, b, (ulong)E); }
    4385              : 
    4386              : GEN
    4387        29009 : FlxV_composedsum(GEN V, ulong p)
    4388        29009 : { return gen_product(V, (void *)p, &_Flx_composedsum); }
    4389              : 
    4390              : GEN
    4391            0 : Flx_composedprod(GEN P, GEN Q, ulong p)
    4392              : {
    4393            0 :   pari_sp av = avma;
    4394            0 :   long n = 1+ degpol(P)*degpol(Q);
    4395            0 :   ulong lead = Fl_mul(Fl_powu(Flx_lead(P), degpol(Q), p),
    4396            0 :                       Fl_powu(Flx_lead(Q), degpol(P), p), p);
    4397              :   GEN R;
    4398            0 :   if (p >= (ulong)n)
    4399              :   {
    4400            0 :     GEN L = Flx_convol(Flx_Newton(P,n,p), Flx_Newton(Q,n,p), p);
    4401            0 :     R = Flx_fromNewton(L, p);
    4402              :   } else
    4403              :   {
    4404            0 :     long w = 1 + ulogint(n, p);
    4405            0 :     GEN qf = powuu(p, w);
    4406            0 :     GEN Pl = FpX_convol(FpX_Newton(Flx_to_ZX(P), n, qf), FpX_Newton(Flx_to_ZX(Q), n, qf), qf);
    4407            0 :     R = ZX_to_Flx(FpX_fromNewton(Pl, qf), p);
    4408              :   }
    4409            0 :   return gc_leaf(av, Flx_Fl_mul(R, lead, p));
    4410              : 
    4411              : }
    4412              : 
    4413              : /* (x+1)^n mod p; assume 2 <= n < 2p prime */
    4414              : static GEN
    4415            0 : Fl_Xp1_powu(ulong n, ulong p, long v)
    4416              : {
    4417            0 :   ulong k, d = (n + 1) >> 1;
    4418            0 :   GEN C, V = identity_zv(d);
    4419              : 
    4420            0 :   Flv_inv_inplace(V, p); /* could restrict to odd integers in [3,d] */
    4421            0 :   C = cgetg(n+3, t_VECSMALL);
    4422            0 :   C[1] = v;
    4423            0 :   uel(C,2) = 1UL;
    4424            0 :   uel(C,3) = n%p;
    4425            0 :   uel(C,4) = Fl_mul(odd(n)? n: n-1, n >> 1, p);
    4426              :     /* binom(n,k) = binom(n,k-1) * (n-k+1) / k */
    4427            0 :   if (SMALL_ULONG(p))
    4428            0 :     for (k = 3; k <= d; k++)
    4429            0 :       uel(C,k+2) = Fl_mul(Fl_mul(n-k+1, uel(C,k+1), p), uel(V,k), p);
    4430              :   else
    4431              :   {
    4432            0 :     ulong pi  = get_Fl_red(p);
    4433            0 :     for (k = 3; k <= d; k++)
    4434            0 :       uel(C,k+2) = Fl_mul_pre(Fl_mul(n-k+1, uel(C,k+1), p), uel(V,k), p, pi);
    4435              :   }
    4436            0 :   for (   ; k <= n; k++) uel(C,2+k) = uel(C,2+n-k);
    4437            0 :   return C; /* normalized */
    4438              : }
    4439              : 
    4440              : /* p arbitrary */
    4441              : GEN
    4442       675512 : Flx_translate1_basecase(GEN P, ulong p)
    4443              : {
    4444       675512 :   GEN R = Flx_copy(P);
    4445       675512 :   long i, k, n = degpol(P);
    4446      3504957 :   for (i = 1; i <= n; i++)
    4447     22817731 :     for (k = n-i; k < n; k++) uel(R,k+2) = Fl_add(uel(R,k+2), uel(R,k+3), p);
    4448       675512 :   return R;
    4449              : }
    4450              : 
    4451              : static int
    4452       688677 : translate_basecase(long n, ulong p)
    4453              : {
    4454              : #ifdef LONG_IS_64BIT
    4455       590910 :   if (p <= 19) return n < 40;
    4456       563070 :   if (p < 1UL<<30) return n < 58;
    4457            0 :   if (p < 1UL<<59) return n < 100;
    4458            0 :   if (p < 1UL<<62) return n < 120;
    4459            0 :   if (p < 1UL<<63) return n < 240;
    4460            0 :   return n < 250;
    4461              : #else
    4462        97767 :   if (p <= 13) return n < 18;
    4463        94146 :   if (p <= 17) return n < 22;
    4464        93524 :   if (p <= 29) return n < 39;
    4465        91620 :   if (p <= 67) return n < 69;
    4466        86207 :   if (p < 1UL<< 15) return n < 80;
    4467         2047 :   if (p < 1UL<< 16) return n < 100;
    4468            0 :   if (p < 1UL<< 28) return n < 300;
    4469            0 :   return n < 650;
    4470              : #endif
    4471              : }
    4472              : /* assume p prime */
    4473              : GEN
    4474       663418 : Flx_translate1(GEN P, ulong p)
    4475              : {
    4476       663418 :   long d, n = degpol(P);
    4477              :   GEN R, Q, S;
    4478       663418 :   if (translate_basecase(n, p)) return Flx_translate1_basecase(P, p);
    4479              :   /* n > 0 */
    4480         1148 :   d = n >> 1;
    4481         1148 :   if ((ulong)n < p)
    4482              :   {
    4483            0 :     R = Flx_translate1(Flxn_red(P, d), p);
    4484            0 :     Q = Flx_translate1(Flx_shift(P, -d), p);
    4485            0 :     S = Fl_Xp1_powu(d, p, P[1]);
    4486            0 :     return Flx_add(Flx_mul(Q, S, p), R, p);
    4487              :   }
    4488              :   else
    4489              :   {
    4490              :     ulong q;
    4491         1148 :     if ((ulong)d > p) (void)ulogintall(d, p, &q); else q = p;
    4492         1148 :     R = Flx_translate1(Flxn_red(P, q), p);
    4493         1148 :     Q = Flx_translate1(Flx_shift(P, -q), p);
    4494         1148 :     S = Flx_add(Flx_shift(Q, q), Q, p);
    4495         1148 :     return Flx_add(S, R, p); /* P(x+1) = Q(x+1) (x^q+1) + R(x+1) */
    4496              :   }
    4497              : }
    4498              : 
    4499              : GEN
    4500            0 : Flx_Fl_translate(GEN P, ulong c, ulong p)
    4501              : {
    4502            0 :   pari_sp av = avma;
    4503              :   GEN Q;
    4504            0 :   if (c==0) return Flx_copy(P);
    4505            0 :   if (c==1) return Flx_translate1(P, p);
    4506            0 :   Q = Flx_unscale(Flx_translate1(Flx_unscale(P, c, p), p), Fl_inv(c, p), p);
    4507            0 :   return gc_leaf(av, Q);
    4508              : }
    4509              : 
    4510              : static GEN
    4511        12017 : zl_Xp1_powu(ulong n, ulong p, ulong q, long e, long vs)
    4512              : {
    4513        12017 :   ulong k, d = n >> 1, c, v = 0;
    4514        12017 :   GEN C, V, W, U = upowers(p, e-1);
    4515        12017 :   init_invlaplace(d, p, &V, &W);
    4516        12017 :   Flv_inv_inplace(V, q);
    4517        12017 :   C = cgetg(n+3, t_VECSMALL);
    4518        12017 :   C[1] = vs;
    4519        12017 :   uel(C,2) = 1UL;
    4520        12017 :   uel(C,3) = n%q;
    4521        12017 :   v = u_lvalrem(n, p, &c);
    4522      1355682 :   for (k = 2; k <= d; k++)
    4523              :   {
    4524              :     ulong w;
    4525      1343665 :     v += u_lvalrem(n-k+1, p, &w) - W[k];
    4526      1343665 :     c = Fl_mul(Fl_mul(w%q, c, q), uel(V,k), q);
    4527      1343665 :     uel(C,2+k) = v >= (ulong)e ? 0: v==0 ? c : Fl_mul(c, uel(U, v+1), q);
    4528              :   }
    4529      1374521 :   for (   ; k <= n; k++) uel(C,2+k) = uel(C,2+n-k);
    4530        12017 :   return C; /* normalized */
    4531              : }
    4532              : 
    4533              : GEN
    4534        25259 : zlx_translate1(GEN P, ulong p, long e)
    4535              : {
    4536        25259 :   ulong d, q = upowuu(p,e), n = degpol(P);
    4537              :   GEN R, Q, S;
    4538        25259 :   if (translate_basecase(n, q)) return Flx_translate1_basecase(P, q);
    4539              :   /* n > 0 */
    4540        12017 :   d = n >> 1;
    4541        12017 :   R = zlx_translate1(Flxn_red(P, d), p, e);
    4542        12017 :   Q = zlx_translate1(Flx_shift(P, -d), p, e);
    4543        12017 :   S = zl_Xp1_powu(d, p, q, e, P[1]);
    4544        12017 :   return Flx_add(Flx_mul(Q, S, q), R, q);
    4545              : }
    4546              : 
    4547              : /***********************************************************************/
    4548              : /**                               Fl2                                 **/
    4549              : /***********************************************************************/
    4550              : /* Fl2 objects are Flv of length 2 [a,b] representing a+bsqrt(D) for
    4551              :  * a nonsquare D. */
    4552              : 
    4553              : INLINE GEN
    4554     27825283 : mkF2(ulong a, ulong b) { return mkvecsmall2(a,b); }
    4555              : 
    4556              : /* allow pi = 0 */
    4557              : GEN
    4558     17131583 : Fl2_mul_pre(GEN x, GEN y, ulong D, ulong p, ulong pi)
    4559              : {
    4560              :   ulong xaya, xbyb, Db2, mid, z1, z2;
    4561     17131583 :   ulong x1 = x[1], x2 = x[2], y1 = y[1], y2 = y[2];
    4562     17131583 :   if (pi)
    4563              :   {
    4564     17131583 :     xaya = Fl_mul_pre(x1,y1,p,pi);
    4565     17131583 :     if (x2==0 && y2==0) return mkF2(xaya,0);
    4566     17054423 :     if (x2==0) return mkF2(xaya,Fl_mul_pre(x1,y2,p,pi));
    4567     12249573 :     if (y2==0) return mkF2(xaya,Fl_mul_pre(x2,y1,p,pi));
    4568     12249353 :     xbyb = Fl_mul_pre(x2,y2,p,pi);
    4569     12249353 :     mid = Fl_mul_pre(Fl_add(x1,x2,p), Fl_add(y1,y2,p),p,pi);
    4570     12249353 :     Db2 = Fl_mul_pre(D, xbyb, p,pi);
    4571              :   }
    4572            0 :   else if (p & HIGHMASK)
    4573              :   {
    4574            0 :     xaya = Fl_mul(x1,y1,p);
    4575            0 :     if (x2==0 && y2==0) return mkF2(xaya,0);
    4576            0 :     if (x2==0) return mkF2(xaya,Fl_mul(x1,y2,p));
    4577            0 :     if (y2==0) return mkF2(xaya,Fl_mul(x2,y1,p));
    4578            0 :     xbyb = Fl_mul(x2,y2,p);
    4579            0 :     mid = Fl_mul(Fl_add(x1,x2,p), Fl_add(y1,y2,p),p);
    4580            0 :     Db2 = Fl_mul(D, xbyb, p);
    4581              :   }
    4582              :   else
    4583              :   {
    4584            0 :     xaya = (x1 * y1) % p;
    4585            0 :     if (x2==0 && y2==0) return mkF2(xaya,0);
    4586            0 :     if (x2==0) return mkF2(xaya, (x1 * y2) % p);
    4587            0 :     if (y2==0) return mkF2(xaya, (x2 * y1) % p);
    4588            0 :     xbyb = (x2 * y2) % p;
    4589            0 :     mid = (Fl_add(x1,x2,p) * Fl_add(y1,y2,p)) % p;
    4590            0 :     Db2 = (D * xbyb) % p;
    4591              :   }
    4592     12249353 :   z1 = Fl_add(xaya,Db2,p);
    4593     12249353 :   z2 = Fl_sub(mid,Fl_add(xaya,xbyb,p),p);
    4594     12249353 :   return mkF2(z1,z2);
    4595              : }
    4596              : 
    4597              : /* allow pi = 0 */
    4598              : GEN
    4599      5022732 : Fl2_sqr_pre(GEN x, ulong D, ulong p, ulong pi)
    4600              : {
    4601      5022732 :   ulong a = x[1], b = x[2];
    4602              :   ulong a2, Db2, ab;
    4603      5022732 :   if (pi)
    4604              :   {
    4605      5022732 :     a2 = Fl_sqr_pre(a,p,pi);
    4606      5022732 :     if (b==0) return mkF2(a2,0);
    4607      4788110 :     Db2= Fl_mul_pre(D, Fl_sqr_pre(b,p,pi), p,pi);
    4608      4788110 :     ab = Fl_mul_pre(a,b,p,pi);
    4609              :   }
    4610            0 :   else if (p & HIGHMASK)
    4611              :   {
    4612            0 :     a2 = Fl_sqr(a,p);
    4613            0 :     if (b==0) return mkF2(a2,0);
    4614            0 :     Db2= Fl_mul(D, Fl_sqr(b,p), p);
    4615            0 :     ab = Fl_mul(a,b,p);
    4616              :   }
    4617              :   else
    4618              :   {
    4619            0 :     a2 = (a * a) % p;
    4620            0 :     if (b==0) return mkF2(a2,0);
    4621            0 :     Db2= (D * ((b * b) % p)) % p;
    4622            0 :     ab = (a * b) % p;
    4623              :   }
    4624      4788110 :   return mkF2(Fl_add(a2,Db2,p), Fl_double(ab,p));
    4625              : }
    4626              : 
    4627              : /* allow pi = 0 */
    4628              : ulong
    4629     97119260 : Fl2_norm_pre(GEN x, ulong D, ulong p, ulong pi)
    4630              : {
    4631     97119260 :   ulong a = x[1], b = x[2], a2;
    4632     97119260 :   if (pi)
    4633              :   {
    4634     97067294 :     a2 = Fl_sqr_pre(a,p,pi);
    4635     97067294 :     return b? Fl_sub(a2, Fl_mul_pre(D, Fl_sqr_pre(b, p,pi), p,pi), p): a2;
    4636              :   }
    4637        51966 :   else if (p & HIGHMASK)
    4638              :   {
    4639            0 :     a2 = Fl_sqr(a,p);
    4640            0 :     return b? Fl_sub(a2, Fl_mul(D, Fl_sqr(b, p), p), p): a2;
    4641              :   }
    4642              :   else
    4643              :   {
    4644        51966 :     a2 = (a * a) % p;
    4645        51966 :     return b? Fl_sub(a2, (D * ((b * b) % p)) % p, p): a2;
    4646              :   }
    4647              : }
    4648              : 
    4649              : /* allow pi = 0 */
    4650              : GEN
    4651       200599 : Fl2_inv_pre(GEN x, ulong D, ulong p, ulong pi)
    4652              : {
    4653       200599 :   ulong a = x[1], b = x[2], n, ni;
    4654       200599 :   if (b == 0) return mkF2(Fl_inv(a,p), 0);
    4655       166213 :   b = Fl_neg(b, p);
    4656       166213 :   if (pi)
    4657              :   {
    4658       166213 :     n = Fl_sub(Fl_sqr_pre(a, p,pi),
    4659              :                Fl_mul_pre(D, Fl_sqr_pre(b, p,pi), p,pi), p);
    4660       166213 :     ni = Fl_inv(n,p);
    4661       166213 :     return mkF2(Fl_mul_pre(a, ni, p,pi), Fl_mul_pre(b, ni, p,pi));
    4662              :   }
    4663            0 :   else if (p & HIGHMASK)
    4664              :   {
    4665            0 :     n = Fl_sub(Fl_sqr(a, p), Fl_mul(D, Fl_sqr(b, p), p), p);
    4666            0 :     ni = Fl_inv(n,p);
    4667            0 :     return mkF2(Fl_mul(a, ni, p), Fl_mul(b, ni, p));
    4668              :   }
    4669              :   else
    4670              :   {
    4671            0 :     n = Fl_sub((a * a) % p, (D * ((b * b) % p)) % p, p);
    4672            0 :     ni = Fl_inv(n,p);
    4673            0 :     return mkF2((a * ni) % p, (b * ni) % p);
    4674              :   }
    4675              : }
    4676              : 
    4677              : int
    4678       457455 : Fl2_equal1(GEN x) { return x[1]==1 && x[2]==0; }
    4679              : 
    4680              : struct _Fl2 {
    4681              :   ulong p, pi, D;
    4682              : };
    4683              : 
    4684              : static GEN
    4685      5022732 : _Fl2_sqr(void *data, GEN x)
    4686              : {
    4687      5022732 :   struct _Fl2 *D = (struct _Fl2*)data;
    4688      5022732 :   return Fl2_sqr_pre(x, D->D, D->p, D->pi);
    4689              : }
    4690              : static GEN
    4691      1963220 : _Fl2_mul(void *data, GEN x, GEN y)
    4692              : {
    4693      1963220 :   struct _Fl2 *D = (struct _Fl2*)data;
    4694      1963220 :   return Fl2_mul_pre(x,y, D->D, D->p, D->pi);
    4695              : }
    4696              : 
    4697              : /* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL; allow pi = 0 */
    4698              : GEN
    4699       682986 : Fl2_pow_pre(GEN x, GEN n, ulong D, ulong p, ulong pi)
    4700              : {
    4701       682986 :   pari_sp av = avma;
    4702              :   struct _Fl2 d;
    4703              :   GEN y;
    4704       682986 :   long s = signe(n);
    4705       682986 :   if (!s) return mkF2(1,0);
    4706       605737 :   if (s < 0)
    4707       200599 :     x = Fl2_inv_pre(x,D,p,pi);
    4708       605737 :   if (is_pm1(n)) return s < 0 ? x : zv_copy(x);
    4709       446225 :   d.p = p; d.pi = pi; d.D=D;
    4710       446225 :   y = gen_pow_i(x, n, (void*)&d, &_Fl2_sqr, &_Fl2_mul);
    4711       446225 :   return gc_leaf(av, y);
    4712              : }
    4713              : 
    4714              : static GEN
    4715       682986 : _Fl2_pow(void *data, GEN x, GEN n)
    4716              : {
    4717       682986 :   struct _Fl2 *D = (struct _Fl2*)data;
    4718       682986 :   return Fl2_pow_pre(x, n, D->D, D->p, D->pi);
    4719              : }
    4720              : 
    4721              : static GEN
    4722       115645 : _Fl2_rand(void *data)
    4723              : {
    4724       115645 :   struct _Fl2 *D = (struct _Fl2*)data;
    4725       115645 :   ulong a = random_Fl(D->p), b=random_Fl(D->p-1)+1;
    4726       115645 :   return mkF2(a,b);
    4727              : }
    4728              : 
    4729              : GEN
    4730        65964 : Fl2_sqrt_pre(GEN z, ulong D, ulong p, ulong pi)
    4731              : {
    4732        65964 :   ulong a = uel(z,1), b = uel(z,2), as2, u, v, s;
    4733        65964 :   ulong y = Fl_2gener_pre_i(D, p, pi);
    4734        65964 :   if (b == 0)
    4735        19029 :     return krouu(a, p)==1 ? mkF2(Fl_sqrt_pre_i(a, y, p, pi), 0)
    4736        19029 :                           : mkF2(0, Fl_sqrt_pre_i(Fl_div(a, D, p), y, p, pi));
    4737        52796 :   s = Fl_sqrt_pre_i(Fl2_norm_pre(z, D, p, pi), y, p, pi);
    4738        52796 :   if (s==~0UL) return NULL;
    4739        49601 :   as2 = Fl_halve(Fl_add(a, s, p), p);
    4740        49601 :   if (krouu(as2, p)==-1) as2 = Fl_sub(as2, s, p);
    4741        49601 :   u = Fl_sqrt_pre_i(as2, y, p, pi);
    4742        49601 :   v = Fl_div(b, Fl_double(u, p), p);
    4743        49601 :   return mkF2(u,v);
    4744              : }
    4745              : 
    4746              : static const struct bb_group Fl2_star={_Fl2_mul, _Fl2_pow, _Fl2_rand,
    4747              :        hash_GEN, zv_equal, Fl2_equal1, NULL};
    4748              : 
    4749              : /* allow pi = 0 */
    4750              : GEN
    4751        77249 : Fl2_sqrtn_pre(GEN a, GEN n, ulong D, ulong p, ulong pi, GEN *zeta)
    4752              : {
    4753              :   struct _Fl2 E;
    4754              :   GEN o;
    4755        77249 :   if (a[1]==0 && a[2]==0)
    4756              :   {
    4757            0 :     if (signe(n) < 0) pari_err_INV("Flxq_sqrtn",a);
    4758            0 :     if (zeta) *zeta=mkF2(1,0);
    4759            0 :     return zv_copy(a);
    4760              :   }
    4761        77249 :   E.p=p; E.pi = pi; E.D = D;
    4762        77249 :   o = subiu(powuu(p,2), 1);
    4763        77249 :   return gen_Shanks_sqrtn(a,n,o,zeta,(void*)&E,&Fl2_star);
    4764              : }
    4765              : 
    4766              : /* allow pi = 0 */
    4767              : GEN
    4768      5214706 : Flx_Fl2_eval_pre(GEN x, GEN y, ulong D, ulong p, ulong pi)
    4769              : {
    4770              :   GEN p1;
    4771      5214706 :   long i = lg(x)-1;
    4772      5214706 :   if (i <= 2)
    4773       771491 :     return mkF2(i == 2? x[2]: 0, 0);
    4774      4443215 :   p1 = mkF2(x[i], 0);
    4775     19611578 :   for (i--; i>=2; i--)
    4776              :   {
    4777     15168363 :     p1 = Fl2_mul_pre(p1, y, D, p, pi);
    4778     15168363 :     uel(p1,1) = Fl_add(uel(p1,1), uel(x,i), p);
    4779              :   }
    4780      4443215 :   return p1;
    4781              : }
    4782              : 
    4783              : /***********************************************************************/
    4784              : /**                               FlxV                                **/
    4785              : /***********************************************************************/
    4786              : /* FlxV are t_VEC with Flx coefficients. */
    4787              : 
    4788              : GEN
    4789        34482 : FlxV_Flc_mul(GEN V, GEN W, ulong p)
    4790              : {
    4791        34482 :   pari_sp ltop=avma;
    4792              :   long i;
    4793        34482 :   GEN z = Flx_Fl_mul(gel(V,1),W[1],p);
    4794       257068 :   for(i=2;i<lg(V);i++)
    4795       222586 :     z=Flx_add(z,Flx_Fl_mul(gel(V,i),W[i],p),p);
    4796        34482 :   return gc_leaf(ltop,z);
    4797              : }
    4798              : 
    4799              : GEN
    4800            0 : ZXV_to_FlxV(GEN x, ulong p)
    4801            0 : { pari_APPLY_type(t_VEC, ZX_to_Flx(gel(x,i), p)) }
    4802              : 
    4803              : GEN
    4804      3835129 : ZXT_to_FlxT(GEN x, ulong p)
    4805              : {
    4806      3835129 :   if (typ(x) == t_POL)
    4807      3774922 :     return ZX_to_Flx(x, p);
    4808              :   else
    4809       197303 :     pari_APPLY_type(t_VEC, ZXT_to_FlxT(gel(x,i), p))
    4810              : }
    4811              : 
    4812              : GEN
    4813       172474 : FlxV_to_Flm(GEN x, long n)
    4814       930524 : { pari_APPLY_type(t_MAT, Flx_to_Flv(gel(x,i), n)) }
    4815              : 
    4816              : GEN
    4817            0 : FlxV_red(GEN x, ulong p)
    4818            0 : { pari_APPLY_type(t_VEC, Flx_red(gel(x,i), p)) }
    4819              : 
    4820              : GEN
    4821       301037 : FlxT_red(GEN x, ulong p)
    4822              : {
    4823       301037 :   if (typ(x) == t_VECSMALL)
    4824       202506 :     return Flx_red(x, p);
    4825              :   else
    4826       330340 :     pari_APPLY_type(t_VEC, FlxT_red(gel(x,i), p))
    4827              : }
    4828              : 
    4829              : GEN
    4830       114702 : FlxqV_dotproduct_pre(GEN x, GEN y, GEN T, ulong p, ulong pi)
    4831              : {
    4832       114702 :   long i, lx = lg(x);
    4833              :   pari_sp av;
    4834              :   GEN c;
    4835       114702 :   if (lx == 1) return pol0_Flx(get_Flx_var(T));
    4836       114702 :   av = avma; c = Flx_mul_pre(gel(x,1),gel(y,1), p, pi);
    4837       489174 :   for (i=2; i<lx; i++) c = Flx_add(c, Flx_mul_pre(gel(x,i),gel(y,i), p, pi), p);
    4838       114702 :   return gc_leaf(av, Flx_rem_pre(c,T,p,pi));
    4839              : }
    4840              : GEN
    4841            0 : FlxqV_dotproduct(GEN x, GEN y, GEN T, ulong p)
    4842            0 : { return FlxqV_dotproduct_pre(x, y, T, p, SMALL_ULONG(p)? 0: get_Fl_red(p)); }
    4843              : 
    4844              : GEN
    4845         1932 : FlxqX_dotproduct(GEN x, GEN y, GEN T, ulong p)
    4846              : {
    4847         1932 :   long i, l = minss(lg(x), lg(y));
    4848              :   ulong pi;
    4849              :   pari_sp av;
    4850              :   GEN c;
    4851         1932 :   if (l == 2) return pol0_Flx(get_Flx_var(T));
    4852         1919 :   av = avma; pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    4853         1919 :   c = Flx_mul_pre(gel(x,2),gel(y,2), p, pi);
    4854         6267 :   for (i=3; i<l; i++) c = Flx_add(c, Flx_mul_pre(gel(x,i),gel(y,i), p, pi), p);
    4855         1919 :   return gc_leaf(av, Flx_rem_pre(c,T,p,pi));
    4856              : }
    4857              : 
    4858              : /* allow pi = 0 */
    4859              : GEN
    4860       325162 : FlxC_eval_powers_pre(GEN z, GEN x, ulong p, ulong pi)
    4861              : {
    4862       325162 :   long i, l = lg(z);
    4863       325162 :   GEN y = cgetg(l, t_VECSMALL);
    4864     15070763 :   for (i=1; i<l; i++) uel(y,i) = Flx_eval_powers_pre(gel(z,i), x, p, pi);
    4865       325162 :   return y;
    4866              : }
    4867              : 
    4868              : /***********************************************************************/
    4869              : /**                               FlxM                                **/
    4870              : /***********************************************************************/
    4871              : /* allow pi = 0 */
    4872              : GEN
    4873        22302 : FlxM_eval_powers_pre(GEN z, GEN x, ulong p, ulong pi)
    4874              : {
    4875        22302 :   long i, l = lg(z);
    4876        22302 :   GEN y = cgetg(l, t_MAT);
    4877       347464 :   for (i=1; i<l; i++) gel(y,i) = FlxC_eval_powers_pre(gel(z,i), x, p, pi);
    4878        22302 :   return y;
    4879              : }
    4880              : 
    4881              : GEN
    4882            0 : zero_FlxC(long n, long sv)
    4883              : {
    4884            0 :   GEN x = cgetg(n + 1, t_COL), z = zero_Flx(sv);
    4885              :   long i;
    4886            0 :   for (i = 1; i <= n; i++) gel(x, i) = z;
    4887            0 :   return x;
    4888              : }
    4889              : 
    4890              : GEN
    4891            0 : FlxC_neg(GEN x, ulong p)
    4892            0 : { pari_APPLY_type(t_COL, Flx_neg(gel(x, i), p)) }
    4893              : 
    4894              : GEN
    4895            0 : FlxC_sub(GEN x, GEN y, ulong p)
    4896            0 : { pari_APPLY_type(t_COL, Flx_sub(gel(x, i), gel(y, i), p)) }
    4897              : 
    4898              : GEN
    4899            0 : zero_FlxM(long r, long c, long sv)
    4900              : {
    4901            0 :   GEN x = cgetg(c + 1, t_MAT), z = zero_FlxC(r, sv);
    4902              :   long j;
    4903            0 :   for (j = 1; j <= c; j++) gel(x, j) = z;
    4904            0 :   return x;
    4905              : }
    4906              : 
    4907              : GEN
    4908            0 : zero_FlxM_copy(long r, long c, long sv)
    4909              : {
    4910            0 :   GEN x = cgetg(c + 1, t_MAT);
    4911              :   long j;
    4912            0 :   for (j = 1; j <= c; j++) gel(x, j) = zero_FlxC(r, sv);
    4913            0 :   return x;
    4914              : }
    4915              : 
    4916              : GEN
    4917            0 : FlxM_neg(GEN x, ulong p)
    4918            0 : { pari_APPLY_same(FlxC_neg(gel(x, i), p)) }
    4919              : 
    4920              : GEN
    4921            0 : FlxM_sub(GEN x, GEN y, ulong p)
    4922            0 : { pari_APPLY_same(FlxC_sub(gel(x, i), gel(y,i), p)) }
    4923              : 
    4924              : GEN
    4925            0 : FlxC_Fl_translate(GEN x, ulong c, ulong p)
    4926            0 : { pari_APPLY_type(t_COL, Flx_Fl_translate(gel(x,i), c, p)) }
    4927              : 
    4928              : GEN
    4929            0 : FlxM_Fl_translate(GEN x, ulong c, ulong p)
    4930            0 : { pari_APPLY_same(FlxC_Fl_translate(gel(x,i), c, p)) }
    4931              : 
    4932              : GEN
    4933       247515 : FlxqC_red_pre(GEN x, GEN T, ulong p, ulong pi)
    4934      4988454 : { pari_APPLY_type(t_COL, Flx_rem_pre(gel(x,i), T, p, pi)) }
    4935              : 
    4936              : GEN
    4937        82442 : FlxqM_red_pre(GEN x, GEN T, ulong p, ulong pi)
    4938       329957 : { pari_APPLY_same(FlxqC_red_pre(gel(x,i), T, p, pi)) }
    4939              : 
    4940              : GEN
    4941            0 : FlxqC_Flxq_mul(GEN x, GEN y, GEN T, ulong p)
    4942            0 : { pari_APPLY_type(t_COL, Flxq_mul(gel(x, i), y, T, p)) }
    4943              : 
    4944              : GEN
    4945            0 : FlxqM_Flxq_mul(GEN x, GEN y, GEN T, ulong p)
    4946            0 : { pari_APPLY_same(FlxqC_Flxq_mul(gel(x, i), y, T, p)) }
    4947              : 
    4948              : static GEN
    4949        47865 : FlxM_pack_ZM(GEN M, GEN (*pack)(GEN, long)) {
    4950              :   long i, j, l, lc;
    4951        47865 :   GEN N = cgetg_copy(M, &l), x;
    4952        47865 :   if (l == 1)
    4953            0 :     return N;
    4954        47865 :   lc = lgcols(M);
    4955       213857 :   for (j = 1; j < l; j++) {
    4956       165992 :     gel(N, j) = cgetg(lc, t_COL);
    4957      1146243 :     for (i = 1; i < lc; i++) {
    4958       980251 :       x = gcoeff(M, i, j);
    4959       980251 :       gcoeff(N, i, j) = pack(x + 2, lgpol(x));
    4960              :     }
    4961              :   }
    4962        47865 :   return N;
    4963              : }
    4964              : 
    4965              : static GEN
    4966       891277 : kron_pack_Flx_spec_half(GEN x, long l) {
    4967       891277 :   if (l == 0) return gen_0;
    4968       544951 :   return Flx_to_int_halfspec(x, l);
    4969              : }
    4970              : 
    4971              : static GEN
    4972        85585 : kron_pack_Flx_spec(GEN x, long l) {
    4973              :   long i;
    4974              :   GEN w, y;
    4975        85585 :   if (l == 0)
    4976        28245 :     return gen_0;
    4977        57340 :   y = cgetipos(l + 2);
    4978       186172 :   for (i = 0, w = int_LSW(y); i < l; i++, w = int_nextW(w))
    4979       128832 :     *w = x[i];
    4980        57340 :   return y;
    4981              : }
    4982              : 
    4983              : static GEN
    4984         3389 : kron_pack_Flx_spec_2(GEN x, long l) { return Flx_eval2BILspec(x, 2, l); }
    4985              : 
    4986              : static GEN
    4987            0 : kron_pack_Flx_spec_3(GEN x, long l) { return Flx_eval2BILspec(x, 3, l); }
    4988              : 
    4989              : static GEN
    4990        78984 : kron_unpack_Flx(GEN z, ulong p)
    4991              : {
    4992        78984 :   long i, l = lgefint(z);
    4993        78984 :   GEN x = cgetg(l, t_VECSMALL), w;
    4994       255639 :   for (w = int_LSW(z), i = 2; i < l; w = int_nextW(w), i++)
    4995       176655 :     x[i] = ((ulong) *w) % p;
    4996        78984 :   return Flx_renormalize(x, l);
    4997              : }
    4998              : 
    4999              : static GEN
    5000         2930 : kron_unpack_Flx_2(GEN x, ulong p) {
    5001         2930 :   long d = (lgefint(x)-1)/2 - 1;
    5002         2930 :   return Z_mod2BIL_Flx_2(x, d, p);
    5003              : }
    5004              : 
    5005              : static GEN
    5006            0 : kron_unpack_Flx_3(GEN x, ulong p) {
    5007            0 :   long d = lgefint(x)/3 - 1;
    5008            0 :   return Z_mod2BIL_Flx_3(x, d, p);
    5009              : }
    5010              : 
    5011              : static GEN
    5012       116931 : FlxM_pack_ZM_bits(GEN M, long b)
    5013              : {
    5014              :   long i, j, l, lc;
    5015       116931 :   GEN N = cgetg_copy(M, &l), x;
    5016       116931 :   if (l == 1)
    5017            0 :     return N;
    5018       116931 :   lc = lgcols(M);
    5019       493598 :   for (j = 1; j < l; j++) {
    5020       376667 :     gel(N, j) = cgetg(lc, t_COL);
    5021      6501067 :     for (i = 1; i < lc; i++) {
    5022      6124400 :       x = gcoeff(M, i, j);
    5023      6124400 :       gcoeff(N, i, j) = kron_pack_Flx_spec_bits(x + 2, b, lgpol(x));
    5024              :     }
    5025              :   }
    5026       116931 :   return N;
    5027              : }
    5028              : 
    5029              : static GEN
    5030        23936 : ZM_unpack_FlxM(GEN M, ulong p, ulong sv, GEN (*unpack)(GEN, ulong))
    5031              : {
    5032              :   long i, j, l, lc;
    5033        23936 :   GEN N = cgetg_copy(M, &l), x;
    5034        23936 :   if (l == 1)
    5035            0 :     return N;
    5036        23936 :   lc = lgcols(M);
    5037       116098 :   for (j = 1; j < l; j++) {
    5038        92162 :     gel(N, j) = cgetg(lc, t_COL);
    5039       902371 :     for (i = 1; i < lc; i++) {
    5040       810209 :       x = unpack(gcoeff(M, i, j), p);
    5041       810209 :       x[1] = sv;
    5042       810209 :       gcoeff(N, i, j) = x;
    5043              :     }
    5044              :   }
    5045        23936 :   return N;
    5046              : }
    5047              : 
    5048              : static GEN
    5049        58506 : ZM_unpack_FlxM_bits(GEN M, long b, ulong p, ulong pi, long sv)
    5050              : {
    5051              :   long i, j, l, lc;
    5052        58506 :   GEN N = cgetg_copy(M, &l), x;
    5053        58506 :   if (l == 1)
    5054            0 :     return N;
    5055        58506 :   lc = lgcols(M);
    5056        58506 :   if (b < BITS_IN_LONG) {
    5057       203927 :     for (j = 1; j < l; j++) {
    5058       147148 :       gel(N, j) = cgetg(lc, t_COL);
    5059      3910526 :       for (i = 1; i < lc; i++) {
    5060      3763378 :         x = kron_unpack_Flx_bits_narrow(gcoeff(M, i, j), b, p);
    5061      3763378 :         x[1] = sv;
    5062      3763378 :         gcoeff(N, i, j) = x;
    5063              :       }
    5064              :     }
    5065              :   } else {
    5066         1727 :     if (!pi) pi = get_Fl_red(p); /* unset if !SMALL_ULONG(p) */
    5067         9932 :     for (j = 1; j < l; j++) {
    5068         8205 :       gel(N, j) = cgetg(lc, t_COL);
    5069       175557 :       for (i = 1; i < lc; i++) {
    5070       167352 :         x = kron_unpack_Flx_bits_wide(gcoeff(M, i, j), b, p, pi);
    5071       167352 :         x[1] = sv;
    5072       167352 :         gcoeff(N, i, j) = x;
    5073              :       }
    5074              :     }
    5075              :   }
    5076        58506 :   return N;
    5077              : }
    5078              : 
    5079              : static GEN
    5080        82442 : FlxM_mul_Kronecker_i(GEN A, GEN B, ulong p, ulong pi, long d, long sv)
    5081              : {
    5082        82442 :   long b, n = lg(A) - 1;
    5083              :   GEN C, z;
    5084              :   GEN (*pack)(GEN, long), (*unpack)(GEN, ulong);
    5085        82442 :   int is_sqr = A==B;
    5086              : 
    5087        82442 :   z = muliu(muliu(sqru(p - 1), d), n);
    5088        82442 :   b = expi(z) + 1;
    5089              :   /* only do expensive bit-packing if it saves at least 1 limb */
    5090        82442 :   if (b <= BITS_IN_HALFULONG)
    5091        77887 :   { if (nbits2nlong(d*b) == (d + 1)/2) b = BITS_IN_HALFULONG; }
    5092              :   else
    5093              :   {
    5094         4555 :     long l = lgefint(z) - 2;
    5095         4555 :     if (nbits2nlong(d*b) == d*l) b = l*BITS_IN_LONG;
    5096              :   }
    5097              : 
    5098        82442 :   switch (b) {
    5099        22849 :   case BITS_IN_HALFULONG:
    5100        22849 :     pack = kron_pack_Flx_spec_half;
    5101        22849 :     unpack = int_to_Flx_half;
    5102        22849 :     break;
    5103         1038 :   case BITS_IN_LONG:
    5104         1038 :     pack = kron_pack_Flx_spec;
    5105         1038 :     unpack = kron_unpack_Flx;
    5106         1038 :     break;
    5107           49 :   case 2*BITS_IN_LONG:
    5108           49 :     pack = kron_pack_Flx_spec_2;
    5109           49 :     unpack = kron_unpack_Flx_2;
    5110           49 :     break;
    5111            0 :   case 3*BITS_IN_LONG:
    5112            0 :     pack = kron_pack_Flx_spec_3;
    5113            0 :     unpack = kron_unpack_Flx_3;
    5114            0 :     break;
    5115        58506 :   default:
    5116        58506 :     A = FlxM_pack_ZM_bits(A, b);
    5117        58506 :     B = is_sqr? A: FlxM_pack_ZM_bits(B, b);
    5118        58506 :     C = ZM_mul(A, B);
    5119        58506 :     return ZM_unpack_FlxM_bits(C, b, p, pi, sv);
    5120              :   }
    5121        23936 :   A = FlxM_pack_ZM(A, pack);
    5122        23936 :   B = is_sqr? A: FlxM_pack_ZM(B, pack);
    5123        23936 :   C = ZM_mul(A, B);
    5124        23936 :   return ZM_unpack_FlxM(C, p, sv, unpack);
    5125              : }
    5126              : 
    5127              : GEN
    5128        82442 : FlxqM_mul_Kronecker(GEN A, GEN B, GEN T, ulong p)
    5129              : {
    5130        82442 :   pari_sp av = avma;
    5131        82442 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    5132        82442 :   long sv = get_Flx_var(T), d = get_Flx_degree(T);
    5133        82442 :   GEN C = FlxM_mul_Kronecker_i(A, B, p, pi, d, sv);
    5134        82442 :   C = FlxqM_red_pre(C, T, p, pi);
    5135        82442 :   return gc_upto(av, C);
    5136              : }
    5137              : 
    5138              : /* assume m > 1 */
    5139              : static long
    5140            0 : FlxV_max_degree_i(GEN x, long m)
    5141              : {
    5142            0 :   long i, l = degpol(gel(x,1));
    5143            0 :   for (i = 2; i < m; i++) l = maxss(l, degpol(gel(x,i)));
    5144            0 :   return l;
    5145              : }
    5146              : 
    5147              : /* assume n > 1 and m > 1 */
    5148              : static long
    5149            0 : FlxM_max_degree_i(GEN x, long n, long m)
    5150              : {
    5151            0 :   long j, l = FlxV_max_degree_i(gel(x,1), m);
    5152            0 :   for (j = 2; j < n; j++) l = maxss(l, FlxV_max_degree_i(gel(x,j), m));
    5153            0 :   return l;
    5154              : }
    5155              : 
    5156              : static long
    5157            0 : FlxM_max_degree(GEN x)
    5158              : {
    5159            0 :   long n = lg(x), m;
    5160            0 :   if (n == 1) return -1;
    5161            0 :   m = lgcols(x); return m == 1? -1: FlxM_max_degree_i(x, n, m);
    5162              : }
    5163              : 
    5164              : GEN
    5165            0 : FlxM_mul(GEN x, GEN y, ulong p)
    5166              : {
    5167            0 :   pari_sp av = avma;
    5168            0 :   ulong pi = SMALL_ULONG(p)? 0: get_Fl_red(p);
    5169              :   long sv, d;
    5170            0 :   if (lg(x) == 1) return cgetg(1,t_MAT);
    5171            0 :   if (lg(gel(x,1))==1) return FlxqM_mul(x, y, NULL, p);
    5172            0 :   sv = mael3(x,1,1,1);
    5173            0 :   d = maxss(FlxM_max_degree(x), FlxM_max_degree(y));
    5174            0 :   return gc_GEN(av, FlxM_mul_Kronecker_i(x, y, p, pi, d+1, sv));
    5175              : }
        

Generated by: LCOV version 2.0-1