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) Hit Total Coverage
Test: PARI/GP v2.12.0 lcov report (development 23344-f0cc1b3f6) Lines: 2865 3117 91.9 %
Date: 2018-12-12 05:41:43 Functions: 349 379 92.1 %
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. It is distributed in the hope that it will be useful, but WITHOUT
       8             : ANY WARRANTY WHATSOEVER.
       9             : 
      10             : Check the License for details. You should have received a copy of it, along
      11             : with the package; see the file 'COPYING'. If not, write to the Free Software
      12             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13             : 
      14             : #include "pari.h"
      15             : #include "paripriv.h"
      16             : 
      17             : /* Not so fast arithmetic with polynomials with small coefficients. */
      18             : 
      19             : static GEN
      20   577406945 : get_Flx_red(GEN T, GEN *B)
      21             : {
      22   577406945 :   if (typ(T)!=t_VEC) { *B=NULL; return T; }
      23     3152314 :   *B = gel(T,1); return gel(T,2);
      24             : }
      25             : 
      26             : /***********************************************************************/
      27             : /**                                                                   **/
      28             : /**               Flx                                                 **/
      29             : /**                                                                   **/
      30             : /***********************************************************************/
      31             : /* Flx objects are defined as follows:
      32             :    Let l an ulong. An Flx is a t_VECSMALL:
      33             :    x[0] = codeword
      34             :    x[1] = evalvarn(variable number)  (signe is not stored).
      35             :    x[2] = a_0 x[3] = a_1, etc.
      36             :    With 0 <= a_i < l
      37             : 
      38             :    signe(x) is not valid. Use degpol(x)>=0 instead.
      39             : */
      40             : /***********************************************************************/
      41             : /**                                                                   **/
      42             : /**          Conversion from Flx                                      **/
      43             : /**                                                                   **/
      44             : /***********************************************************************/
      45             : 
      46             : GEN
      47    43402206 : Flx_to_ZX(GEN z)
      48             : {
      49    43402206 :   long i, l = lg(z);
      50    43402206 :   GEN x = cgetg(l,t_POL);
      51    43402341 :   for (i=2; i<l; i++) gel(x,i) = utoi(z[i]);
      52    43402139 :   x[1] = evalsigne(l-2!=0)| z[1]; return x;
      53             : }
      54             : 
      55             : GEN
      56       26369 : Flx_to_FlxX(GEN z, long sv)
      57             : {
      58       26369 :   long i, l = lg(z);
      59       26369 :   GEN x = cgetg(l,t_POL);
      60       26369 :   for (i=2; i<l; i++) gel(x,i) = Fl_to_Flx(z[i], sv);
      61       26369 :   x[1] = evalsigne(l-2!=0)| z[1]; return x;
      62             : }
      63             : 
      64             : GEN
      65      256647 : Flv_to_ZV(GEN x)
      66      256647 : { pari_APPLY_type(t_VEC, utoi(x[i])) }
      67             : 
      68             : GEN
      69    21007086 : Flc_to_ZC(GEN x)
      70    21007086 : { pari_APPLY_type(t_COL, utoi(x[i])) }
      71             : 
      72             : GEN
      73     8385965 : Flm_to_ZM(GEN x)
      74     8385965 : { pari_APPLY_type(t_MAT, Flc_to_ZC(gel(x,i))) }
      75             : 
      76             : GEN
      77      123364 : Flc_to_ZC_inplace(GEN z)
      78             : {
      79      123364 :   long i, l = lg(z);
      80      123364 :   for (i=1; i<l; i++) gel(z,i) = utoi(z[i]);
      81      123364 :   settyp(z, t_COL);
      82      123364 :   return z;
      83             : }
      84             : 
      85             : GEN
      86       56166 : Flm_to_ZM_inplace(GEN z)
      87             : {
      88       56166 :   long i, l = lg(z);
      89       56166 :   for (i=1; i<l; i++) Flc_to_ZC_inplace(gel(z, i));
      90       56166 :   return z;
      91             : }
      92             : 
      93             : /* same as Flx_to_ZX, in place */
      94             : GEN
      95    44145825 : Flx_to_ZX_inplace(GEN z)
      96             : {
      97    44145825 :   long i, l = lg(z);
      98    44145825 :   for (i=2; i<l; i++) gel(z,i) = utoi(z[i]);
      99    44144667 :   settyp(z, t_POL); z[1]=evalsigne(l-2!=0)|z[1]; return z;
     100             : }
     101             : 
     102             : /*Flx_to_Flv=zx_to_zv*/
     103             : GEN
     104    20445951 : Flx_to_Flv(GEN x, long N)
     105             : {
     106             :   long i, l;
     107    20445951 :   GEN z = cgetg(N+1,t_VECSMALL);
     108    20441765 :   if (typ(x) != t_VECSMALL) pari_err_TYPE("Flx_to_Flv",x);
     109    20446347 :   l = lg(x)-1; x++;
     110    20446347 :   for (i=1; i<l ; i++) z[i]=x[i];
     111    20446347 :   for (   ; i<=N; i++) z[i]=0;
     112    20446347 :   return z;
     113             : }
     114             : 
     115             : /*Flv_to_Flx=zv_to_zx*/
     116             : GEN
     117     9298422 : Flv_to_Flx(GEN x, long sv)
     118             : {
     119     9298422 :   long i, l=lg(x)+1;
     120     9298422 :   GEN z = cgetg(l,t_VECSMALL); z[1]=sv;
     121     9297471 :   x--;
     122     9297471 :   for (i=2; i<l ; i++) z[i]=x[i];
     123     9297471 :   return Flx_renormalize(z,l);
     124             : }
     125             : 
     126             : /*Flm_to_FlxV=zm_to_zxV*/
     127             : GEN
     128        1596 : Flm_to_FlxV(GEN x, long sv)
     129        1596 : { pari_APPLY_type(t_VEC, Flv_to_Flx(gel(x,i), sv)) }
     130             : 
     131             : /*FlxC_to_ZXC=zxC_to_ZXC*/
     132             : GEN
     133       45813 : FlxC_to_ZXC(GEN x)
     134       45813 : { pari_APPLY_type(t_COL, Flx_to_ZX(gel(x,i))) }
     135             : 
     136             : /*FlxC_to_ZXC=zxV_to_ZXV*/
     137             : GEN
     138      162039 : FlxV_to_ZXV(GEN x)
     139      162039 : { pari_APPLY_type(t_VEC, Flx_to_ZX(gel(x,i))) }
     140             : 
     141             : /*FlxM_to_ZXM=zxM_to_ZXM*/
     142             : GEN
     143        5043 : FlxM_to_ZXM(GEN x)
     144        5043 : { pari_APPLY_same(FlxC_to_ZXC(gel(x,i))) }
     145             : 
     146             : GEN
     147           0 : FlxM_Flx_add_shallow(GEN x, GEN y, ulong p)
     148             : {
     149           0 :   long l = lg(x), i, j;
     150           0 :   GEN z = cgetg(l,t_MAT);
     151             : 
     152           0 :   if (l==1) return z;
     153           0 :   if (l != lgcols(x)) pari_err_OP( "+", x, y);
     154           0 :   for (i=1; i<l; i++)
     155             :   {
     156           0 :     GEN zi = cgetg(l,t_COL), xi = gel(x,i);
     157           0 :     gel(z,i) = zi;
     158           0 :     for (j=1; j<l; j++) gel(zi,j) = gel(xi,j);
     159           0 :     gel(zi,i) = Flx_add(gel(zi,i), y, p);
     160             :   }
     161           0 :   return z;
     162             : }
     163             : 
     164             : /***********************************************************************/
     165             : /**                                                                   **/
     166             : /**          Conversion to Flx                                        **/
     167             : /**                                                                   **/
     168             : /***********************************************************************/
     169             : /* Take an integer and return a scalar polynomial mod p,  with evalvarn=vs */
     170             : GEN
     171     9627185 : Fl_to_Flx(ulong x, long sv)
     172             : {
     173     9627185 :   return x? mkvecsmall2(sv, x): pol0_Flx(sv);
     174             : }
     175             : 
     176             : /* a X^d */
     177             : GEN
     178      190581 : monomial_Flx(ulong a, long d, long vs)
     179             : {
     180             :   GEN P;
     181      190581 :   if (a==0) return pol0_Flx(vs);
     182      190581 :   P = const_vecsmall(d+2, 0);
     183      190593 :   P[1] = vs; P[d+2] = a;
     184      190593 :   return P;
     185             : }
     186             : 
     187             : GEN
     188     1098974 : Z_to_Flx(GEN x, ulong p, long sv)
     189             : {
     190     1098974 :   long u = umodiu(x,p);
     191     1099404 :   return u? mkvecsmall2(sv, u): pol0_Flx(sv);
     192             : }
     193             : 
     194             : /* return x[0 .. dx] mod p as t_VECSMALL. Assume x a t_POL*/
     195             : GEN
     196   192706146 : ZX_to_Flx(GEN x, ulong p)
     197             : {
     198   192706146 :   long i, lx = lg(x);
     199   192706146 :   GEN a = cgetg(lx, t_VECSMALL);
     200   192705711 :   a[1]=((ulong)x[1])&VARNBITS;
     201   192705711 :   for (i=2; i<lx; i++) a[i] = umodiu(gel(x,i), p);
     202   192707686 :   return Flx_renormalize(a,lx);
     203             : }
     204             : 
     205             : /* return x[0 .. dx] mod p as t_VECSMALL. Assume x a t_POL*/
     206             : GEN
     207     3712336 : zx_to_Flx(GEN x, ulong p)
     208             : {
     209     3712336 :   long i, lx = lg(x);
     210     3712336 :   GEN a = cgetg(lx, t_VECSMALL);
     211     3712336 :   a[1] = x[1];
     212     3712336 :   for (i=2; i<lx; i++) uel(a,i) = umodsu(x[i], p);
     213     3712336 :   return Flx_renormalize(a,lx);
     214             : }
     215             : 
     216             : ulong
     217    47061215 : Rg_to_Fl(GEN x, ulong p)
     218             : {
     219    47061215 :   switch(typ(x))
     220             :   {
     221    24186571 :     case t_INT: return umodiu(x, p);
     222             :     case t_FRAC: {
     223       32752 :       ulong z = umodiu(gel(x,1), p);
     224       32752 :       if (!z) return 0;
     225       30050 :       return Fl_div(z, umodiu(gel(x,2), p), p);
     226             :     }
     227          49 :     case t_PADIC: return padic_to_Fl(x, p);
     228             :     case t_INTMOD: {
     229    22841843 :       GEN q = gel(x,1), a = gel(x,2);
     230    22841843 :       if (absequaliu(q, p)) return itou(a);
     231           0 :       if (!dvdiu(q,p)) pari_err_MODULUS("Rg_to_Fl", q, utoi(p));
     232           0 :       return umodiu(a, p);
     233             :     }
     234           0 :     default: pari_err_TYPE("Rg_to_Fl",x);
     235             :       return 0; /* LCOV_EXCL_LINE */
     236             :   }
     237             : }
     238             : 
     239             : ulong
     240     1098557 : Rg_to_F2(GEN x)
     241             : {
     242     1098557 :   switch(typ(x))
     243             :   {
     244      256114 :     case t_INT: return mpodd(x);
     245             :     case t_FRAC:
     246           0 :       if (!mpodd(gel(x,2))) (void)Fl_inv(0,2); /* error */
     247           0 :       return mpodd(gel(x,1));
     248             :     case t_PADIC:
     249           0 :       if (!absequaliu(gel(x,2),2)) pari_err_OP("",x, mkintmodu(1,2));
     250           0 :       if (valp(x) < 0) (void)Fl_inv(0,2);
     251           0 :       return valp(x) & 1;
     252             :     case t_INTMOD: {
     253      842443 :       GEN q = gel(x,1), a = gel(x,2);
     254      842443 :       if (mpodd(q)) pari_err_MODULUS("Rg_to_F2", q, gen_2);
     255      842443 :       return mpodd(a);
     256             :     }
     257           0 :     default: pari_err_TYPE("Rg_to_F2",x);
     258             :       return 0; /* LCOV_EXCL_LINE */
     259             :   }
     260             : }
     261             : 
     262             : GEN
     263     1933283 : RgX_to_Flx(GEN x, ulong p)
     264             : {
     265     1933283 :   long i, lx = lg(x);
     266     1933283 :   GEN a = cgetg(lx, t_VECSMALL);
     267     1933283 :   a[1]=((ulong)x[1])&VARNBITS;
     268     1933283 :   for (i=2; i<lx; i++) a[i] = Rg_to_Fl(gel(x,i), p);
     269     1933283 :   return Flx_renormalize(a,lx);
     270             : }
     271             : 
     272             : /* If x is a POLMOD, assume modulus is a multiple of T. */
     273             : GEN
     274     1813300 : Rg_to_Flxq(GEN x, GEN T, ulong p)
     275             : {
     276     1813300 :   long ta, tx = typ(x), v = T[1];
     277             :   GEN a, b;
     278     1813300 :   if (is_const_t(tx))
     279             :   {
     280     1748935 :     if (tx == t_FFELT) return FF_to_Flxq(x);
     281     1022767 :     return Fl_to_Flx(Rg_to_Fl(x, p), v);
     282             :   }
     283       64365 :   switch(tx)
     284             :   {
     285             :     case t_POLMOD:
     286         707 :       b = gel(x,1);
     287         707 :       a = gel(x,2); ta = typ(a);
     288         707 :       if (is_const_t(ta)) return Fl_to_Flx(Rg_to_Fl(a, p), v);
     289         609 :       b = RgX_to_Flx(b, p); if (b[1] != v) break;
     290         609 :       a = RgX_to_Flx(a, p); if (Flx_equal(b,T)) return a;
     291           0 :       if (lgpol(Flx_rem(b,T,p))==0) return Flx_rem(a, T, p);
     292           0 :       break;
     293             :     case t_POL:
     294       63658 :       x = RgX_to_Flx(x,p);
     295       63658 :       if (x[1] != v) break;
     296       63658 :       return Flx_rem(x, T, p);
     297             :     case t_RFRAC:
     298           0 :       a = Rg_to_Flxq(gel(x,1), T,p);
     299           0 :       b = Rg_to_Flxq(gel(x,2), T,p);
     300           0 :       return Flxq_div(a,b, T,p);
     301             :   }
     302           0 :   pari_err_TYPE("Rg_to_Flxq",x);
     303             :   return NULL; /* LCOV_EXCL_LINE */
     304             : }
     305             : 
     306             : /***********************************************************************/
     307             : /**                                                                   **/
     308             : /**          Basic operation on Flx                                   **/
     309             : /**                                                                   **/
     310             : /***********************************************************************/
     311             : /* = zx_renormalize. Similar to normalizepol, in place */
     312             : GEN
     313  1388736757 : Flx_renormalize(GEN /*in place*/ x, long lx)
     314             : {
     315             :   long i;
     316  1619344182 :   for (i = lx-1; i>1; i--)
     317  1570789703 :     if (x[i]) break;
     318  1388736757 :   stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
     319  1387358724 :   setlg(x, i+1); return x;
     320             : }
     321             : 
     322             : GEN
     323      290397 : Flx_red(GEN z, ulong p)
     324             : {
     325      290397 :   long i, l = lg(z);
     326      290397 :   GEN x = cgetg(l, t_VECSMALL);
     327      291109 :   x[1] = z[1];
     328      291109 :   for (i=2; i<l; i++) x[i] = uel(z,i)%p;
     329      291109 :   return Flx_renormalize(x,l);
     330             : }
     331             : 
     332             : GEN
     333     1106574 : random_Flx(long d1, long vs, ulong p)
     334             : {
     335     1106574 :   long i, d = d1+2;
     336     1106574 :   GEN y = cgetg(d,t_VECSMALL); y[1] = vs;
     337     1106280 :   for (i=2; i<d; i++) y[i] = random_Fl(p);
     338     1106629 :   return Flx_renormalize(y,d);
     339             : }
     340             : 
     341             : static GEN
     342        6837 : Flx_addspec(GEN x, GEN y, ulong p, long lx, long ly)
     343             : {
     344             :   long i,lz;
     345             :   GEN z;
     346             : 
     347        6837 :   if (ly>lx) swapspec(x,y, lx,ly);
     348        6837 :   lz = lx+2; z = cgetg(lz, t_VECSMALL) + 2;
     349        6837 :   for (i=0; i<ly; i++) z[i] = Fl_add(x[i], y[i], p);
     350        6837 :   for (   ; i<lx; i++) z[i] = x[i];
     351        6837 :   z -= 2; return Flx_renormalize(z, lz);
     352             : }
     353             : 
     354             : GEN
     355    31973542 : Flx_add(GEN x, GEN y, ulong p)
     356             : {
     357             :   long i,lz;
     358             :   GEN z;
     359    31973542 :   long lx=lg(x);
     360    31973542 :   long ly=lg(y);
     361    31973542 :   if (ly>lx) swapspec(x,y, lx,ly);
     362    31973542 :   lz = lx; z = cgetg(lz, t_VECSMALL); z[1]=x[1];
     363    31998957 :   for (i=2; i<ly; i++) z[i] = Fl_add(x[i], y[i], p);
     364    31978240 :   for (   ; i<lx; i++) z[i] = x[i];
     365    31978240 :   return Flx_renormalize(z, lz);
     366             : }
     367             : 
     368             : GEN
     369     7328573 : Flx_Fl_add(GEN y, ulong x, ulong p)
     370             : {
     371             :   GEN z;
     372             :   long lz, i;
     373     7328573 :   if (!lgpol(y))
     374      274027 :     return Fl_to_Flx(x,y[1]);
     375     7055229 :   lz=lg(y);
     376     7055229 :   z=cgetg(lz,t_VECSMALL);
     377     7053050 :   z[1]=y[1];
     378     7053050 :   z[2] = Fl_add(y[2],x,p);
     379    39277007 :   for(i=3;i<lz;i++)
     380    32223559 :     z[i] = y[i];
     381     7053448 :   if (lz==3) z = Flx_renormalize(z,lz);
     382     7053520 :   return z;
     383             : }
     384             : 
     385             : static GEN
     386     2082490 : Flx_subspec(GEN x, GEN y, ulong p, long lx, long ly)
     387             : {
     388             :   long i,lz;
     389             :   GEN z;
     390             : 
     391     2082490 :   if (ly <= lx)
     392             :   {
     393     2082490 :     lz = lx+2; z = cgetg(lz, t_VECSMALL)+2;
     394     2083332 :     for (i=0; i<ly; i++) z[i] = Fl_sub(x[i],y[i],p);
     395     2082498 :     for (   ; i<lx; i++) z[i] = x[i];
     396             :   }
     397             :   else
     398             :   {
     399           0 :     lz = ly+2; z = cgetg(lz, t_VECSMALL)+2;
     400           0 :     for (i=0; i<lx; i++) z[i] = Fl_sub(x[i],y[i],p);
     401           0 :     for (   ; i<ly; i++) z[i] = Fl_neg(y[i],p);
     402             :   }
     403     2082498 :  return Flx_renormalize(z-2, lz);
     404             : }
     405             : 
     406             : GEN
     407    71424540 : Flx_sub(GEN x, GEN y, ulong p)
     408             : {
     409    71424540 :   long i,lz,lx = lg(x), ly = lg(y);
     410             :   GEN z;
     411             : 
     412    71424540 :   if (ly <= lx)
     413             :   {
     414    39221724 :     lz = lx; z = cgetg(lz, t_VECSMALL);
     415    39219042 :     for (i=2; i<ly; i++) z[i] = Fl_sub(x[i],y[i],p);
     416    39216664 :     for (   ; i<lx; i++) z[i] = x[i];
     417             :   }
     418             :   else
     419             :   {
     420    32202816 :     lz = ly; z = cgetg(lz, t_VECSMALL);
     421    32202562 :     for (i=2; i<lx; i++) z[i] = Fl_sub(x[i],y[i],p);
     422    32202286 :     for (   ; i<ly; i++) z[i] = y[i]? (long)(p - y[i]): y[i];
     423             :   }
     424    71418950 :   z[1]=x[1]; return Flx_renormalize(z, lz);
     425             : }
     426             : 
     427             : static GEN
     428     1078392 : Flx_negspec(GEN x, ulong p, long l)
     429             : {
     430             :   long i;
     431     1078392 :   GEN z = cgetg(l+2, t_VECSMALL) + 2;
     432     1078661 :   for (i=0; i<l; i++) z[i] = Fl_neg(x[i], p);
     433     1078623 :   return z-2;
     434             : }
     435             : 
     436             : 
     437             : GEN
     438     1078363 : Flx_neg(GEN x, ulong p)
     439             : {
     440     1078363 :   GEN z = Flx_negspec(x+2, p, lgpol(x));
     441     1078619 :   z[1] = x[1];
     442     1078619 :   return z;
     443             : }
     444             : 
     445             : GEN
     446        1531 : Flx_neg_inplace(GEN x, ulong p)
     447             : {
     448        1531 :   long i, l = lg(x);
     449      360208 :   for (i=2; i<l; i++)
     450      358677 :     if (x[i]) x[i] = p - x[i];
     451        1531 :   return x;
     452             : }
     453             : 
     454             : GEN
     455     1877679 : Flx_double(GEN y, ulong p)
     456             : {
     457             :   long i, l;
     458     1877679 :   GEN z = cgetg_copy(y, &l); z[1] = y[1];
     459     1877679 :   for(i=2; i<l; i++) z[i] = Fl_double(y[i], p);
     460     1877679 :   return Flx_renormalize(z, l);
     461             : }
     462             : GEN
     463      675223 : Flx_triple(GEN y, ulong p)
     464             : {
     465             :   long i, l;
     466      675223 :   GEN z = cgetg_copy(y, &l); z[1] = y[1];
     467      675223 :   for(i=2; i<l; i++) z[i] = Fl_triple(y[i], p);
     468      675223 :   return Flx_renormalize(z, l);
     469             : }
     470             : GEN
     471     9161264 : Flx_Fl_mul(GEN y, ulong x, ulong p)
     472             : {
     473             :   GEN z;
     474             :   long i, l;
     475     9161264 :   if (!x) return pol0_Flx(y[1]);
     476     8570499 :   z = cgetg_copy(y, &l); z[1] = y[1];
     477     8569919 :   if (HIGHWORD(x | p))
     478      201870 :     for(i=2; i<l; i++) z[i] = Fl_mul(y[i], x, p);
     479             :   else
     480     8368049 :     for(i=2; i<l; i++) z[i] = (y[i] * x) % p;
     481     8569922 :   return Flx_renormalize(z, l);
     482             : }
     483             : GEN
     484     7714119 : Flx_Fl_mul_to_monic(GEN y, ulong x, ulong p)
     485             : {
     486             :   GEN z;
     487             :   long i, l;
     488     7714119 :   z = cgetg_copy(y, &l); z[1] = y[1];
     489     7711168 :   if (HIGHWORD(x | p))
     490     3120613 :     for(i=2; i<l-1; i++) z[i] = Fl_mul(y[i], x, p);
     491             :   else
     492     4590555 :     for(i=2; i<l-1; i++) z[i] = (y[i] * x) % p;
     493     7711151 :   z[l-1] = 1; return z;
     494             : }
     495             : 
     496             : /* Return a*x^n if n>=0 and a\x^(-n) if n<0 */
     497             : GEN
     498     5482134 : Flx_shift(GEN a, long n)
     499             : {
     500     5482134 :   long i, l = lg(a);
     501             :   GEN  b;
     502     5482134 :   if (l==2 || !n) return Flx_copy(a);
     503     5458607 :   if (l+n<=2) return pol0_Flx(a[1]);
     504     5456835 :   b = cgetg(l+n, t_VECSMALL);
     505     5456073 :   b[1] = a[1];
     506     5456073 :   if (n < 0)
     507     1607468 :     for (i=2-n; i<l; i++) b[i+n] = a[i];
     508             :   else
     509             :   {
     510     3848605 :     for (i=0; i<n; i++) b[2+i] = 0;
     511     3848605 :     for (i=2; i<l; i++) b[i+n] = a[i];
     512             :   }
     513     5456073 :   return b;
     514             : }
     515             : 
     516             : GEN
     517    38463825 : Flx_normalize(GEN z, ulong p)
     518             : {
     519    38463825 :   long l = lg(z)-1;
     520    38463825 :   ulong p1 = z[l]; /* leading term */
     521    38463825 :   if (p1 == 1) return z;
     522     7708392 :   return Flx_Fl_mul_to_monic(z, Fl_inv(p1,p), p);
     523             : }
     524             : 
     525             : /* return (x * X^d) + y. Assume d > 0, x > 0 and y >= 0 */
     526             : static GEN
     527        3542 : Flx_addshift(GEN x, GEN y, ulong p, long d)
     528             : {
     529        3542 :   GEN xd,yd,zd = (GEN)avma;
     530        3542 :   long a,lz,ny = lgpol(y), nx = lgpol(x);
     531        3542 :   long vs = x[1];
     532             : 
     533        3542 :   x += 2; y += 2; a = ny-d;
     534        3542 :   if (a <= 0)
     535             :   {
     536           7 :     lz = (a>nx)? ny+2: nx+d+2;
     537           7 :     (void)new_chunk(lz); xd = x+nx; yd = y+ny;
     538           7 :     while (xd > x) *--zd = *--xd;
     539           7 :     x = zd + a;
     540           7 :     while (zd > x) *--zd = 0;
     541             :   }
     542             :   else
     543             :   {
     544        3535 :     xd = new_chunk(d); yd = y+d;
     545        3535 :     x = Flx_addspec(x,yd,p, nx,a);
     546        3535 :     lz = (a>nx)? ny+2: lg(x)+d;
     547        3535 :     x += 2; while (xd > x) *--zd = *--xd;
     548             :   }
     549        3542 :   while (yd > y) *--zd = *--yd;
     550        3542 :   *--zd = vs;
     551        3542 :   *--zd = evaltyp(t_VECSMALL) | evallg(lz); return zd;
     552             : }
     553             : 
     554             : /* shift polynomial + gerepile */
     555             : /* Do not set evalvarn*/
     556             : static GEN
     557   427197609 : Flx_shiftip(pari_sp av, GEN x, long v)
     558             : {
     559   427197609 :   long i, lx = lg(x), ly;
     560             :   GEN y;
     561   427197609 :   if (!v || lx==2) return gerepileuptoleaf(av, x);
     562    98326367 :   ly = lx + v; /* result length */
     563    98326367 :   (void)new_chunk(ly); /* check that result fits */
     564    98514730 :   x += lx; y = (GEN)av;
     565    98514730 :   for (i = 2; i<lx; i++) *--y = *--x;
     566    98514730 :   for (i = 0; i< v; i++) *--y = 0;
     567    98514730 :   y -= 2; y[0] = evaltyp(t_VECSMALL) | evallg(ly);
     568    98434892 :   set_avma((pari_sp)y); return y;
     569             : }
     570             : 
     571             : #define BITS_IN_QUARTULONG (BITS_IN_HALFULONG >> 1)
     572             : #define QUARTMASK ((1UL<<BITS_IN_QUARTULONG)-1UL)
     573             : #define LLQUARTWORD(x) ((x) & QUARTMASK)
     574             : #define HLQUARTWORD(x) (((x) >> BITS_IN_QUARTULONG) & QUARTMASK)
     575             : #define LHQUARTWORD(x) (((x) >> (2*BITS_IN_QUARTULONG)) & QUARTMASK)
     576             : #define HHQUARTWORD(x) (((x) >> (3*BITS_IN_QUARTULONG)) & QUARTMASK)
     577             : INLINE long
     578   469912315 : maxlengthcoeffpol(ulong p, long n)
     579             : {
     580   469912315 :   pari_sp ltop = avma;
     581   469912315 :   GEN z = muliu(sqru(p-1), n);
     582   470516681 :   long l = lgefint(z);
     583   470516681 :   set_avma(ltop);
     584   469772793 :   if (l==3 && HIGHWORD(z[2])==0)
     585             :   {
     586   145770823 :     if (HLQUARTWORD(z[2]) == 0) return -1;
     587    43673593 :     else return 0;
     588             :   }
     589   324001970 :   return l-2;
     590             : }
     591             : 
     592             : INLINE ulong
     593   669863845 : Flx_mullimb_ok(GEN x, GEN y, ulong p, long a, long b)
     594             : { /* Assume OK_ULONG*/
     595   669863845 :   ulong p1 = 0;
     596             :   long i;
     597  2145330184 :   for (i=a; i<b; i++)
     598  1475466339 :     if (y[i])
     599             :     {
     600  1354482805 :       p1 += y[i] * x[-i];
     601  1354482805 :       if (p1 & HIGHBIT) p1 %= p;
     602             :     }
     603   669863845 :   return p1 % p;
     604             : }
     605             : 
     606             : INLINE ulong
     607   755090590 : Flx_mullimb(GEN x, GEN y, ulong p, ulong pi, long a, long b)
     608             : {
     609   755090590 :   ulong p1 = 0;
     610             :   long i;
     611  2379588105 :   for (i=a; i<b; i++)
     612  1623839129 :     if (y[i])
     613  1577708693 :       p1 = Fl_addmul_pre(p1, y[i], x[-i], p, pi);
     614   755748976 :   return p1;
     615             : }
     616             : 
     617             : /* assume nx >= ny > 0 */
     618             : static GEN
     619   183056848 : Flx_mulspec_basecase(GEN x, GEN y, ulong p, long nx, long ny)
     620             : {
     621             :   long i,lz,nz;
     622             :   GEN z;
     623             : 
     624   183056848 :   lz = nx+ny+1; nz = lz-2;
     625   183056848 :   z = cgetg(lz, t_VECSMALL) + 2; /* x:y:z [i] = term of degree i */
     626   183037647 :   if (SMALL_ULONG(p))
     627             :   {
     628   102543450 :     for (i=0; i<ny; i++)z[i] = Flx_mullimb_ok(x+i,y,p,0,i+1);
     629   102629145 :     for (  ; i<nx; i++) z[i] = Flx_mullimb_ok(x+i,y,p,0,ny);
     630   102731114 :     for (  ; i<nz; i++) z[i] = Flx_mullimb_ok(x+i,y,p,i-nx+1,ny);
     631             :   }
     632             :   else
     633             :   {
     634    80494197 :     ulong pi = get_Fl_red(p);
     635    80435545 :     for (i=0; i<ny; i++)z[i] = Flx_mullimb(x+i,y,p,pi,0,i+1);
     636    80634908 :     for (  ; i<nx; i++) z[i] = Flx_mullimb(x+i,y,p,pi,0,ny);
     637    80646551 :     for (  ; i<nz; i++) z[i] = Flx_mullimb(x+i,y,p,pi,i-nx+1,ny);
     638             :   }
     639   183422760 :   z -= 2; return Flx_renormalize(z, lz);
     640             : }
     641             : 
     642             : static GEN
     643    49230971 : int_to_Flx(GEN z, ulong p)
     644             : {
     645    49230971 :   long i, l = lgefint(z);
     646    49230971 :   GEN x = cgetg(l, t_VECSMALL);
     647    49172233 :   for (i=2; i<l; i++) x[i] = uel(z,i)%p;
     648    49172233 :   return Flx_renormalize(x, l);
     649             : }
     650             : 
     651             : INLINE GEN
     652     5862322 : Flx_mulspec_mulii(GEN a, GEN b, ulong p, long na, long nb)
     653             : {
     654     5862322 :   GEN z=muliispec(a,b,na,nb);
     655     5872595 :   return int_to_Flx(z,p);
     656             : }
     657             : 
     658             : static GEN
     659    30148694 : Flx_to_int_halfspec(GEN a, long na)
     660             : {
     661             :   long j;
     662    30148694 :   long n = (na+1)>>1UL;
     663    30148694 :   GEN V = cgetipos(2+n);
     664             :   GEN w;
     665   285351228 :   for (w = int_LSW(V), j=0; j+1<na; j+=2, w=int_nextW(w))
     666   255196476 :     *w = a[j]|(a[j+1]<<BITS_IN_HALFULONG);
     667    30154752 :   if (j<na)
     668    21389399 :     *w = a[j];
     669    30154752 :   return V;
     670             : }
     671             : 
     672             : static GEN
     673    21358085 : int_to_Flx_half(GEN z, ulong p)
     674             : {
     675             :   long i;
     676    21358085 :   long lx = (lgefint(z)-2)*2+2;
     677    21358085 :   GEN w, x = cgetg(lx, t_VECSMALL);
     678   325120314 :   for (w = int_LSW(z), i=2; i<lx; i+=2, w=int_nextW(w))
     679             :   {
     680   303761022 :     x[i]   = LOWWORD((ulong)*w)%p;
     681   303761022 :     x[i+1] = HIGHWORD((ulong)*w)%p;
     682             :   }
     683    21359292 :   return Flx_renormalize(x, lx);
     684             : }
     685             : 
     686             : static GEN
     687     8845528 : Flx_mulspec_halfmulii(GEN a, GEN b, ulong p, long na, long nb)
     688             : {
     689     8845528 :   GEN A = Flx_to_int_halfspec(a,na);
     690     8846954 :   GEN B = Flx_to_int_halfspec(b,nb);
     691     8847441 :   GEN z = mulii(A,B);
     692     8851485 :   return int_to_Flx_half(z,p);
     693             : }
     694             : 
     695             : static GEN
     696    81264070 : Flx_to_int_quartspec(GEN a, long na)
     697             : {
     698             :   long j;
     699    81264070 :   long n = (na+3)>>2UL;
     700    81264070 :   GEN V = cgetipos(2+n);
     701             :   GEN w;
     702   267756785 :   for (w = int_LSW(V), j=0; j+3<na; j+=4, w=int_nextW(w))
     703   186495548 :     *w = a[j]|(a[j+1]<<BITS_IN_QUARTULONG)|(a[j+2]<<(2*BITS_IN_QUARTULONG))|(a[j+3]<<(3*BITS_IN_QUARTULONG));
     704    81261237 :   switch (na-j)
     705             :   {
     706             :   case 3:
     707    24936190 :     *w = a[j]|(a[j+1]<<BITS_IN_QUARTULONG)|(a[j+2]<<(2*BITS_IN_QUARTULONG));
     708    24936190 :     break;
     709             :   case 2:
     710    24353977 :     *w = a[j]|(a[j+1]<<BITS_IN_QUARTULONG);
     711    24353977 :     break;
     712             :   case 1:
     713    21780937 :     *w = a[j];
     714    21780937 :     break;
     715             :   case 0:
     716    10230575 :     break;
     717             :   }
     718    81261237 :   return V;
     719             : }
     720             : 
     721             : static GEN
     722    45949913 : int_to_Flx_quart(GEN z, ulong p)
     723             : {
     724             :   long i;
     725    45949913 :   long lx = (lgefint(z)-2)*4+2;
     726    45949913 :   GEN w, x = cgetg(lx, t_VECSMALL);
     727   294632897 :   for (w = int_LSW(z), i=2; i<lx; i+=4, w=int_nextW(w))
     728             :   {
     729   248675774 :     x[i]   = LLQUARTWORD((ulong)*w)%p;
     730   248675774 :     x[i+1] = HLQUARTWORD((ulong)*w)%p;
     731   248675774 :     x[i+2] = LHQUARTWORD((ulong)*w)%p;
     732   248675774 :     x[i+3] = HHQUARTWORD((ulong)*w)%p;
     733             :   }
     734    45957123 :   return Flx_renormalize(x, lx);
     735             : }
     736             : 
     737             : static GEN
     738    35339986 : Flx_mulspec_quartmulii(GEN a, GEN b, ulong p, long na, long nb)
     739             : {
     740    35339986 :   GEN A = Flx_to_int_quartspec(a,na);
     741    35341854 :   GEN B = Flx_to_int_quartspec(b,nb);
     742    35340314 :   GEN z = mulii(A,B);
     743    35341831 :   return int_to_Flx_quart(z,p);
     744             : }
     745             : 
     746             : /*Eval x in 2^(k*BIL) in linear time, k==2 or 3*/
     747             : static GEN
     748    39176892 : Flx_eval2BILspec(GEN x, long k, long l)
     749             : {
     750    39176892 :   long i, lz = k*l, ki;
     751    39176892 :   GEN pz = cgetipos(2+lz);
     752   819502288 :   for (i=0; i < lz; i++)
     753   780045909 :     *int_W(pz,i) = 0UL;
     754   431914197 :   for (i=0, ki=0; i<l; i++, ki+=k)
     755   392457818 :     *int_W(pz,ki) = x[i];
     756    39456379 :   return int_normalize(pz,0);
     757             : }
     758             : 
     759             : static GEN
     760    29218605 : Z_mod2BIL_Flx_2(GEN x, long d, ulong p)
     761             : {
     762    29218605 :   long i, offset, lm = lgefint(x)-2, l = d+3;
     763    29218605 :   ulong pi = get_Fl_red(p);
     764    29163340 :   GEN pol = cgetg(l, t_VECSMALL);
     765    29059607 :   pol[1] = 0;
     766   550489638 :   for (i=0, offset=0; offset+1 < lm; i++, offset += 2)
     767   521442248 :     pol[i+2] = remll_pre(*int_W(x,offset+1), *int_W(x,offset), p, pi);
     768    29047390 :   if (offset < lm)
     769    21014848 :     pol[i+2] = (*int_W(x,offset)) % p;
     770    29047390 :   return Flx_renormalize(pol,l);
     771             : }
     772             : 
     773             : static GEN
     774       11289 : Z_mod2BIL_Flx_3(GEN x, long d, ulong p)
     775             : {
     776       11289 :   long i, offset, lm = lgefint(x)-2, l = d+3;
     777       11289 :   ulong pi = get_Fl_red(p);
     778       11289 :   GEN pol = cgetg(l, t_VECSMALL);
     779       11289 :   pol[1] = 0;
     780     4310250 :   for (i=0, offset=0; offset+2 < lm; i++, offset += 3)
     781     8597922 :     pol[i+2] = remlll_pre(*int_W(x,offset+2), *int_W(x,offset+1),
     782     4298961 :                           *int_W(x,offset), p, pi);
     783       11289 :   if (offset+1 < lm)
     784        9537 :     pol[i+2] = remll_pre(*int_W(x,offset+1), *int_W(x,offset), p, pi);
     785        1752 :   else if (offset < lm)
     786        1752 :     pol[i+2] = (*int_W(x,offset)) % p;
     787       11289 :   return Flx_renormalize(pol,l);
     788             : }
     789             : 
     790             : static GEN
     791    29250655 : Z_mod2BIL_Flx(GEN x, long bs, long d, ulong p)
     792             : {
     793    29250655 :   return bs==2 ? Z_mod2BIL_Flx_2(x, d, p): Z_mod2BIL_Flx_3(x, d, p);
     794             : }
     795             : 
     796             : static GEN
     797    10175094 : Flx_mulspec_mulii_inflate(GEN x, GEN y, long N, ulong p, long nx, long ny)
     798             : {
     799    10175094 :   pari_sp av = avma;
     800    10175094 :   GEN z = mulii(Flx_eval2BILspec(x,N,nx), Flx_eval2BILspec(y,N,ny));
     801    10231500 :   return gerepileupto(av, Z_mod2BIL_Flx(z, N, nx+ny-2, p));
     802             : }
     803             : 
     804             : /* fast product (Karatsuba) of polynomials a,b. These are not real GENs, a+2,
     805             :  * b+2 were sent instead. na, nb = number of terms of a, b.
     806             :  * Only c, c0, c1, c2 are genuine GEN.
     807             :  */
     808             : static GEN
     809   260923297 : Flx_mulspec(GEN a, GEN b, ulong p, long na, long nb)
     810             : {
     811             :   GEN a0,c,c0;
     812   260923297 :   long n0, n0a, i, v = 0;
     813             :   pari_sp av;
     814             : 
     815   260923297 :   while (na && !a[0]) { a++; na--; v++; }
     816   260923297 :   while (nb && !b[0]) { b++; nb--; v++; }
     817   260923297 :   if (na < nb) swapspec(a,b, na,nb);
     818   260923297 :   if (!nb) return pol0_Flx(0);
     819             : 
     820   243322033 :   av = avma;
     821   243322033 :   switch (maxlengthcoeffpol(p,nb))
     822             :   {
     823             :   case -1:
     824    68447452 :     if (na>=Flx_MUL_QUARTMULII_LIMIT)
     825    35333237 :       return Flx_shiftip(av,Flx_mulspec_quartmulii(a,b,p,na,nb), v);
     826    33114215 :     break;
     827             :   case 0:
     828    19324635 :     if (na>=Flx_MUL_HALFMULII_LIMIT)
     829     8845369 :       return Flx_shiftip(av,Flx_mulspec_halfmulii(a,b,p,na,nb), v);
     830    10479266 :     break;
     831             :   case 1:
     832    66739144 :     if (na>=Flx_MUL_MULII_LIMIT)
     833     5863651 :       return Flx_shiftip(av,Flx_mulspec_mulii(a,b,p,na,nb), v);
     834    60875493 :     break;
     835             :   case 2:
     836    86201297 :     if (na>=Flx_MUL_MULII2_LIMIT)
     837    10167150 :       return Flx_shiftip(av,Flx_mulspec_mulii_inflate(a,b,2,p,na,nb), v);
     838    76034147 :     break;
     839             :   case 3:
     840     2588490 :     if (na>70)
     841        9066 :       return Flx_shiftip(av,Flx_mulspec_mulii_inflate(a,b,3,p,na,nb), v);
     842     2579424 :     break;
     843             :   }
     844   183060322 :   if (nb < Flx_MUL_KARATSUBA_LIMIT)
     845   183058791 :     return Flx_shiftip(av,Flx_mulspec_basecase(a,b,p,na,nb), v);
     846        1531 :   i=(na>>1); n0=na-i; na=i;
     847        1531 :   a0=a+n0; n0a=n0;
     848        1531 :   while (n0a && !a[n0a-1]) n0a--;
     849             : 
     850        1531 :   if (nb > n0)
     851             :   {
     852             :     GEN b0,c1,c2;
     853             :     long n0b;
     854             : 
     855        1531 :     nb -= n0; b0 = b+n0; n0b = n0;
     856        1531 :     while (n0b && !b[n0b-1]) n0b--;
     857        1531 :     c =  Flx_mulspec(a,b,p,n0a,n0b);
     858        1531 :     c0 = Flx_mulspec(a0,b0,p,na,nb);
     859             : 
     860        1531 :     c2 = Flx_addspec(a0,a,p,na,n0a);
     861        1531 :     c1 = Flx_addspec(b0,b,p,nb,n0b);
     862             : 
     863        1531 :     c1 = Flx_mul(c1,c2,p);
     864        1531 :     c2 = Flx_add(c0,c,p);
     865             : 
     866        1531 :     c2 = Flx_neg_inplace(c2,p);
     867        1531 :     c2 = Flx_add(c1,c2,p);
     868        1531 :     c0 = Flx_addshift(c0,c2 ,p, n0);
     869             :   }
     870             :   else
     871             :   {
     872           0 :     c  = Flx_mulspec(a,b,p,n0a,nb);
     873           0 :     c0 = Flx_mulspec(a0,b,p,na,nb);
     874             :   }
     875        1531 :   c0 = Flx_addshift(c0,c,p,n0);
     876        1531 :   return Flx_shiftip(av,c0, v);
     877             : }
     878             : 
     879             : 
     880             : GEN
     881   256435373 : Flx_mul(GEN x, GEN y, ulong p)
     882             : {
     883   256435373 :  GEN z = Flx_mulspec(x+2,y+2,p, lgpol(x),lgpol(y));
     884   256548959 :  z[1] = x[1]; return z;
     885             : }
     886             : 
     887             : static GEN
     888   101053483 : Flx_sqrspec_basecase(GEN x, ulong p, long nx)
     889             : {
     890             :   long i, lz, nz;
     891             :   ulong p1;
     892             :   GEN z;
     893             : 
     894   101053483 :   if (!nx) return pol0_Flx(0);
     895   101053483 :   lz = (nx << 1) + 1, nz = lz-2;
     896   101053483 :   z = cgetg(lz, t_VECSMALL) + 2;
     897   100785583 :   if (SMALL_ULONG(p))
     898             :   {
     899    55748228 :     z[0] = x[0]*x[0]%p;
     900   125212291 :     for (i=1; i<nx; i++)
     901             :     {
     902    69294303 :       p1 = Flx_mullimb_ok(x+i,x,p,0, (i+1)>>1);
     903    69464063 :       p1 <<= 1;
     904    69464063 :       if ((i&1) == 0) p1 += x[i>>1] * x[i>>1];
     905    69464063 :       z[i] = p1 % p;
     906             :     }
     907   126332603 :     for (  ; i<nz; i++)
     908             :     {
     909    69727347 :       p1 = Flx_mullimb_ok(x+i,x,p,i-nx+1, (i+1)>>1);
     910    70414615 :       p1 <<= 1;
     911    70414615 :       if ((i&1) == 0) p1 += x[i>>1] * x[i>>1];
     912    70414615 :       z[i] = p1 % p;
     913             :     }
     914             :   }
     915             :   else
     916             :   {
     917    45037355 :     ulong pi = get_Fl_red(p);
     918    45027993 :     z[0] = Fl_sqr_pre(x[0], p, pi);
     919   214367858 :     for (i=1; i<nx; i++)
     920             :     {
     921   169274976 :       p1 = Flx_mullimb(x+i,x,p,pi,0, (i+1)>>1);
     922   169390588 :       p1 = Fl_add(p1, p1, p);
     923   169325124 :       if ((i&1) == 0) p1 = Fl_add(p1, Fl_sqr_pre(x[i>>1], p, pi), p);
     924   169288679 :       z[i] = p1;
     925             :     }
     926   214430657 :     for (  ; i<nz; i++)
     927             :     {
     928   169323347 :       p1 = Flx_mullimb(x+i,x,p,pi,i-nx+1, (i+1)>>1);
     929   169507546 :       p1 = Fl_add(p1, p1, p);
     930   169470916 :       if ((i&1) == 0) p1 = Fl_add(p1, Fl_sqr_pre(x[i>>1], p, pi), p);
     931   169337775 :       z[i] = p1;
     932             :     }
     933             :   }
     934   101712566 :   z -= 2; return Flx_renormalize(z, lz);
     935             : }
     936             : 
     937             : static GEN
     938    43208618 : Flx_sqrspec_sqri(GEN a, ulong p, long na)
     939             : {
     940    43208618 :   GEN z=sqrispec(a,na);
     941    43588691 :   return int_to_Flx(z,p);
     942             : }
     943             : 
     944             : static GEN
     945    12299591 : Flx_sqrspec_halfsqri(GEN a, ulong p, long na)
     946             : {
     947    12299591 :   GEN z = sqri(Flx_to_int_halfspec(a,na));
     948    12301731 :   return int_to_Flx_half(z,p);
     949             : }
     950             : 
     951             : static GEN
     952    10609233 : Flx_sqrspec_quartsqri(GEN a, ulong p, long na)
     953             : {
     954    10609233 :   GEN z = sqri(Flx_to_int_quartspec(a,na));
     955    10610140 :   return int_to_Flx_quart(z,p);
     956             : }
     957             : 
     958             : static GEN
     959    18899143 : Flx_sqrspec_sqri_inflate(GEN x, long N, ulong p, long nx)
     960             : {
     961    18899143 :   pari_sp av = avma;
     962    18899143 :   GEN  z = sqri(Flx_eval2BILspec(x,N,nx));
     963    19038807 :   return gerepileupto(av, Z_mod2BIL_Flx(z, N, (nx-1)*2, p));
     964             : }
     965             : 
     966             : static GEN
     967   185365270 : Flx_sqrspec(GEN a, ulong p, long na)
     968             : {
     969             :   GEN a0, c, c0;
     970   185365270 :   long n0, n0a, i, v = 0;
     971             :   pari_sp av;
     972             : 
     973   185365270 :   while (na && !a[0]) { a++; na--; v += 2; }
     974   185365270 :   if (!na) return pol0_Flx(0);
     975             : 
     976   185272429 :   av = avma;
     977   185272429 :   switch(maxlengthcoeffpol(p,na))
     978             :   {
     979             :   case -1:
     980    21766118 :     if (na>=Flx_SQR_QUARTSQRI_LIMIT)
     981    10609277 :       return Flx_shiftip(av, Flx_sqrspec_quartsqri(a,p,na), v);
     982    11156841 :     break;
     983             :   case 0:
     984    19029293 :     if (na>=Flx_SQR_HALFSQRI_LIMIT)
     985    12297418 :       return Flx_shiftip(av, Flx_sqrspec_halfsqri(a,p,na), v);
     986     6731875 :     break;
     987             :   case 1:
     988    80012792 :     if (na>=Flx_SQR_SQRI_LIMIT)
     989    43231941 :       return Flx_shiftip(av, Flx_sqrspec_sqri(a,p,na), v);
     990    36780851 :     break;
     991             :   case 2:
     992    64117980 :     if (na>=Flx_SQR_SQRI2_LIMIT)
     993    18900877 :       return Flx_shiftip(av, Flx_sqrspec_sqri_inflate(a,2,p,na), v);
     994    45217103 :     break;
     995             :   case 3:
     996     1209577 :     if (na>70)
     997        2223 :       return Flx_shiftip(av, Flx_sqrspec_sqri_inflate(a,3,p,na), v);
     998     1207354 :     break;
     999             :   }
    1000   101068239 :   if (na < Flx_SQR_KARATSUBA_LIMIT)
    1001   101067999 :     return Flx_shiftip(av, Flx_sqrspec_basecase(a,p,na), v);
    1002         240 :   i=(na>>1); n0=na-i; na=i;
    1003         240 :   a0=a+n0; n0a=n0;
    1004         240 :   while (n0a && !a[n0a-1]) n0a--;
    1005             : 
    1006         240 :   c = Flx_sqrspec(a,p,n0a);
    1007         240 :   c0= Flx_sqrspec(a0,p,na);
    1008         240 :   if (p == 2) n0 *= 2;
    1009             :   else
    1010             :   {
    1011         240 :     GEN c1, t = Flx_addspec(a0,a,p,na,n0a);
    1012         240 :     t = Flx_sqr(t,p);
    1013         240 :     c1= Flx_add(c0,c, p);
    1014         240 :     c1= Flx_sub(t, c1, p);
    1015         240 :     c0 = Flx_addshift(c0,c1,p,n0);
    1016             :   }
    1017         240 :   c0 = Flx_addshift(c0,c,p,n0);
    1018         240 :   return Flx_shiftip(av,c0,v);
    1019             : }
    1020             : 
    1021             : GEN
    1022   185008226 : Flx_sqr(GEN x, ulong p)
    1023             : {
    1024   185008226 :   GEN z = Flx_sqrspec(x+2,p, lgpol(x));
    1025   186210609 :   z[1] = x[1]; return z;
    1026             : }
    1027             : 
    1028             : GEN
    1029        6124 : Flx_powu(GEN x, ulong n, ulong p)
    1030             : {
    1031        6124 :   GEN y = pol1_Flx(x[1]), z;
    1032             :   ulong m;
    1033        6118 :   if (n == 0) return y;
    1034        6118 :   m = n; z = x;
    1035             :   for (;;)
    1036             :   {
    1037       39032 :     if (m&1UL) y = Flx_mul(y,z, p);
    1038       22554 :     m >>= 1; if (!m) return y;
    1039       16438 :     z = Flx_sqr(z, p);
    1040             :   }
    1041             : }
    1042             : 
    1043             : GEN
    1044       13092 : Flx_halve(GEN y, ulong p)
    1045             : {
    1046             :   GEN z;
    1047             :   long i, l;
    1048       13092 :   z = cgetg_copy(y, &l); z[1] = y[1];
    1049       13092 :   for(i=2; i<l; i++) uel(z,i) = Fl_halve(uel(y,i), p);
    1050       13092 :   return z;
    1051             : }
    1052             : 
    1053             : static GEN
    1054     5259260 : Flx_recipspec(GEN x, long l, long n)
    1055             : {
    1056             :   long i;
    1057     5259260 :   GEN z=cgetg(n+2,t_VECSMALL)+2;
    1058   204856274 :   for(i=0; i<l; i++)
    1059   199596834 :     z[n-i-1] = x[i];
    1060     8012724 :   for(   ; i<n; i++)
    1061     2753284 :     z[n-i-1] = 0;
    1062     5259440 :   return Flx_renormalize(z-2,n+2);
    1063             : }
    1064             : 
    1065             : GEN
    1066           0 : Flx_recip(GEN x)
    1067             : {
    1068           0 :   GEN z=Flx_recipspec(x+2,lgpol(x),lgpol(x));
    1069           0 :   z[1]=x[1];
    1070           0 :   return z;
    1071             : }
    1072             : 
    1073             : /* Return h^degpol(P) P(x / h) */
    1074             : GEN
    1075        1086 : Flx_rescale(GEN P, ulong h, ulong p)
    1076             : {
    1077        1086 :   long i, l = lg(P);
    1078        1086 :   GEN Q = cgetg(l,t_VECSMALL);
    1079        1086 :   ulong hi = h;
    1080        1086 :   Q[l-1] = P[l-1];
    1081       12455 :   for (i=l-2; i>=2; i--)
    1082             :   {
    1083       12455 :     Q[i] = Fl_mul(P[i], hi, p);
    1084       12455 :     if (i == 2) break;
    1085       11369 :     hi = Fl_mul(hi,h, p);
    1086             :   }
    1087        1086 :   Q[1] = P[1]; return Q;
    1088             : }
    1089             : 
    1090             : static long
    1091    42762270 : Flx_multhreshold(GEN T, ulong p, long quart, long half, long mul, long mul2, long kara)
    1092             : {
    1093    42762270 :   long na = lgpol(T);
    1094    42761362 :   switch (maxlengthcoeffpol(p,na))
    1095             :   {
    1096             :   case -1:
    1097    12005555 :     if (na>=Flx_MUL_QUARTMULII_LIMIT)
    1098     6099126 :       return na>=quart;
    1099     5906429 :     break;
    1100             :   case 0:
    1101     5410155 :     if (na>=Flx_MUL_HALFMULII_LIMIT)
    1102     2664736 :       return na>=half;
    1103     2745419 :     break;
    1104             :   case 1:
    1105    11568346 :     if (na>=Flx_MUL_MULII_LIMIT)
    1106     3874783 :       return na>=mul;
    1107     7693563 :     break;
    1108             :   case 2:
    1109    12604632 :     if (na>=Flx_MUL_MULII2_LIMIT)
    1110      988278 :       return na>=mul2;
    1111    11616354 :     break;
    1112             :   case 3:
    1113     1168291 :     if (na>=70)
    1114        1348 :       return na>=70;
    1115     1166943 :     break;
    1116             :   }
    1117    29128000 :   return na>=kara;
    1118             : }
    1119             : 
    1120             : /*
    1121             :  * x/polrecip(P)+O(x^n)
    1122             :  */
    1123             : static GEN
    1124       91004 : Flx_invBarrett_basecase(GEN T, ulong p)
    1125             : {
    1126       91004 :   long i, l=lg(T)-1, lr=l-1, k;
    1127       91004 :   GEN r=cgetg(lr,t_VECSMALL); r[1] = T[1];
    1128       91004 :   r[2] = 1;
    1129       91004 :   if (SMALL_ULONG(p))
    1130     4065750 :     for (i=3;i<lr;i++)
    1131             :     {
    1132     3977465 :       ulong u = uel(T, l-i+2);
    1133   121823111 :       for (k=3; k<i; k++)
    1134   117845646 :         { u += uel(T,l-i+k) * uel(r, k); if (u & HIGHBIT) u %= p; }
    1135     3977465 :       r[i] = Fl_neg(u % p, p);
    1136             :     }
    1137             :   else
    1138       53355 :     for (i=3;i<lr;i++)
    1139             :     {
    1140       50637 :       ulong u = Fl_neg(uel(T,l-i+2), p);
    1141      536934 :       for (k=3; k<i; k++)
    1142      486297 :         u = Fl_sub(u, Fl_mul(uel(T,l-i+k), uel(r,k), p), p);
    1143       50637 :       r[i] = u;
    1144             :     }
    1145       91003 :   return Flx_renormalize(r,lr);
    1146             : }
    1147             : 
    1148             : /* Return new lgpol */
    1149             : static long
    1150     4229752 : Flx_lgrenormalizespec(GEN x, long lx)
    1151             : {
    1152             :   long i;
    1153    13341718 :   for (i = lx-1; i>=0; i--)
    1154    13341576 :     if (x[i]) break;
    1155     4229752 :   return i+1;
    1156             : }
    1157             : static GEN
    1158        4348 : Flx_invBarrett_Newton(GEN T, ulong p)
    1159             : {
    1160        4348 :   long nold, lx, lz, lq, l = degpol(T), lQ;
    1161        4348 :   GEN q, y, z, x = zero_zv(l+1) + 2;
    1162        4349 :   ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
    1163             :   pari_sp av;
    1164             : 
    1165        4349 :   y = T+2;
    1166        4349 :   q = Flx_recipspec(y,l+1,l+1); lQ = lgpol(q); q+=2;
    1167        4349 :   av = avma;
    1168             :   /* We work on _spec_ Flx's, all the l[xzq12] below are lgpol's */
    1169             : 
    1170             :   /* initialize */
    1171        4349 :   x[0] = Fl_inv(q[0], p);
    1172        4349 :   if (lQ>1 && q[1])
    1173        2043 :   {
    1174        2043 :     ulong u = q[1];
    1175        2043 :     if (x[0] != 1) u = Fl_mul(u, Fl_sqr(x[0],p), p);
    1176        2043 :     x[1] = p - u; lx = 2;
    1177             :   }
    1178             :   else
    1179        2306 :     lx = 1;
    1180        4349 :   nold = 1;
    1181       32060 :   for (; mask > 1; set_avma(av))
    1182             :   { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
    1183       27714 :     long i, lnew, nnew = nold << 1;
    1184             : 
    1185       27714 :     if (mask & 1) nnew--;
    1186       27714 :     mask >>= 1;
    1187             : 
    1188       27714 :     lnew = nnew + 1;
    1189       27714 :     lq = Flx_lgrenormalizespec(q, minss(lQ, lnew));
    1190       27718 :     z = Flx_mulspec(x, q, p, lx, lq); /* FIXME: high product */
    1191       27713 :     lz = lgpol(z); if (lz > lnew) lz = lnew;
    1192       27713 :     z += 2;
    1193             :     /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
    1194       27713 :     for (i = nold; i < lz; i++) if (z[i]) break;
    1195       27713 :     nold = nnew;
    1196       27713 :     if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
    1197             : 
    1198             :     /* z + i represents (x*q - 1) / t^i */
    1199       19315 :     lz = Flx_lgrenormalizespec (z+i, lz-i);
    1200       19315 :     z = Flx_mulspec(x, z+i, p, lx, lz); /* FIXME: low product */
    1201       19314 :     lz = lgpol(z); z += 2;
    1202       19314 :     if (lz > lnew-i) lz = Flx_lgrenormalizespec(z, lnew-i);
    1203             : 
    1204       19314 :     lx = lz+ i;
    1205       19314 :     y  = x + i; /* x -= z * t^i, in place */
    1206       19314 :     for (i = 0; i < lz; i++) y[i] = Fl_neg(z[i], p);
    1207             :   }
    1208        4349 :   x -= 2; setlg(x, lx + 2); x[1] = T[1];
    1209        4349 :   return x;
    1210             : }
    1211             : 
    1212             : /* x/polrecip(T)+O(x^deg(T)) */
    1213             : GEN
    1214       95354 : Flx_invBarrett(GEN T, ulong p)
    1215             : {
    1216       95354 :   pari_sp ltop=avma;
    1217       95354 :   long l=lg(T);
    1218             :   GEN r;
    1219       95354 :   if (l<5) return pol0_Flx(T[1]);
    1220       95352 :   if (!Flx_multhreshold(T,p, Flx_INVBARRETT_QUARTMULII_LIMIT,
    1221             :                              Flx_INVBARRETT_HALFMULII_LIMIT,
    1222             :                              Flx_INVBARRETT_MULII_LIMIT,
    1223             :                              Flx_INVBARRETT_MULII2_LIMIT,
    1224             :                              Flx_INVBARRETT_KARATSUBA_LIMIT))
    1225             :   {
    1226       91004 :     ulong c = T[l-1];
    1227       91004 :     if (c!=1)
    1228             :     {
    1229         529 :       ulong ci = Fl_inv(c,p);
    1230         529 :       T=Flx_Fl_mul(T, ci, p);
    1231         529 :       r=Flx_invBarrett_basecase(T,p);
    1232         529 :       r=Flx_Fl_mul(r,ci,p);
    1233             :     }
    1234             :     else
    1235       90475 :       r=Flx_invBarrett_basecase(T,p);
    1236             :   }
    1237             :   else
    1238        4348 :     r = Flx_invBarrett_Newton(T,p);
    1239       95353 :   return gerepileuptoleaf(ltop, r);
    1240             : }
    1241             : 
    1242             : GEN
    1243    42614761 : Flx_get_red(GEN T, ulong p)
    1244             : {
    1245    42614761 :   if (typ(T)!=t_VECSMALL || !Flx_multhreshold(T,p,
    1246             :                          Flx_BARRETT_QUARTMULII_LIMIT,
    1247             :                          Flx_BARRETT_HALFMULII_LIMIT,
    1248             :                          Flx_BARRETT_MULII_LIMIT,
    1249             :                          Flx_BARRETT_MULII2_LIMIT,
    1250             :                          Flx_BARRETT_KARATSUBA_LIMIT))
    1251    42511022 :     return T;
    1252       94437 :   retmkvec2(Flx_invBarrett(T,p),T);
    1253             : }
    1254             : 
    1255             : /* separate from Flx_divrem for maximal speed. */
    1256             : static GEN
    1257   452896644 : Flx_rem_basecase(GEN x, GEN y, ulong p)
    1258             : {
    1259             :   pari_sp av;
    1260             :   GEN z, c;
    1261             :   long dx,dy,dy1,dz,i,j;
    1262             :   ulong p1,inv;
    1263   452896644 :   long vs=x[1];
    1264             : 
    1265   452896644 :   dy = degpol(y); if (!dy) return pol0_Flx(x[1]);
    1266   438605037 :   dx = degpol(x);
    1267   438609243 :   dz = dx-dy; if (dz < 0) return Flx_copy(x);
    1268   438609243 :   x += 2; y += 2;
    1269   438609243 :   inv = y[dy];
    1270   438609243 :   if (inv != 1UL) inv = Fl_inv(inv,p);
    1271   439709305 :   for (dy1=dy-1; dy1>=0 && !y[dy1]; dy1--);
    1272             : 
    1273   439709305 :   c = cgetg(dy+3, t_VECSMALL); c[1]=vs; c += 2; av=avma;
    1274   437708034 :   z = cgetg(dz+3, t_VECSMALL); z[1]=vs; z += 2;
    1275             : 
    1276   437291255 :   if (SMALL_ULONG(p))
    1277             :   {
    1278   260082838 :     z[dz] = (inv*x[dx]) % p;
    1279  1011169458 :     for (i=dx-1; i>=dy; --i)
    1280             :     {
    1281   751086620 :       p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1282  4622683099 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1283             :       {
    1284  3871596479 :         p1 += z[j]*y[i-j];
    1285  3871596479 :         if (p1 & HIGHBIT) p1 %= p;
    1286             :       }
    1287   751086620 :       p1 %= p;
    1288   751086620 :       z[i-dy] = p1? ((p - p1)*inv) % p: 0;
    1289             :     }
    1290  1840065496 :     for (i=0; i<dy; i++)
    1291             :     {
    1292  1579018607 :       p1 = z[0]*y[i];
    1293  6612284045 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1294             :       {
    1295  5033265438 :         p1 += z[j]*y[i-j];
    1296  5033265438 :         if (p1 & HIGHBIT) p1 %= p;
    1297             :       }
    1298  1577644898 :       c[i] = Fl_sub(x[i], p1%p, p);
    1299             :     }
    1300             :   }
    1301             :   else
    1302             :   {
    1303   177208417 :     ulong pi = get_Fl_red(p);
    1304   177140326 :     z[dz] = Fl_mul_pre(inv, x[dx], p, pi);
    1305   582302051 :     for (i=dx-1; i>=dy; --i)
    1306             :     {
    1307   404749226 :       p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1308  1699260174 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1309  1293077319 :         p1 = Fl_addmul_pre(p1, z[j], y[i - j], p, pi);
    1310   406182855 :       z[i-dy] = p1? Fl_mul_pre(p - p1, inv, p, pi): 0;
    1311             :     }
    1312  1223696100 :     for (i=0; i<dy; i++)
    1313             :     {
    1314  1046374031 :       p1 = Fl_mul_pre(z[0],y[i],p,pi);
    1315  3000428699 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1316  1950091057 :         p1 = Fl_addmul_pre(p1, z[j], y[i - j], p, pi);
    1317  1037757942 :       c[i] = Fl_sub(x[i], p1, p);
    1318             :     }
    1319             :   }
    1320   438368958 :   i = dy-1; while (i>=0 && !c[i]) i--;
    1321   438368958 :   set_avma(av);
    1322   437807021 :   return Flx_renormalize(c-2, i+3);
    1323             : }
    1324             : 
    1325             : /* as FpX_divrem but working only on ulong types.
    1326             :  * if relevant, *pr is the last object on stack */
    1327             : static GEN
    1328    26904539 : Flx_divrem_basecase(GEN x, GEN y, ulong p, GEN *pr)
    1329             : {
    1330             :   GEN z,q,c;
    1331             :   long dx,dy,dy1,dz,i,j;
    1332             :   ulong p1,inv;
    1333    26904539 :   long sv=x[1];
    1334             : 
    1335    26904539 :   dy = degpol(y);
    1336    26903217 :   if (dy<0) pari_err_INV("Flx_divrem",y);
    1337    26908936 :   if (pr == ONLY_REM) return Flx_rem_basecase(x, y, p);
    1338    26908934 :   if (!dy)
    1339             :   {
    1340     4342107 :     if (pr && pr != ONLY_DIVIDES) *pr = pol0_Flx(sv);
    1341     4342107 :     if (y[2] == 1UL) return Flx_copy(x);
    1342     2744577 :     return Flx_Fl_mul(x, Fl_inv(y[2], p), p);
    1343             :   }
    1344    22566827 :   dx = degpol(x);
    1345    22567202 :   dz = dx-dy;
    1346    22567202 :   if (dz < 0)
    1347             :   {
    1348      238711 :     q = pol0_Flx(sv);
    1349      238704 :     if (pr && pr != ONLY_DIVIDES) *pr = Flx_copy(x);
    1350      238704 :     return q;
    1351             :   }
    1352    22328491 :   x += 2;
    1353    22328491 :   y += 2;
    1354    22328491 :   z = cgetg(dz + 3, t_VECSMALL); z[1] = sv; z += 2;
    1355    22327408 :   inv = uel(y, dy);
    1356    22327408 :   if (inv != 1UL) inv = Fl_inv(inv,p);
    1357    22328804 :   for (dy1=dy-1; dy1>=0 && !y[dy1]; dy1--);
    1358             : 
    1359    22328804 :   if (SMALL_ULONG(p))
    1360             :   {
    1361    21416446 :     z[dz] = (inv*x[dx]) % p;
    1362    58800906 :     for (i=dx-1; i>=dy; --i)
    1363             :     {
    1364    37384460 :       p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1365   180890210 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1366             :       {
    1367   143505750 :         p1 += z[j]*y[i-j];
    1368   143505750 :         if (p1 & HIGHBIT) p1 %= p;
    1369             :       }
    1370    37384460 :       p1 %= p;
    1371    37384460 :       z[i-dy] = p1? (long) ((p - p1)*inv) % p: 0;
    1372             :     }
    1373             :   }
    1374             :   else
    1375             :   {
    1376      912358 :     z[dz] = Fl_mul(inv, x[dx], p);
    1377     7389486 :     for (i=dx-1; i>=dy; --i)
    1378             :     { /* compute -p1 instead of p1 (pb with ulongs otherwise) */
    1379     6477129 :       p1 = p - uel(x,i);
    1380    36696279 :       for (j=i-dy1; j<=i && j<=dz; j++)
    1381    30219151 :         p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
    1382     6477128 :       z[i-dy] = p1? Fl_mul(p - p1, inv, p): 0;
    1383             :     }
    1384             :   }
    1385    22328803 :   q = Flx_renormalize(z-2, dz+3);
    1386    22329138 :   if (!pr) return q;
    1387             : 
    1388    17439340 :   c = cgetg(dy + 3, t_VECSMALL); c[1] = sv; c += 2;
    1389    17438918 :   if (SMALL_ULONG(p))
    1390             :   {
    1391   197283520 :     for (i=0; i<dy; i++)
    1392             :     {
    1393   180687212 :       p1 = (ulong)z[0]*y[i];
    1394   405272381 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1395             :       {
    1396   224585169 :         p1 += (ulong)z[j]*y[i-j];
    1397   224585169 :         if (p1 & HIGHBIT) p1 %= p;
    1398             :       }
    1399   180689034 :       c[i] = Fl_sub(x[i], p1%p, p);
    1400             :     }
    1401             :   }
    1402             :   else
    1403             :   {
    1404     9796874 :     for (i=0; i<dy; i++)
    1405             :     {
    1406     8953804 :       p1 = Fl_mul(z[0],y[i],p);
    1407    54924162 :       for (j=maxss(1,i-dy1); j<=i && j<=dz; j++)
    1408    45970358 :         p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p);
    1409     8953804 :       c[i] = Fl_sub(x[i], p1, p);
    1410             :     }
    1411             :   }
    1412    17439378 :   i=dy-1; while (i>=0 && !c[i]) i--;
    1413    17439378 :   c = Flx_renormalize(c-2, i+3);
    1414    17439240 :   if (pr == ONLY_DIVIDES)
    1415         260 :   { if (lg(c) != 2) return NULL; }
    1416             :   else
    1417    17438980 :     *pr = c;
    1418    17439163 :   return q;
    1419             : }
    1420             : 
    1421             : 
    1422             : /* Compute x mod T where 2 <= degpol(T) <= l+1 <= 2*(degpol(T)-1)
    1423             :  * and mg is the Barrett inverse of T. */
    1424             : static GEN
    1425     2082554 : Flx_divrem_Barrettspec(GEN x, long l, GEN mg, GEN T, ulong p, GEN *pr)
    1426             : {
    1427             :   GEN q, r;
    1428     2082554 :   long lt = degpol(T); /*We discard the leading term*/
    1429             :   long ld, lm, lT, lmg;
    1430     2082539 :   ld = l-lt;
    1431     2082539 :   lm = minss(ld, lgpol(mg));
    1432     2082530 :   lT  = Flx_lgrenormalizespec(T+2,lt);
    1433     2082522 :   lmg = Flx_lgrenormalizespec(mg+2,lm);
    1434     2082515 :   q = Flx_recipspec(x+lt,ld,ld);               /* q = rec(x)      lz<=ld*/
    1435     2082440 :   q = Flx_mulspec(q+2,mg+2,p,lgpol(q),lmg);    /* q = rec(x) * mg lz<=ld+lm*/
    1436     2082570 :   q = Flx_recipspec(q+2,minss(ld,lgpol(q)),ld);/* q = rec (rec(x) * mg) lz<=ld*/
    1437     2082458 :   if (!pr) return q;
    1438     2082431 :   r = Flx_mulspec(q+2,T+2,p,lgpol(q),lT);      /* r = q*pol       lz<=ld+lt*/
    1439     2082536 :   r = Flx_subspec(x,r+2,p,lt,minss(lt,lgpol(r)));/* r = x - q*pol lz<=lt */
    1440     2082404 :   if (pr == ONLY_REM) return r;
    1441        5411 :   *pr = r; return q;
    1442             : }
    1443             : 
    1444             : static GEN
    1445     2077094 : Flx_divrem_Barrett(GEN x, GEN mg, GEN T, ulong p, GEN *pr)
    1446             : {
    1447     2077094 :   GEN q = NULL, r = Flx_copy(x);
    1448     2077204 :   long l = lgpol(x), lt = degpol(T), lm = 2*lt-1, v = T[1];
    1449             :   long i;
    1450     2077180 :   if (l <= lt)
    1451             :   {
    1452           0 :     if (pr == ONLY_REM) return Flx_copy(x);
    1453           0 :     if (pr == ONLY_DIVIDES) return lgpol(x)? NULL: pol0_Flx(v);
    1454           0 :     if (pr) *pr = Flx_copy(x);
    1455           0 :     return pol0_Flx(v);
    1456             :   }
    1457     2077180 :   if (lt <= 1)
    1458           2 :     return Flx_divrem_basecase(x,T,p,pr);
    1459     2077178 :   if (pr != ONLY_REM && l>lm)
    1460           0 :     q = zero_zv(l-lt+1);
    1461     4159746 :   while (l>lm)
    1462             :   {
    1463        5396 :     GEN zr, zq = Flx_divrem_Barrettspec(r+2+l-lm,lm,mg,T,p,&zr);
    1464        5390 :     long lz = lgpol(zr);
    1465        5396 :     if (pr != ONLY_REM)
    1466             :     {
    1467           0 :       long lq = lgpol(zq);
    1468           0 :       for(i=0; i<lq; i++) q[2+l-lm+i] = zq[2+i];
    1469             :     }
    1470        5396 :     for(i=0; i<lz; i++)   r[2+l-lm+i] = zr[2+i];
    1471        5396 :     l = l-lm+lz;
    1472             :   }
    1473     2077175 :   if (pr == ONLY_REM)
    1474             :   {
    1475     2077127 :     if (l > lt)
    1476     2077123 :       r = Flx_divrem_Barrettspec(r+2,l,mg,T,p,ONLY_REM);
    1477             :     else
    1478           4 :       r = Flx_renormalize(r, l+2);
    1479     2076988 :     r[1] = v; return r;
    1480             :   }
    1481          48 :   if (l > lt)
    1482             :   {
    1483          48 :     GEN zq = Flx_divrem_Barrettspec(r+2,l,mg,T,p, pr ? &r: NULL);
    1484          48 :     if (!q) q = zq;
    1485             :     else
    1486             :     {
    1487           0 :       long lq = lgpol(zq);
    1488           0 :       for(i=0; i<lq; i++) q[2+i] = zq[2+i];
    1489             :     }
    1490             :   }
    1491           0 :   else if (pr)
    1492           0 :     r = Flx_renormalize(r, l+2);
    1493          48 :   q[1] = v; q = Flx_renormalize(q, lg(q));
    1494          48 :   if (pr == ONLY_DIVIDES) return lgpol(r)? NULL: q;
    1495          48 :   if (pr) { r[1] = v; *pr = r; }
    1496          48 :   return q;
    1497             : }
    1498             : 
    1499             : GEN
    1500    64018282 : Flx_divrem(GEN x, GEN T, ulong p, GEN *pr)
    1501             : {
    1502    64018282 :   GEN B, y = get_Flx_red(T, &B);
    1503    64019280 :   long dy = degpol(y), dx = degpol(x), d = dx-dy;
    1504    64017825 :   if (pr==ONLY_REM) return Flx_rem(x, y, p);
    1505    26904273 :   if (!B && d+3 < Flx_DIVREM_BARRETT_LIMIT)
    1506    26904225 :     return Flx_divrem_basecase(x,y,p,pr);
    1507             :   else
    1508             :   {
    1509          48 :     pari_sp av = avma;
    1510          48 :     GEN mg = B? B: Flx_invBarrett(y, p);
    1511          48 :     GEN q1 = Flx_divrem_Barrett(x,mg,y,p,pr);
    1512          48 :     if (!q1) return gc_NULL(av);
    1513          48 :     if (!pr || pr==ONLY_DIVIDES) return gerepileuptoleaf(av, q1);
    1514          21 :     gerepileall(av,2,&q1,pr);
    1515          21 :     return q1;
    1516             :   }
    1517             : }
    1518             : 
    1519             : GEN
    1520   512983010 : Flx_rem(GEN x, GEN T, ulong p)
    1521             : {
    1522   512983010 :   GEN B, y = get_Flx_red(T, &B);
    1523   513082158 :   long dy = degpol(y), dx = degpol(x), d = dx-dy;
    1524   513358295 :   if (d < 0) return Flx_copy(x);
    1525   455093409 :   if (!B && d+3 < Flx_REM_BARRETT_LIMIT)
    1526   453016359 :     return Flx_rem_basecase(x,y,p);
    1527             :   else
    1528             :   {
    1529     2077050 :     pari_sp av=avma;
    1530     2077050 :     GEN mg = B ? B: Flx_invBarrett(y, p);
    1531     2077050 :     GEN r  = Flx_divrem_Barrett(x, mg, y, p, ONLY_REM);
    1532     2076980 :     return gerepileuptoleaf(av, r);
    1533             :   }
    1534             : }
    1535             : 
    1536             : /* reduce T mod (X^n - 1, p). Shallow function */
    1537             : GEN
    1538     4933678 : Flx_mod_Xnm1(GEN T, ulong n, ulong p)
    1539             : {
    1540     4933678 :   long i, j, L = lg(T), l = n+2;
    1541             :   GEN S;
    1542     4933678 :   if (L <= l || n & ~LGBITS) return T;
    1543         217 :   S = cgetg(l, t_VECSMALL);
    1544         217 :   S[1] = T[1];
    1545         217 :   for (i = 2; i < l; i++) S[i] = T[i];
    1546         560 :   for (j = 2; i < L; i++) {
    1547         343 :     S[j] = Fl_add(S[j], T[i], p);
    1548         343 :     if (++j == l) j = 2;
    1549             :   }
    1550         217 :   return Flx_renormalize(S, l);
    1551             : }
    1552             : /* reduce T mod (X^n + 1, p). Shallow function */
    1553             : GEN
    1554        4951 : Flx_mod_Xn1(GEN T, ulong n, ulong p)
    1555             : {
    1556        4951 :   long i, j, L = lg(T), l = n+2;
    1557             :   GEN S;
    1558        4951 :   if (L <= l || n & ~LGBITS) return T;
    1559         168 :   S = cgetg(l, t_VECSMALL);
    1560         168 :   S[1] = T[1];
    1561         168 :   for (i = 2; i < l; i++) S[i] = T[i];
    1562         427 :   for (j = 2; i < L; i++) {
    1563         259 :     S[j] = Fl_sub(S[j], T[i], p);
    1564         259 :     if (++j == l) j = 2;
    1565             :   }
    1566         168 :   return Flx_renormalize(S, l);
    1567             : }
    1568             : 
    1569             : struct _Flxq {
    1570             :   GEN aut;
    1571             :   GEN T;
    1572             :   ulong p;
    1573             : };
    1574             : 
    1575             : static GEN
    1576           0 : _Flx_divrem(void * E, GEN x, GEN y, GEN *r)
    1577             : {
    1578           0 :   struct _Flxq *D = (struct _Flxq*) E;
    1579           0 :   return Flx_divrem(x, y, D->p, r);
    1580             : }
    1581             : static GEN
    1582       53116 : _Flx_add(void * E, GEN x, GEN y) {
    1583       53116 :   struct _Flxq *D = (struct _Flxq*) E;
    1584       53116 :   return Flx_add(x, y, D->p);
    1585             : }
    1586             : static GEN
    1587     9033433 : _Flx_mul(void *E, GEN x, GEN y) {
    1588     9033433 :   struct _Flxq *D = (struct _Flxq*) E;
    1589     9033433 :   return Flx_mul(x, y, D->p);
    1590             : }
    1591             : static GEN
    1592           0 : _Flx_sqr(void *E, GEN x) {
    1593           0 :   struct _Flxq *D = (struct _Flxq*) E;
    1594           0 :   return Flx_sqr(x, D->p);
    1595             : }
    1596             : 
    1597             : static struct bb_ring Flx_ring = { _Flx_add,_Flx_mul,_Flx_sqr };
    1598             : 
    1599             : GEN
    1600           0 : Flx_digits(GEN x, GEN T, ulong p)
    1601             : {
    1602           0 :   pari_sp av = avma;
    1603             :   struct _Flxq D;
    1604           0 :   long d = degpol(T), n = (lgpol(x)+d-1)/d;
    1605             :   GEN z;
    1606           0 :   D.p = p;
    1607           0 :   z = gen_digits(x,T,n,(void *)&D, &Flx_ring, _Flx_divrem);
    1608           0 :   return gerepileupto(av, z);
    1609             : }
    1610             : 
    1611             : GEN
    1612           0 : FlxV_Flx_fromdigits(GEN x, GEN T, ulong p)
    1613             : {
    1614           0 :   pari_sp av = avma;
    1615             :   struct _Flxq D;
    1616             :   GEN z;
    1617           0 :   D.p = p;
    1618           0 :   z = gen_fromdigits(x,T,(void *)&D, &Flx_ring);
    1619           0 :   return gerepileupto(av, z);
    1620             : }
    1621             : 
    1622             : long
    1623     3128104 : Flx_val(GEN x)
    1624             : {
    1625     3128104 :   long i, l=lg(x);
    1626     3128104 :   if (l==2)  return LONG_MAX;
    1627     3128104 :   for (i=2; i<l && x[i]==0; i++) /*empty*/;
    1628     3128104 :   return i-2;
    1629             : }
    1630             : long
    1631    22086969 : Flx_valrem(GEN x, GEN *Z)
    1632             : {
    1633    22086969 :   long v, i, l=lg(x);
    1634             :   GEN y;
    1635    22086969 :   if (l==2) { *Z = Flx_copy(x); return LONG_MAX; }
    1636    22086969 :   for (i=2; i<l && x[i]==0; i++) /*empty*/;
    1637    22086969 :   v = i-2;
    1638    22086969 :   if (v == 0) { *Z = x; return 0; }
    1639       37255 :   l -= v;
    1640       37255 :   y = cgetg(l, t_VECSMALL); y[1] = x[1];
    1641       37255 :   for (i=2; i<l; i++) y[i] = x[i+v];
    1642       37255 :   *Z = y; return v;
    1643             : }
    1644             : 
    1645             : GEN
    1646     5898766 : Flx_deriv(GEN z, ulong p)
    1647             : {
    1648     5898766 :   long i,l = lg(z)-1;
    1649             :   GEN x;
    1650     5898766 :   if (l < 2) l = 2;
    1651     5898766 :   x = cgetg(l, t_VECSMALL); x[1] = z[1]; z++;
    1652     5898441 :   if (HIGHWORD(l | p))
    1653     1273455 :     for (i=2; i<l; i++) x[i] = Fl_mul((ulong)i-1, z[i], p);
    1654             :   else
    1655     4624986 :     for (i=2; i<l; i++) x[i] = ((i-1) * z[i]) % p;
    1656     5898401 :   return Flx_renormalize(x,l);
    1657             : }
    1658             : 
    1659             : GEN
    1660           0 : Flx_integ(GEN x, ulong p)
    1661             : {
    1662           0 :   long i, lx = lg(x);
    1663             :   GEN y;
    1664           0 :   if (lx == 2) return Flx_copy(x);
    1665           0 :   y = cgetg(lx+1, t_POL); y[1] = x[1];
    1666           0 :   uel(y,2) = 0;
    1667           0 :   for (i=3; i<=lx; i++)
    1668           0 :     uel(y,i) = uel(x,i-1) ? Fl_div(uel(x,i-1), (i-2)%p, p): 0UL;
    1669           0 :   return Flx_renormalize(y, lx+1);;
    1670             : }
    1671             : 
    1672             : GEN
    1673       11822 : Flx_translate1(GEN P, ulong p)
    1674             : {
    1675       11822 :   long i, k, n = degpol(P);
    1676       11822 :   GEN R = Flx_copy(P);
    1677       51375 :   for (i=1; i<=n; i++)
    1678      145743 :     for (k=n-i; k<n; k++)
    1679      106182 :       uel(R,k+2) = Fl_add(uel(R,k+2), uel(R,k+3), p);
    1680       11822 :   return R;
    1681             : }
    1682             : 
    1683             : GEN
    1684       11822 : Flx_diff1(GEN P, ulong p)
    1685             : {
    1686       11822 :   return Flx_sub(Flx_translate1(P, p), P, p);
    1687             : }
    1688             : 
    1689             : GEN
    1690       75765 : Flx_deflate(GEN x0, long d)
    1691             : {
    1692             :   GEN z, y, x;
    1693       75765 :   long i,id, dy, dx = degpol(x0);
    1694       75765 :   if (d == 1 || dx <= 0) return Flx_copy(x0);
    1695       69374 :   dy = dx/d;
    1696       69374 :   y = cgetg(dy+3, t_VECSMALL); y[1] = x0[1];
    1697       69374 :   z = y + 2;
    1698       69374 :   x = x0+ 2;
    1699       69374 :   for (i=id=0; i<=dy; i++,id+=d) z[i] = x[id];
    1700       69374 :   return y;
    1701             : }
    1702             : 
    1703             : GEN
    1704       47902 : Flx_inflate(GEN x0, long d)
    1705             : {
    1706       47902 :   long i, id, dy, dx = degpol(x0);
    1707       47896 :   GEN x = x0 + 2, z, y;
    1708       47896 :   if (dx <= 0) return Flx_copy(x0);
    1709       46740 :   dy = dx*d;
    1710       46740 :   y = cgetg(dy+3, t_VECSMALL); y[1] = x0[1];
    1711       46772 :   z = y + 2;
    1712       46772 :   for (i=0; i<=dy; i++) z[i] = 0;
    1713       46772 :   for (i=id=0; i<=dx; i++,id+=d) z[id] = x[i];
    1714       46772 :   return y;
    1715             : }
    1716             : 
    1717             : /* write p(X) = a_0(X^k) + X*a_1(X^k) + ... + X^(k-1)*a_{k-1}(X^k) */
    1718             : GEN
    1719      137475 : Flx_splitting(GEN p, long k)
    1720             : {
    1721      137475 :   long n = degpol(p), v = p[1], m, i, j, l;
    1722             :   GEN r;
    1723             : 
    1724      137471 :   m = n/k;
    1725      137471 :   r = cgetg(k+1,t_VEC);
    1726      649162 :   for(i=1; i<=k; i++)
    1727             :   {
    1728      511666 :     gel(r,i) = cgetg(m+3, t_VECSMALL);
    1729      511659 :     mael(r,i,1) = v;
    1730             :   }
    1731     3195192 :   for (j=1, i=0, l=2; i<=n; i++)
    1732             :   {
    1733     3057696 :     mael(r,j,l) = p[2+i];
    1734     3057696 :     if (j==k) { j=1; l++; } else j++;
    1735             :   }
    1736      649198 :   for(i=1; i<=k; i++)
    1737      511731 :     gel(r,i) = Flx_renormalize(gel(r,i),i<j?l+1:l);
    1738      137467 :   return r;
    1739             : }
    1740             : static GEN
    1741      186418 : Flx_halfgcd_basecase(GEN a, GEN b, ulong p)
    1742             : {
    1743      186418 :   pari_sp av=avma;
    1744             :   GEN u,u1,v,v1;
    1745      186418 :   long vx = a[1];
    1746      186418 :   long n = lgpol(a)>>1;
    1747      186418 :   u1 = v = pol0_Flx(vx);
    1748      186420 :   u = v1 = pol1_Flx(vx);
    1749      933739 :   while (lgpol(b)>n)
    1750             :   {
    1751      560892 :     GEN r, q = Flx_divrem(a,b,p, &r);
    1752      560916 :     a = b; b = r; swap(u,u1); swap(v,v1);
    1753      560916 :     u1 = Flx_sub(u1, Flx_mul(u, q, p), p);
    1754      560881 :     v1 = Flx_sub(v1, Flx_mul(v, q ,p), p);
    1755      560910 :     if (gc_needed(av,2))
    1756             :     {
    1757           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_halfgcd (d = %ld)",degpol(b));
    1758           0 :       gerepileall(av,6, &a,&b,&u1,&v1,&u,&v);
    1759             :     }
    1760             :   }
    1761      186406 :   return gerepilecopy(av, mkmat2(mkcol2(u,u1), mkcol2(v,v1)));
    1762             : }
    1763             : /* ux + vy */
    1764             : static GEN
    1765        5770 : Flx_addmulmul(GEN u, GEN v, GEN x, GEN y, ulong p)
    1766        5770 : { return Flx_add(Flx_mul(u,x, p), Flx_mul(v,y, p), p); }
    1767             : 
    1768             : static GEN
    1769        2882 : FlxM_Flx_mul2(GEN M, GEN x, GEN y, ulong p)
    1770             : {
    1771        2882 :   GEN res = cgetg(3, t_COL);
    1772        2882 :   gel(res, 1) = Flx_addmulmul(gcoeff(M,1,1), gcoeff(M,1,2), x, y, p);
    1773        2882 :   gel(res, 2) = Flx_addmulmul(gcoeff(M,2,1), gcoeff(M,2,2), x, y, p);
    1774        2882 :   return res;
    1775             : }
    1776             : 
    1777             : #if 0
    1778             : static GEN
    1779             : FlxM_mul2_old(GEN M, GEN N, ulong p)
    1780             : {
    1781             :   GEN res = cgetg(3, t_MAT);
    1782             :   gel(res, 1) = FlxM_Flx_mul2(M,gcoeff(N,1,1),gcoeff(N,2,1),p);
    1783             :   gel(res, 2) = FlxM_Flx_mul2(M,gcoeff(N,1,2),gcoeff(N,2,2),p);
    1784             :   return res;
    1785             : }
    1786             : #endif
    1787             : /* A,B are 2x2 matrices, Flx entries. Return A x B using Strassen 7M formula */
    1788             : static GEN
    1789        1674 : FlxM_mul2(GEN A, GEN B, ulong p)
    1790             : {
    1791        1674 :   GEN A11=gcoeff(A,1,1),A12=gcoeff(A,1,2), B11=gcoeff(B,1,1),B12=gcoeff(B,1,2);
    1792        1674 :   GEN A21=gcoeff(A,2,1),A22=gcoeff(A,2,2), B21=gcoeff(B,2,1),B22=gcoeff(B,2,2);
    1793        1674 :   GEN M1 = Flx_mul(Flx_add(A11,A22, p), Flx_add(B11,B22, p), p);
    1794        1674 :   GEN M2 = Flx_mul(Flx_add(A21,A22, p), B11, p);
    1795        1674 :   GEN M3 = Flx_mul(A11, Flx_sub(B12,B22, p), p);
    1796        1674 :   GEN M4 = Flx_mul(A22, Flx_sub(B21,B11, p), p);
    1797        1674 :   GEN M5 = Flx_mul(Flx_add(A11,A12, p), B22, p);
    1798        1674 :   GEN M6 = Flx_mul(Flx_sub(A21,A11, p), Flx_add(B11,B12, p), p);
    1799        1674 :   GEN M7 = Flx_mul(Flx_sub(A12,A22, p), Flx_add(B21,B22, p), p);
    1800        1674 :   GEN T1 = Flx_add(M1,M4, p), T2 = Flx_sub(M7,M5, p);
    1801        1674 :   GEN T3 = Flx_sub(M1,M2, p), T4 = Flx_add(M3,M6, p);
    1802        1674 :   retmkmat2(mkcol2(Flx_add(T1,T2, p), Flx_add(M2,M4, p)),
    1803             :             mkcol2(Flx_add(M3,M5, p), Flx_add(T3,T4, p)));
    1804             : }
    1805             : 
    1806             : /* Return [0,1;1,-q]*M */
    1807             : static GEN
    1808        1671 : Flx_FlxM_qmul(GEN q, GEN M, ulong p)
    1809             : {
    1810        1671 :   GEN u, v, res = cgetg(3, t_MAT);
    1811        1671 :   u = Flx_sub(gcoeff(M,1,1), Flx_mul(gcoeff(M,2,1), q, p), p);
    1812        1671 :   gel(res,1) = mkcol2(gcoeff(M,2,1), u);
    1813        1671 :   v = Flx_sub(gcoeff(M,1,2), Flx_mul(gcoeff(M,2,2), q, p), p);
    1814        1671 :   gel(res,2) = mkcol2(gcoeff(M,2,2), v);
    1815        1671 :   return res;
    1816             : }
    1817             : 
    1818             : static GEN
    1819           3 : matid2_FlxM(long v)
    1820             : {
    1821           3 :   return mkmat2(mkcol2(pol1_Flx(v),pol0_Flx(v)),
    1822             :                 mkcol2(pol0_Flx(v),pol1_Flx(v)));
    1823             : }
    1824             : 
    1825             : static GEN
    1826        2856 : Flx_halfgcd_split(GEN x, GEN y, ulong p)
    1827             : {
    1828        2856 :   pari_sp av=avma;
    1829             :   GEN R, S, V;
    1830             :   GEN y1, r, q;
    1831        2856 :   long l = lgpol(x), n = l>>1, k;
    1832        2856 :   if (lgpol(y)<=n) return matid2_FlxM(x[1]);
    1833        2856 :   R = Flx_halfgcd(Flx_shift(x,-n),Flx_shift(y,-n),p);
    1834        2856 :   V = FlxM_Flx_mul2(R,x,y,p); y1 = gel(V,2);
    1835        2856 :   if (lgpol(y1)<=n) return gerepilecopy(av, R);
    1836        1671 :   q = Flx_divrem(gel(V,1), y1, p, &r);
    1837        1671 :   k = 2*n-degpol(y1);
    1838        1671 :   S = Flx_halfgcd(Flx_shift(y1,-k), Flx_shift(r,-k),p);
    1839        1671 :   return gerepileupto(av, FlxM_mul2(S,Flx_FlxM_qmul(q,R,p),p));
    1840             : }
    1841             : 
    1842             : /* Return M in GL_2(Fl[X]) such that:
    1843             : if [a',b']~=M*[a,b]~ then degpol(a')>= (lgpol(a)>>1) >degpol(b')
    1844             : */
    1845             : 
    1846             : static GEN
    1847      189290 : Flx_halfgcd_i(GEN x, GEN y, ulong p)
    1848             : {
    1849      189290 :   if (!Flx_multhreshold(x,p,
    1850             :                              Flx_HALFGCD_QUARTMULII_LIMIT,
    1851             :                              Flx_HALFGCD_HALFMULII_LIMIT,
    1852             :                              Flx_HALFGCD_MULII_LIMIT,
    1853             :                              Flx_HALFGCD_MULII2_LIMIT,
    1854             :                              Flx_HALFGCD_KARATSUBA_LIMIT))
    1855      186418 :     return Flx_halfgcd_basecase(x,y,p);
    1856        2856 :   return Flx_halfgcd_split(x,y,p);
    1857             : }
    1858             : 
    1859             : GEN
    1860      189293 : Flx_halfgcd(GEN x, GEN y, ulong p)
    1861             : {
    1862             :   pari_sp av;
    1863             :   GEN M,q,r;
    1864      189293 :   long lx=lgpol(x), ly=lgpol(y);
    1865      189290 :   if (!lx)
    1866             :   {
    1867           0 :       long v = x[1];
    1868           0 :       retmkmat2(mkcol2(pol0_Flx(v),pol1_Flx(v)),
    1869             :                 mkcol2(pol1_Flx(v),pol0_Flx(v)));
    1870             :   }
    1871      189290 :   if (ly < lx) return Flx_halfgcd_i(x,y,p);
    1872        2633 :   av = avma;
    1873        2633 :   q = Flx_divrem(y,x,p,&r);
    1874        2633 :   M = Flx_halfgcd_i(x,r,p);
    1875        2633 :   gcoeff(M,1,1) = Flx_sub(gcoeff(M,1,1), Flx_mul(q, gcoeff(M,1,2), p), p);
    1876        2633 :   gcoeff(M,2,1) = Flx_sub(gcoeff(M,2,1), Flx_mul(q, gcoeff(M,2,2), p), p);
    1877        2633 :   return gerepilecopy(av, M);
    1878             : }
    1879             : 
    1880             : /*Do not garbage collect*/
    1881             : static GEN
    1882    35731436 : Flx_gcd_basecase(GEN a, GEN b, ulong p)
    1883             : {
    1884    35731436 :   pari_sp av = avma;
    1885    35731436 :   ulong iter = 0;
    1886    35731436 :   if (lg(b) > lg(a)) swap(a, b);
    1887   168146907 :   while (lgpol(b))
    1888             :   {
    1889    96860047 :     GEN c = Flx_rem(a,b,p);
    1890    96684035 :     iter++; a = b; b = c;
    1891    96684035 :     if (gc_needed(av,2))
    1892             :     {
    1893           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_gcd (d = %ld)",degpol(c));
    1894           0 :       gerepileall(av,2, &a,&b);
    1895             :     }
    1896             :   }
    1897    35641580 :   return iter < 2 ? Flx_copy(a) : a;
    1898             : }
    1899             : 
    1900             : GEN
    1901    36441555 : Flx_gcd(GEN x, GEN y, ulong p)
    1902             : {
    1903    36441555 :   pari_sp av = avma;
    1904    36441555 :   if (!lgpol(x)) return Flx_copy(y);
    1905    71445870 :   while (lg(y)>Flx_GCD_LIMIT)
    1906             :   {
    1907             :     GEN c;
    1908          23 :     if (lgpol(y)<=(lgpol(x)>>1))
    1909             :     {
    1910           0 :       GEN r = Flx_rem(x, y, p);
    1911           0 :       x = y; y = r;
    1912             :     }
    1913          23 :     c = FlxM_Flx_mul2(Flx_halfgcd(x,y, p), x, y, p);
    1914          23 :     x = gel(c,1); y = gel(c,2);
    1915          23 :     if (gc_needed(av,2))
    1916             :     {
    1917           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_gcd (y = %ld)",degpol(y));
    1918           0 :       gerepileall(av,2,&x,&y);
    1919             :     }
    1920             :   }
    1921    35722292 :   return gerepileuptoleaf(av, Flx_gcd_basecase(x,y,p));
    1922             : }
    1923             : 
    1924             : int
    1925     3517551 : Flx_is_squarefree(GEN z, ulong p)
    1926             : {
    1927     3517551 :   pari_sp av = avma;
    1928     3517551 :   GEN d = Flx_gcd(z, Flx_deriv(z,p) , p);
    1929     3517551 :   return gc_bool(av, degpol(d) == 0);
    1930             : }
    1931             : 
    1932             : static long
    1933      137990 : Flx_is_smooth_squarefree(GEN f, long r, ulong p)
    1934             : {
    1935      137990 :   pari_sp av = avma;
    1936             :   long i;
    1937      137990 :   GEN sx = polx_Flx(f[1]), a = sx;
    1938      588770 :   for(i=1;;i++)
    1939             :   {
    1940     1039726 :     if (degpol(f)<=r) return gc_long(av,1);
    1941      563597 :     a = Flxq_powu(Flx_rem(a,f,p), p, f, p);
    1942      567447 :     if (Flx_equal(a, sx)) return gc_long(av,1);
    1943      563344 :     if (i==r) return gc_long(av,0);
    1944      452965 :     f = Flx_div(f, Flx_gcd(Flx_sub(a,sx,p),f,p),p);
    1945             :   }
    1946             : }
    1947             : 
    1948             : static long
    1949        9070 : Flx_is_l_pow(GEN x, ulong p)
    1950             : {
    1951        9070 :   ulong i, lx = lgpol(x);
    1952       18289 :   for (i=1; i<lx; i++)
    1953       16331 :     if (x[i+2] && i%p) return 0;
    1954        1958 :   return 1;
    1955             : }
    1956             : 
    1957             : int
    1958      137954 : Flx_is_smooth(GEN g, long r, ulong p)
    1959             : {
    1960             :   while (1)
    1961        9070 :   {
    1962      137954 :     GEN f = Flx_gcd(g, Flx_deriv(g, p), p);
    1963      137931 :     if (!Flx_is_smooth_squarefree(Flx_div(g, f, p), r, p))
    1964      110315 :       return 0;
    1965       27803 :     if (degpol(f)==0) return 1;
    1966        9070 :     g = Flx_is_l_pow(f,p) ? Flx_deflate(f, p): f;
    1967             :   }
    1968             : }
    1969             : 
    1970             : static GEN
    1971     3894304 : Flx_extgcd_basecase(GEN a, GEN b, ulong p, GEN *ptu, GEN *ptv)
    1972             : {
    1973     3894304 :   pari_sp av=avma;
    1974             :   GEN u,v,d,d1,v1;
    1975     3894304 :   long vx = a[1];
    1976     3894304 :   d = a; d1 = b;
    1977     3894304 :   v = pol0_Flx(vx); v1 = pol1_Flx(vx);
    1978    25683915 :   while (lgpol(d1))
    1979             :   {
    1980    17895307 :     GEN r, q = Flx_divrem(d,d1,p, &r);
    1981    17895307 :     v = Flx_sub(v,Flx_mul(q,v1,p),p);
    1982    17895307 :     u=v; v=v1; v1=u;
    1983    17895307 :     u=r; d=d1; d1=u;
    1984    17895307 :     if (gc_needed(av,2))
    1985             :     {
    1986           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_extgcd (d = %ld)",degpol(d));
    1987           0 :       gerepileall(av,5, &d,&d1,&u,&v,&v1);
    1988             :     }
    1989             :   }
    1990     3894304 :   if (ptu) *ptu = Flx_div(Flx_sub(d, Flx_mul(b,v,p), p), a, p);
    1991     3894304 :   *ptv = v; return d;
    1992             : }
    1993             : 
    1994             : static GEN
    1995           3 : Flx_extgcd_halfgcd(GEN x, GEN y, ulong p, GEN *ptu, GEN *ptv)
    1996             : {
    1997           3 :   pari_sp av=avma;
    1998           3 :   GEN u,v,R = matid2_FlxM(x[1]);
    1999           9 :   while (lg(y)>Flx_EXTGCD_LIMIT)
    2000             :   {
    2001             :     GEN M, c;
    2002           3 :     if (lgpol(y)<=(lgpol(x)>>1))
    2003             :     {
    2004           0 :       GEN r, q = Flx_divrem(x, y, p, &r);
    2005           0 :       x = y; y = r;
    2006           0 :       R = Flx_FlxM_qmul(q, R, p);
    2007             :     }
    2008           3 :     M = Flx_halfgcd(x,y, p);
    2009           3 :     c = FlxM_Flx_mul2(M, x,y, p);
    2010           3 :     R = FlxM_mul2(M, R, p);
    2011           3 :     x = gel(c,1); y = gel(c,2);
    2012           3 :     gerepileall(av,3,&x,&y,&R);
    2013             :   }
    2014           3 :   y = Flx_extgcd_basecase(x,y,p,&u,&v);
    2015           3 :   if (ptu) *ptu = Flx_addmulmul(u,v,gcoeff(R,1,1),gcoeff(R,2,1),p);
    2016           3 :   *ptv = Flx_addmulmul(u,v,gcoeff(R,1,2),gcoeff(R,2,2),p);
    2017           3 :   return y;
    2018             : }
    2019             : 
    2020             : /* x and y in Z[X], return lift(gcd(x mod p, y mod p)). Set u and v st
    2021             :  * ux + vy = gcd (mod p) */
    2022             : GEN
    2023     3894304 : Flx_extgcd(GEN x, GEN y, ulong p, GEN *ptu, GEN *ptv)
    2024             : {
    2025             :   GEN d;
    2026     3894304 :   pari_sp ltop=avma;
    2027     3894304 :   if (lg(y)>Flx_EXTGCD_LIMIT)
    2028           3 :     d = Flx_extgcd_halfgcd(x, y, p, ptu, ptv);
    2029             :   else
    2030     3894301 :     d = Flx_extgcd_basecase(x, y, p, ptu, ptv);
    2031     3894304 :   gerepileall(ltop,ptu?3:2,&d,ptv,ptu);
    2032     3894304 :   return d;
    2033             : }
    2034             : 
    2035             : ulong
    2036     2019910 : Flx_resultant(GEN a, GEN b, ulong p)
    2037             : {
    2038             :   long da,db,dc,cnt;
    2039     2019910 :   ulong lb, res = 1UL;
    2040             :   pari_sp av;
    2041             :   GEN c;
    2042             : 
    2043     2019910 :   if (lgpol(a)==0 || lgpol(b)==0) return 0;
    2044     2019980 :   da = degpol(a);
    2045     2019984 :   db = degpol(b);
    2046     2032851 :   if (db > da)
    2047             :   {
    2048       85950 :     swapspec(a,b, da,db);
    2049       85950 :     if (both_odd(da,db)) res = p-res;
    2050             :   }
    2051     1946901 :   else if (!da) return 1; /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
    2052     2032852 :   cnt = 0; av = avma;
    2053    32437845 :   while (db)
    2054             :   {
    2055    28386309 :     lb = b[db+2];
    2056    28386309 :     c = Flx_rem(a,b, p);
    2057    27967199 :     a = b; b = c; dc = degpol(c);
    2058    27965860 :     if (dc < 0) return gc_long(av,0);
    2059             : 
    2060    27965697 :     if (both_odd(da,db)) res = p - res;
    2061    27989530 :     if (lb != 1) res = Fl_mul(res, Fl_powu(lb, da - dc, p), p);
    2062    28372140 :     if (++cnt == 100) { cnt = 0; gerepileall(av, 2, &a, &b); }
    2063    28372141 :     da = db; /* = degpol(a) */
    2064    28372141 :     db = dc; /* = degpol(b) */
    2065             :   }
    2066     2018684 :   return gc_ulong(av, Fl_mul(res, Fl_powu(b[2], da, p), p));
    2067             : }
    2068             : 
    2069             : /* If resultant is 0, *ptU and *ptU are not set */
    2070             : ulong
    2071      135397 : Flx_extresultant(GEN a, GEN b, ulong p, GEN *ptU, GEN *ptV)
    2072             : {
    2073      135397 :   GEN z,q,u,v, x = a, y = b;
    2074      135397 :   ulong lb, res = 1UL;
    2075      135397 :   pari_sp av = avma;
    2076             :   long dx, dy, dz;
    2077      135397 :   long vs=a[1];
    2078             : 
    2079      135397 :   dx = degpol(x);
    2080      135397 :   dy = degpol(y);
    2081      135397 :   if (dy > dx)
    2082             :   {
    2083         421 :     swap(x,y); lswap(dx,dy); pswap(ptU, ptV);
    2084         421 :     a = x; b = y;
    2085         421 :     if (both_odd(dx,dy)) res = p-res;
    2086             :   }
    2087             :   /* dx <= dy */
    2088      135397 :   if (dx < 0) return 0;
    2089             : 
    2090      135397 :   u = pol0_Flx(vs);
    2091      135397 :   v = pol1_Flx(vs); /* v = 1 */
    2092      931108 :   while (dy)
    2093             :   { /* b u = x (a), b v = y (a) */
    2094      660328 :     lb = y[dy+2];
    2095      660328 :     q = Flx_divrem(x,y, p, &z);
    2096      660314 :     x = y; y = z; /* (x,y) = (y, x - q y) */
    2097      660314 :     dz = degpol(z); if (dz < 0) return gc_ulong(av,0);
    2098      660314 :     z = Flx_sub(u, Flx_mul(q,v, p), p);
    2099      660314 :     u = v; v = z; /* (u,v) = (v, u - q v) */
    2100             : 
    2101      660314 :     if (both_odd(dx,dy)) res = p - res;
    2102      660314 :     if (lb != 1) res = Fl_mul(res, Fl_powu(lb, dx-dz, p), p);
    2103      660314 :     dx = dy; /* = degpol(x) */
    2104      660314 :     dy = dz; /* = degpol(y) */
    2105             :   }
    2106      135383 :   res = Fl_mul(res, Fl_powu(y[2], dx, p), p);
    2107      135383 :   lb = Fl_mul(res, Fl_inv(y[2],p), p);
    2108      135383 :   v = gerepileuptoleaf(av, Flx_Fl_mul(v, lb, p));
    2109      135383 :   av = avma;
    2110      135383 :   u = Flx_sub(Fl_to_Flx(res,vs), Flx_mul(b,v,p), p);
    2111      135383 :   u = gerepileuptoleaf(av, Flx_div(u,a,p)); /* = (res - b v) / a */
    2112      135383 :   *ptU = u;
    2113      135383 :   *ptV = v; return res;
    2114             : }
    2115             : 
    2116             : ulong
    2117    31186152 : Flx_eval_powers_pre(GEN x, GEN y, ulong p, ulong pi)
    2118             : {
    2119    31186152 :   ulong l0, l1, h0, h1, v1,  i = 1, lx = lg(x)-1;
    2120             :   LOCAL_OVERFLOW;
    2121             :   LOCAL_HIREMAINDER;
    2122    31186152 :   x++;
    2123             : 
    2124    31186152 :   if (lx == 1)
    2125     3426688 :     return 0;
    2126    27759464 :   l1 = mulll(uel(x,i), uel(y,i)); h1 = hiremainder; v1 = 0;
    2127    90803876 :   while (++i < lx) {
    2128    35284948 :     l0 = mulll(uel(x,i), uel(y,i)); h0 = hiremainder;
    2129    35284948 :     l1 = addll(l0, l1); h1 = addllx(h0, h1); v1 += overflow;
    2130             :   }
    2131    27759464 :   if (v1 == 0) return remll_pre(h1, l1, p, pi);
    2132        5514 :   else return remlll_pre(v1, h1, l1, p, pi);
    2133             : }
    2134             : 
    2135             : INLINE ulong
    2136     3368151 : Flx_eval_pre_i(GEN x, ulong y, ulong p, ulong pi)
    2137             : {
    2138             :   ulong p1;
    2139     3368151 :   long i=lg(x)-1;
    2140     3368151 :   if (i<=2)
    2141     1446822 :     return (i==2)? x[2]: 0;
    2142     1921329 :   p1 = x[i];
    2143     9058816 :   for (i--; i>=2; i--)
    2144     7135037 :     p1 = Fl_addmul_pre(uel(x, i), p1, y, p, pi);
    2145     1923779 :   return p1;
    2146             : }
    2147             : 
    2148             : ulong
    2149     3446159 : Flx_eval_pre(GEN x, ulong y, ulong p, ulong pi)
    2150             : {
    2151     3446159 :   if (degpol(x) > 15)
    2152             :   {
    2153       78955 :     pari_sp av = avma;
    2154       78955 :     GEN v = Fl_powers_pre(y, degpol(x), p, pi);
    2155       79123 :     ulong r =  Flx_eval_powers_pre(x, v, p, pi);
    2156       79201 :     return gc_ulong(av,r);
    2157             :   }
    2158             :   else
    2159     3366967 :     return Flx_eval_pre_i(x, y, p, pi);
    2160             : }
    2161             : 
    2162             : ulong
    2163     3441222 : Flx_eval(GEN x, ulong y, ulong p)
    2164             : {
    2165     3441222 :   return Flx_eval_pre(x, y, p, get_Fl_red(p));
    2166             : }
    2167             : 
    2168             : ulong
    2169        3073 : Flv_prod_pre(GEN x, ulong p, ulong pi)
    2170             : {
    2171        3073 :   pari_sp ltop = avma;
    2172             :   GEN v;
    2173        3073 :   long i,k,lx = lg(x);
    2174        3073 :   if (lx == 1) return 1UL;
    2175        3073 :   if (lx == 2) return uel(x,1);
    2176        2884 :   v = cgetg(1+(lx << 1), t_VECSMALL);
    2177        2884 :   k = 1;
    2178       27244 :   for (i=1; i<lx-1; i+=2)
    2179       24360 :     uel(v,k++) = Fl_mul_pre(uel(x,i), uel(x,i+1), p, pi);
    2180        2884 :   if (i < lx) uel(v,k++) = uel(x,i);
    2181       15848 :   while (k > 2)
    2182             :   {
    2183       10080 :     lx = k; k = 1;
    2184       34440 :     for (i=1; i<lx-1; i+=2)
    2185       24360 :       uel(v,k++) = Fl_mul_pre(uel(v,i), uel(v,i+1), p, pi);
    2186       10080 :     if (i < lx) uel(v,k++) = uel(v,i);
    2187             :   }
    2188        2884 :   return gc_ulong(ltop, uel(v,1));
    2189             : }
    2190             : 
    2191             : ulong
    2192           0 : Flv_prod(GEN v, ulong p)
    2193             : {
    2194           0 :   return Flv_prod_pre(v, p, get_Fl_red(p));
    2195             : }
    2196             : 
    2197             : GEN
    2198           0 : FlxV_prod(GEN V, ulong p)
    2199             : {
    2200             :   struct _Flxq D;
    2201           0 :   D.T = NULL; D.aut = NULL; D.p = p;
    2202           0 :   return gen_product(V, (void *)&D, &_Flx_mul);
    2203             : }
    2204             : 
    2205             : /* compute prod (x - a[i]) */
    2206             : GEN
    2207      615416 : Flv_roots_to_pol(GEN a, ulong p, long vs)
    2208             : {
    2209             :   struct _Flxq D;
    2210      615416 :   long i,k,lx = lg(a);
    2211             :   GEN p1;
    2212      615416 :   if (lx == 1) return pol1_Flx(vs);
    2213      615416 :   p1 = cgetg(lx, t_VEC);
    2214    10192663 :   for (k=1,i=1; i<lx-1; i+=2)
    2215    19155922 :     gel(p1,k++) = mkvecsmall4(vs, Fl_mul(a[i], a[i+1], p),
    2216     9577658 :                               Fl_neg(Fl_add(a[i],a[i+1],p),p), 1);
    2217      615005 :   if (i < lx)
    2218       52211 :     gel(p1,k++) = mkvecsmall3(vs, Fl_neg(a[i],p), 1);
    2219      615005 :   D.T = NULL; D.aut = NULL; D.p = p;
    2220      615005 :   setlg(p1, k); return gen_product(p1, (void *)&D, _Flx_mul);
    2221             : }
    2222             : 
    2223             : /* set v[i] = w[i]^{-1}; may be called with w = v, suitable for "large" p */
    2224             : INLINE void
    2225    10329912 : Flv_inv_pre_indir(GEN w, GEN v, ulong p, ulong pi)
    2226             : {
    2227    10329912 :   pari_sp av = avma;
    2228    10329912 :   long n = lg(w), i;
    2229             :   ulong u;
    2230             :   GEN c;
    2231             : 
    2232    10329912 :   if (n == 1) return;
    2233    10329912 :   c = cgetg(n, t_VECSMALL); c[1] = w[1];
    2234    10329912 :   for (i = 2; i < n; ++i) c[i] = Fl_mul_pre(w[i], c[i-1], p, pi);
    2235    10329915 :   i = n-1; u = Fl_inv(c[i], p);
    2236    56705347 :   for ( ; i > 1; --i)
    2237             :   {
    2238    46375431 :     ulong t = Fl_mul_pre(u, c[i-1], p, pi);
    2239    46375469 :     u = Fl_mul_pre(u, w[i], p, pi); v[i] = t;
    2240             :   }
    2241    10329916 :   v[1] = u; set_avma(av);
    2242             : }
    2243             : 
    2244             : void
    2245    10295855 : Flv_inv_pre_inplace(GEN v, ulong p, ulong pi) { Flv_inv_pre_indir(v,v, p, pi); }
    2246             : 
    2247             : GEN
    2248       10679 : Flv_inv_pre(GEN w, ulong p, ulong pi)
    2249       10679 : { GEN v = cgetg(lg(w), t_VECSMALL); Flv_inv_pre_indir(w, v, p, pi); return v; }
    2250             : 
    2251             : /* set v[i] = w[i]^{-1}; may be called with w = v, suitable for SMALL_ULONG p */
    2252             : INLINE void
    2253       29128 : Flv_inv_indir(GEN w, GEN v, ulong p)
    2254             : {
    2255       29128 :   pari_sp av = avma;
    2256       29128 :   long n = lg(w), i;
    2257             :   ulong u;
    2258             :   GEN c;
    2259             : 
    2260       29128 :   if (n == 1) return;
    2261       29128 :   c = cgetg(n, t_VECSMALL); c[1] = w[1];
    2262       29122 :   for (i = 2; i < n; ++i) c[i] = Fl_mul(w[i], c[i-1], p);
    2263       29140 :   i = n-1; u = Fl_inv(c[i], p);
    2264      367989 :   for ( ; i > 1; --i)
    2265             :   {
    2266      338846 :     ulong t = Fl_mul(u, c[i-1], p);
    2267      338843 :     u = Fl_mul(u, w[i], p); v[i] = t;
    2268             :   }
    2269       29143 :   v[1] = u; set_avma(av);
    2270             : }
    2271             : static void
    2272       52508 : Flv_inv_i(GEN v, GEN w, ulong p)
    2273             : {
    2274       52508 :   if (SMALL_ULONG(p)) Flv_inv_indir(w, v, p);
    2275       23379 :   else Flv_inv_pre_indir(w, v, p, get_Fl_red(p));
    2276       52522 : }
    2277             : void
    2278           0 : Flv_inv_inplace(GEN v, ulong p) { Flv_inv_i(v, v, p); }
    2279             : GEN
    2280       52516 : Flv_inv(GEN w, ulong p)
    2281       52516 : { GEN v = cgetg(lg(w), t_VECSMALL); Flv_inv_i(v, w, p); return v; }
    2282             : 
    2283             : GEN
    2284    28597906 : Flx_div_by_X_x(GEN a, ulong x, ulong p, ulong *rem)
    2285             : {
    2286    28597906 :   long l = lg(a), i;
    2287             :   GEN a0, z0;
    2288    28597906 :   GEN z = cgetg(l-1,t_VECSMALL);
    2289    28407203 :   z[1] = a[1];
    2290    28407203 :   a0 = a + l-1;
    2291    28407203 :   z0 = z + l-2; *z0 = *a0--;
    2292    28407203 :   if (SMALL_ULONG(p))
    2293             :   {
    2294    69940695 :     for (i=l-3; i>1; i--) /* z[i] = (a[i+1] + x*z[i+1]) % p */
    2295             :     {
    2296    52437142 :       ulong t = (*a0-- + x *  *z0--) % p;
    2297    52437142 :       *z0 = (long)t;
    2298             :     }
    2299    17503553 :     if (rem) *rem = (*a0 + x *  *z0) % p;
    2300             :   }
    2301             :   else
    2302             :   {
    2303    43124259 :     for (i=l-3; i>1; i--)
    2304             :     {
    2305    32073672 :       ulong t = Fl_add((ulong)*a0--, Fl_mul(x, *z0--, p), p);
    2306    32220609 :       *z0 = (long)t;
    2307             :     }
    2308    11050587 :     if (rem) *rem = Fl_add((ulong)*a0, Fl_mul(x, *z0, p), p);
    2309             :   }
    2310    28551673 :   return z;
    2311             : }
    2312             : 
    2313             : /* xa, ya = t_VECSMALL */
    2314             : static GEN
    2315       52510 : Flv_producttree(GEN xa, GEN s, ulong p, long vs)
    2316             : {
    2317       52510 :   long n = lg(xa)-1;
    2318       52510 :   long m = n==1 ? 1: expu(n-1)+1;
    2319       52508 :   long i, j, k, ls = lg(s);
    2320       52508 :   GEN T = cgetg(m+1, t_VEC);
    2321       52504 :   GEN t = cgetg(ls, t_VEC);
    2322      619428 :   for (j=1, k=1; j<ls; k+=s[j++])
    2323     1133855 :     gel(t, j) = s[j] == 1 ?
    2324      773683 :              mkvecsmall3(vs, Fl_neg(xa[k], p), 1):
    2325      206725 :              mkvecsmall4(vs, Fl_mul(xa[k], xa[k+1], p),
    2326      206719 :                  Fl_neg(Fl_add(xa[k],xa[k+1],p),p), 1);
    2327       52501 :   gel(T,1) = t;
    2328      194215 :   for (i=2; i<=m; i++)
    2329             :   {
    2330      141699 :     GEN u = gel(T, i-1);
    2331      141699 :     long n = lg(u)-1;
    2332      141699 :     GEN t = cgetg(((n+1)>>1)+1, t_VEC);
    2333      656187 :     for (j=1, k=1; k<n; j++, k+=2)
    2334      514473 :       gel(t, j) = Flx_mul(gel(u, k), gel(u, k+1), p);
    2335      141714 :     gel(T, i) = t;
    2336             :   }
    2337       52516 :   return T;
    2338             : }
    2339             : 
    2340             : static GEN
    2341       52515 : Flx_Flv_multieval_tree(GEN P, GEN xa, GEN T, ulong p)
    2342             : {
    2343             :   long i,j,k;
    2344       52515 :   long m = lg(T)-1;
    2345             :   GEN t;
    2346       52515 :   GEN R = cgetg(lg(xa), t_VECSMALL);
    2347       52510 :   GEN Tp = cgetg(m+1, t_VEC);
    2348       52510 :   gel(Tp, m) = mkvec(P);
    2349      194179 :   for (i=m-1; i>=1; i--)
    2350             :   {
    2351      141671 :     GEN u = gel(T, i);
    2352      141671 :     GEN v = gel(Tp, i+1);
    2353      141671 :     long n = lg(u)-1;
    2354      141671 :     t = cgetg(n+1, t_VEC);
    2355      655817 :     for (j=1, k=1; k<n; j++, k+=2)
    2356             :     {
    2357      514143 :       gel(t, k)   = Flx_rem(gel(v, j), gel(u, k), p);
    2358      514120 :       gel(t, k+1) = Flx_rem(gel(v, j), gel(u, k+1), p);
    2359             :     }
    2360      141674 :     gel(Tp, i) = t;
    2361             :   }
    2362             :   {
    2363       52508 :     GEN u = gel(T, i+1);
    2364       52508 :     GEN v = gel(Tp, i+1);
    2365       52508 :     long n = lg(u)-1;
    2366      619858 :     for (j=1, k=1; j<=n; j++)
    2367             :     {
    2368      567336 :       long c, d = degpol(gel(u,j));
    2369     1341294 :       for (c=1; c<=d; c++, k++)
    2370      773944 :         R[k] = Flx_eval(gel(v, j), xa[k], p);
    2371             :     }
    2372       52522 :     set_avma((pari_sp)R); return R;
    2373             :   }
    2374             : }
    2375             : 
    2376             : static GEN
    2377      740156 : FlvV_polint_tree(GEN T, GEN R, GEN s, GEN xa, GEN ya, ulong p, long vs)
    2378             : {
    2379      740156 :   pari_sp av = avma;
    2380      740156 :   long m = lg(T)-1;
    2381      740156 :   long i, j, k, ls = lg(s);
    2382      740156 :   GEN Tp = cgetg(m+1, t_VEC);
    2383      739484 :   GEN t = cgetg(ls, t_VEC);
    2384    12866736 :   for (j=1, k=1; j<ls; k+=s[j++])
    2385    12127672 :     if (s[j]==2)
    2386             :     {
    2387     4149444 :       ulong a = Fl_mul(ya[k], R[k], p);
    2388     4188803 :       ulong b = Fl_mul(ya[k+1], R[k+1], p);
    2389    12580666 :       gel(t, j) = mkvecsmall3(vs, Fl_neg(Fl_add(Fl_mul(xa[k], b, p ),
    2390     8387200 :                   Fl_mul(xa[k+1], a, p), p), p), Fl_add(a, b, p));
    2391     4190676 :       gel(t, j) = Flx_renormalize(gel(t, j), 4);
    2392             :     }
    2393             :     else
    2394     7978228 :       gel(t, j) = Fl_to_Flx(Fl_mul(ya[k], R[k], p), vs);
    2395      739064 :   gel(Tp, 1) = t;
    2396     3285956 :   for (i=2; i<=m; i++)
    2397             :   {
    2398     2546563 :     GEN u = gel(T, i-1);
    2399     2546563 :     GEN t = cgetg(lg(gel(T,i)), t_VEC);
    2400     2541630 :     GEN v = gel(Tp, i-1);
    2401     2541630 :     long n = lg(v)-1;
    2402    13918755 :     for (j=1, k=1; k<n; j++, k+=2)
    2403    34115589 :       gel(t, j) = Flx_add(Flx_mul(gel(u, k), gel(v, k+1), p),
    2404    22743726 :                           Flx_mul(gel(u, k+1), gel(v, k), p), p);
    2405     2546892 :     gel(Tp, i) = t;
    2406             :   }
    2407      739393 :   return gerepileuptoleaf(av, gmael(Tp,m,1));
    2408             : }
    2409             : 
    2410             : GEN
    2411           0 : Flx_Flv_multieval(GEN P, GEN xa, ulong p)
    2412             : {
    2413           0 :   pari_sp av = avma;
    2414           0 :   GEN s = producttree_scheme(lg(xa)-1);
    2415           0 :   GEN T = Flv_producttree(xa, s, p, P[1]);
    2416           0 :   return gerepileuptoleaf(av, Flx_Flv_multieval_tree(P, xa, T, p));
    2417             : }
    2418             : 
    2419             : GEN
    2420       12709 : Flv_polint(GEN xa, GEN ya, ulong p, long vs)
    2421             : {
    2422       12709 :   pari_sp av = avma;
    2423       12709 :   GEN s = producttree_scheme(lg(xa)-1);
    2424       12709 :   GEN T = Flv_producttree(xa, s, p, vs);
    2425       12709 :   long m = lg(T)-1;
    2426       12709 :   GEN P = Flx_deriv(gmael(T, m, 1), p);
    2427       12709 :   GEN R = Flv_inv(Flx_Flv_multieval_tree(P, xa, T, p), p);
    2428       12709 :   return gerepileuptoleaf(av, FlvV_polint_tree(T, R, s, xa, ya, p, vs));
    2429             : }
    2430             : 
    2431             : GEN
    2432       35232 : Flv_Flm_polint(GEN xa, GEN ya, ulong p, long vs)
    2433             : {
    2434       35232 :   pari_sp av = avma;
    2435       35232 :   GEN s = producttree_scheme(lg(xa)-1);
    2436       35230 :   GEN T = Flv_producttree(xa, s, p, vs);
    2437       35232 :   long i, m = lg(T)-1, l = lg(ya)-1;
    2438       35232 :   GEN P = Flx_deriv(gmael(T, m, 1), p);
    2439       35229 :   GEN R = Flv_inv(Flx_Flv_multieval_tree(P, xa, T, p), p);
    2440       35238 :   GEN M = cgetg(l+1, t_VEC);
    2441      762648 :   for (i=1; i<=l; i++)
    2442      727416 :     gel(M,i) = FlvV_polint_tree(T, R, s, xa, gel(ya,i), p, vs);
    2443       35232 :   return gerepileupto(av, M);
    2444             : }
    2445             : 
    2446             : GEN
    2447        4575 : Flv_invVandermonde(GEN L, ulong den, ulong p)
    2448             : {
    2449        4575 :   pari_sp av = avma;
    2450        4575 :   long i, n = lg(L);
    2451             :   GEN M, R;
    2452        4575 :   GEN s = producttree_scheme(n-1);
    2453        4575 :   GEN tree = Flv_producttree(L, s, p, 0);
    2454        4575 :   long m = lg(tree)-1;
    2455        4575 :   GEN T = gmael(tree, m, 1);
    2456        4575 :   R = Flv_inv(Flx_Flv_multieval_tree(Flx_deriv(T, p), L, tree, p), p);
    2457        4575 :   if (den!=1) R = Flv_Fl_mul(R, den, p);
    2458        4575 :   M = cgetg(n, t_MAT);
    2459       18462 :   for (i = 1; i < n; i++)
    2460             :   {
    2461       13887 :     GEN P = Flx_Fl_mul(Flx_div_by_X_x(T, uel(L,i), p, NULL), uel(R,i), p);
    2462       13888 :     gel(M,i) = Flx_to_Flv(P, n-1);
    2463             :   }
    2464        4575 :   return gerepilecopy(av, M);
    2465             : }
    2466             : 
    2467             : /***********************************************************************/
    2468             : /**                                                                   **/
    2469             : /**                               Flxq                                **/
    2470             : /**                                                                   **/
    2471             : /***********************************************************************/
    2472             : /* Flxq objects are defined as follows:
    2473             :    They are Flx modulo another Flx called q.
    2474             : */
    2475             : 
    2476             : /* Product of y and x in Z/pZ[X]/(T), as t_VECSMALL. */
    2477             : GEN
    2478   123674957 : Flxq_mul(GEN x,GEN y,GEN T,ulong p)
    2479             : {
    2480   123674957 :   return Flx_rem(Flx_mul(x,y,p),T,p);
    2481             : }
    2482             : 
    2483             : /* Square of y in Z/pZ[X]/(T), as t_VECSMALL. */
    2484             : GEN
    2485   184402569 : Flxq_sqr(GEN x,GEN T,ulong p)
    2486             : {
    2487   184402569 :   return Flx_rem(Flx_sqr(x,p),T,p);
    2488             : }
    2489             : 
    2490             : static GEN
    2491      102529 : _Flxq_red(void *E, GEN x)
    2492      102529 : { struct _Flxq *s = (struct _Flxq *)E;
    2493      102529 :   return Flx_rem(x, s->T, s->p); }
    2494             : #if 0
    2495             : static GEN
    2496             : _Flx_sub(void *E, GEN x, GEN y)
    2497             : { struct _Flxq *s = (struct _Flxq *)E;
    2498             :   return Flx_sub(x,y,s->p); }
    2499             : #endif
    2500             : static GEN
    2501   178605071 : _Flxq_sqr(void *data, GEN x)
    2502             : {
    2503   178605071 :   struct _Flxq *D = (struct _Flxq*)data;
    2504   178605071 :   return Flxq_sqr(x, D->T, D->p);
    2505             : }
    2506             : static GEN
    2507   101077649 : _Flxq_mul(void *data, GEN x, GEN y)
    2508             : {
    2509   101077649 :   struct _Flxq *D = (struct _Flxq*)data;
    2510   101077649 :   return Flxq_mul(x,y, D->T, D->p);
    2511             : }
    2512             : static GEN
    2513     4361507 : _Flxq_one(void *data)
    2514             : {
    2515     4361507 :   struct _Flxq *D = (struct _Flxq*)data;
    2516     4361507 :   return pol1_Flx(get_Flx_var(D->T));
    2517             : }
    2518             : #if 0
    2519             : static GEN
    2520             : _Flxq_zero(void *data)
    2521             : {
    2522             :   struct _Flxq *D = (struct _Flxq*)data;
    2523             :   return pol0_Flx(get_Flx_var(D->T));
    2524             : }
    2525             : static GEN
    2526             : _Flxq_cmul(void *data, GEN P, long a, GEN x)
    2527             : {
    2528             :   struct _Flxq *D = (struct _Flxq*)data;
    2529             :   return Flx_Fl_mul(x, P[a+2], D->p);
    2530             : }
    2531             : #endif
    2532             : 
    2533             : /* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
    2534             : GEN
    2535    11218573 : Flxq_powu(GEN x, ulong n, GEN T, ulong p)
    2536             : {
    2537    11218573 :   pari_sp av = avma;
    2538             :   struct _Flxq D;
    2539             :   GEN y;
    2540    11218573 :   switch(n)
    2541             :   {
    2542           0 :     case 0: return pol1_Flx(T[1]);
    2543       50336 :     case 1: return Flx_copy(x);
    2544      148850 :     case 2: return Flxq_sqr(x, T, p);
    2545             :   }
    2546    11019387 :   D.T = Flx_get_red(T, p); D.p = p;
    2547    11015802 :   y = gen_powu_i(x, n, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
    2548    11004842 :   return gerepileuptoleaf(av, y);
    2549             : }
    2550             : 
    2551             : /* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
    2552             : GEN
    2553    22155745 : Flxq_pow(GEN x, GEN n, GEN T, ulong p)
    2554             : {
    2555    22155745 :   pari_sp av = avma;
    2556             :   struct _Flxq D;
    2557             :   GEN y;
    2558    22155745 :   long s = signe(n);
    2559    22155745 :   if (!s) return pol1_Flx(get_Flx_var(T));
    2560    21961706 :   if (s < 0)
    2561      598891 :     x = Flxq_inv(x,T,p);
    2562    21961706 :   if (is_pm1(n)) return s < 0 ? x : Flx_copy(x);
    2563    21166160 :   D.T = Flx_get_red(T, p); D.p = p;
    2564    21166153 :   y = gen_pow_i(x, n, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
    2565    21166162 :   return gerepileuptoleaf(av, y);
    2566             : }
    2567             : 
    2568             : GEN
    2569          28 : Flxq_pow_init(GEN x, GEN n, long k,  GEN T, ulong p)
    2570             : {
    2571             :   struct _Flxq D;
    2572          28 :   D.T = Flx_get_red(T, p); D.p = p;
    2573          28 :   return gen_pow_init(x, n, k, (void*)&D, &_Flxq_sqr, &_Flxq_mul);
    2574             : }
    2575             : 
    2576             : GEN
    2577        4397 : Flxq_pow_table(GEN R, GEN n, GEN T, ulong p)
    2578             : {
    2579             :   struct _Flxq D;
    2580        4397 :   D.T = Flx_get_red(T, p); D.p = p;
    2581        4397 :   return gen_pow_table(R, n, (void*)&D, &_Flxq_one, &_Flxq_mul);
    2582             : }
    2583             : 
    2584             : /* Inverse of x in Z/lZ[X]/(T) or NULL if inverse doesn't exist
    2585             :  * not stack clean.
    2586             :  */
    2587             : GEN
    2588     3759881 : Flxq_invsafe(GEN x, GEN T, ulong p)
    2589             : {
    2590     3759881 :   GEN V, z = Flx_extgcd(get_Flx_mod(T), x, p, NULL, &V);
    2591             :   ulong iz;
    2592     3759881 :   if (degpol(z)) return NULL;
    2593     3759853 :   iz = Fl_inv (uel(z,2), p);
    2594     3759853 :   return Flx_Fl_mul(V, iz, p);
    2595             : }
    2596             : 
    2597             : GEN
    2598     3695384 : Flxq_inv(GEN x,GEN T,ulong p)
    2599             : {
    2600     3695384 :   pari_sp av=avma;
    2601     3695384 :   GEN U = Flxq_invsafe(x, T, p);
    2602     3695384 :   if (!U) pari_err_INV("Flxq_inv",Flx_to_ZX(x));
    2603     3695356 :   return gerepileuptoleaf(av, U);
    2604             : }
    2605             : 
    2606             : GEN
    2607     1943690 : Flxq_div(GEN x,GEN y,GEN T,ulong p)
    2608             : {
    2609     1943690 :   pari_sp av = avma;
    2610     1943690 :   return gerepileuptoleaf(av, Flxq_mul(x,Flxq_inv(y,T,p),T,p));
    2611             : }
    2612             : 
    2613             : GEN
    2614     4357211 : Flxq_powers(GEN x, long l, GEN T, ulong p)
    2615             : {
    2616             :   struct _Flxq D;
    2617     4357211 :   int use_sqr = 2*degpol(x) >= get_Flx_degree(T);
    2618     4357137 :   D.T = Flx_get_red(T, p); D.p = p;
    2619     4356845 :   return gen_powers(x, l, use_sqr, (void*)&D, &_Flxq_sqr, &_Flxq_mul, &_Flxq_one);
    2620             : }
    2621             : 
    2622             : GEN
    2623       33243 : Flxq_matrix_pow(GEN y, long n, long m, GEN P, ulong l)
    2624             : {
    2625       33243 :   return FlxV_to_Flm(Flxq_powers(y,m-1,P,l),n);
    2626             : }
    2627             : 
    2628             : GEN
    2629     3897988 : Flx_Frobenius(GEN T, ulong p)
    2630             : {
    2631     3897988 :   return Flxq_powu(polx_Flx(get_Flx_var(T)), p, T, p);
    2632             : }
    2633             : 
    2634             : GEN
    2635       17080 : Flx_matFrobenius(GEN T, ulong p)
    2636             : {
    2637       17080 :   long n = get_Flx_degree(T);
    2638       17080 :   return Flxq_matrix_pow(Flx_Frobenius(T, p), n, n, T, p);
    2639             : }
    2640             : 
    2641             : static GEN
    2642     4431162 : Flx_blocks_Flm(GEN P, long n, long m)
    2643             : {
    2644     4431162 :   GEN z = cgetg(m+1,t_MAT);
    2645     4431271 :   long i,j, k=2, l = lg(P);
    2646    13588008 :   for(i=1; i<=m; i++)
    2647             :   {
    2648     9157723 :     GEN zi = cgetg(n+1,t_VECSMALL);
    2649     9156737 :     gel(z,i) = zi;
    2650    41828914 :     for(j=1; j<=n; j++)
    2651    32672177 :       uel(zi, j) = k==l ? 0 : uel(P,k++);
    2652             :   }
    2653     4430285 :   return z;
    2654             : }
    2655             : 
    2656             : static GEN
    2657     4431200 : FlxV_to_Flm_lg(GEN x, long m, long n)
    2658             : {
    2659             :   long i;
    2660     4431200 :   GEN y = cgetg(n+1, t_MAT);
    2661     4430535 :   for (i=1; i<=n; i++) gel(y,i) = Flx_to_Flv(gel(x,i), m);
    2662     4431104 :   return y;
    2663             : }
    2664             : 
    2665             : GEN
    2666     4627660 : Flx_FlxqV_eval(GEN Q, GEN x, GEN T, ulong p)
    2667             : {
    2668     4627660 :   pari_sp btop, av = avma;
    2669     4627660 :   long sv = get_Flx_var(T), m = get_Flx_degree(T);
    2670     4627741 :   long i, l = lg(x)-1, lQ = lgpol(Q), n,  d;
    2671             :   GEN A, B, C, S, g;
    2672     4628047 :   if (lQ == 0) return pol0_Flx(sv);
    2673     4431471 :   if (lQ <= l)
    2674             :   {
    2675     2080591 :     n = l;
    2676     2080591 :     d = 1;
    2677             :   }
    2678             :   else
    2679             :   {
    2680     2350880 :     n = l-1;
    2681     2350880 :     d = (lQ+n-1)/n;
    2682             :   }
    2683     4431471 :   A = FlxV_to_Flm_lg(x, m, n);
    2684     4431113 :   B = Flx_blocks_Flm(Q, n, d);
    2685     4430316 :   C = gerepileupto(av, Flm_mul(A, B, p));
    2686     4431693 :   g = gel(x, l);
    2687     4431693 :   btop = avma;
    2688     4431693 :   S = Flv_to_Flx(gel(C, d), sv);
    2689     9160576 :   for (i = d-1; i>0; i--)
    2690             :   {
    2691     4729721 :     S = Flx_add(Flxq_mul(S, g, T, p), Flv_to_Flx(gel(C,i), sv), p);
    2692     4729143 :     if (gc_needed(btop,1))
    2693           0 :       S = gerepileuptoleaf(btop, S);
    2694             :   }
    2695     4430855 :   return gerepileuptoleaf(av, S);
    2696             : }
    2697             : 
    2698             : GEN
    2699     1012907 : Flx_Flxq_eval(GEN Q, GEN x, GEN T, ulong p)
    2700             : {
    2701     1012907 :   pari_sp av = avma;
    2702             :   GEN z, V;
    2703     1012907 :   long d = degpol(Q), rtd;
    2704     1012903 :   if (d < 0) return pol0_Flx(get_Flx_var(T));
    2705     1012826 :   rtd = (long) sqrt((double)d);
    2706     1012826 :   T = Flx_get_red(T, p);
    2707     1012776 :   V = Flxq_powers(x, rtd, T, p);
    2708     1012824 :   z = Flx_FlxqV_eval(Q, V, T, p);
    2709     1012831 :   return gerepileupto(av, z);
    2710             : }
    2711             : 
    2712             : #if 0
    2713             : static struct bb_algebra Flxq_algebra = { _Flxq_red, _Flx_add, _Flx_sub,
    2714             :               _Flxq_mul, _Flxq_sqr, _Flxq_one, _Flxq_zero};
    2715             : #endif
    2716             : 
    2717             : static GEN
    2718      377514 : Flxq_autpow_sqr(void *E, GEN x)
    2719             : {
    2720      377514 :   struct _Flxq *D = (struct _Flxq*)E;
    2721      377514 :   return Flx_Flxq_eval(x, x, D->T, D->p);
    2722             : }
    2723             : static GEN
    2724       20655 : Flxq_autpow_mul(void *E, GEN x, GEN y)
    2725             : {
    2726       20655 :   struct _Flxq *D = (struct _Flxq*)E;
    2727       20655 :   return Flx_Flxq_eval(x, y, D->T, D->p);
    2728             : }
    2729             : 
    2730             : GEN
    2731      304349 : Flxq_autpow(GEN x, ulong n, GEN T, ulong p)
    2732             : {
    2733             :   struct _Flxq D;
    2734      304349 :   if (n==0) return Flx_rem(polx_Flx(x[1]), T, p);
    2735      304342 :   if (n==1) return Flx_rem(x, T, p);
    2736      303873 :   D.T = Flx_get_red(T, p); D.p = p;
    2737      303873 :   return gen_powu(x,n,(void*)&D,Flxq_autpow_sqr,Flxq_autpow_mul);
    2738             : }
    2739             : 
    2740             : static GEN
    2741      615824 : Flxq_autsum_mul(void *E, GEN x, GEN y)
    2742             : {
    2743      615824 :   struct _Flxq *D = (struct _Flxq*)E;
    2744      615824 :   GEN T = D->T;
    2745      615824 :   ulong p = D->p;
    2746      615824 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    2747      615824 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    2748      615824 :   ulong d = brent_kung_optpow(maxss(degpol(phi1),degpol(a1)),2,1);
    2749      615824 :   GEN V2 = Flxq_powers(phi2, d, T, p);
    2750      615824 :   GEN phi3 = Flx_FlxqV_eval(phi1, V2, T, p);
    2751      615824 :   GEN aphi = Flx_FlxqV_eval(a1, V2, T, p);
    2752      615824 :   GEN a3 = Flxq_mul(aphi, a2, T, p);
    2753      615824 :   return mkvec2(phi3, a3);
    2754             : }
    2755             : static GEN
    2756      367851 : Flxq_autsum_sqr(void *E, GEN x)
    2757      367851 : { return Flxq_autsum_mul(E, x, x); }
    2758             : 
    2759             : GEN
    2760      308535 : Flxq_autsum(GEN x, ulong n, GEN T, ulong p)
    2761             : {
    2762             :   struct _Flxq D;
    2763      308535 :   D.T = Flx_get_red(T, p); D.p = p;
    2764      308535 :   return gen_powu(x,n,(void*)&D,Flxq_autsum_sqr,Flxq_autsum_mul);
    2765             : }
    2766             : 
    2767             : static GEN
    2768      217406 : Flxq_auttrace_mul(void *E, GEN x, GEN y)
    2769             : {
    2770      217406 :   struct _Flxq *D = (struct _Flxq*)E;
    2771      217406 :   GEN T = D->T;
    2772      217406 :   ulong p = D->p;
    2773      217406 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    2774      217406 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    2775      217406 :   ulong d = brent_kung_optpow(maxss(degpol(phi1),degpol(a1)),2,1);
    2776      217437 :   GEN V1 = Flxq_powers(phi1, d, T, p);
    2777      217380 :   GEN phi3 = Flx_FlxqV_eval(phi2, V1, T, p);
    2778      217415 :   GEN aphi = Flx_FlxqV_eval(a2, V1, T, p);
    2779      217418 :   GEN a3 = Flx_add(a1, aphi, p);
    2780      217402 :   return mkvec2(phi3, a3);
    2781             : }
    2782             : 
    2783             : static GEN
    2784      170859 : Flxq_auttrace_sqr(void *E, GEN x)
    2785      170859 : { return Flxq_auttrace_mul(E, x, x); }
    2786             : 
    2787             : GEN
    2788      226291 : Flxq_auttrace(GEN x, ulong n, GEN T, ulong p)
    2789             : {
    2790             :   struct _Flxq D;
    2791      226291 :   D.T = Flx_get_red(T, p); D.p = p;
    2792      226279 :   return gen_powu(x,n,(void*)&D,Flxq_auttrace_sqr,Flxq_auttrace_mul);
    2793             : }
    2794             : 
    2795             : static long
    2796      669330 : bounded_order(ulong p, GEN b, long k)
    2797             : {
    2798             :   long i;
    2799      669330 :   GEN a=modii(utoi(p),b);
    2800     1704757 :   for(i=1;i<k;i++)
    2801             :   {
    2802     1410958 :     if (equali1(a))
    2803      375531 :       return i;
    2804     1035427 :     a = modii(muliu(a,p),b);
    2805             :   }
    2806      293799 :   return 0;
    2807             : }
    2808             : 
    2809             : /*
    2810             :   n = (p^d-a)\b
    2811             :   b = bb*p^vb
    2812             :   p^k = 1 [bb]
    2813             :   d = m*k+r+vb
    2814             :   u = (p^k-1)/bb;
    2815             :   v = (p^(r+vb)-a)/b;
    2816             :   w = (p^(m*k)-1)/(p^k-1)
    2817             :   n = p^r*w*u+v
    2818             :   w*u = p^vb*(p^(m*k)-1)/b
    2819             :   n = p^(r+vb)*(p^(m*k)-1)/b+(p^(r+vb)-a)/b
    2820             : */
    2821             : 
    2822             : static GEN
    2823    21924348 : Flxq_pow_Frobenius(GEN x, GEN n, GEN aut, GEN T, ulong p)
    2824             : {
    2825    21924348 :   pari_sp av=avma;
    2826    21924348 :   long d = get_Flx_degree(T);
    2827    21924348 :   GEN an = absi_shallow(n), z, q;
    2828    21924348 :   if (abscmpiu(an,p)<0 || cmpis(an,d)<=0) return Flxq_pow(x, n, T, p);
    2829      670197 :   q = powuu(p, d);
    2830      670197 :   if (dvdii(q, n))
    2831             :   {
    2832         818 :     long vn = logint(an,utoi(p));
    2833         818 :     GEN autvn = vn==1 ? aut: Flxq_autpow(aut,vn,T,p);
    2834         818 :     z = Flx_Flxq_eval(x,autvn,T,p);
    2835             :   } else
    2836             :   {
    2837      669379 :     GEN b = diviiround(q, an), a = subii(q, mulii(an,b));
    2838             :     GEN bb, u, v, autk;
    2839      669379 :     long vb = Z_lvalrem(b,p,&bb);
    2840      669379 :     long m, r, k = is_pm1(bb) ? 1 : bounded_order(p,bb,d);
    2841      669379 :     if (!k || d-vb<k) return Flxq_pow(x,n, T, p);
    2842      375573 :     m = (d-vb)/k; r = (d-vb)%k;
    2843      375573 :     u = diviiexact(subiu(powuu(p,k),1),bb);
    2844      375573 :     v = diviiexact(subii(powuu(p,r+vb),a),b);
    2845      375573 :     autk = k==1 ? aut: Flxq_autpow(aut,k,T,p);
    2846      375573 :     if (r)
    2847             :     {
    2848       92772 :       GEN autr = r==1 ? aut: Flxq_autpow(aut,r,T,p);
    2849       92772 :       z = Flx_Flxq_eval(x,autr,T,p);
    2850      282801 :     } else z = x;
    2851      375573 :     if (m > 1) z = gel(Flxq_autsum(mkvec2(autk, z), m, T, p), 2);
    2852      375573 :     if (!is_pm1(u)) z = Flxq_pow(z, u, T, p);
    2853      375573 :     if (signe(v)) z = Flxq_mul(z, Flxq_pow(x, v, T, p), T, p);
    2854             :   }
    2855      376391 :   return gerepileupto(av,signe(n)>0 ? z : Flxq_inv(z,T,p));
    2856             : }
    2857             : 
    2858             : static GEN
    2859    21904316 : _Flxq_pow(void *data, GEN x, GEN n)
    2860             : {
    2861    21904316 :   struct _Flxq *D = (struct _Flxq*)data;
    2862    21904316 :   return Flxq_pow_Frobenius(x, n, D->aut, D->T, D->p);
    2863             : }
    2864             : 
    2865             : static GEN
    2866      319718 : _Flxq_rand(void *data)
    2867             : {
    2868      319718 :   pari_sp av=avma;
    2869      319718 :   struct _Flxq *D = (struct _Flxq*)data;
    2870             :   GEN z;
    2871             :   do
    2872             :   {
    2873      322756 :     set_avma(av);
    2874      322756 :     z = random_Flx(get_Flx_degree(D->T),get_Flx_var(D->T),D->p);
    2875      322756 :   } while (lgpol(z)==0);
    2876      319718 :   return z;
    2877             : }
    2878             : 
    2879             : /* discrete log in FpXQ for a in Fp^*, g in FpXQ^* of order ord */
    2880             : static GEN
    2881       10927 : Fl_Flxq_log(ulong a, GEN g, GEN o, GEN T, ulong p)
    2882             : {
    2883       10927 :   pari_sp av = avma;
    2884             :   GEN q,n_q,ord,ordp, op;
    2885             : 
    2886       10927 :   if (a == 1UL) return gen_0;
    2887             :   /* p > 2 */
    2888             : 
    2889       10927 :   ordp = utoi(p - 1);
    2890       10927 :   ord  = get_arith_Z(o);
    2891       10927 :   if (!ord) ord = T? subiu(powuu(p, get_FpX_degree(T)), 1): ordp;
    2892       10927 :   if (a == p - 1) /* -1 */
    2893         768 :     return gerepileuptoint(av, shifti(ord,-1));
    2894       10159 :   ordp = gcdii(ordp, ord);
    2895       10159 :   op = typ(o)==t_MAT ? famat_Z_gcd(o, ordp) : ordp;
    2896             : 
    2897       10159 :   q = NULL;
    2898       10159 :   if (T)
    2899             :   { /* we want < g > = Fp^* */
    2900       10159 :     if (!equalii(ord,ordp)) {
    2901         591 :       q = diviiexact(ord,ordp);
    2902         591 :       g = Flxq_pow(g,q,T,p);
    2903             :     }
    2904             :   }
    2905       10159 :   n_q = Fp_log(utoi(a), utoi(uel(g,2)), op, utoi(p));
    2906       10159 :   if (lg(n_q)==1) return gerepileuptoleaf(av, n_q);
    2907       10159 :   if (q) n_q = mulii(q, n_q);
    2908       10159 :   return gerepileuptoint(av, n_q);
    2909             : }
    2910             : 
    2911             : static GEN
    2912      330355 : Flxq_easylog(void* E, GEN a, GEN g, GEN ord)
    2913             : {
    2914      330355 :   struct _Flxq *f = (struct _Flxq *)E;
    2915      330355 :   GEN T = f->T;
    2916      330355 :   ulong p = f->p;
    2917      330355 :   long d = get_Flx_degree(T);
    2918      330355 :   if (Flx_equal1(a)) return gen_0;
    2919      276399 :   if (Flx_equal(a,g)) return gen_1;
    2920       61210 :   if (!degpol(a))
    2921       10927 :     return Fl_Flxq_log(uel(a,2), g, ord, T, p);
    2922       50283 :   if (typ(ord)!=t_INT || d <= 4 || d == 6 || abscmpiu(ord,1UL<<27)<0)
    2923       50255 :     return NULL;
    2924          28 :   return Flxq_log_index(a, g, ord, T, p);
    2925             : }
    2926             : 
    2927             : int
    2928    23571746 : Flx_equal(GEN V, GEN W)
    2929             : {
    2930    23571746 :   long l = lg(V);
    2931    23571746 :   if (lg(W) != l) return 0;
    2932    46587887 :   while (--l > 1) /* do not compare variables, V[1] */
    2933    23226972 :     if (V[l] != W[l]) return 0;
    2934      617255 :   return 1;
    2935             : }
    2936             : 
    2937             : static const struct bb_group Flxq_star={_Flxq_mul,_Flxq_pow,_Flxq_rand,hash_GEN,Flx_equal,Flx_equal1,Flxq_easylog};
    2938             : 
    2939             : const struct bb_group *
    2940      215764 : get_Flxq_star(void **E, GEN T, ulong p)
    2941             : {
    2942      215764 :   struct _Flxq *e = (struct _Flxq *) stack_malloc(sizeof(struct _Flxq));
    2943      215764 :   e->T = T; e->p  = p; e->aut =  Flx_Frobenius(T, p);
    2944      215764 :   *E = (void*)e; return &Flxq_star;
    2945             : }
    2946             : 
    2947             : GEN
    2948       12748 : Flxq_order(GEN a, GEN ord, GEN T, ulong p)
    2949             : {
    2950             :   void *E;
    2951       12748 :   const struct bb_group *S = get_Flxq_star(&E,T,p);
    2952       12748 :   return gen_order(a,ord,E,S);
    2953             : }
    2954             : 
    2955             : GEN
    2956       37036 : Flxq_log(GEN a, GEN g, GEN ord, GEN T, ulong p)
    2957             : {
    2958             :   void *E;
    2959       37036 :   pari_sp av = avma;
    2960       37036 :   const struct bb_group *S = get_Flxq_star(&E,T,p);
    2961       37036 :   GEN v = get_arith_ZZM(ord), F = gmael(v,2,1);
    2962       37036 :   if (Flxq_log_use_index(gel(F,lg(F)-1), T, p))
    2963        9989 :     v = mkvec2(gel(v, 1), ZM_famat_limit(gel(v, 2), int2n(27)));
    2964       37036 :   return gerepileuptoleaf(av, gen_PH_log(a, g, v, E, S));
    2965             : }
    2966             : 
    2967             : GEN
    2968      169200 : Flxq_sqrtn(GEN a, GEN n, GEN T, ulong p, GEN *zeta)
    2969             : {
    2970      169200 :   if (!lgpol(a))
    2971             :   {
    2972        3220 :     if (signe(n) < 0) pari_err_INV("Flxq_sqrtn",a);
    2973        3213 :     if (zeta)
    2974           0 :       *zeta=pol1_Flx(get_Flx_var(T));
    2975        3213 :     return pol0_Flx(get_Flx_var(T));
    2976             :   }
    2977             :   else
    2978             :   {
    2979             :     void *E;
    2980      165980 :     pari_sp av = avma;
    2981      165980 :     const struct bb_group *S = get_Flxq_star(&E,T,p);
    2982      165980 :     GEN o = subiu(powuu(p,get_Flx_degree(T)), 1);
    2983      165980 :     GEN s = gen_Shanks_sqrtn(a,n,o,zeta,E,S);
    2984      165980 :     if (s) gerepileall(av, zeta?2:1, &s, zeta);
    2985      165980 :     return s;
    2986             :   }
    2987             : }
    2988             : 
    2989             : GEN
    2990      161126 : Flxq_sqrt(GEN a, GEN T, ulong p)
    2991             : {
    2992      161126 :   return Flxq_sqrtn(a, gen_2, T, p, NULL);
    2993             : }
    2994             : 
    2995             : /* assume T irreducible mod p */
    2996             : int
    2997      356442 : Flxq_issquare(GEN x, GEN T, ulong p)
    2998             : {
    2999      356442 :   if (lgpol(x) == 0 || p == 2) return 1;
    3000      353173 :   return krouu(Flxq_norm(x,T,p), p) == 1;
    3001             : }
    3002             : 
    3003             : /* assume T irreducible mod p */
    3004             : int
    3005         280 : Flxq_is2npower(GEN x, long n, GEN T, ulong p)
    3006             : {
    3007             :   pari_sp av;
    3008             :   GEN m;
    3009         280 :   if (n==1) return Flxq_issquare(x, T, p);
    3010         280 :   if (lgpol(x) == 0 || p == 2) return 1;
    3011         280 :   av = avma;
    3012         280 :   m = shifti(subiu(powuu(p, get_Flx_degree(T)), 1), -n);
    3013         280 :   return gc_bool(av, Flx_equal1(Flxq_pow(x, m, T, p)));
    3014             : }
    3015             : 
    3016             : GEN
    3017      113505 : Flxq_lroot_fast(GEN a, GEN sqx, GEN T, long p)
    3018             : {
    3019      113505 :   pari_sp av=avma;
    3020      113505 :   GEN A = Flx_splitting(a,p);
    3021      113505 :   return gerepileuptoleaf(av, FlxqV_dotproduct(A,sqx,T,p));
    3022             : }
    3023             : 
    3024             : GEN
    3025       25032 : Flxq_lroot(GEN a, GEN T, long p)
    3026             : {
    3027       25032 :   pari_sp av=avma;
    3028       25032 :   long n = get_Flx_degree(T), d = degpol(a);
    3029             :   GEN sqx, V;
    3030       25032 :   if (n==1) return leafcopy(a);
    3031       25032 :   if (n==2) return Flxq_powu(a, p, T, p);
    3032       25032 :   sqx = Flxq_autpow(Flx_Frobenius(T, p), n-1, T, p);
    3033       25032 :   if (d==1 && a[2]==0 && a[3]==1) return gerepileuptoleaf(av, sqx);
    3034           0 :   if (d>=p)
    3035             :   {
    3036           0 :     V = Flxq_powers(sqx,p-1,T,p);
    3037           0 :     return gerepileuptoleaf(av, Flxq_lroot_fast(a,V,T,p));
    3038             :   } else
    3039           0 :     return gerepileuptoleaf(av, Flx_Flxq_eval(a,sqx,T,p));
    3040             : }
    3041             : 
    3042             : ulong
    3043      384505 : Flxq_norm(GEN x, GEN TB, ulong p)
    3044             : {
    3045      384505 :   GEN T = get_Flx_mod(TB);
    3046      384505 :   ulong y = Flx_resultant(T, x, p);
    3047      384505 :   ulong L = Flx_lead(T);
    3048      384505 :   if ( L==1 || lgpol(x)==0) return y;
    3049           0 :   return Fl_div(y, Fl_powu(L, (ulong)degpol(x), p), p);
    3050             : }
    3051             : 
    3052             : ulong
    3053        3352 : Flxq_trace(GEN x, GEN TB, ulong p)
    3054             : {
    3055        3352 :   pari_sp av = avma;
    3056             :   ulong t;
    3057        3352 :   GEN T = get_Flx_mod(TB);
    3058        3352 :   long n = degpol(T)-1;
    3059        3352 :   GEN z = Flxq_mul(x, Flx_deriv(T, p), TB, p);
    3060        3352 :   t = degpol(z)<n ? 0 : Fl_div(z[2+n],T[3+n],p);
    3061        3352 :   return gc_ulong(av, t);
    3062             : }
    3063             : 
    3064             : /*x must be reduced*/
    3065             : GEN
    3066          27 : Flxq_charpoly(GEN x, GEN TB, ulong p)
    3067             : {
    3068          27 :   pari_sp ltop=avma;
    3069          27 :   GEN T = get_Flx_mod(TB);
    3070          27 :   long vs = evalvarn(fetch_var());
    3071          27 :   GEN xm1 = deg1pol_shallow(pol1_Flx(x[1]),Flx_neg(x,p),vs);
    3072          27 :   GEN r = Flx_FlxY_resultant(T, xm1, p);
    3073          27 :   r[1] = x[1];
    3074          27 :   (void)delete_var(); return gerepileupto(ltop, r);
    3075             : }
    3076             : 
    3077             : /* Computing minimal polynomial :                         */
    3078             : /* cf Shoup 'Efficient Computation of Minimal Polynomials */
    3079             : /*          in Algebraic Extensions of Finite Fields'     */
    3080             : 
    3081             : /* Let v a linear form, return the linear form z->v(tau*z)
    3082             :    that is, v*(M_tau) */
    3083             : 
    3084             : static GEN
    3085      364197 : Flxq_transmul_init(GEN tau, GEN T, ulong p)
    3086             : {
    3087             :   GEN bht;
    3088      364197 :   GEN h, Tp = get_Flx_red(T, &h);
    3089      364195 :   long n = degpol(Tp), vT = Tp[1];
    3090      364188 :   GEN ft = Flx_recipspec(Tp+2, n+1, n+1);
    3091      364166 :   GEN bt = Flx_recipspec(tau+2, lgpol(tau), n);
    3092      364169 :   ft[1] = vT; bt[1] = vT;
    3093      364169 :   if (h)
    3094        2230 :     bht = Flxn_mul(bt, h, n-1, p);
    3095             :   else
    3096             :   {
    3097      361939 :     GEN bh = Flx_div(Flx_shift(tau, n-1), T, p);
    3098      361936 :     bht = Flx_recipspec(bh+2, lgpol(bh), n-1);
    3099      361941 :     bht[1] = vT;
    3100             :   }
    3101      364171 :   return mkvec3(bt, bht, ft);
    3102             : }
    3103             : 
    3104             : static GEN
    3105      920642 : Flxq_transmul(GEN tau, GEN a, long n, ulong p)
    3106             : {
    3107      920642 :   pari_sp ltop = avma;
    3108             :   GEN t1, t2, t3, vec;
    3109      920642 :   GEN bt = gel(tau, 1), bht = gel(tau, 2), ft = gel(tau, 3);
    3110      920642 :   if (lgpol(a)==0) return pol0_Flx(a[1]);
    3111      911530 :   t2  = Flx_shift(Flx_mul(bt, a, p),1-n);
    3112      911355 :   if (lgpol(bht)==0) return gerepileuptoleaf(ltop, t2);
    3113      689806 :   t1  = Flx_shift(Flx_mul(ft, a, p),-n);
    3114      689845 :   t3  = Flxn_mul(t1, bht, n-1, p);
    3115      689724 :   vec = Flx_sub(t2, Flx_shift(t3, 1), p);
    3116      689829 :   return gerepileuptoleaf(ltop, vec);
    3117             : }
    3118             : 
    3119             : GEN
    3120      167500 : Flxq_minpoly(GEN x, GEN T, ulong p)
    3121             : {
    3122      167500 :   pari_sp ltop = avma;
    3123      167500 :   long vT = get_Flx_var(T), n = get_Flx_degree(T);
    3124             :   GEN v_x;
    3125      167493 :   GEN g = pol1_Flx(vT), tau = pol1_Flx(vT);
    3126      167467 :   T = Flx_get_red(T, p);
    3127      167485 :   v_x = Flxq_powers(x, usqrt(2*n), T, p);
    3128      517037 :   while (lgpol(tau) != 0)
    3129             :   {
    3130             :     long i, j, m, k1;
    3131             :     GEN M, v, tr;
    3132             :     GEN g_prime, c;
    3133      182083 :     if (degpol(g) == n) { tau = pol1_Flx(vT); g = pol1_Flx(vT); }
    3134      182085 :     v = random_Flx(n, vT, p);
    3135      182103 :     tr = Flxq_transmul_init(tau, T, p);
    3136      182094 :     v = Flxq_transmul(tr, v, n, p);
    3137      182106 :     m = 2*(n-degpol(g));
    3138      182104 :     k1 = usqrt(m);
    3139      182105 :     tr = Flxq_transmul_init(gel(v_x,k1+1), T, p);
    3140      182084 :     c = cgetg(m+2,t_VECSMALL);
    3141      182009 :     c[1] = T[1];
    3142      920550 :     for (i=0; i<m; i+=k1)
    3143             :     {
    3144      738445 :       long mj = minss(m-i, k1);
    3145     3039869 :       for (j=0; j<mj; j++)
    3146     2301225 :         uel(c,m+1-(i+j)) = Flx_dotproduct(v, gel(v_x,j+1), p);
    3147      738644 :       v = Flxq_transmul(tr, v, n, p);
    3148             :     }
    3149      182105 :     c = Flx_renormalize(c, m+2);
    3150             :     /* now c contains <v,x^i> , i = 0..m-1  */
    3151      182102 :     M = Flx_halfgcd(monomial_Flx(1, m, vT), c, p);
    3152      182110 :     g_prime = gmael(M, 2, 2);
    3153      182110 :     if (degpol(g_prime) < 1) continue;
    3154      178755 :     g = Flx_mul(g, g_prime, p);
    3155      178742 :     tau = Flxq_mul(tau, Flx_FlxqV_eval(g_prime, v_x, T, p), T, p);
    3156             :   }
    3157      167480 :   g = Flx_normalize(g,p);
    3158      167494 :   return gerepileuptoleaf(ltop,g);
    3159             : }
    3160             : 
    3161             : GEN
    3162          20 : Flxq_conjvec(GEN x, GEN T, ulong p)
    3163             : {
    3164          20 :   long i, l = 1+get_Flx_degree(T);
    3165          20 :   GEN z = cgetg(l,t_COL);
    3166          20 :   T = Flx_get_red(T,p);
    3167          20 :   gel(z,1) = Flx_copy(x);
    3168          20 :   for (i=2; i<l; i++) gel(z,i) = Flxq_powu(gel(z,i-1), p, T, p);
    3169          20 :   return z;
    3170             : }
    3171             : 
    3172             : GEN
    3173       11289 : gener_Flxq(GEN T, ulong p, GEN *po)
    3174             : {
    3175             :   long i, j;
    3176       11289 :   long vT = get_Flx_var(T), f =get_Flx_degree(T);
    3177             :   ulong p_1;
    3178             :   GEN g, L, L2, o, q, F;
    3179             :   pari_sp av0, av;
    3180             : 
    3181       11289 :   if (f == 1) {
    3182             :     GEN fa;
    3183          28 :     o = utoipos(p-1);
    3184          28 :     fa = Z_factor(o);
    3185          28 :     L = gel(fa,1);
    3186          28 :     L = vecslice(L, 2, lg(L)-1); /* remove 2 for efficiency */
    3187          28 :     g = Fl_to_Flx(pgener_Fl_local(p, vec_to_vecsmall(L)), vT);
    3188          28 :     if (po) *po = mkvec2(o, fa);
    3189          28 :     return g;
    3190             :   }
    3191             : 
    3192       11261 :   av0 = avma; p_1 = p - 1;
    3193       11261 :   q = diviuexact(subiu(powuu(p,f), 1), p_1);
    3194             : 
    3195       11261 :   L = cgetg(1, t_VECSMALL);
    3196       11261 :   if (p > 3)
    3197             :   {
    3198             :     ulong t;
    3199        1468 :     (void)u_lvalrem(p_1, 2, &t);
    3200        1468 :     L = gel(factoru(t),1);
    3201        1468 :     for (i=lg(L)-1; i; i--) L[i] = p_1 / L[i];
    3202             :   }
    3203       11261 :   o = factor_pn_1(utoipos(p),f);
    3204       11261 :   L2 = leafcopy( gel(o, 1) );
    3205       29873 :   for (i = j = 1; i < lg(L2); i++)
    3206             :   {
    3207       18612 :     if (umodui(p_1, gel(L2,i)) == 0) continue;
    3208       14769 :     gel(L2,j++) = diviiexact(q, gel(L2,i));
    3209             :   }
    3210       11261 :   setlg(L2, j);
    3211       11261 :   F = Flx_Frobenius(T, p);
    3212       24958 :   for (av = avma;; set_avma(av))
    3213       13697 :   {
    3214             :     GEN tt;
    3215       24958 :     g = random_Flx(f, vT, p);
    3216       24958 :     if (degpol(g) < 1) continue;
    3217       17949 :     if (p == 2) tt = g;
    3218             :     else
    3219             :     {
    3220        6280 :       ulong t = Flxq_norm(g, T, p);
    3221        6280 :       if (t == 1 || !is_gener_Fl(t, p, p_1, L)) continue;
    3222        3581 :       tt = Flxq_powu(g, p_1>>1, T, p);
    3223             :     }
    3224       31293 :     for (i = 1; i < j; i++)
    3225             :     {
    3226       20032 :       GEN a = Flxq_pow_Frobenius(tt, gel(L2,i), F, T, p);
    3227       20032 :       if (!degpol(a) && uel(a,2) == p_1) break;
    3228             :     }
    3229       15250 :     if (i == j) break;
    3230             :   }
    3231       11261 :   if (!po)
    3232             :   {
    3233         180 :     set_avma((pari_sp)g);
    3234         180 :     g = gerepileuptoleaf(av0, g);
    3235             :   }
    3236             :   else {
    3237       11081 :     *po = mkvec2(subiu(powuu(p,f), 1), o);
    3238       11081 :     gerepileall(av0, 2, &g, po);
    3239             :   }
    3240       11261 :   return g;
    3241             : }
    3242             : 
    3243             : static GEN
    3244        6517 : _Flxq_neg(void *E, GEN x)
    3245        6517 : { struct _Flxq *s = (struct _Flxq *)E;
    3246        6517 :   return Flx_neg(x,s->p); }
    3247             : 
    3248             : static GEN
    3249      111125 : _Flxq_rmul(void *E, GEN x, GEN y)
    3250      111125 : { struct _Flxq *s = (struct _Flxq *)E;
    3251      111125 :   return Flx_mul(x,y,s->p); }
    3252             : 
    3253             : static GEN
    3254        6118 : _Flxq_inv(void *E, GEN x)
    3255        6118 : { struct _Flxq *s = (struct _Flxq *)E;
    3256        6118 :   return Flxq_inv(x,s->T,s->p); }
    3257             : 
    3258             : static int
    3259       42777 : _Flxq_equal0(GEN x) { return lgpol(x)==0; }
    3260             : 
    3261             : static GEN
    3262       12761 : _Flxq_s(void *E, long x)
    3263       12761 : { struct _Flxq *s = (struct _Flxq *)E;
    3264       12761 :   ulong u = x<0 ? s->p+x: (ulong)x;
    3265       12761 :   return Fl_to_Flx(u, get_Flx_var(s->T));
    3266             : }
    3267             : 
    3268             : static const struct bb_field Flxq_field={_Flxq_red,_Flx_add,_Flxq_rmul,_Flxq_neg,
    3269             :                                          _Flxq_inv,_Flxq_equal0,_Flxq_s};
    3270             : 
    3271        6958 : const struct bb_field *get_Flxq_field(void **E, GEN T, ulong p)
    3272             : {
    3273        6958 :   GEN z = new_chunk(sizeof(struct _Flxq));
    3274        6958 :   struct _Flxq *e = (struct _Flxq *) z;
    3275        6958 :   e->T = Flx_get_red(T, p); e->p  = p; *E = (void*)e;
    3276        6958 :   return &Flxq_field;
    3277             : }
    3278             : 
    3279             : /***********************************************************************/
    3280             : /**                                                                   **/
    3281             : /**                               Flxn                                **/
    3282             : /**                                                                   **/
    3283             : /***********************************************************************/
    3284             : 
    3285             : GEN
    3286      708568 : Flxn_red(GEN a, long n)
    3287             : {
    3288      708568 :   long i, L, l = lg(a);
    3289             :   GEN  b;
    3290      708568 :   if (l == 2 || !n) return zero_Flx(a[1]);
    3291      707585 :   L = n+2; if (L > l) L = l;
    3292      707585 :   b = cgetg(L, t_VECSMALL); b[1] = a[1];
    3293      707398 :   for (i=2; i<L; i++) b[i] = a[i];
    3294      707398 :   return Flx_renormalize(b,L);
    3295             : }
    3296             : 
    3297             : GEN
    3298      707219 : Flxn_mul(GEN a, GEN b, long n, ulong p)
    3299      707219 : { return Flxn_red(Flx_mul(a, b, p), n); }
    3300             : 
    3301             : GEN
    3302           0 : Flxn_sqr(GEN a, long n, ulong p)
    3303           0 : { return Flxn_red(Flx_sqr(a, p), n); }
    3304             : 
    3305             : GEN
    3306         189 : Flxn_inv(GEN f, long e, ulong p)
    3307             : {
    3308         189 :   pari_sp av = avma, av2;
    3309             :   ulong mask;
    3310             :   GEN W;
    3311         189 :   long n=1;
    3312         189 :   if (lg(f)==2) pari_err_INV("Flxn_inv",f);
    3313         189 :   W = Fl_to_Flx(Fl_inv(uel(f,2),p), f[1]);
    3314         189 :   mask = quadratic_prec_mask(e);
    3315         189 :   av2 = avma;
    3316        1533 :   for (;mask>1;)
    3317             :   {
    3318             :     GEN u, fr;
    3319        1155 :     long n2 = n;
    3320        1155 :     n<<=1; if (mask & 1) n--;
    3321        1155 :     mask >>= 1;
    3322        1155 :     fr = Flxn_red(f, n);
    3323        1155 :     u = Flx_shift(Flxn_mul(W, fr, n, p), -n2);
    3324        1155 :     W = Flx_sub(W, Flx_shift(Flxn_mul(u, W, n-n2, p), n2), p);
    3325        1155 :     if (gc_needed(av2,2))
    3326             :     {
    3327           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"Flxn_inv, e = %ld", n);
    3328           0 :       W = gerepileupto(av2, W);
    3329             :     }
    3330             :   }
    3331         189 :   return gerepileupto(av, W);
    3332             : }
    3333             : 
    3334             : 
    3335             : /***********************************************************************/
    3336             : /**                                                                   **/
    3337             : /**                               Fl2                                 **/
    3338             : /**                                                                   **/
    3339             : /***********************************************************************/
    3340             : /* Fl2 objects are Flv of length 2 [a,b] representing a+bsqrt(D) for
    3341             :    a non-square D.
    3342             : */
    3343             : 
    3344             : INLINE GEN
    3345     6238926 : mkF2(ulong a, ulong b) { return mkvecsmall2(a,b); }
    3346             : 
    3347             : GEN
    3348     1676182 : Fl2_mul_pre(GEN x, GEN y, ulong D, ulong p, ulong pi)
    3349             : {
    3350             :   ulong xaya, xbyb, Db2, mid;
    3351             :   ulong z1, z2;
    3352     1676182 :   ulong x1 = x[1], x2 = x[2], y1 = y[1], y2 = y[2];
    3353     1676182 :   xaya = Fl_mul_pre(x1,y1,p,pi);
    3354     1676389 :   if (x2==0 && y2==0) return mkF2(xaya,0);
    3355     1622303 :   if (x2==0) return mkF2(xaya,Fl_mul_pre(x1,y2,p,pi));
    3356     1601321 :   if (y2==0) return mkF2(xaya,Fl_mul_pre(x2,y1,p,pi));
    3357     1601015 :   xbyb = Fl_mul_pre(x2,y2,p,pi);
    3358     1601005 :   mid = Fl_mul_pre(Fl_add(x1,x2,p), Fl_add(y1,y2,p),p,pi);
    3359     1601028 :   Db2 = Fl_mul_pre(D, xbyb, p,pi);
    3360     1601016 :   z1 = Fl_add(xaya,Db2,p);
    3361     1600988 :   z2 = Fl_sub(mid,Fl_add(xaya,xbyb,p),p);
    3362     1600945 :   return mkF2(z1,z2);
    3363             : }
    3364             : 
    3365             : GEN
    3366     4225886 : Fl2_sqr_pre(GEN x, ulong D, ulong p, ulong pi)
    3367             : {
    3368     4225886 :   ulong a = x[1], b = x[2];
    3369             :   ulong a2, Db2, ab;
    3370     4225886 :   a2 = Fl_sqr_pre(a,p,pi);
    3371     4227094 :   if (b==0) return mkF2(a2,0);
    3372     4051464 :   Db2= Fl_mul_pre(D, Fl_sqr_pre(b,p,pi), p,pi);
    3373     4051387 :   ab = Fl_mul_pre(a,b,p,pi);
    3374     4051402 :   return mkF2(Fl_add(a2,Db2,p), Fl_double(ab,p));
    3375             : }
    3376             : 
    3377             : ulong
    3378       66163 : Fl2_norm_pre(GEN x, ulong D, ulong p, ulong pi)
    3379             : {
    3380       66163 :   ulong a2 = Fl_sqr_pre(x[1],p,pi);
    3381       66163 :   return x[2]? Fl_sub(a2, Fl_mul_pre(D, Fl_sqr_pre(x[2], p,pi), p,pi), p): a2;
    3382             : }
    3383             : 
    3384             : GEN
    3385      166611 : Fl2_inv_pre(GEN x, ulong D, ulong p, ulong pi)
    3386             : {
    3387             :   ulong n, ni;
    3388      166611 :   if (x[2] == 0) return mkF2(Fl_inv(x[1],p),0);
    3389      142683 :   n = Fl_sub(Fl_sqr_pre(x[1], p,pi),
    3390      142683 :              Fl_mul_pre(D, Fl_sqr_pre(x[2], p,pi), p,pi), p);
    3391      142683 :   ni = Fl_inv(n,p);
    3392      142683 :   return mkF2(Fl_mul_pre(x[1], ni, p,pi),
    3393      142683 :                Fl_neg(Fl_mul_pre(x[2], ni, p,pi), p));
    3394             : }
    3395             : 
    3396             : int
    3397      378426 : Fl2_equal1(GEN x) { return x[1]==1 && x[2]==0; }
    3398             : 
    3399             : struct _Fl2 {
    3400             :   ulong p, pi, D;
    3401             : };
    3402             : 
    3403             : 
    3404             : static GEN
    3405     4225689 : _Fl2_sqr(void *data, GEN x)
    3406             : {
    3407     4225689 :   struct _Fl2 *D = (struct _Fl2*)data;
    3408     4225689 :   return Fl2_sqr_pre(x, D->D, D->p, D->pi);
    3409             : }
    3410             : static GEN
    3411     1648391 : _Fl2_mul(void *data, GEN x, GEN y)
    3412             : {
    3413     1648391 :   struct _Fl2 *D = (struct _Fl2*)data;
    3414     1648391 :   return Fl2_mul_pre(x,y, D->D, D->p, D->pi);
    3415             : }
    3416             : 
    3417             : /* n-Power of x in Z/pZ[X]/(T), as t_VECSMALL. */
    3418             : GEN
    3419      566046 : Fl2_pow_pre(GEN x, GEN n, ulong D, ulong p, ulong pi)
    3420             : {
    3421      566046 :   pari_sp av = avma;
    3422             :   struct _Fl2 d;
    3423             :   GEN y;
    3424      566046 :   long s = signe(n);
    3425      566046 :   if (!s) return mkF2(1,0);
    3426      501724 :   if (s < 0)
    3427      166612 :     x = Fl2_inv_pre(x,D,p,pi);
    3428      501722 :   if (is_pm1(n)) return s < 0 ? x : zv_copy(x);
    3429      368940 :   d.p = p; d.pi = pi; d.D=D;
    3430      368940 :   y = gen_pow_i(x, n, (void*)&d, &_Fl2_sqr, &_Fl2_mul);
    3431      368923 :   return gerepileuptoleaf(av, y);
    3432             : }
    3433             : 
    3434             : static GEN
    3435      566034 : _Fl2_pow(void *data, GEN x, GEN n)
    3436             : {
    3437      566034 :   struct _Fl2 *D = (struct _Fl2*)data;
    3438      566034 :   return Fl2_pow_pre(x, n, D->D, D->p, D->pi);
    3439             : }
    3440             : 
    3441             : static GEN
    3442       95841 : _Fl2_rand(void *data)
    3443             : {
    3444       95841 :   struct _Fl2 *D = (struct _Fl2*)data;
    3445       95841 :   ulong a = random_Fl(D->p), b=random_Fl(D->p-1)+1;
    3446       95841 :   return mkF2(a,b);
    3447             : }
    3448             : 
    3449             : static const struct bb_group Fl2_star={_Fl2_mul, _Fl2_pow, _Fl2_rand,
    3450             :        hash_GEN, zv_equal, Fl2_equal1, NULL};
    3451             : 
    3452             : GEN
    3453       64322 : Fl2_sqrtn_pre(GEN a, GEN n, ulong D, ulong p, ulong pi, GEN *zeta)
    3454             : {
    3455             :   struct _Fl2 E;
    3456             :   GEN o;
    3457       64322 :   if (a[1]==0 && a[2]==0)
    3458             :   {
    3459           0 :     if (signe(n) < 0) pari_err_INV("Flxq_sqrtn",a);
    3460           0 :     if (zeta) *zeta=mkF2(1,0);
    3461           0 :     return zv_copy(a);
    3462             :   }
    3463       64322 :   E.p=p; E.pi = pi; E.D = D;
    3464       64322 :   o = subiu(powuu(p,2), 1);
    3465       64322 :   return gen_Shanks_sqrtn(a,n,o,zeta,(void*)&E,&Fl2_star);
    3466             : }
    3467             : 
    3468             : GEN
    3469       10108 : Flx_Fl2_eval_pre(GEN x, GEN y, ulong D, ulong p, ulong pi)
    3470             : {
    3471             :   GEN p1;
    3472       10108 :   long i = lg(x)-1;
    3473       10108 :   if (i <= 2)
    3474        1883 :     return mkF2(i == 2? x[2]: 0, 0);
    3475        8225 :   p1 = mkF2(x[i], 0);
    3476       35952 :   for (i--; i>=2; i--)
    3477             :   {
    3478       27727 :     p1 = Fl2_mul_pre(p1, y, D, p, pi);
    3479       27727 :     uel(p1,1) = Fl_add(uel(p1,1), uel(x,i), p);
    3480             :   }
    3481        8225 :   return p1;
    3482             : }
    3483             : 
    3484             : 
    3485             : /***********************************************************************/
    3486             : /**                                                                   **/
    3487             : /**                               FlxV                                **/
    3488             : /**                                                                   **/
    3489             : /***********************************************************************/
    3490             : /* FlxV are t_VEC with Flx coefficients. */
    3491             : 
    3492             : GEN
    3493           0 : FlxV_Flc_mul(GEN V, GEN W, ulong p)
    3494             : {
    3495           0 :   pari_sp ltop=avma;
    3496             :   long i;
    3497           0 :   GEN z = Flx_Fl_mul(gel(V,1),W[1],p);
    3498           0 :   for(i=2;i<lg(V);i++)
    3499           0 :     z=Flx_add(z,Flx_Fl_mul(gel(V,i),W[i],p),p);
    3500           0 :   return gerepileuptoleaf(ltop,z);
    3501             : }
    3502             : 
    3503             : GEN
    3504           0 : ZXV_to_FlxV(GEN x, ulong p)
    3505           0 : { pari_APPLY_type(t_VEC, ZX_to_Flx(gel(x,i), p)) }
    3506             : 
    3507             : GEN
    3508     1399415 : ZXT_to_FlxT(GEN x, ulong p)
    3509             : {
    3510     1399415 :   if (typ(x) == t_POL)
    3511     1352340 :     return ZX_to_Flx(x, p);
    3512             :   else
    3513       47075 :     pari_APPLY_type(t_VEC, ZXT_to_FlxT(gel(x,i), p))
    3514             : }
    3515             : 
    3516             : GEN
    3517       33243 : FlxV_to_Flm(GEN x, long n)
    3518       33243 : { pari_APPLY_type(t_MAT, Flx_to_Flv(gel(x,i), n)) }
    3519             : 
    3520             : GEN
    3521           0 : FlxV_red(GEN x, ulong p)
    3522           0 : { pari_APPLY_type(t_VEC, Flx_red(gel(x,i), p)) }
    3523             : 
    3524             : GEN
    3525      206031 : FlxT_red(GEN x, ulong p)
    3526             : {
    3527      206031 :   if (typ(x) == t_VECSMALL)
    3528      139091 :     return Flx_red(x, p);
    3529             :   else
    3530       66940 :     pari_APPLY_type(t_VEC, FlxT_red(gel(x,i), p))
    3531             : }
    3532             : 
    3533             : GEN
    3534      113505 : FlxqV_dotproduct(GEN x, GEN y, GEN T, ulong p)
    3535             : {
    3536      113505 :   long i, lx = lg(x);
    3537             :   pari_sp av;
    3538             :   GEN c;
    3539      113505 :   if (lx == 1) return pol0_Flx(get_Flx_var(T));
    3540      113505 :   av = avma; c = Flx_mul(gel(x,1),gel(y,1), p);
    3541      113505 :   for (i=2; i<lx; i++) c = Flx_add(c, Flx_mul(gel(x,i),gel(y,i), p), p);
    3542      113505 :   return gerepileuptoleaf(av, Flx_rem(c,T,p));
    3543             : }
    3544             : 
    3545             : GEN
    3546         968 : FlxqX_dotproduct(GEN x, GEN y, GEN T, ulong p)
    3547             : {
    3548         968 :   long i, l = minss(lg(x), lg(y));
    3549             :   pari_sp av;
    3550             :   GEN c;
    3551         968 :   if (l == 2) return pol0_Flx(get_Flx_var(T));
    3552         940 :   av = avma; c = Flx_mul(gel(x,2),gel(y,2), p);
    3553         940 :   for (i=3; i<l; i++) c = Flx_add(c, Flx_mul(gel(x,i),gel(y,i), p), p);
    3554         940 :   return gerepileuptoleaf(av, Flx_rem(c,T,p));
    3555             : }
    3556             : 
    3557             : GEN
    3558      203405 : FlxC_eval_powers_pre(GEN z, GEN x, ulong p, ulong pi)
    3559             : {
    3560      203405 :   long i, l = lg(z);
    3561      203405 :   GEN y = cgetg(l, t_VECSMALL);
    3562     6970846 :   for (i=1; i<l; i++)
    3563     6767438 :     uel(y,i) = Flx_eval_powers_pre(gel(z,i), x, p, pi);
    3564      203408 :   return y;
    3565             : }
    3566             : 
    3567             : /***********************************************************************/
    3568             : /**                                                                   **/
    3569             : /**                               FlxM                                **/
    3570             : /**                                                                   **/
    3571             : /***********************************************************************/
    3572             : 
    3573             : GEN
    3574       16912 : FlxM_eval_powers_pre(GEN z, GEN x, ulong p, ulong pi)
    3575             : {
    3576       16912 :   long i, l = lg(z);
    3577       16912 :   GEN y = cgetg(l, t_MAT);
    3578      220319 :   for (i=1; i<l; i++)
    3579      203407 :     gel(y,i) = FlxC_eval_powers_pre(gel(z,i), x, p, pi);
    3580       16912 :   return y;
    3581             : }
    3582             : 
    3583             : GEN
    3584        3031 : zero_FlxC(long n, long sv)
    3585             : {
    3586             :   long i;
    3587        3031 :   GEN x = cgetg(n + 1, t_COL);
    3588        3031 :   GEN z = zero_Flx(sv);
    3589       14490 :   for (i = 1; i <= n; i++)
    3590       11459 :     gel(x, i) = z;
    3591        3031 :   return x;
    3592             : }
    3593             : 
    3594             : GEN
    3595        6433 : FlxC_neg(GEN x, ulong p)
    3596        6433 : { pari_APPLY_type(t_COL, Flx_neg(gel(x, i), p)) }
    3597             : 
    3598             : GEN
    3599      158802 : FlxC_sub(GEN x, GEN y, ulong p)
    3600      158802 : { pari_APPLY_type(t_COL, Flx_sub(gel(x, i), gel(y, i), p)) }
    3601             : 
    3602             : GEN
    3603        3017 : zero_FlxM(long r, long c, long sv)
    3604             : {
    3605             :   long j;
    3606        3017 :   GEN x = cgetg(c + 1, t_MAT);
    3607        3017 :   GEN z = zero_FlxC(r, sv);
    3608       11354 :   for (j = 1; j <= c; j++)
    3609        8337 :     gel(x, j) = z;
    3610        3017 :   return x;
    3611             : }
    3612             : 
    3613             : GEN
    3614        1813 : FlxM_neg(GEN x, ulong p)
    3615        1813 : { pari_APPLY_same(FlxC_neg(gel(x, i), p)) }
    3616             : 
    3617             : GEN
    3618       22470 : FlxM_sub(GEN x, GEN y, ulong p)
    3619       22470 : { pari_APPLY_same(FlxC_sub(gel(x, i), gel(y,i), p)) }
    3620             : 
    3621             : /***********************************************************************/
    3622             : /**                                                                   **/
    3623             : /**                               FlxX                                **/
    3624             : /**                                                                   **/
    3625             : /***********************************************************************/
    3626             : 
    3627             : /* FlxX are t_POL with Flx coefficients.
    3628             :  * Normally the variable ordering should be respected.*/
    3629             : 
    3630             : /*Similar to normalizepol, in place*/
    3631             : /*FlxX_renormalize=zxX_renormalize */
    3632             : GEN
    3633     8689823 : FlxX_renormalize(GEN /*in place*/ x, long lx)
    3634             : {
    3635             :   long i;
    3636    11634996 :   for (i = lx-1; i>1; i--)
    3637    10753109 :     if (lgpol(gel(x,i))) break;
    3638     8689827 :   stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1));
    3639     8689831 :   setlg(x, i+1); setsigne(x, i!=1); return x;
    3640             : }
    3641             : 
    3642             : GEN
    3643      877484 : pol1_FlxX(long v, long sv)
    3644             : {
    3645      877484 :   GEN z = cgetg(3, t_POL);
    3646      877484 :   z[1] = evalsigne(1) | evalvarn(v);
    3647      877484 :   gel(z,2) = pol1_Flx(sv); return z;
    3648             : }
    3649             : 
    3650             : GEN
    3651        7094 : polx_FlxX(long v, long sv)
    3652             : {
    3653        7094 :   GEN z = cgetg(4, t_POL);
    3654        7094 :   z[1] = evalsigne(1) | evalvarn(v);
    3655        7094 :   gel(z,2) = pol0_Flx(sv);
    3656        7094 :   gel(z,3) = pol1_Flx(sv); return z;
    3657             : }
    3658             : 
    3659             : long
    3660     1823502 : FlxY_degreex(GEN b)
    3661             : {
    3662     1823502 :   long deg = -1, i;
    3663     1823502 :   if (!signe(b)) return -1;
    3664     6638150 :   for (i = 2; i < lg(b); ++i)
    3665     4814648 :     deg = maxss(deg, degpol(gel(b, i)));
    3666     1823502 :   return deg;
    3667             : }
    3668             : 
    3669             : /*Lift coefficient of B to constant Flx, to give a FlxY*/
    3670             : GEN
    3671        2021 : Fly_to_FlxY(GEN B, long sv)
    3672             : {
    3673        2021 :   long lb=lg(B);
    3674             :   long i;
    3675        2021 :   GEN b=cgetg(lb,t_POL);
    3676        2029 :   b[1]=evalsigne(1)|(((ulong)B[1])&VARNBITS);
    3677       45684 :   for (i=2; i<lb; i++)
    3678       43664 :     gel(b,i) = Fl_to_Flx(B[i], sv);
    3679        2020 :   return FlxX_renormalize(b, lb);
    3680             : }
    3681             : 
    3682             : GEN
    3683     1643610 : zxX_to_FlxX(GEN B, ulong p)
    3684             : {
    3685     1643610 :   long i, lb = lg(B);
    3686     1643610 :   GEN b = cgetg(lb,t_POL);
    3687     5355946 :   for (i=2; i<lb; i++)
    3688     3712336 :     gel(b,i) = zx_to_Flx(gel(B,i), p);
    3689     1643610 :   b[1] = B[1]; return FlxX_renormalize(b, lb);
    3690             : }
    3691             : 
    3692             : GEN
    3693      426296 : FlxX_to_ZXX(GEN B)
    3694             : {
    3695      426296 :   long i, lb = lg(B);
    3696      426296 :   GEN b = cgetg(lb,t_POL);
    3697     2384781 :   for (i=2; i<lb; i++)
    3698             :   {
    3699     1958485 :     GEN c = gel(B,i);
    3700     1958485 :     switch(lgpol(c))
    3701             :     {
    3702       41758 :       case 0:  c = gen_0; break;
    3703       58596 :       case 1:  c = utoi(c[2]); break;
    3704     1858131 :       default: c = Flx_to_ZX(c); break;
    3705             :     }
    3706     1958485 :     gel(b,i) = c;
    3707             :   }
    3708      426296 :   b[1] = B[1]; return b;
    3709             : }
    3710             : 
    3711             : GEN
    3712        1470 : FlxXC_to_ZXXC(GEN x)
    3713        1470 : { pari_APPLY_type(t_COL, FlxX_to_ZXX(gel(x,i))) }
    3714             : 
    3715             : GEN
    3716           0 : FlxXM_to_ZXXM(GEN x)
    3717           0 : { pari_APPLY_same(FlxXC_to_ZXXC(gel(x,i))) }
    3718             : 
    3719             : /* Note: v is used _only_ for the t_INT. It must match
    3720             :  * the variable of any t_POL coefficients. */
    3721             : GEN
    3722      492159 : ZXX_to_FlxX(GEN B, ulong p, long v)
    3723             : {
    3724      492159 :   long lb=lg(B);
    3725             :   long i;
    3726      492159 :   GEN b=cgetg(lb,t_POL);
    3727      492163 :   b[1]=evalsigne(1)|(((ulong)B[1])&VARNBITS);
    3728     4222555 :   for (i=2; i<lb; i++)
    3729     3730398 :     switch (typ(gel(B,i)))
    3730             :     {
    3731             :     case t_INT:
    3732      473543 :       gel(b,i) = Z_to_Flx(gel(B,i), p, evalvarn(v));
    3733      473535 :       break;
    3734             :     case t_POL:
    3735     3256883 :       gel(b,i) = ZX_to_Flx(gel(B,i), p);
    3736     3256885 :       break;
    3737             :     }
    3738      492157 :   return FlxX_renormalize(b, lb);
    3739             : }
    3740             : 
    3741             : GEN
    3742          12 : ZXXV_to_FlxXV(GEN x, ulong p, long v)
    3743          12 : { pari_APPLY_type(t_VEC, ZXX_to_FlxX(gel(x,i), p, v)) }
    3744             : 
    3745             : GEN
    3746         320 : ZXXT_to_FlxXT(GEN x, ulong p, long v)
    3747             : {
    3748         320 :   if (typ(x) == t_POL)
    3749         306 :     return ZXX_to_FlxX(x, p, v);
    3750             :   else
    3751          14 :     pari_APPLY_type(t_VEC, ZXXT_to_FlxXT(gel(x,i), p, v))
    3752             : }
    3753             : 
    3754             : GEN
    3755           0 : FlxX_to_FlxC(GEN x, long N, long sv)
    3756             : {
    3757             :   long i, l;
    3758             :   GEN z;
    3759           0 :   l = lg(x)-1; x++;
    3760           0 :   if (l > N+1) l = N+1; /* truncate higher degree terms */
    3761           0 :   z = cgetg(N+1,t_COL);
    3762           0 :   for (i=1; i<l ; i++) gel(z,i) = gel(x,i);
    3763           0 :   for (   ; i<=N; i++) gel(z,i) = pol0_Flx(sv);
    3764           0 :   return z;
    3765             : }
    3766             : 
    3767             : GEN
    3768           0 : FlxXV_to_FlxM(GEN v, long n, long sv)
    3769             : {
    3770           0 :   long j, N = lg(v);
    3771           0 :   GEN y = cgetg(N, t_MAT);
    3772           0 :   for (j=1; j<N; j++) gel(y,j) = FlxX_to_FlxC(gel(v,j), n, sv);
    3773           0 :   return y;
    3774             : }
    3775             : 
    3776             : /* matrix whose entries are given by the coeffs of the polynomial v in
    3777             :  * two variables (considered as degree n polynomials) */
    3778             : GEN
    3779       11732 : FlxX_to_Flm(GEN v, long n)
    3780             : {
    3781       11732 :   long j, N = lg(v)-1;
    3782       11732 :   GEN y = cgetg(N, t_MAT);
    3783       11735 :   v++;
    3784       11735 :   for (j=1; j<N; j++) gel(y,j) = Flx_to_Flv(gel(v,j), n);
    3785       11729 :   return y;
    3786             : }
    3787             : 
    3788             : GEN
    3789       41740 : FlxX_to_Flx(GEN f)
    3790             : {
    3791       41740 :   long i, l = lg(f);
    3792       41740 :   GEN V = cgetg(l, t_VECSMALL);
    3793       41740 :   V[1] = ((ulong)f[1])&VARNBITS;
    3794      595098 :   for(i=2; i<l; i++)
    3795      553358 :     V[i] = lgpol(gel(f,i)) ? mael(f,i,2): 0L;
    3796       41740 :   return V;
    3797             : }
    3798             : 
    3799             : GEN
    3800       30326 : Flm_to_FlxX(GEN x, long v,long w)
    3801             : {
    3802       30326 :   long j, lx = lg(x);
    3803       30326 :   GEN y = cgetg(lx+1, t_POL);
    3804       30315 :   y[1]=evalsigne(1) | v;
    3805       30315 :   y++;
    3806       30315 :   for (j=1; j<lx; j++) gel(y,j) = Flv_to_Flx(gel(x,j), w);
    3807       30321 :   return FlxX_renormalize(--y, lx+1);
    3808             : }
    3809             : 
    3810             : /* P(X,Y) --> P(Y,X), n-1 is the degree in Y */
    3811             : GEN
    3812       19768 : FlxX_swap(GEN x, long n, long ws)
    3813             : {
    3814       19768 :   long j, lx = lg(x), ly = n+3;
    3815       19768 :   GEN y = cgetg(ly, t_POL);
    3816       19768 :   y[1] = x[1];
    3817      216454 :   for (j=2; j<ly; j++)
    3818             :   {
    3819             :     long k;
    3820      196686 :     GEN p1 = cgetg(lx, t_VECSMALL);
    3821      196686 :     p1[1] = ws;
    3822     6276781 :     for (k=2; k<lx; k++)
    3823     6080095 :       if (j<lg(gel(x,k)))
    3824     4912884 :         p1[k] = mael(x,k,j);
    3825             :       else
    3826     1167211 :         p1[k] = 0;
    3827      196686 :     gel(y,j) = Flx_renormalize(p1,lx);
    3828             :   }
    3829       19768 :   return FlxX_renormalize(y,ly);
    3830             : }
    3831             : 
    3832             : static GEN
    3833     1399099 : zxX_to_Kronecker_spec(GEN P, long lp, long n)
    3834             : { /* P(X) = sum Pi(Y) * X^i, return P( Y^(2n-1) ) */
    3835     1399099 :   long i, j, k, l, N = (n<<1) + 1;
    3836     1399099 :   GEN y = cgetg((N-2)*lp + 2, t_VECSMALL) + 2;
    3837    14921474 :   for (k=i=0; i<lp; i++)
    3838             :   {
    3839    14918216 :     GEN c = gel(P,i);
    3840    14918216 :     l = lg(c);
    3841    14918216 :     if (l-3 >= n)
    3842           0 :       pari_err_BUG("zxX_to_Kronecker, P is not reduced mod Q");
    3843    14918216 :     for (j=2; j < l; j++) y[k++] = c[j];
    3844    14918216 :     if (i == lp-1) break;
    3845    13522375 :     for (   ; j < N; j++) y[k++] = 0;
    3846             :   }
    3847     1399099 :   y -= 2;
    3848     1399099 :   y[1] = P[1]; setlg(y, k+2); return y;
    3849             : }
    3850             : 
    3851             : GEN
    3852     1071345 : zxX_to_Kronecker(GEN P, GEN Q)
    3853             : {
    3854     1071345 :   GEN z = zxX_to_Kronecker_spec(P+2, lg(P)-2, degpol(Q));
    3855     1071345 :   z[1] = P[1]; return z;
    3856             : }
    3857             : 
    3858             : GEN
    3859      540960 : FlxX_add(GEN x, GEN y, ulong p)
    3860             : {
    3861             :   long i,lz;
    3862             :   GEN z;
    3863      540960 :   long lx=lg(x);
    3864      540960 :   long ly=lg(y);
    3865      540960 :   if (ly>lx) swapspec(x,y, lx,ly);
    3866      540960 :   lz = lx; z = cgetg(lz, t_POL); z[1]=x[1];
    3867      540960 :   for (i=2; i<ly; i++) gel(z,i) = Flx_add(gel(x,i), gel(y,i), p);
    3868      540960 :   for (   ; i<lx; i++) gel(z,i) = Flx_copy(gel(x,i));
    3869      540960 :   return FlxX_renormalize(z, lz);
    3870             : }
    3871             : 
    3872             : GEN
    3873         392 : FlxX_Flx_add(GEN y, GEN x, ulong p)
    3874             : {
    3875         392 :   long i, lz = lg(y);
    3876             :   GEN z;
    3877         392 :   if (signe(y) == 0) return scalarpol(x, varn(y));
    3878         392 :   z = cgetg(lz,t_POL); z[1] = y[1];
    3879         392 :   gel(z,2) = Flx_add(gel(y,2), x, p);
    3880         392 :   if (lz == 3) z = FlxX_renormalize(z,lz);
    3881             :   else
    3882         322 :     for(i=3;i<lz;i++) gel(z,i) = Flx_copy(gel(y,i));
    3883         392 :   return z;
    3884             : }
    3885             : 
    3886             : GEN
    3887       10525 : FlxX_Flx_sub(GEN y, GEN x, ulong p)
    3888             : {
    3889       10525 :   long i, lz = lg(y);
    3890             :   GEN z;
    3891       10525 :   if (signe(y) == 0) return scalarpol(x, varn(y));
    3892       10525 :   z = cgetg(lz,t_POL); z[1] = y[1];
    3893       10525 :   gel(z,2) = Flx_sub(gel(y,2), x, p);
    3894       10525 :   if (lz == 3) z = FlxX_renormalize(z,lz);
    3895             :   else
    3896        8699 :     for(i=3;i<lz;i++) gel(z,i) = Flx_copy(gel(y,i));
    3897       10525 :   return z;
    3898             : }
    3899             : 
    3900             : GEN
    3901        1013 : FlxX_neg(GEN x, ulong p)
    3902             : {
    3903        1013 :   long i, lx=lg(x);
    3904        1013 :   GEN z = cgetg(lx, t_POL);
    3905        1013 :   z[1]=x[1];
    3906        1013 :   for (i=2; i<lx; i++) gel(z,i) = Flx_neg(gel(x,i), p);
    3907        1013 :   return z;
    3908             : }
    3909             : 
    3910             : GEN
    3911         219 : FlxX_Fl_mul(GEN x, ulong y, ulong p)
    3912             : {
    3913         219 :   long i, lx=lg(x);
    3914         219 :   GEN z = cgetg(lx, t_POL);
    3915         219 :   z[1]=x[1];
    3916         219 :   for (i=2; i<lx; i++) gel(z,i) = Flx_Fl_mul(gel(x,i), y, p);
    3917         219 :   return FlxX_renormalize(z, lx);
    3918             : }
    3919             : 
    3920             : GEN
    3921           0 : FlxX_triple(GEN x, ulong p)
    3922             : {
    3923           0 :   long i, lx=lg(x);
    3924           0 :   GEN z = cgetg(lx, t_POL);
    3925           0 :   z[1]=x[1];
    3926           0 :   for (i=2; i<lx; i++) gel(z,i) = Flx_triple(gel(x,i), p);
    3927           0 :   return FlxX_renormalize(z, lx);
    3928             : }
    3929             : 
    3930             : GEN
    3931         219 : FlxX_double(GEN x, ulong p)
    3932             : {
    3933         219 :   long i, lx=lg(x);
    3934         219 :   GEN z = cgetg(lx, t_POL);
    3935         219 :   z[1]=x[1];
    3936         219 :   for (i=2; i<lx; i++) gel(z,i) = Flx_double(gel(x,i), p);
    3937         219 :   return FlxX_renormalize(z, lx);
    3938             : }
    3939             : 
    3940             : GEN
    3941       62864 : FlxX_deriv(GEN z, ulong p)
    3942             : {
    3943       62864 :   long i,l = lg(z)-1;
    3944             :   GEN x;
    3945       62864 :   if (l < 2) l = 2;
    3946       62864 :   x = cgetg(l, t_POL); x[1] = z[1];
    3947       62864 :   for (i=2; i<l; i++) gel(x,i) = Flx_mulu(gel(z,i+1), (ulong) i-1, p);
    3948       62864 :   return FlxX_renormalize(x,l);
    3949             : }
    3950             : 
    3951             : static GEN
    3952       63649 : FlxX_subspec(GEN x, GEN y, ulong p, long lx, long ly)
    3953             : {
    3954             :   long i,lz;
    3955             :   GEN z;
    3956             : 
    3957       63649 :   if (ly <= lx)
    3958             :   {
    3959       63649 :     lz = lx+2; z = cgetg(lz, t_POL)+2;
    3960       63649 :     for (i=0; i<ly; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
    3961       63649 :     for (   ; i<lx; i++) gel(z,i) = Flx_copy(gel(x,i));
    3962             :   }
    3963             :   else
    3964             :   {
    3965           0 :     lz = ly+2; z = cgetg(lz, t_POL)+2;
    3966           0 :     for (i=0; i<lx; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
    3967           0 :     for (   ; i<ly; i++) gel(z,i) = Flx_neg(gel(y,i),p);
    3968             :   }
    3969       63649 :  return FlxX_renormalize(z-2, lz);
    3970             : }
    3971             : 
    3972             : GEN
    3973      108516 : FlxX_sub(GEN x, GEN y, ulong p)
    3974             : {
    3975             :   long lx,ly,i,lz;
    3976             :   GEN z;
    3977      108516 :   lx = lg(x); ly = lg(y);
    3978      108516 :   lz=maxss(lx,ly);
    3979      108516 :   z = cgetg(lz,t_POL);
    3980      108516 :   if (lx >= ly)
    3981             :   {
    3982       67891 :     z[1] = x[1];
    3983       67891 :     for (i=2; i<ly; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
    3984       67891 :     for (   ; i<lx; i++) gel(z,i) = Flx_copy(gel(x,i));
    3985       67891 :     if (lx==ly) z = FlxX_renormalize(z, lz);
    3986             :   }
    3987             :   else
    3988             :   {
    3989       40625 :     z[1] = y[1];
    3990       40625 :     for (i=2; i<lx; i++) gel(z,i) = Flx_sub(gel(x,i),gel(y,i),p);
    3991       40625 :     for (   ; i<ly; i++) gel(z,i) = Flx_neg(gel(y,i),p);
    3992             :   }
    3993      108516 :   if (!lgpol(z)) { set_avma((pari_sp)(z + lz)); z = pol_0(varn(x)); }
    3994      108516 :   return z;
    3995             : }
    3996             : 
    3997             : GEN
    3998      657480 : FlxX_Flx_mul(GEN P, GEN U, ulong p)
    3999             : {
    4000      657480 :   long i, lP = lg(P);
    4001      657480 :   GEN res = cgetg(lP,t_POL);
    4002      657480 :   res[1] = P[1];
    4003     7056471 :   for(i=2; i<lP; i++)
    4004     6398991 :     gel(res,i) = Flx_mul(U,gel(P,i), p);
    4005      657480 :   return FlxX_renormalize(res, lP);
    4006             : }
    4007             : 
    4008             : GEN
    4009      258053 : FlxY_evalx(GEN Q, ulong x, ulong p)
    4010             : {
    4011             :   GEN z;
    4012      258053 :   long i, lb = lg(Q);
    4013      258053 :   z = cgetg(lb,t_VECSMALL); z[1] = evalvarn(varn(Q));
    4014      257572 :   for (i=2; i<lb; i++) z[i] = Flx_eval(gel(Q,i), x, p);
    4015      258218 :   return Flx_renormalize(z, lb);
    4016             : }
    4017             : 
    4018             : GEN
    4019           0 : FlxY_Flx_translate(GEN P, GEN c, ulong p)
    4020             : {
    4021           0 :   pari_sp av = avma;
    4022             :   GEN Q;
    4023             :   long i, k, n;
    4024             : 
    4025           0 :   if (!signe(P) || gequal0(c)) return RgX_copy(P);
    4026           0 :   Q = leafcopy(P); n = degpol(P);
    4027           0 :   for (i=1; i<=n; i++)
    4028             :   {
    4029           0 :     for (k=n-i; k<n; k++)
    4030           0 :       gel(Q,2+k) = Flx_add(gel(Q,2+k), Flx_mul(gel(Q,2+k+1), c, p), p);
    4031           0 :     if (gc_needed(av,2))
    4032             :     {
    4033           0 :       if(DEBUGMEM>1)
    4034           0 :         pari_warn(warnmem,"FlxY_Flx_translate, i = %ld/%ld", i,n);
    4035           0 :       Q = gerepilecopy(av, Q);
    4036             :     }
    4037             :   }
    4038           0 :   return gerepilecopy(av, Q);
    4039             : }
    4040             : 
    4041             : GEN
    4042     7953264 : FlxY_evalx_powers_pre(GEN pol, GEN ypowers, ulong p, ulong pi)
    4043             : {
    4044     7953264 :   long i, len = lg(pol);
    4045     7953264 :   GEN res = cgetg(len, t_VECSMALL);
    4046     7953264 :   res[1] = pol[1] & VARNBITS;
    4047    26956622 :   for (i = 2; i < len; ++i)
    4048    19003358 :     res[i] = Flx_eval_powers_pre(gel(pol, i), ypowers, p, pi);
    4049     7953264 :   return Flx_renormalize(res, len);
    4050             : }
    4051             : 
    4052             : ulong
    4053     5333092 : FlxY_eval_powers_pre(GEN pol, GEN ypowers, GEN xpowers, ulong p, ulong pi)
    4054             : {
    4055     5333092 :   pari_sp av = avma;
    4056     5333092 :   GEN t = FlxY_evalx_powers_pre(pol, ypowers, p, pi);
    4057     5333092 :   return gc_ulong(av, Flx_eval_powers_pre(t, xpowers, p, pi));
    4058             : }
    4059             : 
    4060             : GEN
    4061      120658 : FlxY_FlxqV_evalx(GEN P, GEN x, GEN T, ulong p)
    4062             : {
    4063      120658 :   long i, lP = lg(P);
    4064      120658 :   GEN res = cgetg(lP,t_POL);
    4065      120658 :   res[1] = P[1];
    4066      766408 :   for(i=2; i<lP; i++)
    4067      645750 :     gel(res,i) = Flx_FlxqV_eval(gel(P,i), x, T, p);
    4068      120658 :   return FlxX_renormalize(res, lP);
    4069             : }
    4070             : 
    4071             : GEN
    4072           0 : FlxY_Flxq_evalx(GEN P, GEN x, GEN T, ulong p)
    4073             : {
    4074           0 :   pari_sp av = avma;
    4075           0 :   long n = brent_kung_optpow(get_Flx_degree(T)-1,lgpol(P),1);
    4076           0 :   GEN xp = Flxq_powers(x, n, T, p);
    4077           0 :   return gerepileupto(av, FlxY_FlxqV_evalx(P, xp, T, p));
    4078             : }
    4079             : 
    4080             : GEN
    4081        6216 : FlxY_Flx_div(GEN x, GEN y, ulong p)
    4082             : {
    4083             :   long i, l;
    4084             :   GEN z;
    4085        6216 :   if (degpol(y) == 0)
    4086             :   {
    4087        4372 :     ulong t = uel(y,2);
    4088        4372 :     if (t == 1) return x;
    4089          42 :     t = Fl_inv(t, p);
    4090          42 :     z = cgetg_copy(x, &l); z[1] = x[1];
    4091          43 :     for (i=2; i<l; i++) gel(z,i) = Flx_Fl_mul(gel(x,i),t,p);
    4092             :   }
    4093             :   else
    4094             :   {
    4095        1844 :     z = cgetg_copy(x, &l); z[1] = x[1];
    4096        1842 :     for (i=2; i<l; i++) gel(z,i) = Flx_div(gel(x,i),y,p);
    4097             :   }
    4098        1888 :   return z;
    4099             : }
    4100             : 
    4101             : GEN
    4102           0 : FlxX_shift(GEN a, long n, long vs)
    4103             : {
    4104           0 :   long i, l = lg(a);
    4105             :   GEN  b;
    4106           0 :   if (l == 2 || !n) return a;
    4107           0 :   l += n;
    4108           0 :   if (n < 0)
    4109             :   {
    4110           0 :     if (l <= 2) return pol_0(varn(a));
    4111           0 :     b = cgetg(l, t_POL); b[1] = a[1];
    4112           0 :     a -= n;
    4113           0 :     for (i=2; i<l; i++) gel(b,i) = gel(a,i);
    4114             :   } else {
    4115           0 :     b = cgetg(l, t_POL); b[1] = a[1];
    4116           0 :     a -= n; n += 2;
    4117           0 :     for (i=2; i<n; i++) gel(b,i) = pol0_Flx(vs);
    4118           0 :     for (   ; i<l; i++) gel(b,i) = gel(a,i);
    4119             :   }
    4120           0 :   return b;
    4121             : }
    4122             : 
    4123             : static GEN
    4124      131953 : FlxX_recipspec(GEN x, long l, long n, long vs)
    4125             : {
    4126             :   long i;
    4127      131953 :   GEN z = cgetg(n+2,t_POL);
    4128      131953 :   z[1] = 0; z += 2;
    4129     3091298 :   for(i=0; i<l; i++)
    4130     2959345 :     gel(z,n-i-1) = Flx_copy(gel(x,i));
    4131      137417 :   for(   ; i<n; i++)
    4132        5464 :     gel(z,n-i-1) = pol0_Flx(vs);
    4133      131953 :   return FlxX_renormalize(z-2,n+2);
    4134             : }
    4135             : 
    4136             : /***********************************************************************/
    4137             : /**                                                                   **/
    4138             : /**                               FlxqX                               **/
    4139             : /**                                                                   **/
    4140             : /***********************************************************************/
    4141             : 
    4142             : static GEN
    4143     1503650 : get_FlxqX_red(GEN T, GEN *B)
    4144             : {
    4145     1503650 :   if (typ(T)!=t_VEC) { *B=NULL; return T; }
    4146       74779 :   *B = gel(T,1); return gel(T,2);
    4147             : }
    4148             : 
    4149             : GEN
    4150       31457 : RgX_to_FlxqX(GEN x, GEN T, ulong p)
    4151             : {
    4152       31457 :   long i, l = lg(x);
    4153       31457 :   GEN z = cgetg(l, t_POL); z[1] = x[1];
    4154      581833 :   for (i = 2; i < l; i++)
    4155      550376 :     gel(z,i) = Rg_to_Flxq(gel(x,i), T, p);
    4156       31457 :   return FlxX_renormalize(z, l);
    4157             : }
    4158             : 
    4159             : /* FlxqX are t_POL with Flxq coefficients.
    4160             :  * Normally the variable ordering should be respected.*/
    4161             : 
    4162             : GEN
    4163         474 : random_FlxqX(long d1, long v, GEN T, ulong p)
    4164             : {
    4165         474 :   long dT = get_Flx_degree(T), vT = get_Flx_var(T);
    4166         474 :   long i, d = d1+2;
    4167         474 :   GEN y = cgetg(d,t_POL); y[1] = evalsigne(1) | evalvarn(v);
    4168         474 :   for (i=2; i<d; i++) gel(y,i) = random_Flx(dT, vT, p);
    4169         474 :   return FlxX_renormalize(y,d);
    4170             : }
    4171             : 
    4172             : /*Not stack clean*/
    4173             : GEN
    4174      805355 : Kronecker_to_FlxqX(GEN z, GEN T, ulong p)
    4175             : {
    4176      805355 :   long i,j,lx,l, N = (get_Flx_degree(T)<<1) + 1;
    4177      805355 :   GEN x, t = cgetg(N,t_VECSMALL);
    4178      805355 :   t[1] = get_Flx_var(T);
    4179      805355 :   l = lg(z); lx = (l-2) / (N-2);
    4180      805355 :   x = cgetg(lx+3,t_POL);
    4181      805355 :   x[1] = z[1];
    4182    15403941 :   for (i=2; i<lx+2; i++)
    4183             :   {
    4184    14598586 :     for (j=2; j<N; j++) t[j] = z[j];
    4185    14598586 :     z += (N-2);
    4186    14598586 :     gel(x,i) = Flx_rem(Flx_renormalize(t,N), T,p);
    4187             :   }
    4188      805355 :   N = (l-2) % (N-2) + 2;
    4189      805355 :   for (j=2; j<N; j++) t[j] = z[j];
    4190      805355 :   gel(x,i) = Flx_rem(Flx_renormalize(t,N), T,p);
    4191      805355 :   return FlxX_renormalize(x, i+1);
    4192             : }
    4193             : 
    4194             : GEN
    4195     1047342 : FlxqX_red(GEN z, GEN T, ulong p)
    4196             : {
    4197             :   GEN res;
    4198     1047342 :   long i, l = lg(z);
    4199     1047342 :   res = cgetg(l,t_POL); res[1] = z[1];
    4200     1047342 :   for(i=2;i<l;i++) gel(res,i) = Flx_rem(gel(z,i),T,p);
    4201     1047342 :   return FlxX_renormalize(res,l);
    4202             : }
    4203             : 
    4204             : static GEN
    4205      163877 : FlxqX_mulspec(GEN x, GEN y, GEN T, ulong p, long lx, long ly)
    4206             : {
    4207      163877 :   pari_sp ltop=avma;
    4208             :   GEN z,kx,ky;
    4209      163877 :   long dT =  get_Flx_degree(T);
    4210      163877 :   kx= zxX_to_Kronecker_spec(x,lx,dT);
    4211      163877 :   ky= zxX_to_Kronecker_spec(y,ly,dT);
    4212      163877 :   z = Flx_mul(ky, kx, p);
    4213      163877 :   z = Kronecker_to_FlxqX(z,T,p);
    4214      163877 :   return gerepileupto(ltop,z);
    4215             : }
    4216             : 
    4217             : GEN
    4218      429867 : FlxqX_mul(GEN x, GEN y, GEN T, ulong p)
    4219             : {
    4220      429867 :   pari_sp ltop=avma;
    4221             :   GEN z,kx,ky;
    4222      429867 :   kx= zxX_to_Kronecker(x,get_Flx_mod(T));
    4223      429867 :   ky= zxX_to_Kronecker(y,get_Flx_mod(T));
    4224      429867 :   z = Flx_mul(ky, kx, p);
    4225      429867 :   z = Kronecker_to_FlxqX(z,T,p);
    4226      429867 :   return gerepileupto(ltop,z);
    4227             : }
    4228             : 
    4229             : GEN
    4230      211611 : FlxqX_sqr(GEN x, GEN T, ulong p)
    4231             : {
    4232      211611 :   pari_sp ltop=avma;
    4233             :   GEN z,kx;
    4234      211611 :   kx= zxX_to_Kronecker(x,get_Flx_mod(T));
    4235      211611 :   z = Flx_sqr(kx, p);
    4236      211611 :   z = Kronecker_to_FlxqX(z,T,p);
    4237      211611 :   return gerepileupto(ltop,z);
    4238             : }
    4239             : 
    4240             : GEN
    4241        8498 : FlxqX_Flxq_mul(GEN P, GEN U, GEN T, ulong p)
    4242             : {
    4243        8498 :   long i, lP = lg(P);
    4244        8498 :   GEN res = cgetg(lP,t_POL);
    4245        8498 :   res[1] = P[1];
    4246       38808 :   for(i=2; i<lP; i++)
    4247       30310 :     gel(res,i) = Flxq_mul(U,gel(P,i), T,p);
    4248        8498 :   return FlxX_renormalize(res, lP);
    4249             : }
    4250             : GEN
    4251      235126 : FlxqX_Flxq_mul_to_monic(GEN P, GEN U, GEN T, ulong p)
    4252             : {
    4253      235126 :   long i, lP = lg(P);
    4254      235126 :   GEN res = cgetg(lP,t_POL);
    4255      235126 :   res[1] = P[1];
    4256      235126 :   for(i=2; i<lP-1; i++) gel(res,i) = Flxq_mul(U,gel(P,i), T,p);
    4257      235126 :   gel(res,lP-1) = pol1_Flx(get_Flx_var(T));
    4258      235126 :   return FlxX_renormalize(res, lP);
    4259             : }
    4260             : 
    4261             : GEN
    4262      172329 : FlxqX_normalize(GEN z, GEN T, ulong p)
    4263             : {
    4264      172329 :   GEN p1 = leading_coeff(z);
    4265      172329 :   if (!lgpol(z) || (!degpol(p1) && p1[1] == 1)) return z;
    4266      172308 :   return FlxqX_Flxq_mul_to_monic(z, Flxq_inv(p1,T,p), T,p);
    4267             : }
    4268             : 
    4269             : /* x and y in Z[Y][X]. Assume T irreducible mod p */
    4270             : static GEN
    4271     1221198 : FlxqX_divrem_basecase(GEN x, GEN y, GEN T, ulong p, GEN *pr)
    4272             : {
    4273             :   long vx, dx, dy, dz, i, j, sx, lr;
    4274             :   pari_sp av0, av, tetpil;
    4275             :   GEN z,p1,rem,lead;
    4276             : 
    4277     1221198 :   if (!signe(y)) pari_err_INV("FlxqX_divrem",y);
    4278     1221198 :   vx=varn(x); dy=degpol(y); dx=degpol(x);
    4279     1221198 :   if (dx < dy)
    4280             :   {
    4281       13185 :     if (pr)
    4282             :     {
    4283       13046 :       av0 = avma; x = FlxqX_red(x, T, p);
    4284       13046 :       if (pr == ONLY_DIVIDES) { set_avma(av0); return signe(x)? NULL: pol_0(vx); }
    4285       13046 :       if (pr == ONLY_REM) return x;
    4286       13046 :       *pr = x;
    4287             :     }
    4288       13185 :     return pol_0(vx);
    4289             :   }
    4290     1208013 :   lead = leading_coeff(y);
    4291     1208013 :   if (!dy) /* y is constant */
    4292             :   {
    4293      118169 :     if (pr && pr != ONLY_DIVIDES)
    4294             :     {
    4295      113577 :       if (pr == ONLY_REM) return pol_0(vx);
    4296        6146 :       *pr = pol_0(vx);
    4297             :     }
    4298       10738 :     if (Flx_equal1(lead)) return gcopy(x);
    4299        6636 :     av0 = avma; x = FlxqX_Flxq_mul(x,Flxq_inv(lead,T,p),T,p);
    4300        6636 :     return gerepileupto(av0,x);
    4301             :   }
    4302     1089844 :   av0 = avma; dz = dx-dy;
    4303     1089844 :   lead = Flx_equal1(lead)? NULL: gclone(Flxq_inv(lead,T,p));
    4304     1089844 :   set_avma(av0);
    4305     1089844 :   z = cgetg(dz+3,t_POL); z[1] = x[1];
    4306     1089844 :   x += 2; y += 2; z += 2;
    4307             : 
    4308     1089844 :   p1 = gel(x,dx); av = avma;
    4309     1089844 :   gel(z,dz) = lead? gerepileupto(av, Flxq_mul(p1,lead, T, p)): gcopy(p1);
    4310     3036616 :   for (i=dx-1; i>=dy; i--)
    4311             :   {
    4312     1946772 :     av=avma; p1=gel(x,i);
    4313     7190802 :     for (j=i-dy+1; j<=i && j<=dz; j++)
    4314     5244030 :       p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p),p);
    4315     1946772 :     if (lead) p1 = Flx_mul(p1, lead,p);
    4316     1946772 :     tetpil=avma; gel(z,i-dy) = gerepile(av,tetpil,Flx_rem(p1,T,p));
    4317             :   }
    4318     1089844 :   if (!pr) { if (lead) gunclone(lead); return z-2; }
    4319             : 
    4320     1058232 :   rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
    4321     1303181 :   for (sx=0; ; i--)
    4322             :   {
    4323     1548130 :     p1 = gel(x,i);
    4324     4418667 :     for (j=0; j<=i && j<=dz; j++)
    4325     3115486 :       p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p),p);
    4326     1303181 :     tetpil=avma; p1 = Flx_rem(p1, T, p); if (lgpol(p1)) { sx = 1; break; }
    4327      301194 :     if (!i) break;
    4328      244949 :     set_avma(av);
    4329             :   }
    4330     1058232 :   if (pr == ONLY_DIVIDES)
    4331             :   {
    4332           0 :     if (lead) gunclone(lead);
    4333           0 :     if (sx) return gc_NULL(av0);
    4334           0 :     set_avma((pari_sp)rem); return z-2;
    4335             :   }
    4336     1058232 :   lr=i+3; rem -= lr;
    4337     1058232 :   rem[0] = evaltyp(t_POL) | evallg(lr);
    4338     1058232 :   rem[1] = z[-1];
    4339     1058232 :   p1 = gerepile((pari_sp)rem,tetpil,p1);
    4340     1058232 :   rem += 2; gel(rem,i) = p1;
    4341    10061997 :   for (i--; i>=0; i--)
    4342             :   {
    4343     9003765 :     av=avma; p1 = gel(x,i);
    4344    31955356 :     for (j=0; j<=i && j<=dz; j++)
    4345    22951591 :       p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p), p);
    4346     9003765 :     tetpil=avma; gel(rem,i) = gerepile(av,tetpil, Flx_rem(p1, T, p));
    4347             :   }
    4348     1058232 :   rem -= 2;
    4349     1058232 :   if (lead) gunclone(lead);
    4350     1058232 :   if (!sx) (void)FlxX_renormalize(rem, lr);
    4351     1058232 :   if (pr == ONLY_REM) return gerepileupto(av0,rem);
    4352      196350 :   *pr = rem; return z-2;
    4353             : }
    4354             : 
    4355             : static GEN
    4356        1522 : FlxqX_invBarrett_basecase(GEN T, GEN Q, ulong p)
    4357             : {
    4358        1522 :   long i, l=lg(T)-1, lr = l-1, k;
    4359        1522 :   long sv=Q[1];
    4360        1522 :   GEN r=cgetg(lr,t_POL); r[1]=T[1];
    4361        1522 :   gel(r,2) = pol1_Flx(sv);
    4362       15491 :   for (i=3;i<lr;i++)
    4363             :   {
    4364       13969 :     pari_sp ltop=avma;
    4365       13969 :     GEN u = Flx_neg(gel(T,l-i+2),p);
    4366       96533 :     for (k=3;k<i;k++)
    4367       82564 :       u = Flx_sub(u, Flxq_mul(gel(T,l-i+k),gel(r,k),Q,p),p);
    4368       13969 :     gel(r,i) = gerepileupto(ltop, u);
    4369             :   }
    4370        1522 :   r = FlxX_renormalize(r,lr);
    4371        1522 :   return r;
    4372             : }
    4373             : 
    4374             : /* Return new lgpol */
    4375             : static long
    4376      180858 : FlxX_lgrenormalizespec(GEN x, long lx)
    4377             : {
    4378             :   long i;
    4379      206878 :   for (i = lx-1; i>=0; i--)
    4380      206878 :     if (lgpol(gel(x,i))) break;
    4381      180858 :   return i+1;
    4382             : }
    4383             : 
    4384             : static GEN
    4385        3149 : FlxqX_invBarrett_Newton(GEN S, GEN T, ulong p)
    4386             : {
    4387        3149 :   pari_sp av = avma;
    4388        3149 :   long nold, lx, lz, lq, l = degpol(S), i, lQ;
    4389        3149 :   GEN q, y, z, x = cgetg(l+2, t_POL) + 2;
    4390        3149 :   long dT = get_Flx_degree(T);
    4391        3149 :   ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
    4392        3149 :   for (i=0;i<l;i++) gel(x,i) = pol0_Flx(T[1]);
    4393        3149 :   q = FlxX_recipspec(S+2,l+1,l+1,dT);
    4394        3149 :   lQ = lgpol(q); q+=2;
    4395             :   /* We work on _spec_ FlxX's, all the l[xzq] below are lgpol's */
    4396             : 
    4397             :   /* initialize */
    4398        3149 :   gel(x,0) = Flxq_inv(gel(q,0),T, p);
    4399        3149 :   if (lQ>1 && degpol(gel(q,1)) >= dT)
    4400           0 :     gel(q,1) = Flx_rem(gel(q,1), T, p);
    4401        3149 :   if (lQ>1 && lgpol(gel(q,1)))
    4402        2145 :   {
    4403        2145 :     GEN u = gel(q, 1);
    4404        2145 :     if (!Flx_equal1(gel(x,0))) u = Flxq_mul(u, Flxq_sqr(gel(x,0), T,p), T,p);
    4405        2145 :     gel(x,1) = Flx_neg(u, p); lx = 2;
    4406             :   }
    4407             :   else
    4408        1004 :     lx = 1;
    4409        3149 :   nold = 1;
    4410       24892 :   for (; mask > 1; )
    4411             :   { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
    4412       18594 :     long i, lnew, nnew = nold << 1;
    4413             : 
    4414       18594 :     if (mask & 1) nnew--;
    4415       18594 :     mask >>= 1;
    4416             : 
    4417       18594 :     lnew = nnew + 1;
    4418       18594 :     lq = FlxX_lgrenormalizespec(q, minss(lQ,lnew));
    4419       18594 :     z = FlxqX_mulspec(x, q, T, p, lx, lq); /* FIXME: high product */
    4420       18594 :     lz = lgpol(z); if (lz > lnew) lz = lnew;
    4421       18594 :     z += 2;
    4422             :     /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
    4423       18594 :     for (i = nold; i < lz; i++) if (lgpol(gel(z,i))) break;
    4424       18594 :     nold = nnew;
    4425       18594 :     if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
    4426             : 
    4427             :     /* z + i represents (x*q - 1) / t^i */
    4428       17712 :     lz = FlxX_lgrenormalizespec (z+i, lz-i);
    4429       17712 :     z = FlxqX_mulspec(x, z+i, T,p, lx, lz); /* FIXME: low product */
    4430       17712 :     lz = lgpol(z); z += 2;
    4431       17712 :     if (lz > lnew-i) lz = FlxX_lgrenormalizespec(z, lnew-i);
    4432             : 
    4433       17712 :     lx = lz+ i;
    4434       17712 :     y  = x + i; /* x -= z * t^i, in place */
    4435       17712 :     for (i = 0; i < lz; i++) gel(y,i) = Flx_neg(gel(z,i), p);
    4436             :   }
    4437        3149 :   x -= 2; setlg(x, lx + 2); x[1] = S[1];
    4438        3149 :   return gerepilecopy(av, x);
    4439             : }
    4440             : 
    4441             : /* x/polrecip(P)+O(x^n) */
    4442             : GEN
    4443        4671 : FlxqX_invBarrett(GEN T, GEN Q, ulong p)
    4444             : {
    4445        4671 :   pari_sp ltop=avma;
    4446        4671 :   long l=lg(T), v = varn(T);
    4447             :   GEN r;
    4448        4671 :   GEN c = gel(T,l-1);
    4449        4671 :   if (l<5) return pol_0(v);
    4450        4671 :   if (l<=FlxqX_INVBARRETT_LIMIT)
    4451             :   {
    4452        1522 :     if (!Flx_equal1(c))
    4453             :     {
    4454           0 :       GEN ci = Flxq_inv(c,Q,p);
    4455           0 :       T = FlxqX_Flxq_mul(T, ci, Q, p);
    4456           0 :       r = FlxqX_invBarrett_basecase(T,Q,p);
    4457           0 :       r = FlxqX_Flxq_mul(r,ci,Q,p);
    4458             :     } else
    4459        1522 :       r = FlxqX_invBarrett_basecase(T,Q,p);
    4460             :   } else
    4461        3149 :     r = FlxqX_invBarrett_Newton(T,Q,p);
    4462        4671 :   return gerepileupto(ltop, r);
    4463             : }
    4464             : 
    4465             : GEN
    4466      298577 : FlxqX_get_red(GEN S, GEN T, ulong p)
    4467             : {
    4468      298577 :   if (typ(S)==t_POL && lg(S)>FlxqX_BARRETT_LIMIT)
    4469        3104 :     retmkvec2(FlxqX_invBarrett(S, T, p), S);
    4470      295473 :   return S;
    4471             : }
    4472             : 
    4473             : /* Compute x mod S where 2 <= degpol(S) <= l+1 <= 2*(degpol(S)-1)
    4474             :  *  * and mg is the Barrett inverse of S. */
    4475             : static GEN
    4476       63922 : FlxqX_divrem_Barrettspec(GEN x, long l, GEN mg, GEN S, GEN T, ulong p, GEN *pr)
    4477             : {
    4478             :   GEN q, r;
    4479       63922 :   long lt = degpol(S); /*We discard the leading term*/
    4480             :   long ld, lm, lT, lmg;
    4481       63922 :   ld = l-lt;
    4482       63922 :   lm = minss(ld, lgpol(mg));
    4483       63922 :   lT  = FlxX_lgrenormalizespec(S+2,lt);
    4484       63922 :   lmg = FlxX_lgrenormalizespec(mg+2,lm);
    4485       63922 :   q = FlxX_recipspec(x+lt,ld,ld,0);               /* q = rec(x)     lq<=ld*/
    4486       63922 :   q = FlxqX_mulspec(q+2,mg+2,T,p,lgpol(q),lmg);   /* q = rec(x) * mg lq<=ld+lm*/
    4487       63922 :   q = FlxX_recipspec(q+2,minss(ld,lgpol(q)),ld,0);/* q = rec (rec(x) * mg) lq<=ld*/
    4488       63922 :   if (!pr) return q;
    4489       63649 :   r = FlxqX_mulspec(q+2,S+2,T,p,lgpol(q),lT);     /* r = q*pol        lr<=ld+lt*/
    4490       63649 :   r = FlxX_subspec(x,r+2,p,lt,minss(lt,lgpol(r)));/* r = x - r   lr<=lt */
    4491       63649 :   if (pr == ONLY_REM) return r;
    4492       11085 :   *pr = r; return q;
    4493             : }
    4494             : 
    4495             : static GEN
    4496       53629 : FlxqX_divrem_Barrett(GEN x, GEN mg, GEN S, GEN T, ulong p, GEN *pr)
    4497             : {
    4498       53629 :   GEN q = NULL, r = FlxqX_red(x, T, p);
    4499       53629 :   long l = lgpol(r), lt = degpol(S), lm = 2*lt-1, v = varn(S);
    4500             :   long i;
    4501       53629 :   if (l <= lt)
    4502             :   {
    4503           0 :     if (pr == ONLY_REM) return r;
    4504           0 :     if (pr == ONLY_DIVIDES) return signe(r)? NULL: pol_0(v);
    4505           0 :     if (pr) *pr = r;
    4506           0 :     return pol_0(v);
    4507             :   }
    4508       53629 :   if (lt <= 1)
    4509           0 :     return FlxqX_divrem_basecase(x,S,T,p,pr);
    4510       53629 :   if (pr != ONLY_REM && l>lm)
    4511             :   {
    4512         750 :     long vT = get_Flx_var(T);
    4513         750 :     q = cgetg(l-lt+2, t_POL);
    4514         750 :     for (i=0;i<l-lt;i++) gel(q+2,i) = pol0_Flx(vT);
    4515             :   }
    4516      117633 :   while (l>lm)
    4517             :   {
    4518       10375 :     GEN zr, zq = FlxqX_divrem_Barrettspec(r+2+l-lm,lm,mg,S,T,p,&zr);
    4519       10375 :     long lz = lgpol(zr);
    4520       10375 :     if (pr != ONLY_REM)
    4521             :     {
    4522        2568 :       long lq = lgpol(zq);
    4523        2568 :       for(i=0; i<lq; i++) gel(q+2+l-lm,i) = gel(zq,2+i);
    4524             :     }
    4525       10375 :     for(i=0; i<lz; i++) gel(r+2+l-lm,i) = gel(zr,2+i);
    4526       10375 :     l = l-lm+lz;
    4527             :   }
    4528       53629 :   if (pr == ONLY_REM)
    4529             :   {
    4530       52564 :     if (l > lt)
    4531       52564 :       r = FlxqX_divrem_Barrettspec(r+2,l,mg,S,T,p,ONLY_REM);
    4532             :     else
    4533           0 :       r = FlxX_renormalize(r, l+2);
    4534       52564 :     setvarn(r, v); return r;
    4535             :   }
    4536        1065 :   if (l > lt)
    4537             :   {
    4538         983 :     GEN zq = FlxqX_divrem_Barrettspec(r+2,l,mg,S,T,p,pr? &r: NULL);
    4539         983 :     if (!q) q = zq;
    4540             :     else
    4541             :     {
    4542         668 :       long lq = lgpol(zq);
    4543         668 :       for(i=0; i<lq; i++) gel(q+2,i) = gel(zq,2+i);
    4544             :     }
    4545             :   }
    4546          82 :   else if (pr)
    4547          82 :     r = FlxX_renormalize(r, l+2);
    4548        1065 :   setvarn(q, v); q = FlxX_renormalize(q, lg(q));
    4549        1065 :   if (pr == ONLY_DIVIDES) return signe(r)? NULL: q;
    4550        1065 :   if (pr) { setvarn(r, v); *pr = r; }
    4551        1065 :   return q;
    4552             : }
    4553             : 
    4554             : GEN
    4555      252950 : FlxqX_divrem(GEN x, GEN S, GEN T, ulong p, GEN *pr)
    4556             : {
    4557      252950 :   GEN B, y = get_FlxqX_red(S, &B);
    4558      252950 :   long dy = degpol(y), dx = degpol(x), d = dx-dy;
    4559      252950 :   if (pr==ONLY_REM) return FlxqX_rem(x, y, T, p);
    4560      252950 :   if (!B && d+3 < FlxqX_DIVREM_BARRETT_LIMIT)
    4561      251885 :     return FlxqX_divrem_basecase(x,y,T,p,pr);
    4562             :   else
    4563             :   {
    4564        1065 :     pari_sp av = avma;
    4565        1065 :     GEN mg = B? B: FlxqX_invBarrett(y, T, p);
    4566        1065 :     GEN q = FlxqX_divrem_Barrett(x,mg,y,T,p,pr);
    4567        1065 :     if (!q) return gc_NULL(av);
    4568        1065 :     if (!pr || pr==ONLY_DIVIDES) return gerepilecopy(av, q);
    4569         792 :     gerepileall(av,2,&q,pr);
    4570         792 :     return q;
    4571             :   }
    4572             : }
    4573             : 
    4574             : GEN
    4575     1250380 : FlxqX_rem(GEN x, GEN S, GEN T, ulong p)
    4576             : {
    4577     1250380 :   GEN B, y = get_FlxqX_red(S, &B);
    4578     1250380 :   long dy = degpol(y), dx = degpol(x), d = dx-dy;
    4579     1250380 :   if (d < 0) return FlxqX_red(x, T, p);
    4580     1021877 :   if (!B && d+3 < FlxqX_REM_BARRETT_LIMIT)
    4581      969313 :     return FlxqX_divrem_basecase(x,y, T, p, ONLY_REM);
    4582             :   else
    4583             :   {
    4584       52564 :     pari_sp av=avma;
    4585       52564 :     GEN mg = B? B: FlxqX_invBarrett(y, T, p);
    4586       52564 :     GEN r = FlxqX_divrem_Barrett(x, mg, y, T, p, ONLY_REM);
    4587       52564 :     return gerepileupto(av, r);
    4588             :   }
    4589             : }
    4590             : 
    4591             : static GEN
    4592         541 : FlxqX_halfgcd_basecase(GEN a, GEN b, GEN T, ulong p)
    4593             : {
    4594         541 :   pari_sp av=avma;
    4595             :   GEN u,u1,v,v1;
    4596         541 :   long vx = varn(a);
    4597         541 :   long n = lgpol(a)>>1;
    4598         541 :   u1 = v = pol_0(vx);
    4599         541 :   u = v1 = pol1_FlxX(vx, get_Flx_var(T));
    4600        9201 :   while (lgpol(b)>n)
    4601             :   {
    4602        8119 :     GEN r, q = FlxqX_divrem(a,b, T, p, &r);
    4603        8119 :     a = b; b = r; swap(u,u1); swap(v,v1);
    4604        8119 :     u1 = FlxX_sub(u1, FlxqX_mul(u, q, T, p), p);
    4605        8119 :     v1 = FlxX_sub(v1, FlxqX_mul(v, q ,T, p), p);
    4606        8119 :     if (gc_needed(av,2))
    4607             :     {
    4608           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_halfgcd (d = %ld)",degpol(b));
    4609           0 :       gerepileall(av,6, &a,&b,&u1,&v1,&u,&v);
    4610             :     }
    4611             :   }
    4612         541 :   return gerepilecopy(av, mkmat2(mkcol2(u,u1), mkcol2(v,v1)));
    4613             : }
    4614             : static GEN
    4615         768 : FlxqX_addmulmul(GEN u, GEN v, GEN x, GEN y, GEN T, ulong p)
    4616             : {
    4617         768 :   return FlxX_add(FlxqX_mul(u, x, T, p),FlxqX_mul(v, y, T, p), p);
    4618             : }
    4619             : 
    4620             : static GEN
    4621         384 : FlxqXM_FlxqX_mul2(GEN M, GEN x, GEN y, GEN T, ulong p)
    4622             : {
    4623         384 :   GEN res = cgetg(3, t_COL);
    4624         384 :   gel(res, 1) = FlxqX_addmulmul(gcoeff(M,1,1), gcoeff(M,1,2), x, y, T, p);
    4625         384 :   gel(res, 2) = FlxqX_addmulmul(gcoeff(M,2,1), gcoeff(M,2,2), x, y, T, p);
    4626         384 :   return res;
    4627             : }
    4628             : 
    4629             : static GEN
    4630         360 : FlxqXM_mul2(GEN A, GEN B, GEN T, ulong p)
    4631             : {
    4632         360 :   GEN A11=gcoeff(A,1,1),A12=gcoeff(A,1,2), B11=gcoeff(B,1,1),B12=gcoeff(B,1,2);
    4633         360 :   GEN A21=gcoeff(A,2,1),A22=gcoeff(A,2,2), B21=gcoeff(B,2,1),B22=gcoeff(B,2,2);
    4634         360 :   GEN M1 = FlxqX_mul(FlxX_add(A11,A22, p), FlxX_add(B11,B22, p), T, p);
    4635         360 :   GEN M2 = FlxqX_mul(FlxX_add(A21,A22, p), B11, T, p);
    4636         360 :   GEN M3 = FlxqX_mul(A11, FlxX_sub(B12,B22, p), T, p);
    4637         360 :   GEN M4 = FlxqX_mul(A22, FlxX_sub(B21,B11, p), T, p);
    4638         360 :   GEN M5 = FlxqX_mul(FlxX_add(A11,A12, p), B22, T, p);
    4639         360 :   GEN M6 = FlxqX_mul(FlxX_sub(A21,A11, p), FlxX_add(B11,B12, p), T, p);
    4640         360 :   GEN M7 = FlxqX_mul(FlxX_sub(A12,A22, p), FlxX_add(B21,B22, p), T, p);
    4641         360 :   GEN T1 = FlxX_add(M1,M4, p), T2 = FlxX_sub(M7,M5, p);
    4642         360 :   GEN T3 = FlxX_sub(M1,M2, p), T4 = FlxX_add(M3,M6, p);
    4643         360 :   retmkmat2(mkcol2(FlxX_add(T1,T2, p), FlxX_add(M2,M4, p)),
    4644             :             mkcol2(FlxX_add(M3,M5, p), FlxX_add(T3,T4, p)));
    4645             : }
    4646             : 
    4647             : /* Return [0,1;1,-q]*M */
    4648             : static GEN
    4649         360 : FlxqX_FlxqXM_qmul(GEN q, GEN M, GEN T, ulong p)
    4650             : {
    4651         360 :   GEN u, v, res = cgetg(3, t_MAT);
    4652         360 :   u = FlxX_sub(gcoeff(M,1,1), FlxqX_mul(gcoeff(M,2,1), q, T, p), p);
    4653         360 :   gel(res,1) = mkcol2(gcoeff(M,2,1), u);
    4654         360 :   v = FlxX_sub(gcoeff(M,1,2), FlxqX_mul(gcoeff(M,2,2), q, T, p), p);
    4655         360 :   gel(res,2) = mkcol2(gcoeff(M,2,2), v);
    4656         360 :   return res;
    4657             : }
    4658             : 
    4659             : static GEN
    4660           0 : matid2_FlxXM(long v, long sv)
    4661             : {
    4662           0 :   retmkmat2(mkcol2(pol1_FlxX(v, sv),pol_0(v)),
    4663             :             mkcol2(pol_0(v),pol1_FlxX(v, sv)));
    4664             : }
    4665             : 
    4666             : static GEN
    4667         363 : FlxqX_halfgcd_split(GEN x, GEN y, GEN T, ulong p)
    4668             : {
    4669         363 :   pari_sp av=avma;
    4670             :   GEN R, S, V;
    4671             :   GEN y1, r, q;
    4672         363 :   long l = lgpol(x), n = l>>1, k;
    4673         363 :   if (lgpol(y)<=n) return matid2_FlxXM(varn(x),T[1]);
    4674         363 :   R = FlxqX_halfgcd(RgX_shift_shallow(x,-n),RgX_shift_shallow(y,-n), T, p);
    4675         363 :   V = FlxqXM_FlxqX_mul2(R,x,y, T, p); y1 = gel(V,2);
    4676         363 :   if (lgpol(y1)<=n) return gerepilecopy(av, R);
    4677         360 :   q = FlxqX_divrem(gel(V,1), y1, T, p, &r);
    4678         360 :   k = 2*n-degpol(y1);
    4679         360 :   S = FlxqX_halfgcd(RgX_shift_shallow(y1,-k), RgX_shift_shallow(r,-k), T, p);
    4680         360 :   return gerepileupto(av, FlxqXM_mul2(S,FlxqX_FlxqXM_qmul(q,R, T, p), T, p));
    4681             : }
    4682             : 
    4683             : /* Return M in GL_2(Fp[X]) such that:
    4684             : if [a',b']~=M*[a,b]~ then degpol(a')>= (lgpol(a)>>1) >degpol(b')
    4685             : */
    4686             : 
    4687             : static GEN
    4688         904 : FlxqX_halfgcd_i(GEN x, GEN y, GEN T, ulong p)
    4689             : {
    4690         904 :   if (lg(x)<=FlxqX_HALFGCD_LIMIT) return FlxqX_halfgcd_basecase(x, y, T, p);
    4691         363 :   return FlxqX_halfgcd_split(x, y, T, p);
    4692             : }
    4693             : 
    4694             : GEN
    4695         904 : FlxqX_halfgcd(GEN x, GEN y, GEN T, ulong p)
    4696             : {
    4697         904 :   pari_sp av = avma;
    4698             :   GEN M,q,r;
    4699         904 :   if (!signe(x))
    4700             :   {
    4701           0 :     long v = varn(x), vT = get_Flx_var(T);
    4702           0 :     retmkmat2(mkcol2(pol_0(v),pol1_FlxX(v,vT)),
    4703             :         mkcol2(pol1_FlxX(v,vT),pol_0(v)));
    4704             :   }
    4705         904 :   if (degpol(y)<degpol(x)) return FlxqX_halfgcd_i(x, y, T, p);
    4706          12 :   q = FlxqX_divrem(y, x, T, p, &r);
    4707          12 :   M = FlxqX_halfgcd_i(x, r, T, p);
    4708          12 :   gcoeff(M,1,1) = FlxX_sub(gcoeff(M,1,1), FlxqX_mul(q, gcoeff(M,1,2), T, p), p);
    4709          12 :   gcoeff(M,2,1) = FlxX_sub(gcoeff(M,2,1), FlxqX_mul(q, gcoeff(M,2,2), T, p), p);
    4710          12 :   return gerepilecopy(av, M);
    4711             : }
    4712             : 
    4713             : static GEN
    4714      151463 : FlxqX_gcd_basecase(GEN a, GEN b, GEN T, ulong p)
    4715             : {
    4716      151463 :   pari_sp av = avma, av0=avma;
    4717      977263 :   while (signe(b))
    4718             :   {
    4719             :     GEN c;
    4720      674337 :     if (gc_needed(av0,2))
    4721             :     {
    4722          28 :       if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_gcd (d = %ld)",degpol(b));
    4723          28 :       gerepileall(av0,2, &a,&b);
    4724             :     }
    4725      674337 :     av = avma; c = FlxqX_rem(a, b, T, p); a=b; b=c;
    4726             :   }
    4727      151463 :   set_avma(av); return a;
    4728             : }
    4729             : 
    4730             : GEN
    4731      156223 : FlxqX_gcd(GEN x, GEN y, GEN T, ulong p)
    4732             : {
    4733      156223 :   pari_sp av = avma;
    4734      156223 :   x = FlxqX_red(x, T, p);
    4735      156223 :   y = FlxqX_red(y, T, p);
    4736      156223 :   if (!signe(x)) return gerepileupto(av, y);
    4737      302947 :   while (lg(y)>FlxqX_GCD_LIMIT)
    4738             :   {
    4739             :     GEN c;
    4740          21 :     if (lgpol(y)<=(lgpol(x)>>1))
    4741             :     {
    4742           0 :       GEN r = FlxqX_rem(x, y, T, p);
    4743           0 :       x = y; y = r;
    4744             :     }
    4745          21 :     c = FlxqXM_FlxqX_mul2(FlxqX_halfgcd(x,y, T, p), x, y, T, p);
    4746          21 :     x = gel(c,1); y = gel(c,2);
    4747          21 :     gerepileall(av,2,&x,&y);
    4748             :   }
    4749      151463 :   return gerepileupto(av, FlxqX_gcd_basecase(x, y, T, p));
    4750             : }
    4751             : 
    4752             : static GEN
    4753        6153 : FlxqX_extgcd_basecase(GEN a, GEN b, GEN T, ulong p, GEN *ptu, GEN *ptv)
    4754             : {
    4755        6153 :   pari_sp av=avma;
    4756             :   GEN u,v,d,d1,v1;
    4757        6153 :   long vx = varn(a);
    4758        6153 :   d = a; d1 = b;
    4759        6153 :   v = pol_0(vx); v1 = pol1_FlxX(vx, get_Flx_var(T));
    4760       31017 :   while (signe(d1))
    4761             :   {
    4762       18711 :     GEN r, q = FlxqX_divrem(d, d1, T, p, &r);
    4763       18711 :     v = FlxX_sub(v,FlxqX_mul(q,v1,T, p),p);
    4764       18711 :     u=v; v=v1; v1=u;
    4765       18711 :     u=r; d=d1; d1=u;
    4766       18711 :     if (gc_needed(av,2))
    4767             :     {
    4768           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_extgcd (d = %ld)",degpol(d));
    4769           0 :       gerepileall(av,5, &d,&d1,&u,&v,&v1);
    4770             :     }
    4771             :   }
    4772        6153 :   if (ptu) *ptu = FlxqX_div(FlxX_sub(d,FlxqX_mul(b,v, T, p), p), a, T, p);
    4773        6153 :   *ptv = v; return d;
    4774             : }
    4775             : 
    4776             : static GEN
    4777           0 : FlxqX_extgcd_halfgcd(GEN x, GEN y, GEN T, ulong p, GEN *ptu, GEN *ptv)
    4778             : {
    4779           0 :   pari_sp av=avma;
    4780           0 :   GEN u,v,R = matid2_FlxXM(varn(x), get_Flx_var(T));
    4781           0 :   while (lg(y)>FlxqX_EXTGCD_LIMIT)
    4782             :   {
    4783             :     GEN M, c;
    4784           0 :     if (lgpol(y)<=(lgpol(x)>>1))
    4785             :     {
    4786           0 :       GEN r, q = FlxqX_divrem(x, y, T, p, &r);
    4787           0 :       x = y; y = r;
    4788           0 :       R = FlxqX_FlxqXM_qmul(q, R, T, p);
    4789             :     }
    4790           0 :     M = FlxqX_halfgcd(x,y, T, p);
    4791           0 :     c = FlxqXM_FlxqX_mul2(M, x,y, T, p);
    4792           0 :     R = FlxqXM_mul2(M, R, T, p);
    4793           0 :     x = gel(c,1); y = gel(c,2);
    4794           0 :     gerepileall(av,3,&x,&y,&R);
    4795             :   }
    4796           0 :   y = FlxqX_extgcd_basecase(x,y, T, p, &u,&v);
    4797           0 :   if (ptu) *ptu = FlxqX_addmulmul(u,v,gcoeff(R,1,1),gcoeff(R,2,1), T, p);
    4798           0 :   *ptv = FlxqX_addmulmul(u,v,gcoeff(R,1,2),gcoeff(R,2,2), T, p);
    4799           0 :   return y;
    4800             : }
    4801             : 
    4802             : /* x and y in Z[Y][X], return lift(gcd(x mod T,p, y mod T,p)). Set u and v st
    4803             :  * ux + vy = gcd (mod T,p) */
    4804             : GEN
    4805        6153 : FlxqX_extgcd(GEN x, GEN y, GEN T, ulong p, GEN *ptu, GEN *ptv)
    4806             : {
    4807             :   GEN d;
    4808        6153 :   pari_sp ltop=avma;
    4809        6153 :   x = FlxqX_red(x, T, p);
    4810        6153 :   y = FlxqX_red(y, T, p);
    4811        6153 :   if (lg(y)>FlxqX_EXTGCD_LIMIT)
    4812           0 :     d = FlxqX_extgcd_halfgcd(x, y, T, p, ptu, ptv);
    4813             :   else
    4814        6153 :     d = FlxqX_extgcd_basecase(x, y, T, p, ptu, ptv);
    4815        6153 :   gerepileall(ltop,ptu?3:2,&d,ptv,ptu);
    4816        6153 :   return d;
    4817             : }
    4818             : 
    4819             : GEN
    4820       12942 : FlxqX_safegcd(GEN P, GEN Q, GEN T, ulong p)
    4821             : {
    4822       12942 :   pari_sp av = avma;
    4823             :   GEN U;
    4824       12942 :   if (!signe(P)) return gcopy(Q);
    4825       12942 :   if (!signe(Q)) return gcopy(P);
    4826             :   for(;;)
    4827             :   {
    4828       86810 :     U = Flxq_invsafe(leading_coeff(Q), T, p);
    4829       49876 :     if (!U) return gc_NULL(av);
    4830       49876 :     Q = FlxqX_Flxq_mul_to_monic(Q,U,T,p);
    4831       49876 :     P = FlxqX_rem(P,Q,T,p);
    4832       49876 :     if (!signe(P)) break;
    4833       36934 :     if (gc_needed(av, 1))
    4834             :     {
    4835           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_safegcd");
    4836           0 :       gerepileall(av, 2, &P,&Q);
    4837             :     }
    4838       36934 :     swap(P, Q);
    4839             :   }
    4840       12942 :   U = Flxq_invsafe(leading_coeff(Q), T, p);
    4841       12942 :   if (!U) return gc_NULL(av);
    4842       12942 :   Q = FlxqX_Flxq_mul_to_monic(Q,U,T,p);
    4843       12942 :   return gerepileupto(av, Q);
    4844             : }
    4845             : 
    4846             : struct _FlxqX {ulong p; GEN T;};
    4847        2484 : static GEN _FlxqX_mul(void *data,GEN a,GEN b)
    4848             : {
    4849        2484 :   struct _FlxqX *d=(struct _FlxqX*)data;
    4850        2484 :   return FlxqX_mul(a,b,d->T,d->p);
    4851             : }
    4852       10255 : static GEN _FlxqX_sqr(void *data,GEN a)
    4853             : {
    4854       10255 :   struct _FlxqX *d=(struct _FlxqX*)data;
    4855       10255 :   return FlxqX_sqr(a,d->T,d->p);
    4856             : }
    4857             : 
    4858             : GEN
    4859       10227 : FlxqX_powu(GEN V, ulong n, GEN T, ulong p)
    4860             : {
    4861       10227 :   struct _FlxqX d; d.p=p; d.T=T;
    4862       10227 :   return gen_powu(V, n, (void*)&d, &_FlxqX_sqr, &_FlxqX_mul);
    4863             : }
    4864             : 
    4865             : /* Res(A,B) = Res(B,R) * lc(B)^(a-r) * (-1)^(ab), with R=A%B, a=deg(A) ...*/
    4866             : GEN
    4867          56 : FlxqX_resultant(GEN a, GEN b, GEN T, ulong p)
    4868             : {
    4869          56 :   long vT = get_Flx_var(T);
    4870             :   long da,db,dc;
    4871             :   pari_sp av;
    4872          56 :   GEN c,lb, res = pol1_Flx(vT);
    4873             : 
    4874          56 :   if (!signe(a) || !signe(b)) return pol0_Flx(vT);
    4875             : 
    4876          56 :   da = degpol(a);
    4877          56 :   db = degpol(b);
    4878          56 :   if (db > da)
    4879             :   {
    4880          21 :     swapspec(a,b, da,db);
    4881          21 :     if (both_odd(da,db)) res = Flx_neg(res, p);
    4882             :   }
    4883          56 :   if (!da) return pol1_Flx(vT); /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
    4884          56 :   av = avma;
    4885         203 :   while (db)
    4886             :   {
    4887          91 :     lb = gel(b,db+2);
    4888          91 :     c = FlxqX_rem(a,b, T,p);
    4889          91 :     a = b; b = c; dc = degpol(c);
    4890          91 :     if (dc < 0) { set_avma(av); return pol0_Flx(vT); }
    4891             : 
    4892          91 :     if (both_odd(da,db)) res = Flx_neg(res, p);
    4893          91 :     if (!equali1(lb)) res = Flxq_mul(res, Flxq_powu(lb, da - dc, T, p), T, p);
    4894          91 :     if (gc_needed(av,2))
    4895             :     {
    4896           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_resultant (da = %ld)",da);
    4897           0 :       gerepileall(av,3, &a,&b,&res);
    4898             :     }
    4899          91 :     da = db; /* = degpol(a) */
    4900          91 :     db = dc; /* = degpol(b) */
    4901             :   }
    4902          56 :   res = Flxq_mul(res, Flxq_powu(gel(b,2), da, T, p), T, p);
    4903          56 :   return gerepileupto(av, res);
    4904             : }
    4905             : 
    4906             : /* disc P = (-1)^(n(n-1)/2) lc(P)^(n - deg P' - 2) Res(P,P'), n = deg P */
    4907             : GEN
    4908          14 : FlxqX_disc(GEN P, GEN T, ulong p)
    4909             : {
    4910          14 :   pari_sp av = avma;
    4911          14 :   GEN L, dP = FlxX_deriv(P, p), D = FlxqX_resultant(P, dP, T, p);
    4912             :   long dd;
    4913          14 :   if (!lgpol(D)) return pol0_Flx(get_Flx_var(T));
    4914          14 :   dd = degpol(P) - 2 - degpol(dP); /* >= -1; > -1 iff p | deg(P) */
    4915          14 :   L = leading_coeff(P);
    4916          14 :   if (dd && !Flx_equal1(L))
    4917           0 :     D = (dd == -1)? Flxq_div(D,L,T,p): Flxq_mul(D, Flxq_powu(L, dd, T, p), T, p);
    4918          14 :   if (degpol(P) & 2) D = Flx_neg(D, p);
    4919          14 :   return gerepileupto(av, D);
    4920             : }
    4921             : 
    4922             : GEN
    4923        1002 : FlxqXV_prod(GEN V, GEN T, ulong p)
    4924             : {
    4925        1002 :   struct _FlxqX d; d.p=p; d.T=T;
    4926        1002 :   return gen_product(V, (void*)&d, &_FlxqX_mul);
    4927             : }
    4928             : 
    4929             : static GEN
    4930         990 : FlxqV_roots_to_deg1(GEN x, GEN T, ulong p, long v)
    4931             : {
    4932         990 :   long sv = get_Flx_var(T);
    4933         990 :   pari_APPLY_same(deg1pol_shallow(pol1_Flx(sv),Flx_neg(gel(x,i),p),v))
    4934             : }
    4935             : 
    4936             : GEN
    4937         990 : FlxqV_roots_to_pol(GEN V, GEN T, ulong p, long v)
    4938             : {
    4939         990 :   pari_sp ltop = avma;
    4940         990 :   GEN W = FlxqV_roots_to_deg1(V, T, p, v);
    4941         990 :   return gerepileupto(ltop, FlxqXV_prod(W, T, p));
    4942             : }
    4943             : 
    4944             : /*** FlxqM ***/
    4945             : 
    4946             : GEN
    4947       71239 : FlxqC_Flxq_mul(GEN x, GEN y, GEN T, ulong p)
    4948       71239 : { pari_APPLY_type(t_COL, Flxq_mul(gel(x, i), y, T, p)) }
    4949             : 
    4950             : GEN
    4951       11844 : FlxqM_Flxq_mul(GEN x, GEN y, GEN T, ulong p)
    4952       11844 : { pari_APPLY_same(FlxqC_Flxq_mul(gel(x, i), y, T, p)) }
    4953             : 
    4954             : static GEN
    4955      356149 : kron_pack_Flx_spec_half(GEN x, long l) {
    4956      356149 :   if (l == 0)
    4957      173276 :     return gen_0;
    4958      182873 :   return Flx_to_int_halfspec(x, l);
    4959             : }
    4960             : 
    4961             : static GEN
    4962       21790 : kron_pack_Flx_spec(GEN x, long l) {
    4963             :   long i;
    4964             :   GEN w, y;
    4965       21790 :   if (l == 0)
    4966        3880 :     return gen_0;
    4967       17910 :   y = cgetipos(l + 2);
    4968       71481 :   for (i = 0, w = int_LSW(y); i < l; i++, w = int_nextW(w))
    4969       53571 :     *w = x[i];
    4970       17910 :   return y;
    4971             : }
    4972             : 
    4973             : static GEN
    4974           0 : kron_pack_Flx_spec_2(GEN x, long l) {
    4975           0 :   return Flx_eval2BILspec(x, 2, l);
    4976             : }
    4977             : 
    4978             : static GEN
    4979           0 : kron_pack_Flx_spec_3(GEN x, long l) {
    4980           0 :   return Flx_eval2BILspec(x, 3, l);
    4981             : }
    4982             : 
    4983             : static GEN
    4984       98876 : kron_pack_Flx_spec_bits(GEN x, long b, long l) {
    4985             :   GEN y;
    4986             :   long i;
    4987       98876 :   if (l == 0)
    4988       24185 :     return gen_0;
    4989       74691 :   y = cgetg(l + 1, t_VECSMALL);
    4990      379524 :   for(i = 1; i <= l; i++)
    4991      304833 :     y[i] = x[l - i];
    4992       74691 :   return nv_fromdigits_2k(y, b);
    4993             : }
    4994             : 
    4995             : static GEN
    4996       14740 : kron_unpack_Flx(GEN z, ulong p)
    4997             : {
    4998       14740 :   long i, l = lgefint(z);
    4999       14740 :   GEN x = cgetg(l, t_VECSMALL), w;
    5000       74179 :   for (w = int_LSW(z), i = 2; i < l; w = int_nextW(w), i++)
    5001       59439 :     x[i] = ((ulong) *w) % p;
    5002       14740 :   return Flx_renormalize(x, l);
    5003             : }
    5004             : 
    5005             : static GEN
    5006           0 : kron_unpack_Flx_2(GEN x, ulong p) {
    5007           0 :   long d = (lgefint(x)-1)/2 - 1;
    5008           0 :   return Z_mod2BIL_Flx_2(x, d, p);
    5009             : }
    5010             : 
    5011             : static GEN
    5012           0 : kron_unpack_Flx_3(GEN x, ulong p) {
    5013           0 :   long d = lgefint(x)/3 - 1;
    5014           0 :   return Z_mod2BIL_Flx_3(x, d, p);
    5015             : }
    5016             : 
    5017             : /* assume b < BITS_IN_LONG */
    5018             : static GEN
    5019       53174 : kron_unpack_Flx_bits_narrow(GEN z, long b, ulong p) {
    5020       53174 :   GEN v = binary_2k_nv(z, b), x;
    5021       53174 :   long i, l = lg(v) + 1;
    5022       53174 :   x = cgetg(l, t_VECSMALL);
    5023      332225 :   for (i = 2; i < l; i++)
    5024      279051 :     x[i] = v[l - i] % p;
    5025       53174 :   return Flx_renormalize(x, l);
    5026             : }
    5027             : 
    5028             : static GEN
    5029        7000 : kron_unpack_Flx_bits_wide(GEN z, long b, ulong p, ulong pi) {
    5030        7000 :   GEN v = binary_2k(z, b), x, y;
    5031        7000 :   long i, l = lg(v) + 1, ly;
    5032        7000 :   x = cgetg(l, t_VECSMALL);
    5033       70000 :   for (i = 2; i < l; i++) {
    5034       63000 :     y = gel(v, l - i);
    5035       63000 :     ly = lgefint(y);
    5036       63000 :     switch (ly) {
    5037           0 :     case 2: x[i] = 0; break;
    5038        7849 :     case 3: x[i] = *int_W_lg(y, 0, ly) % p; break;
    5039       31574 :     case 4: x[i] = remll_pre(*int_W_lg(y, 1, ly), *int_W_lg(y, 0, ly), p, pi); break;
    5040       47154 :     case 5: x[i] = remlll_pre(*int_W_lg(y, 2, ly), *int_W_lg(y, 1, ly),
    5041       47154 :                               *int_W_lg(y, 0, ly), p, pi); break;
    5042           0 :     default: x[i] = umodiu(gel(v, l - i), p);
    5043             :     }
    5044             :   }
    5045        7000 :   return Flx_renormalize(x, l);
    5046             : }
    5047             : 
    5048             : static GEN
    5049       21002 : FlxM_pack_ZM(GEN M, GEN (*pack)(GEN, long)) {
    5050             :   long i, j, l, lc;
    5051       21002 :   GEN N = cgetg_copy(M, &l), x;
    5052       21002 :   if (l == 1)
    5053           0 :     return N;
    5054       21002 :   lc = lgcols(M);
    5055      132983 :   for (j = 1; j < l; j++) {
    5056      111981 :     gel(N, j) = cgetg(lc, t_COL);
    5057      489920 :     for (i = 1; i < lc; i++) {
    5058      377939 :       x = gcoeff(M, i, j);
    5059      377939 :       gcoeff(N, i, j) = pack(x + 2, lgpol(x));
    5060             :     }
    5061             :   }
    5062       21002 :   return N;
    5063             : }
    5064             : 
    5065             : static GEN
    5066        3825 : FlxM_pack_ZM_bits(GEN M, long b)
    5067             : {
    5068             :   long i, j, l, lc;
    5069        3825 :   GEN N = cgetg_copy(M, &l), x;
    5070        3825 :   if (l == 1)
    5071           0 :     return N;
    5072        3825 :   lc = lgcols(M);
    5073       23144 :   for (j = 1; j < l; j++) {
    5074       19319 :     gel(N, j) = cgetg(lc, t_COL);
    5075      118195 :     for (i = 1; i < lc; i++) {
    5076       98876 :       x = gcoeff(M, i, j);
    5077       98876 :       gcoeff(N, i, j) = kron_pack_Flx_spec_bits(x + 2, b, lgpol(x));
    5078             :     }
    5079             :   }
    5080        3825 :   return N;
    5081             : }
    5082             : 
    5083             : static GEN
    5084       10501 : ZM_unpack_FlxqM(GEN M, GEN T, ulong p, GEN (*unpack)(GEN, ulong))
    5085             : {
    5086       10501 :   long i, j, l, lc, sv = get_Flx_var(T);
    5087       10501 :   GEN N = cgetg_copy(M, &l), x;
    5088       10501 :   if (l == 1)
    5089           0 :     return N;
    5090       10501 :   lc = lgcols(M);
    5091       92313 :   for (j = 1; j < l; j++) {
    5092       81812 :     gel(N, j) = cgetg(lc, t_COL);
    5093      301950 :     for (i = 1; i < lc; i++) {
    5094      220138 :       x = unpack(gcoeff(M, i, j), p);
    5095      220138 :       x[1] = sv;
    5096      220138 :       gcoeff(N, i, j) = Flx_rem(x, T, p);
    5097             :     }
    5098             :   }
    5099       10501 :   return N;
    5100             : }
    5101             : 
    5102             : static GEN
    5103        1926 : ZM_unpack_FlxqM_bits(GEN M, long b, GEN T, ulong p)
    5104             : {
    5105        1926 :   long i, j, l, lc, sv = get_Flx_var(T);
    5106        1926 :   GEN N = cgetg_copy(M, &l), x;
    5107        1926 :   if (l == 1)
    5108           0 :     return N;
    5109        1926 :   lc = lgcols(M);
    5110        1926 :   if (b < BITS_IN_LONG) {
    5111       13899 :     for (j = 1; j < l; j++) {
    5112       12043 :       gel(N, j) = cgetg(lc, t_COL);
    5113       65217 :       for (i = 1; i < lc; i++) {
    5114       53174 :         x = kron_unpack_Flx_bits_narrow(gcoeff(M, i, j), b, p);
    5115       53174 :         x[1] = sv;
    5116       53174 :         gcoeff(N, i, j) = Flx_rem(x, T, p);
    5117             :       }
    5118             :     }
    5119             :   } else {
    5120          70 :     ulong pi = get_Fl_red(p);
    5121         770 :     for (j = 1; j < l; j++) {
    5122         700 :       gel(N, j) = cgetg(lc, t_COL);
    5123        7700 :       for (i = 1; i < lc; i++) {
    5124        7000 :         x = kron_unpack_Flx_bits_wide(gcoeff(M, i, j), b, p, pi);
    5125        7000 :         x[1] = sv;
    5126        7000 :         gcoeff(N, i, j) = Flx_rem(x, T, p);
    5127             :       }
    5128             :     }
    5129             :   }
    5130        1926 :   return N;
    5131             : }
    5132             : 
    5133             : GEN
    5134       12427 : FlxqM_mul_Kronecker(GEN A, GEN B, GEN T, ulong p)
    5135             : {
    5136       12427 :   pari_sp av = avma;
    5137       12427 :   long b, d = degpol(T), n = lg(A) - 1;
    5138             :   GEN C, D, z;
    5139             :   GEN (*pack)(GEN, long), (*unpack)(GEN, ulong);
    5140       12427 :   int is_sqr = A==B;
    5141             : 
    5142       12427 :   z = muliu(muliu(sqru(p - 1), d), n);
    5143       12427 :   b = expi(z) + 1;
    5144             :   /* only do expensive bit-packing if it saves at least 1 limb */
    5145       12427 :   if (b <= BITS_IN_HALFULONG) {
    5146       11971 :     if (nbits2lg(d*b) - 2 == (d + 1)/2)
    5147       10150 :       b = BITS_IN_HALFULONG;
    5148             :   } else {
    5149         456 :     long l = lgefint(z) - 2;
    5150         456 :     if (nbits2lg(d*b) - 2 == d*l)
    5151         351 :       b = l*BITS_IN_LONG;
    5152             :   }
    5153       12427 :   set_avma(av);
    5154             : 
    5155       12427 :   switch (b) {
    5156             :   case BITS_IN_HALFULONG:
    5157       10150 :     pack = kron_pack_Flx_spec_half;
    5158       10150 :     unpack = int_to_Flx_half;
    5159       10150 :     break;
    5160             :   case BITS_IN_LONG:
    5161         351 :     pack = kron_pack_Flx_spec;
    5162         351 :     unpack = kron_unpack_Flx;
    5163         351 :     break;
    5164             :   case 2*BITS_IN_LONG:
    5165           0 :     pack = kron_pack_Flx_spec_2;
    5166           0 :     unpack = kron_unpack_Flx_2;
    5167           0 :     break;
    5168             :   case 3*BITS_IN_LONG:
    5169           0 :     pack = kron_pack_Flx_spec_3;
    5170           0 :     unpack = kron_unpack_Flx_3;
    5171           0 :     break;
    5172             :   default:
    5173        1926 :     A = FlxM_pack_ZM_bits(A, b);
    5174        1926 :     B = is_sqr? A: FlxM_pack_ZM_bits(B, b);
    5175        1926 :     C = ZM_mul(A, B);
    5176        1926 :     D = ZM_unpack_FlxqM_bits(C, b, T, p);
    5177        1926 :     return gerepilecopy(av, D);
    5178             :   }
    5179       10501 :   A = FlxM_pack_ZM(A, pack);
    5180       10501 :   B = is_sqr? A: FlxM_pack_ZM(B, pack);
    5181       10501 :   C = ZM_mul(A, B);
    5182       10501 :   D = ZM_unpack_FlxqM(C, T, p, unpack);
    5183       10501 :   return gerepilecopy(av, D);
    5184             : }
    5185             : 
    5186             : /*******************************************************************/
    5187             : /*                                                                 */
    5188             : /*                       (Fl[X]/T(X))[Y] / S(Y)                    */
    5189             : /*                                                                 */
    5190             : /*******************************************************************/
    5191             : 
    5192             : GEN
    5193      312400 : FlxqXQ_mul(GEN x, GEN y, GEN S, GEN T, ulong p) {
    5194      312400 :   return FlxqX_rem(FlxqX_mul(x,y,T,p),S,T,p);
    5195             : }
    5196             : 
    5197             : GEN
    5198      193334 : FlxqXQ_sqr(GEN x, GEN S, GEN T, ulong p) {
    5199      193334 :   return FlxqX_rem(FlxqX_sqr(x,T,p),S,T,p);
    5200             : }
    5201             : 
    5202             : GEN
    5203          14 : FlxqXQ_invsafe(GEN x, GEN S, GEN T, ulong p)
    5204             : {
    5205          14 :   GEN V, z = FlxqX_extgcd(get_FlxqX_mod(S), x, T, p, NULL, &V);
    5206          14 :   if (degpol(z)) return NULL;
    5207          14 :   z = Flxq_invsafe(gel(z,2),T,p);
    5208          14 :   if (!z) return NULL;
    5209          14 :   return FlxqX_Flxq_mul(V, z, T, p);
    5210             : }
    5211             : 
    5212             : GEN
    5213          14 : FlxqXQ_inv(GEN x, GEN S, GEN T,ulong p)
    5214             : {
    5215          14 :   pari_sp av = avma;
    5216          14 :   GEN U = FlxqXQ_invsafe(x, S, T, p);
    5217          14 :   if (!U) pari_err_INV("FlxqXQ_inv",x);
    5218          14 :   return gerepileupto(av, U);
    5219             : }
    5220             : 
    5221             : GEN
    5222           0 : FlxqXQ_div(GEN x, GEN y, GEN S, GEN T, ulong p) {
    5223           0 :   return FlxqXQ_mul(x, FlxqXQ_inv(y,S,T,p),S,T,p);
    5224             : }
    5225             : 
    5226             : struct _FlxqXQ {
    5227             :   GEN T, S;
    5228             :   ulong p;
    5229             : };
    5230             : static GEN
    5231      535852 : _FlxqXQ_add(void *data, GEN x, GEN y) {
    5232      535852 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5233      535852 :   return FlxX_add(x,y, d->p);
    5234             : }
    5235             : static GEN
    5236        2121 : _FlxqXQ_sub(void *data, GEN x, GEN y) {
    5237        2121 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5238        2121 :   return FlxX_sub(x,y, d->p);
    5239             : }
    5240             : static GEN
    5241      657480 : _FlxqXQ_cmul(void *data, GEN P, long a, GEN x) {
    5242      657480 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5243      657480 :   return FlxX_Flx_mul(x,gel(P,a+2), d->p);
    5244             : }
    5245             : static GEN
    5246      351851 : _FlxqXQ_red(void *data, GEN x) {
    5247      351851 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5248      351851 :   return FlxqX_red(x, d->T, d->p);
    5249             : }
    5250             : static GEN
    5251      282704 : _FlxqXQ_mul(void *data, GEN x, GEN y) {
    5252      282704 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5253      282704 :   return FlxqXQ_mul(x,y, d->S,d->T, d->p);
    5254             : }
    5255             : static GEN
    5256      192935 : _FlxqXQ_sqr(void *data, GEN x) {
    5257      192935 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5258      192935 :   return FlxqXQ_sqr(x, d->S,d->T, d->p);
    5259             : }
    5260             : 
    5261             : static GEN
    5262      363723 : _FlxqXQ_one(void *data) {
    5263      363723 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5264      363723 :   return pol1_FlxX(get_FlxqX_var(d->S),get_Flx_var(d->T));
    5265             : }
    5266             : 
    5267             : static GEN
    5268         191 : _FlxqXQ_zero(void *data) {
    5269         191 :   struct _FlxqXQ *d = (struct _FlxqXQ*) data;
    5270         191 :   return pol_0(get_FlxqX_var(d->S));
    5271             : }
    5272             : 
    5273             : static struct bb_algebra FlxqXQ_algebra = { _FlxqXQ_red, _FlxqXQ_add,
    5274             :        _FlxqXQ_sub, _FlxqXQ_mul, _FlxqXQ_sqr, _FlxqXQ_one, _FlxqXQ_zero };
    5275             : 
    5276             : const struct bb_algebra *
    5277         219 : get_FlxqXQ_algebra(void **E, GEN S, GEN T, ulong p)
    5278             : {
    5279         219 :   GEN z = new_chunk(sizeof(struct _FlxqXQ));
    5280         219 :   struct _FlxqXQ *e = (struct _FlxqXQ *) z;
    5281         219 :   e->T = Flx_get_red(T, p);
    5282         219 :   e->S = FlxqX_get_red(S, e->T, p);
    5283         219 :   e->p  = p; *E = (void*)e;
    5284         219 :   return &FlxqXQ_algebra;
    5285             : }
    5286             : 
    5287             : /* x over Fq, return lift(x^n) mod S */
    5288             : GEN
    5289          42 : FlxqXQ_pow(GEN x, GEN n, GEN S, GEN T, ulong p)
    5290             : {
    5291             :   struct _FlxqXQ D;
    5292          42 :   long s = signe(n);
    5293          42 :   if (!s) return pol1_FlxX(get_FlxqX_var(S),get_Flx_var(T));
    5294          42 :   if (s < 0) x = FlxqXQ_inv(x,S,T,p);
    5295          42 :   if (is_pm1(n)) return s < 0 ? x : gcopy(x);
    5296          42 :   if (degpol(x) >= get_FlxqX_degree(S)) x = FlxqX_rem(x,S,T,p);
    5297          42 :   T = Flx_get_red(T, p);
    5298          42 :   S = FlxqX_get_red(S, T, p);
    5299          42 :   D.S = S;
    5300          42 :   D.T = T;
    5301          42 :   D.p = p;
    5302          42 :   return gen_pow(x, n, (void*)&D, &_FlxqXQ_sqr, &_FlxqXQ_mul);
    5303             : }
    5304             : 
    5305             : /* x over Fq, return lift(x^n) mod S */
    5306             : GEN
    5307       72827 : FlxqXQ_powu(GEN x, ulong n, GEN S, GEN T, ulong p)
    5308             : {
    5309             :   struct _FlxqXQ D;
    5310       72827 :   switch(n)
    5311             :   {
    5312           0 :     case 0: return pol1_FlxX(get_FlxqX_var(S),get_Flx_var(T));
    5313        7532 :     case 1: return gcopy(x);
    5314         399 :     case 2: return FlxqXQ_sqr(x, S, T, p);
    5315             :   }
    5316       64896 :   T = Flx_get_red(T, p);
    5317       64896 :   S = FlxqX_get_red(S, T, p);
    5318       64896 :   D.S = S; D.T = T; D.p = p;
    5319       64896 :   return gen_powu(x, n, (void*)&D, &_FlxqXQ_sqr, &_FlxqXQ_mul);
    5320             : }
    5321             : 
    5322             : GEN
    5323       29052 : FlxqXQ_powers(GEN x, long l, GEN S, GEN T, ulong p)
    5324             : {
    5325             :   struct _FlxqXQ D;
    5326       29052 :   int use_sqr = 2*degpol(x) >= get_FlxqX_degree(S);
    5327       29052 :   T = Flx_get_red(T, p);
    5328       29052 :   S = FlxqX_get_red(S, T, p);
    5329       29052 :   D.S = S; D.T = T; D.p = p;
    5330       29052 :   return gen_powers(x, l, use_sqr, (void*)&D, &_FlxqXQ_sqr, &_FlxqXQ_mul,&_FlxqXQ_one);
    5331             : }
    5332             : 
    5333             : static GEN
    5334         486 : FlxqXn_mul(GEN a, GEN b, long n, GEN T, ulong p)
    5335             : {
    5336         486 :   return RgXn_red_shallow(FlxqX_mul(a, b, T, p), n);
    5337             : }
    5338             : 
    5339             : /* Let v a linear form, return the linear form z->v(tau*z)
    5340             :    that is, v*(M_tau) */
    5341             : 
    5342             : static GEN
    5343         320 : FlxqXQ_transmul_init(GEN tau, GEN S, GEN T, ulong p)
    5344             : {
    5345             :   GEN bht;
    5346         320 :   GEN h, Sp = get_FlxqX_red(S, &h);
    5347         320 :   long n = degpol(Sp), vS = varn(Sp), vT = get_Flx_var(T);
    5348         320 :   GEN ft = FlxX_recipspec(Sp+2, n+1, n+1, vT);
    5349         320 :   GEN bt = FlxX_recipspec(tau+2, lgpol(tau), n, vT);
    5350         320 :   setvarn(ft, vS); setvarn(bt, vS);
    5351         320 :   if (h)
    5352           0 :     bht = FlxqXn_mul(bt, h, n-1, T, p);
    5353             :   else
    5354             :   {
    5355         320 :     GEN bh = FlxqX_div(RgX_shift_shallow(tau, n-1), S, T, p);
    5356         320 :     bht = FlxX_recipspec(bh+2, lgpol(bh), n-1, vT);
    5357         320 :     setvarn(bht, vS);
    5358             :   }
    5359         320 :   return mkvec3(bt, bht, ft);
    5360             : }
    5361             : 
    5362             : static GEN
    5363         632 : FlxqXQ_transmul(GEN tau, GEN a, long n, GEN T, ulong p)
    5364             : {
    5365         632 :   pari_sp ltop = avma;
    5366             :   GEN t1, t2, t3, vec;
    5367         632 :   GEN bt = gel(tau, 1), bht = gel(tau, 2), ft = gel(tau, 3);
    5368         632 :   if (signe(a)==0) return pol_0(varn(a));
    5369         618 :   t2 = RgX_shift_shallow(FlxqX_mul(bt, a, T, p),1-n);
    5370         618 :   if (signe(bht)==0) return gerepilecopy(ltop, t2);
    5371         486 :   t1 = RgX_shift_shallow(FlxqX_mul(ft, a, T, p),-n);
    5372         486 :   t3 = FlxqXn_mul(t1, bht, n-1, T, p);
    5373         486 :   vec = FlxX_sub(t2, RgX_shift_shallow(t3, 1), p);
    5374         486 :   return gerepileupto(ltop, vec);
    5375             : }
    5376             : 
    5377             : static GEN
    5378         160 : polxn_FlxX(long n, long v, long vT)
    5379             : {
    5380         160 :   long i, a = n+2;
    5381         160 :   GEN p = cgetg(a+1, t_POL);
    5382         160 :   p[1] = evalsigne(1)|evalvarn(v);
    5383         160 :   for (i = 2; i < a; i++) gel(p,i) = pol0_Flx(vT);
    5384         160 :   gel(p,a) = pol1_Flx(vT); return p;
    5385             : }
    5386             : 
    5387             : GEN
    5388         132 : FlxqXQ_minpoly(GEN x, GEN S, GEN T, ulong p)
    5389             : {
    5390         132 :   pari_sp ltop = avma;
    5391             :   long vS, vT, n;
    5392             :   GEN v_x, g, tau;
    5393         132 :   vS = get_FlxqX_var(S);
    5394         132 :   vT = get_Flx_var(T);
    5395         132 :   n = get_FlxqX_degree(S);
    5396         132 :   g = pol1_FlxX(vS,vT);
    5397         132 :   tau = pol1_FlxX(vS,vT);
    5398         132 :   S = FlxqX_get_red(S, T, p);
    5399         132 :   v_x = FlxqXQ_powers(x, usqrt(2*n), S, T, p);
    5400         424 :   while(signe(tau) != 0)
    5401             :   {
    5402             :     long i, j, m, k1;
    5403             :     GEN M, v, tr;
    5404             :     GEN g_prime, c;
    5405         160 :     if (degpol(g) == n) { tau = pol1_FlxX(vS, vT); g = pol1_FlxX(vS, vT); }
    5406         160 :     v = random_FlxqX(n, vS, T, p);
    5407         160 :     tr = FlxqXQ_transmul_init(tau, S, T, p);
    5408         160 :     v = FlxqXQ_transmul(tr, v, n, T, p);
    5409         160 :     m = 2*(n-degpol(g));
    5410         160 :     k1 = usqrt(m);
    5411         160 :     tr = FlxqXQ_transmul_init(gel(v_x,k1+1), S, T, p);
    5412         160 :     c = cgetg(m+2,t_POL);
    5413         160 :     c[1] = evalsigne(1)|evalvarn(vS);
    5414         632 :     for (i=0; i<m; i+=k1)
    5415             :     {
    5416         472 :       long mj = minss(m-i, k1);
    5417        1440 :       for (j=0; j<mj; j++)
    5418         968 :         gel(c,m+1-(i+j)) = FlxqX_dotproduct(v, gel(v_x,j+1), T, p);
    5419         472 :       v = FlxqXQ_transmul(tr, v, n, T, p);
    5420             :     }
    5421         160 :     c = FlxX_renormalize(c, m+2);
    5422             :     /* now c contains <v,x^i> , i = 0..m-1  */
    5423         160 :     M = FlxqX_halfgcd(polxn_FlxX(m, vS, vT), c, T, p);
    5424         160 :     g_prime = gmael(M, 2, 2);
    5425         160 :     if (degpol(g_prime) < 1) continue;
    5426         153 :     g = FlxqX_mul(g, g_prime, T, p);
    5427         153 :     tau = FlxqXQ_mul(tau, FlxqX_FlxqXQV_eval(g_prime, v_x, S, T, p), S, T, p);
    5428             :   }
    5429         132 :   g = FlxqX_normalize(g,T, p);
    5430         132 :   return gerepilecopy(ltop,g);
    5431             : }
    5432             : 
    5433             : GEN
    5434           0 : FlxqXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, ulong p)
    5435             : {
    5436           0 :   return FlxXV_to_FlxM(FlxqXQ_powers(y,m-1,S,T,p), n, T[1]);
    5437             : }
    5438             : 
    5439             : GEN
    5440       57343 : FlxqX_FlxqXQV_eval(GEN P, GEN V, GEN S, GEN T, ulong p)
    5441             : {
    5442             :   struct _FlxqXQ D;
    5443       57343 :   T = Flx_get_red(T, p);
    5444       57343 :   S = FlxqX_get_red(S, T, p);
    5445       57343 :   D.S=S; D.T=T; D.p=p;
    5446       57343 :   return gen_bkeval_powers(P, degpol(P), V, (void*)&D, &FlxqXQ_algebra,
    5447             :                                                    _FlxqXQ_cmul);
    5448             : }
    5449             : 
    5450             : GEN
    5451       64285 : FlxqX_FlxqXQ_eval(GEN Q, GEN x, GEN S, GEN T, ulong p)
    5452             : {
    5453             :   struct _FlxqXQ D;
    5454       64285 :   int use_sqr = 2*degpol(x) >= get_FlxqX_degree(S);
    5455       64285 :   T = Flx_get_red(T, p);
    5456       64285 :   S = FlxqX_get_red(S, T, p);
    5457       64285 :   D.S=S; D.T=T; D.p=p;
    5458       64285 :   return gen_bkeval(Q, degpol(Q), x, use_sqr, (void*)&D, &FlxqXQ_algebra,
    5459             :                                                     _FlxqXQ_cmul);
    5460             : }
    5461             : 
    5462             : static GEN
    5463       62930 : FlxqXQ_autpow_sqr(void * E, GEN x)
    5464             : {
    5465       62930 :   struct _FlxqXQ *D = (struct _FlxqXQ *)E;
    5466       62930 :   GEN S = D->S, T = D->T;
    5467       62930 :   ulong p = D->p;
    5468       62930 :   GEN phi = gel(x,1), S1 = gel(x,2);
    5469       62930 :   long n = brent_kung_optpow(get_Flx_degree(T)-1,lgpol(S1)+1,1);
    5470       62930 :   GEN V = Flxq_powers(phi, n, T, p);
    5471       62930 :   GEN phi2 = Flx_FlxqV_eval(phi, V, T, p);
    5472       62930 :   GEN Sphi = FlxY_FlxqV_evalx(S1, V, T, p);
    5473       62930 :   GEN S2 = FlxqX_FlxqXQ_eval(Sphi, S1, S, T, p);
    5474       62930 :   return mkvec2(phi2, S2);
    5475             : }
    5476             : 
    5477             : static GEN
    5478        1126 : FlxqXQ_autpow_mul(void * E, GEN x, GEN y)
    5479             : {
    5480        1126 :   struct _FlxqXQ *D = (struct _FlxqXQ *)E;
    5481        1126 :   GEN S = D->S, T = D->T;
    5482        1126 :   ulong p = D->p;
    5483        1126 :   GEN phi1 = gel(x,1), S1 = gel(x,2);
    5484        1126 :   GEN phi2 = gel(y,1), S2 = gel(y,2);
    5485        1126 :   long n = brent_kung_optpow(get_Flx_degree(T)-1,lgpol(S1)+1,1);
    5486        1126 :   GEN V = Flxq_powers(phi2, n, T, p);
    5487        1126 :   GEN phi3 = Flx_FlxqV_eval(phi1, V, T, p);
    5488        1126 :   GEN Sphi = FlxY_FlxqV_evalx(S1, V, T, p);
    5489        1126 :   GEN S3 = FlxqX_FlxqXQ_eval(Sphi, S2, S, T, p);
    5490        1126 :   return mkvec2(phi3, S3);
    5491             : }
    5492             : 
    5493             : GEN
    5494       61572 : FlxqXQ_autpow(GEN aut, long n, GEN S, GEN T, ulong p)
    5495             : {
    5496             :   struct _FlxqXQ D;
    5497       61572 :   T = Flx_get_red(T, p);
    5498       61572 :   S = FlxqX_get_red(S, T, p);
    5499       61572 :   D.S=S; D.T=T; D.p=p;
    5500       61572 :   return gen_powu(aut,n,&D,FlxqXQ_autpow_sqr,FlxqXQ_autpow_mul);
    5501             : }
    5502             : 
    5503             : static GEN
    5504       28301 : FlxqXQ_autsum_mul(void *E, GEN x, GEN y)
    5505             : {
    5506       28301 :   struct _FlxqXQ *D = (struct _FlxqXQ *)E;
    5507       28301 :   GEN S = D->S, T = D->T;
    5508       28301 :   ulong p = D->p;
    5509       28301 :   GEN phi1 = gel(x,1), S1 = gel(x,2), a1 = gel(x,3);
    5510       28301 :   GEN phi2 = gel(y,1), S2 = gel(y,2), a2 = gel(y,3);
    5511       28301 :   long n2 = brent_kung_optpow(get_Flx_degree(T)-1, lgpol(S1)+lgpol(a1)+1,1);
    5512       28301 :   GEN V2 = Flxq_powers(phi2, n2, T, p);
    5513       28301 :   GEN phi3 = Flx_FlxqV_eval(phi1, V2, T, p);
    5514       28301 :   GEN Sphi = FlxY_FlxqV_evalx(S1, V2, T, p);
    5515       28301 :   GEN aphi = FlxY_FlxqV_evalx(a1, V2, T, p);
    5516       28301 :   long n = brent_kung_optpow(maxss(degpol(Sphi),degpol(aphi)),2,1);
    5517       28301 :   GEN V = FlxqXQ_powers(S2, n, S, T, p);
    5518       28301 :   GEN S3 = FlxqX_FlxqXQV_eval(Sphi, V, S, T, p);
    5519       28301 :   GEN aS = FlxqX_FlxqXQV_eval(aphi, V, S, T, p);
    5520       28301 :   GEN a3 = FlxqXQ_mul(aS, a2, S, T, p);
    5521       28301 :   return mkvec3(phi3, S3, a3);
    5522             : }
    5523             : 
    5524             : static GEN
    5525       16947 : FlxqXQ_autsum_sqr(void * T, GEN x)
    5526       16947 : { return FlxqXQ_autsum_mul(T, x, x); }
    5527             : 
    5528             : GEN
    5529       10817 : FlxqXQ_autsum(GEN aut, long n, GEN S, GEN T, ulong p)
    5530             : {
    5531             :   struct _FlxqXQ D;
    5532       10817 :   T = Flx_get_red(T, p);
    5533       10817 :   S = FlxqX_get_red(S, T, p);
    5534       10817 :   D.S=S; D.T=T; D.p=p;
    5535       10817 :   return gen_powu(aut,n,&D,FlxqXQ_autsum_sqr,FlxqXQ_autsum_mul);
    5536             : }
    5537             : 
    5538             : static GEN
    5539          20 : FlxqXQ_auttrace_mul(void *E, GEN x, GEN y)
    5540             : {
    5541          20 :   struct _FlxqXQ *D = (struct _FlxqXQ *)E;
    5542          20 :   GEN S = D->S, T = D->T;
    5543          20 :   ulong p = D->p;
    5544          20 :   GEN S1 = gel(x,1), a1 = gel(x,2);
    5545          20 :   GEN S2 = gel(y,1), a2 = gel(y,2);
    5546          20 :   long n = brent_kung_optpow(maxss(degpol(S1),degpol(a1)),2,1);
    5547          20 :   GEN V = FlxqXQ_powers(S2, n, S, T, p);
    5548          20 :   GEN S3 = FlxqX_FlxqXQV_eval(S1, V, S, T, p);
    5549          20 :   GEN aS = FlxqX_FlxqXQV_eval(a1, V, S, T, p);
    5550          20 :   GEN a3 = FlxX_add(aS, a2, p);
    5551          20 :   return mkvec2(S3, a3);
    5552             : }
    5553             : 
    5554             : static GEN
    5555          20 : FlxqXQ_auttrace_sqr(void *E, GEN x)
    5556          20 : { return FlxqXQ_auttrace_mul(E, x, x); }
    5557             : 
    5558             : GEN
    5559         314 : FlxqXQ_auttrace(GEN x, ulong n, GEN S, GEN T, ulong p)
    5560             : {
    5561             :   struct _FlxqXQ D;
    5562         314 :   T = Flx_get_red(T, p);
    5563         314 :   S = FlxqX_get_red(S, T, p);
    5564         314 :   D.S=S; D.T=T; D.p=p;
    5565         314 :   return gen_powu(x,n,(void*)&D,FlxqXQ_auttrace_sqr,FlxqXQ_auttrace_mul);
    5566             : }
    5567             : 
    5568             : /*******************************************************************/
    5569             : /*                                                                 */
    5570             : /*                      FlxYqQ                                     */
    5571             : /*                                                                 */
    5572             : /*******************************************************************/
    5573             : 
    5574             : /*Preliminary implementation to speed up FpX_ffisom*/
    5575             : typedef struct {
    5576             :   GEN S, T;
    5577             :   ulong p;
    5578             : } FlxYqq_muldata;
    5579             : 
    5580             : /* reduce x in Fl[X, Y] in the algebra Fl[X, Y]/ (P(X),Q(Y)) */
    5581             : static GEN
    5582        9884 : FlxYqq_redswap(GEN x, GEN S, GEN T, ulong p)
    5583             : {
    5584        9884 :   pari_sp ltop=avma;
    5585        9884 :   long n = get_Flx_degree(S);
    5586        9884 :   long m = get_Flx_degree(T);
    5587        9884 :   long w = get_Flx_var(T);
    5588        9884 :   GEN V = FlxX_swap(x,m,w);
    5589        9884 :   V = FlxqX_red(V,S,p);
    5590        9884 :   V = FlxX_swap(V,n,w);
    5591        9884 :   return gerepilecopy(ltop,V);
    5592             : }
    5593             : static GEN
    5594        6902 : FlxYqq_sqr(void *data, GEN x)
    5595             : {
    5596        6902 :   FlxYqq_muldata *D = (FlxYqq_muldata*)data;
    5597        6902 :   return FlxYqq_redswap(FlxqX_sqr(x, D->T, D->p),D->S,D->T,D->p);
    5598             : }
    5599             : 
    5600             : static GEN
    5601        2982 : FlxYqq_mul(void *data, GEN x, GEN y)
    5602             : {
    5603        2982 :   FlxYqq_muldata *D = (FlxYqq_muldata*)data;
    5604        2982 :   return FlxYqq_redswap(FlxqX_mul(x,y, D->T, D->p),D->S,D->T,D->p);
    5605             : }
    5606             : 
    5607             : /* x in Z[X,Y], S in Z[X] over Fq = Z[Y]/(p,T); compute lift(x^n mod (S,T,p)) */
    5608             : GEN
    5609        3696 : FlxYqq_pow(GEN x, GEN n, GEN S, GEN T, ulong p)
    5610             : {
    5611        3696 :   pari_sp av = avma;
    5612             :   FlxYqq_muldata D;
    5613             :   GEN y;
    5614        3696 :   D.S = S;
    5615        3696 :   D.T = T;
    5616        3696 :   D.p = p;
    5617        3696 :   y = gen_pow(x, n, (void*)&D, &FlxYqq_sqr, &FlxYqq_mul);
    5618        3696 :   return gerepileupto(av, y);
    5619             : }

Generated by: LCOV version 1.13