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 - FpX.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.18.0 lcov report (development 29804-254f602fce) Lines: 1626 1788 90.9 %
Date: 2024-12-18 09:08:59 Functions: 184 197 93.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2007  The PARI group.
       2             : 
       3             : This file is part of the PARI/GP package.
       4             : 
       5             : PARI/GP is free software; you can redistribute it and/or modify it under the
       6             : terms of the GNU General Public License as published by the Free Software
       7             : Foundation; either version 2 of the License, or (at your option) any later
       8             : version. It is distributed in the hope that it will be useful, but WITHOUT
       9             : ANY WARRANTY WHATSOEVER.
      10             : 
      11             : Check the License for details. You should have received a copy of it, along
      12             : with the package; see the file 'COPYING'. If not, write to the Free Software
      13             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      14             : 
      15             : #include "pari.h"
      16             : #include "paripriv.h"
      17             : 
      18             : /* Not so fast arithmetic with polynomials over Fp */
      19             : 
      20             : static GEN
      21    85924448 : get_FpX_red(GEN T, GEN *B)
      22             : {
      23    85924448 :   if (typ(T)!=t_VEC) { *B=NULL; return T; }
      24      251105 :   *B = gel(T,1); return gel(T,2);
      25             : }
      26             : 
      27             : /***********************************************************************/
      28             : /**                                                                   **/
      29             : /**                              FpX                                  **/
      30             : /**                                                                   **/
      31             : /***********************************************************************/
      32             : 
      33             : /* FpX are polynomials over Z/pZ represented as t_POL with
      34             :  * t_INT coefficients.
      35             :  * 1) Coefficients should belong to {0,...,p-1}, though nonreduced
      36             :  * coefficients should work but be slower.
      37             :  *
      38             :  * 2) p is not assumed to be prime, but it is assumed that impossible divisions
      39             :  *    will not happen.
      40             :  * 3) Theses functions let some garbage on the stack, but are gerepileupto
      41             :  * compatible.
      42             :  */
      43             : 
      44             : static ulong
      45    44539927 : to_Flx(GEN *P, GEN *Q, GEN p)
      46             : {
      47    44539927 :   ulong pp = uel(p,2);
      48    44539927 :   *P = ZX_to_Flx(*P, pp);
      49    44545335 :   if(Q) *Q = ZX_to_Flx(*Q, pp);
      50    44544230 :   return pp;
      51             : }
      52             : 
      53             : static ulong
      54     2131808 : to_Flxq(GEN *P, GEN *T, GEN p)
      55             : {
      56     2131808 :   ulong pp = uel(p,2);
      57     2131808 :   if (P) *P = ZX_to_Flx(*P, pp);
      58     2131819 :   *T = ZXT_to_FlxT(*T, pp); return pp;
      59             : }
      60             : 
      61             : GEN
      62        1726 : Z_to_FpX(GEN a, GEN p, long v)
      63             : {
      64        1726 :   pari_sp av = avma;
      65        1726 :   GEN z = cgetg(3, t_POL);
      66        1726 :   GEN x = modii(a, p);
      67        1726 :   if (!signe(x)) { set_avma(av); return pol_0(v); }
      68        1726 :   z[1] = evalsigne(1) | evalvarn(v);
      69        1726 :   gel(z,2) = x; return z;
      70             : }
      71             : 
      72             : /* z in Z[X], return lift(z * Mod(1,p)), normalized*/
      73             : GEN
      74    93728900 : FpX_red(GEN z, GEN p)
      75             : {
      76    93728900 :   long i, l = lg(z);
      77    93728900 :   GEN x = cgetg(l, t_POL);
      78   969153636 :   for (i=2; i<l; i++) gel(x,i) = modii(gel(z,i),p);
      79    93327306 :   x[1] = z[1]; return FpX_renormalize(x,l);
      80             : }
      81             : 
      82             : GEN
      83      404429 : FpXV_red(GEN x, GEN p)
      84     1901479 : { pari_APPLY_type(t_VEC, FpX_red(gel(x,i), p)) }
      85             : 
      86             : GEN
      87     1664020 : FpXT_red(GEN x, GEN p)
      88             : {
      89     1664020 :   if (typ(x) == t_POL)
      90     1575915 :     return FpX_red(x, p);
      91             :   else
      92      392024 :     pari_APPLY_type(t_VEC, FpXT_red(gel(x,i), p))
      93             : }
      94             : 
      95             : GEN
      96     1836711 : FpX_normalize(GEN z, GEN p)
      97             : {
      98     1836711 :   GEN p1 = leading_coeff(z);
      99     1836715 :   if (lg(z) == 2 || equali1(p1)) return z;
     100      125635 :   return FpX_Fp_mul_to_monic(z, Fp_inv(p1,p), p);
     101             : }
     102             : 
     103             : GEN
     104      311435 : FpX_center(GEN T, GEN p, GEN pov2)
     105             : {
     106      311435 :   long i, l = lg(T);
     107      311435 :   GEN P = cgetg(l,t_POL);
     108     1407884 :   for(i=2; i<l; i++) gel(P,i) = Fp_center(gel(T,i), p, pov2);
     109      311429 :   P[1] = T[1]; return P;
     110             : }
     111             : GEN
     112     1235728 : FpX_center_i(GEN T, GEN p, GEN pov2)
     113             : {
     114     1235728 :   long i, l = lg(T);
     115     1235728 :   GEN P = cgetg(l,t_POL);
     116     5609980 :   for(i=2; i<l; i++) gel(P,i) = Fp_center_i(gel(T,i), p, pov2);
     117     1235779 :   P[1] = T[1]; return P;
     118             : }
     119             : 
     120             : GEN
     121    16909167 : FpX_add(GEN x,GEN y,GEN p)
     122             : {
     123    16909167 :   long lx = lg(x), ly = lg(y), i;
     124             :   GEN z;
     125    16909167 :   if (lx < ly) swapspec(x,y, lx,ly);
     126    16909167 :   z = cgetg(lx,t_POL); z[1] = x[1];
     127   148736675 :   for (i=2; i<ly; i++) gel(z,i) = Fp_add(gel(x,i),gel(y,i), p);
     128    35597992 :   for (   ; i<lx; i++) gel(z,i) = modii(gel(x,i), p);
     129    16909121 :   z = ZX_renormalize(z, lx);
     130    16909163 :   if (!lgpol(z)) { set_avma((pari_sp)(z + lx)); return pol_0(varn(x)); }
     131    16589085 :   return z;
     132             : }
     133             : 
     134             : static GEN
     135       21043 : Fp_red_FpX(GEN x, GEN p, long v)
     136             : {
     137             :   GEN z;
     138       21043 :   if (!signe(x)) return pol_0(v);
     139       14595 :   z = cgetg(3, t_POL);
     140       14595 :   gel(z,2) = Fp_red(x,p);
     141       14595 :   z[1] = evalvarn(v);
     142       14595 :   return FpX_renormalize(z, 3);
     143             : }
     144             : 
     145             : static GEN
     146         935 : Fp_neg_FpX(GEN x, GEN p, long v)
     147             : {
     148             :   GEN z;
     149         935 :   if (!signe(x)) return pol_0(v);
     150         794 :   z = cgetg(3, t_POL);
     151         794 :   gel(z,2) = Fp_neg(x,p);
     152         794 :   z[1] = evalvarn(v);
     153         794 :   return FpX_renormalize(z, 3);
     154             : }
     155             : 
     156             : GEN
     157      906675 : FpX_Fp_add(GEN y,GEN x,GEN p)
     158             : {
     159      906675 :   long i, lz = lg(y);
     160             :   GEN z;
     161      906675 :   if (lz == 2) return Fp_red_FpX(x,p,varn(y));
     162      885632 :   z = cgetg(lz,t_POL); z[1] = y[1];
     163      885632 :   gel(z,2) = Fp_add(gel(y,2),x, p);
     164      885632 :   if (lz == 3) z = FpX_renormalize(z,lz);
     165             :   else
     166     2249352 :     for(i=3;i<lz;i++) gel(z,i) = icopy(gel(y,i));
     167      885632 :   return z;
     168             : }
     169             : GEN
     170           0 : FpX_Fp_add_shallow(GEN y,GEN x,GEN p)
     171             : {
     172           0 :   long i, lz = lg(y);
     173             :   GEN z;
     174           0 :   if (lz == 2) return scalar_ZX_shallow(x,varn(y));
     175           0 :   z = cgetg(lz,t_POL); z[1] = y[1];
     176           0 :   gel(z,2) = Fp_add(gel(y,2),x, p);
     177           0 :   if (lz == 3) z = FpX_renormalize(z,lz);
     178             :   else
     179           0 :     for(i=3;i<lz;i++) gel(z,i) = gel(y,i);
     180           0 :   return z;
     181             : }
     182             : GEN
     183      588320 : FpX_Fp_sub(GEN y,GEN x,GEN p)
     184             : {
     185      588320 :   long i, lz = lg(y);
     186             :   GEN z;
     187      588320 :   if (lz == 2) return Fp_neg_FpX(x,p,varn(y));
     188      587385 :   z = cgetg(lz,t_POL); z[1] = y[1];
     189      587385 :   gel(z,2) = Fp_sub(gel(y,2),x, p);
     190      587386 :   if (lz == 3) z = FpX_renormalize(z,lz);
     191             :   else
     192     1357382 :     for(i=3;i<lz;i++) gel(z,i) = icopy(gel(y,i));
     193      587386 :   return z;
     194             : }
     195             : GEN
     196       11146 : FpX_Fp_sub_shallow(GEN y,GEN x,GEN p)
     197             : {
     198       11146 :   long i, lz = lg(y);
     199             :   GEN z;
     200       11146 :   if (lz == 2) return Fp_neg_FpX(x,p,varn(y));
     201       11146 :   z = cgetg(lz,t_POL); z[1] = y[1];
     202       11146 :   gel(z,2) = Fp_sub(gel(y,2),x, p);
     203       11146 :   if (lz == 3) z = FpX_renormalize(z,lz);
     204             :   else
     205       37357 :     for(i=3;i<lz;i++) gel(z,i) = gel(y,i);
     206       11146 :   return z;
     207             : }
     208             : 
     209             : GEN
     210      467782 : FpX_neg(GEN x,GEN p)
     211     5005496 : { pari_APPLY_ZX(Fp_neg(gel(x,i), p)); }
     212             : 
     213             : static GEN
     214    14519134 : FpX_subspec(GEN x,GEN y,GEN p, long nx, long ny)
     215             : {
     216             :   long i, lz;
     217             :   GEN z;
     218    14519134 :   if (nx >= ny)
     219             :   {
     220    10135711 :     lz = nx+2;
     221    10135711 :     z = cgetg(lz,t_POL); z[1] = 0; z += 2;
     222    62387116 :     for (i=0; i<ny; i++) gel(z,i) = Fp_sub(gel(x,i),gel(y,i), p);
     223    10876510 :     for (   ; i<nx; i++) gel(z,i) = modii(gel(x,i), p);
     224             :   }
     225             :   else
     226             :   {
     227     4383423 :     lz = ny+2;
     228     4383423 :     z = cgetg(lz,t_POL); z[1] = 0; z += 2;
     229    22831324 :     for (i=0; i<nx; i++) gel(z,i) = Fp_sub(gel(x,i),gel(y,i), p);
     230    14522169 :     for (   ; i<ny; i++) gel(z,i) = Fp_neg(gel(y,i), p);
     231             :   }
     232    14514335 :   z = FpX_renormalize(z-2, lz);
     233    14519139 :   if (!lgpol(z)) { set_avma((pari_sp)(z + lz)); return pol_0(0); }
     234    14261222 :   return z;
     235             : }
     236             : 
     237             : GEN
     238    14305520 : FpX_sub(GEN x,GEN y,GEN p)
     239             : {
     240    14305520 :   GEN z = FpX_subspec(x+2,y+2,p,lgpol(x),lgpol(y));
     241    14305527 :   setvarn(z, varn(x));
     242    14305527 :   return z;
     243             : }
     244             : 
     245             : GEN
     246       25690 : Fp_FpX_sub(GEN x, GEN y, GEN p)
     247             : {
     248       25690 :   long ly = lg(y), i;
     249             :   GEN z;
     250       25690 :   if (ly <= 3) {
     251         482 :     z = cgetg(3, t_POL);
     252         482 :     x = (ly == 3)? Fp_sub(x, gel(y,2), p): modii(x, p);
     253         482 :     if (!signe(x)) { set_avma((pari_sp)(z + 3)); return pol_0(varn(y)); }
     254         399 :     z[1] = evalsigne(1)|y[1]; gel(z,2) = x; return z;
     255             :   }
     256       25208 :   z = cgetg(ly,t_POL);
     257       25208 :   gel(z,2) = Fp_sub(x, gel(y,2), p);
     258       93612 :   for (i = 3; i < ly; i++) gel(z,i) = Fp_neg(gel(y,i), p);
     259       25208 :   z = ZX_renormalize(z, ly);
     260       25208 :   if (!lgpol(z)) { set_avma((pari_sp)(z + ly)); return pol_0(varn(x)); }
     261       25208 :   z[1] = y[1]; return z;
     262             : }
     263             : 
     264             : GEN
     265         994 : FpX_convol(GEN x, GEN y, GEN p)
     266             : {
     267         994 :   long lx = lg(x), ly = lg(y), i;
     268             :   GEN z;
     269         994 :   if (lx < ly) swapspec(x,y, lx,ly);
     270         994 :   z = cgetg(ly,t_POL); z[1] = x[1];
     271       58751 :   for (i=2; i<ly; i++) gel(z,i) = Fp_mul(gel(x,i),gel(y,i), p);
     272         994 :   z = ZX_renormalize(z, ly);
     273         994 :   if (!lgpol(z)) { set_avma((pari_sp)(z + lx)); return pol_0(varn(x)); }
     274         994 :   return z;
     275             : }
     276             : 
     277             : GEN
     278    26567599 : FpX_mul(GEN x,GEN y,GEN p)
     279             : {
     280    26567599 :   if (lgefint(p) == 3)
     281             :   {
     282    13614095 :     ulong pp = to_Flx(&x, &y, p);
     283    13614837 :     return Flx_to_ZX(Flx_mul(x, y, pp));
     284             :   }
     285    12953504 :   return FpX_red(ZX_mul(x, y), p);
     286             : }
     287             : 
     288             : GEN
     289     7997791 : FpX_mulspec(GEN a, GEN b, GEN p, long na, long nb)
     290     7997791 : { return FpX_red(ZX_mulspec(a, b, na, nb), p); }
     291             : 
     292             : GEN
     293     6216097 : FpX_sqr(GEN x,GEN p)
     294             : {
     295     6216097 :   if (lgefint(p) == 3)
     296             :   {
     297      354876 :     ulong pp = to_Flx(&x, NULL, p);
     298      354877 :     return Flx_to_ZX(Flx_sqr(x, pp));
     299             :   }
     300     5861221 :   return FpX_red(ZX_sqr(x), p);
     301             : }
     302             : 
     303             : GEN
     304     1057404 : FpX_mulu(GEN x, ulong t,GEN p)
     305             : {
     306     1057404 :   t = umodui(t, p); if (!t) return zeropol(varn(x));
     307     6628185 :   pari_APPLY_ZX(Fp_mulu(gel(x,i), t, p));
     308             : }
     309             : 
     310             : GEN
     311        8610 : FpX_divu(GEN y, ulong x, GEN p)
     312        8610 : { return FpX_Fp_div(y, utoi(umodui(x, p)), p); }
     313             : 
     314             : GEN
     315     5687142 : FpX_Fp_mulspec(GEN y,GEN x,GEN p,long ly)
     316             : {
     317             :   GEN z;
     318             :   long i;
     319     5687142 :   if (!signe(x)) return pol_0(0);
     320     5635277 :   z = cgetg(ly+2,t_POL); z[1] = evalsigne(1);
     321    33271344 :   for(i=0; i<ly; i++) gel(z,i+2) = Fp_mul(gel(y,i), x, p);
     322     5633530 :   return ZX_renormalize(z, ly+2);
     323             : }
     324             : 
     325             : GEN
     326     5672461 : FpX_Fp_mul(GEN y,GEN x,GEN p)
     327             : {
     328     5672461 :   GEN z = FpX_Fp_mulspec(y+2,x,p,lgpol(y));
     329     5672132 :   setvarn(z, varn(y)); return z;
     330             : }
     331             : 
     332             : GEN
     333      612189 : FpX_Fp_div(GEN y, GEN x, GEN p)
     334             : {
     335      612189 :   return FpX_Fp_mul(y, Fp_inv(x, p), p);
     336             : }
     337             : 
     338             : GEN
     339      133736 : FpX_Fp_mul_to_monic(GEN y,GEN x,GEN p)
     340             : {
     341             :   GEN z;
     342             :   long i, l;
     343      133736 :   z = cgetg_copy(y, &l); z[1] = y[1];
     344      575317 :   for(i=2; i<l-1; i++) gel(z,i) = Fp_mul(gel(y,i), x, p);
     345      133736 :   gel(z,l-1) = gen_1; return z;
     346             : }
     347             : 
     348             : struct _FpXQ {
     349             :   GEN T, p, aut;
     350             : };
     351             : 
     352             : struct _FpX
     353             : {
     354             :   GEN p;
     355             :   long v;
     356             : };
     357             : 
     358             : static GEN
     359      370084 : _FpX_mul(void* E, GEN x, GEN y)
     360      370084 : { struct _FpX *D = (struct _FpX *)E; return FpX_mul(x, y, D->p); }
     361             : static GEN
     362       86200 : _FpX_sqr(void *E, GEN x)
     363       86200 : { struct _FpX *D = (struct _FpX *)E; return FpX_sqr(x, D->p); }
     364             : 
     365             : GEN
     366      317243 : FpX_powu(GEN x, ulong n, GEN p)
     367             : {
     368             :   struct _FpX D;
     369      317243 :   if (n==0) return pol_1(varn(x));
     370       60455 :   D.p = p;
     371       60455 :   return gen_powu(x, n, (void *)&D, _FpX_sqr, _FpX_mul);
     372             : }
     373             : 
     374             : GEN
     375      309052 : FpXV_prod(GEN V, GEN p)
     376             : {
     377             :   struct _FpX D;
     378      309052 :   D.p = p;
     379      309052 :   return gen_product(V, (void *)&D, &_FpX_mul);
     380             : }
     381             : 
     382             : static GEN
     383       34499 : _FpX_pow(void* E, GEN x, GEN y)
     384       34499 : { struct _FpX *D = (struct _FpX *)E; return FpX_powu(x, itou(y), D->p); }
     385             : static GEN
     386           0 : _FpX_one(void *E)
     387           0 : { struct _FpX *D = (struct _FpX *)E; return pol_1(D->v); }
     388             : 
     389             : GEN
     390       22216 : FpXV_factorback(GEN f, GEN e, GEN p, long v)
     391             : {
     392             :   struct _FpX D;
     393       22216 :   D.p = p; D.v = v;
     394       22216 :   return gen_factorback(f, e, (void *)&D, &_FpX_mul, &_FpX_pow, &_FpX_one);
     395             : }
     396             : 
     397             : GEN
     398       94891 : FpX_halve(GEN x, GEN p)
     399      281501 : { pari_APPLY_pol_normalized(Fp_halve(gel(x,i), p)); }
     400             : 
     401             : static GEN
     402    66569730 : FpX_divrem_basecase(GEN x, GEN y, GEN p, GEN *pr)
     403             : {
     404             :   long vx, dx, dy, dy1, dz, i, j, sx, lr;
     405             :   pari_sp av0, av;
     406             :   GEN z,p1,rem,lead;
     407             : 
     408    66569730 :   if (!signe(y)) pari_err_INV("FpX_divrem",y);
     409    66569730 :   vx = varn(x);
     410    66569730 :   dy = degpol(y);
     411    66569100 :   dx = degpol(x);
     412    66569718 :   if (dx < dy)
     413             :   {
     414      126026 :     if (pr)
     415             :     {
     416      125492 :       av0 = avma; x = FpX_red(x, p);
     417      125492 :       if (pr == ONLY_DIVIDES) { set_avma(av0); return signe(x)? NULL: pol_0(vx); }
     418      125492 :       if (pr == ONLY_REM) return x;
     419      125492 :       *pr = x;
     420             :     }
     421      126026 :     return pol_0(vx);
     422             :   }
     423    66443692 :   lead = leading_coeff(y);
     424    66447824 :   if (!dy) /* y is constant */
     425             :   {
     426      606616 :     if (pr && pr != ONLY_DIVIDES)
     427             :     {
     428      603007 :       if (pr == ONLY_REM) return pol_0(vx);
     429      584750 :       *pr = pol_0(vx);
     430             :     }
     431      588359 :     av0 = avma;
     432      588359 :     if (equali1(lead)) return FpX_red(x, p);
     433      583859 :     else return gerepileupto(av0, FpX_Fp_div(x, lead, p));
     434             :   }
     435    65841208 :   av0 = avma; dz = dx-dy;
     436    65841208 :   if (lgefint(p) == 3)
     437             :   { /* assume ab != 0 mod p */
     438    28355051 :     ulong pp = to_Flx(&x, &y, p);
     439    28358895 :     z = Flx_divrem(x, y, pp, pr);
     440    28340321 :     set_avma(av0); /* HACK: assume pr last on stack, then z */
     441    28338745 :     if (!z) return NULL;
     442    28338605 :     z = leafcopy(z);
     443    28340512 :     if (pr && pr != ONLY_DIVIDES && pr != ONLY_REM)
     444             :     {
     445     5593634 :       *pr = leafcopy(*pr);
     446     5593716 :       *pr = Flx_to_ZX_inplace(*pr);
     447             :     }
     448    28340589 :     return Flx_to_ZX_inplace(z);
     449             :   }
     450    37486157 :   lead = equali1(lead)? NULL: gclone(Fp_inv(lead,p));
     451    37485890 :   set_avma(av0);
     452    37485899 :   z=cgetg(dz+3,t_POL); z[1] = x[1];
     453    37485854 :   x += 2; y += 2; z += 2;
     454    40382273 :   for (dy1=dy-1; dy1>=0 && !signe(gel(y, dy1)); dy1--);
     455             : 
     456    37485854 :   p1 = gel(x,dx); av = avma;
     457    37485854 :   gel(z,dz) = lead? gerepileuptoint(av, Fp_mul(p1,lead, p)): icopy(p1);
     458   113967384 :   for (i=dx-1; i>=dy; i--)
     459             :   {
     460    76480475 :     av=avma; p1=gel(x,i);
     461   958131556 :     for (j=i-dy1; j<=i && j<=dz; j++)
     462   881660702 :       p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
     463    76470854 :     if (lead) p1 = mulii(p1,lead);
     464    76470854 :     gel(z,i-dy) = gerepileuptoint(av,modii(p1, p));
     465             :   }
     466    37486909 :   if (!pr) { guncloneNULL(lead); return z-2; }
     467             : 
     468    37409214 :   rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3);
     469    41343491 :   for (sx=0; ; i--)
     470             :   {
     471    41343491 :     p1 = gel(x,i);
     472   227809918 :     for (j=maxss(0,i-dy1); j<=i && j<=dz; j++)
     473   186467180 :       p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
     474    41342657 :     p1 = modii(p1,p); if (signe(p1)) { sx = 1; break; }
     475     4071667 :     if (!i) break;
     476     3934328 :     set_avma(av);
     477             :   }
     478    37407607 :   if (pr == ONLY_DIVIDES)
     479             :   {
     480           0 :     guncloneNULL(lead);
     481           0 :     if (sx) return gc_NULL(av0);
     482           0 :     return gc_const((pari_sp)rem, z-2);
     483             :   }
     484    37407607 :   lr=i+3; rem -= lr;
     485    37407607 :   rem[0] = evaltyp(t_POL) | _evallg(lr);
     486    37407607 :   rem[1] = z[-1];
     487    37407607 :   p1 = gerepileuptoint((pari_sp)rem, p1);
     488    37408891 :   rem += 2; gel(rem,i) = p1;
     489   167802022 :   for (i--; i>=0; i--)
     490             :   {
     491   130393064 :     av=avma; p1 = gel(x,i);
     492  1088238869 :     for (j=maxss(0,i-dy1); j<=i && j<=dz; j++)
     493   957867347 :       p1 = subii(p1, mulii(gel(z,j),gel(y,i-j)));
     494   130371168 :     gel(rem,i) = gerepileuptoint(av, modii(p1,p));
     495             :   }
     496    37408958 :   rem -= 2;
     497    37408958 :   guncloneNULL(lead);
     498    37408916 :   if (!sx) (void)FpX_renormalize(rem, lr);
     499    37408924 :   if (pr == ONLY_REM) return gerepileupto(av0,rem);
     500     2504127 :   *pr = rem; return z-2;
     501             : }
     502             : 
     503             : GEN
     504      164909 : FpX_div_by_X_x(GEN a, GEN x, GEN p, GEN *r)
     505             : {
     506      164909 :   long l = lg(a), i;
     507             :   GEN z;
     508      164909 :   if (l <= 3)
     509             :   {
     510           0 :     if (r) *r = l == 2? gen_0: icopy(gel(a,2));
     511           0 :     return pol_0(varn(a));
     512             :   }
     513      164909 :   l--; z = cgetg(l, t_POL); z[1] = a[1];
     514      164910 :   gel(z, l-1) = gel(a,l);
     515     2481269 :   for (i = l-2; i > 1; i--) /* z[i] = a[i+1] + x*z[i+1] */
     516     2316362 :     gel(z,i) = Fp_addmul(gel(a,i+1), x, gel(z,i+1), p);
     517      164907 :   if (r) *r = Fp_addmul(gel(a,2), x, gel(z,2), p);
     518      164907 :   return z;
     519             : }
     520             : 
     521             : static GEN
     522      134778 : _FpX_divrem(void * E, GEN x, GEN y, GEN *r)
     523             : {
     524      134778 :   struct _FpX *D = (struct _FpX*) E;
     525      134778 :   return FpX_divrem(x, y, D->p, r);
     526             : }
     527             : static GEN
     528       20062 : _FpX_add(void * E, GEN x, GEN y) {
     529       20062 :   struct _FpX *D = (struct _FpX*) E;
     530       20062 :   return FpX_add(x, y, D->p);
     531             : }
     532             : 
     533             : static struct bb_ring FpX_ring = { _FpX_add,_FpX_mul,_FpX_sqr };
     534             : 
     535             : GEN
     536       11403 : FpX_digits(GEN x, GEN T, GEN p)
     537             : {
     538             :   struct _FpX D;
     539       11403 :   long d = degpol(T), n = (lgpol(x)+d-1)/d;
     540       11403 :   D.p = p;
     541       11403 :   return gen_digits(x,T,n,(void *)&D, &FpX_ring, _FpX_divrem);
     542             : }
     543             : 
     544             : GEN
     545        4564 : FpXV_FpX_fromdigits(GEN x, GEN T, GEN p)
     546             : {
     547             :   struct _FpX D;
     548        4564 :   D.p = p;
     549        4564 :   return gen_fromdigits(x,T,(void *)&D, &FpX_ring);
     550             : }
     551             : 
     552             : long
     553      253213 : FpX_valrem(GEN x, GEN t, GEN p, GEN *py)
     554             : {
     555      253213 :   pari_sp av=avma;
     556             :   long k;
     557             :   GEN r, y;
     558             : 
     559      253213 :   for (k=0; ; k++)
     560             :   {
     561      646349 :     y = FpX_divrem(x, t, p, &r);
     562      646346 :     if (signe(r)) break;
     563      393136 :     x = y;
     564             :   }
     565      253210 :   *py = gerepilecopy(av,x);
     566      253219 :   return k;
     567             : }
     568             : 
     569             : static GEN
     570       81037 : FpX_addmulmul(GEN u, GEN v, GEN x, GEN y, GEN p)
     571             : {
     572       81037 :   return FpX_add(FpX_mul(u, x, p),FpX_mul(v, y, p), p);
     573             : }
     574             : 
     575             : static GEN
     576       33664 : FpXM_FpX_mul2(GEN M, GEN x, GEN y, GEN p)
     577             : {
     578       33664 :   GEN res = cgetg(3, t_COL);
     579       33664 :   gel(res, 1) = FpX_addmulmul(gcoeff(M,1,1), gcoeff(M,1,2), x, y, p);
     580       33664 :   gel(res, 2) = FpX_addmulmul(gcoeff(M,2,1), gcoeff(M,2,2), x, y, p);
     581       33664 :   return res;
     582             : }
     583             : 
     584             : static GEN
     585       16389 : FpXM_mul2(GEN A, GEN B, GEN p)
     586             : {
     587       16389 :   GEN A11=gcoeff(A,1,1),A12=gcoeff(A,1,2), B11=gcoeff(B,1,1),B12=gcoeff(B,1,2);
     588       16389 :   GEN A21=gcoeff(A,2,1),A22=gcoeff(A,2,2), B21=gcoeff(B,2,1),B22=gcoeff(B,2,2);
     589       16389 :   GEN M1 = FpX_mul(FpX_add(A11,A22, p), FpX_add(B11,B22, p), p);
     590       16389 :   GEN M2 = FpX_mul(FpX_add(A21,A22, p), B11, p);
     591       16389 :   GEN M3 = FpX_mul(A11, FpX_sub(B12,B22, p), p);
     592       16389 :   GEN M4 = FpX_mul(A22, FpX_sub(B21,B11, p), p);
     593       16389 :   GEN M5 = FpX_mul(FpX_add(A11,A12, p), B22, p);
     594       16389 :   GEN M6 = FpX_mul(FpX_sub(A21,A11, p), FpX_add(B11,B12, p), p);
     595       16389 :   GEN M7 = FpX_mul(FpX_sub(A12,A22, p), FpX_add(B21,B22, p), p);
     596       16389 :   GEN T1 = FpX_add(M1,M4, p), T2 = FpX_sub(M7,M5, p);
     597       16389 :   GEN T3 = FpX_sub(M1,M2, p), T4 = FpX_add(M3,M6, p);
     598       16389 :   retmkmat22(FpX_add(T1,T2, p), FpX_add(M3,M5, p),
     599             :              FpX_add(M2,M4, p), FpX_add(T3,T4, p));
     600             : }
     601             : 
     602             : /* Return [0,1;1,-q]*M */
     603             : static GEN
     604       16298 : FpX_FpXM_qmul(GEN q, GEN M, GEN p)
     605             : {
     606       16298 :   GEN u = FpX_mul(gcoeff(M,2,1), q, p);
     607       16298 :   GEN v = FpX_mul(gcoeff(M,2,2), q, p);
     608       16298 :   retmkmat22(gcoeff(M,2,1), gcoeff(M,2,2),
     609             :     FpX_sub(gcoeff(M,1,1), u, p), FpX_sub(gcoeff(M,1,2), v, p));
     610             : }
     611             : 
     612             : static GEN
     613          24 : matid2_FpXM(long v)
     614          24 : { retmkmat22(pol_1(v), pol_0(v), pol_0(v), pol_1(v)); }
     615             : 
     616             : static GEN
     617           8 : matJ2_FpXM(long v)
     618           8 : { retmkmat22(pol_0(v), pol_1(v), pol_1(v), pol_0(v)); }
     619             : 
     620             : INLINE GEN
     621      958783 : FpX_shift(GEN a, long n) { return RgX_shift_shallow(a, n); }
     622             : 
     623             : INLINE GEN
     624      199478 : FpXn_red(GEN a, long n) { return RgXn_red_shallow(a, n); }
     625             : 
     626             : /* Fast resultant formula from William Hart in Flint <http://flintlib.org/> */
     627             : 
     628             : struct FpX_res
     629             : {
     630             :    GEN res, lc;
     631             :    long deg0, deg1, off;
     632             : };
     633             : 
     634             : INLINE void
     635        3749 : FpX_halfres_update(long da, long db, long dr, GEN p, struct FpX_res *res)
     636             : {
     637        3749 :   if (dr >= 0)
     638             :   {
     639        3749 :     if (!equali1(res->lc))
     640             :     {
     641        3749 :       res->lc  = Fp_powu(res->lc, da - dr, p);
     642        3749 :       res->res = Fp_mul(res->res, res->lc, p);
     643             :     }
     644        3749 :     if (both_odd(da + res->off, db + res->off))
     645           0 :       res->res = Fp_neg(res->res, p);
     646             :   } else
     647             :   {
     648           0 :     if (db == 0)
     649             :     {
     650           0 :       if (!equali1(res->lc))
     651             :       {
     652           0 :           res->lc  = Fp_powu(res->lc, da, p);
     653           0 :           res->res = Fp_mul(res->res, res->lc, p);
     654             :       }
     655             :     } else
     656           0 :       res->res = gen_0;
     657             :   }
     658        3749 : }
     659             : 
     660             : static GEN
     661       31487 : FpX_halfres_basecase(GEN a, GEN b, GEN p, GEN *pa, GEN *pb, struct FpX_res *res)
     662             : {
     663       31487 :   pari_sp av=avma;
     664             :   GEN u,u1,v,v1, M;
     665       31487 :   long vx = varn(a), n = lgpol(a)>>1;
     666       31487 :   u1 = v = pol_0(vx);
     667       31487 :   u = v1 = pol_1(vx);
     668      441971 :   while (lgpol(b)>n)
     669             :   {
     670             :     GEN r, q;
     671      410484 :     q = FpX_divrem(a,b,p, &r);
     672      410484 :     if (res)
     673             :     {
     674        3625 :       long da = degpol(a), db=degpol(b), dr = degpol(r);
     675        3625 :       res->lc = leading_coeff(b);
     676        3625 :       if (dr >= n)
     677        3403 :         FpX_halfres_update(da,db,dr,p,res);
     678             :       else
     679             :       {
     680         222 :         res->deg0 = da;
     681         222 :         res->deg1 = db;
     682             :       }
     683             :     }
     684      410484 :     a = b; b = r; swap(u,u1); swap(v,v1);
     685      410484 :     u1 = FpX_sub(u1, FpX_mul(u, q, p), p);
     686      410484 :     v1 = FpX_sub(v1, FpX_mul(v, q, p), p);
     687      410484 :     if (gc_needed(av,2))
     688             :     {
     689           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_halfgcd (d = %ld)",degpol(b));
     690           0 :       if (res)
     691           0 :         gerepileall(av, 8, &a,&b,&u1,&v1,&u,&v,&res->res,&res->lc);
     692             :       else
     693           0 :         gerepileall(av, 6, &a,&b,&u1,&v1,&u,&v);
     694             :     }
     695             :   }
     696       31487 :   M = mkmat22(u,v,u1,v1); *pa = a; *pb = b;
     697         222 :   return res ? gc_all(av, 5, &M, pa, pb, &res->res, &res->lc)
     698       31709 :              : gc_all(av, 3, &M, pa, pb);
     699             : }
     700             : 
     701             : static GEN FpX_halfres_i(GEN x, GEN y, GEN p, GEN *a, GEN *b, struct FpX_res *res);
     702             : 
     703             : static GEN
     704       17382 : FpX_halfres_split(GEN x, GEN y, GEN p, GEN *a, GEN *b, struct FpX_res *res)
     705             : {
     706       17382 :   pari_sp av = avma;
     707             :   GEN R, S, T, V1, V2;
     708             :   GEN x1, y1, r, q;
     709       17382 :   long l = lgpol(x), n = l>>1, k;
     710       17382 :   if (lgpol(y) <= n)
     711           8 :     { *a = RgX_copy(x); *b = RgX_copy(y); return matid2_FpXM(varn(x)); }
     712       17374 :   if (res)
     713             :   {
     714         166 :      res->lc = leading_coeff(y);
     715         166 :      res->deg0 -= n;
     716         166 :      res->deg1 -= n;
     717         166 :      res->off += n;
     718             :   }
     719       17374 :   R = FpX_halfres_i(FpX_shift(x,-n), FpX_shift(y,-n), p, a, b, res);
     720       17374 :   if (res)
     721             :   {
     722         166 :     res->off -= n;
     723         166 :     res->deg0 += n;
     724         166 :     res->deg1 += n;
     725             :   }
     726       17374 :   V1 = FpXM_FpX_mul2(R, FpXn_red(x,n), FpXn_red(y,n), p);
     727       17374 :   x1 = FpX_add(FpX_shift(*a,n), gel(V1,1), p);
     728       17374 :   y1 = FpX_add(FpX_shift(*b,n), gel(V1,2), p);
     729       17374 :   if (lgpol(y1) <= n)
     730             :   {
     731        1084 :     *a = x1; *b = y1;
     732          42 :     return res ? gc_all(av, 5, &R, a, b, &res->res, &res->lc)
     733        1126 :                : gc_all(av, 3, &R, a, b);
     734             :   }
     735       16290 :   k = 2*n-degpol(y1);
     736       16290 :   q = FpX_divrem(x1, y1, p, &r);
     737       16290 :   if (res)
     738             :   {
     739         124 :     long dx1 = degpol(x1), dy1 = degpol(y1), dr = degpol(r);
     740         124 :     if (dy1 < degpol(y))
     741         116 :       FpX_halfres_update(res->deg0, res->deg1, dy1, p,res);
     742         124 :     res->lc = gel(y1, dy1+2);
     743         124 :     res->deg0 = dx1;
     744         124 :     res->deg1 = dy1;
     745         124 :     if (dr >= n)
     746             :     {
     747         124 :       FpX_halfres_update(dx1, dy1, dr, p,res);
     748         124 :       res->deg0 = dy1;
     749         124 :       res->deg1 = dr;
     750             :     }
     751         124 :     res->deg0 -= k;
     752         124 :     res->deg1 -= k;
     753         124 :     res->off += k;
     754             :   }
     755       16290 :   S = FpX_halfres_i(FpX_shift(y1,-k), FpX_shift(r,-k), p, a, b, res);
     756       16290 :   if (res)
     757             :   {
     758         124 :     res->deg0 += k;
     759         124 :     res->deg1 += k;
     760         124 :     res->off -= k;
     761             :   }
     762       16290 :   T = FpXM_mul2(S, FpX_FpXM_qmul(q, R, p), p);
     763       16290 :   V2 = FpXM_FpX_mul2(S, FpXn_red(y1,k), FpXn_red(r,k), p);
     764       16290 :   *a = FpX_add(FpX_shift(*a,k), gel(V2,1), p);
     765       16290 :   *b = FpX_add(FpX_shift(*b,k), gel(V2,2), p);
     766         124 :   return res ? gc_all(av, 5, &T, a, b, &res->res, &res->lc)
     767       16414 :              : gc_all(av, 3, &T, a, b);
     768             : }
     769             : 
     770             : static GEN
     771       48869 : FpX_halfres_i(GEN x, GEN y, GEN p, GEN *a, GEN *b, struct FpX_res *res)
     772             : {
     773       48869 :   if (lgpol(x) < FpX_HALFGCD_LIMIT)
     774       31487 :     return FpX_halfres_basecase(x, y, p, a, b, res);
     775       17382 :   return FpX_halfres_split(x, y, p, a, b, res);
     776             : }
     777             : 
     778             : static GEN
     779       15099 : FpX_halfgcd_all_i(GEN x, GEN y, GEN p, GEN *pa, GEN *pb)
     780             : {
     781             :   GEN a, b;
     782       15099 :   GEN R = FpX_halfres_i(x, y, p, &a, &b, NULL);
     783       15099 :   if (pa) *pa = a;
     784       15099 :   if (pb) *pb = b;
     785       15099 :   return R;
     786             : }
     787             : 
     788             : /* Return M in GL_2(Fp[X]) such that:
     789             : if [a',b']~=M*[a,b]~ then degpol(a')>= (lgpol(a)>>1) >degpol(b')
     790             : */
     791             : 
     792             : GEN
     793       15239 : FpX_halfgcd_all(GEN x, GEN y, GEN p, GEN *a, GEN *b)
     794             : {
     795       15239 :   pari_sp av = avma;
     796             :   GEN R, q, r;
     797       15239 :   if (lgefint(p)==3)
     798             :   {
     799         140 :     ulong pp = to_Flx(&x, &y, p);
     800         140 :     R = Flx_halfgcd_all(x, y, pp, a, b);
     801         140 :     R = FlxM_to_ZXM(R);
     802         140 :     if (a) *a = Flx_to_ZX(*a);
     803         140 :     if (b) *b = Flx_to_ZX(*b);
     804         140 :     return !a && b ? gc_all(av, 2, &R, b): gc_all(av, 1+!!a+!!b, &R, a, b);
     805             :   }
     806       15099 :   if (!signe(x))
     807             :   {
     808           0 :     if (a) *a = RgX_copy(y);
     809           0 :     if (b) *b = RgX_copy(x);
     810           0 :     return matJ2_FpXM(varn(x));
     811             :   }
     812       15099 :   if (degpol(y)<degpol(x)) return FpX_halfgcd_all_i(x, y, p, a, b);
     813         389 :   q = FpX_divrem(y,x,p,&r);
     814         389 :   R = FpX_halfgcd_all_i(x, r, p, a, b);
     815         389 :   gcoeff(R,1,1) = FpX_sub(gcoeff(R,1,1), FpX_mul(q, gcoeff(R,1,2), p), p);
     816         389 :   gcoeff(R,2,1) = FpX_sub(gcoeff(R,2,1), FpX_mul(q, gcoeff(R,2,2), p), p);
     817         389 :   return !a && b ? gc_all(av, 2, &R, b): gc_all(av, 1+!!a+!!b, &R, a, b);
     818             : }
     819             : 
     820             : GEN
     821         658 : FpX_halfgcd(GEN x, GEN y, GEN p)
     822         658 : { return FpX_halfgcd_all(x, y, p, NULL, NULL); }
     823             : 
     824             : static GEN
     825       52544 : FpX_gcd_basecase(GEN a, GEN b, GEN p)
     826             : {
     827       52544 :   pari_sp av = avma, av0=avma;
     828      449966 :   while (signe(b))
     829             :   {
     830             :     GEN c;
     831      397689 :     if (gc_needed(av0,2))
     832             :     {
     833           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_gcd (d = %ld)",degpol(b));
     834           0 :       gerepileall(av0,2, &a,&b);
     835             :     }
     836      397689 :     av = avma; c = FpX_rem(a,b,p); a=b; b=c;
     837             :   }
     838       52277 :   return gc_const(av, a);
     839             : }
     840             : 
     841             : GEN
     842     1011887 : FpX_gcd(GEN x, GEN y, GEN p)
     843             : {
     844     1011887 :   pari_sp av = avma;
     845     1011887 :   if (lgefint(p)==3)
     846             :   {
     847             :     ulong pp;
     848      958921 :     (void)new_chunk((lg(x) + lg(y)) << 2); /* scratch space */
     849      958921 :     pp = to_Flx(&x, &y, p);
     850      958922 :     x = Flx_gcd(x, y, pp);
     851      958921 :     set_avma(av); return Flx_to_ZX(x);
     852             :   }
     853       52966 :   x = FpX_red(x, p);
     854       52966 :   y = FpX_red(y, p);
     855       52966 :   if (!signe(x)) return gerepileupto(av, y);
     856       53591 :   while (lgpol(y) >= FpX_GCD_LIMIT)
     857             :   {
     858        1047 :     if (lgpol(y)<=(lgpol(x)>>1))
     859             :     {
     860           0 :       GEN r = FpX_rem(x, y, p);
     861           0 :       x = y; y = r;
     862             :     }
     863        1047 :     (void) FpX_halfgcd_all(x, y, p, &x, &y);
     864        1047 :     if (gc_needed(av,2))
     865             :     {
     866           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_gcd (y = %ld)",degpol(y));
     867           0 :       gerepileall(av,2,&x,&y);
     868             :     }
     869             :   }
     870       52544 :   return gerepileupto(av, FpX_gcd_basecase(x,y,p));
     871             : }
     872             : 
     873             : /* Return NULL if gcd can be computed else return a factor of p */
     874             : GEN
     875         804 : FpX_gcd_check(GEN x, GEN y, GEN p)
     876             : {
     877         804 :   pari_sp av = avma;
     878             :   GEN a,b,c;
     879             : 
     880         804 :   a = FpX_red(x, p);
     881         804 :   b = FpX_red(y, p);
     882        8905 :   while (signe(b))
     883             :   {
     884             :     GEN g;
     885        8157 :     if (!invmod(leading_coeff(b), p, &g)) return gerepileuptoint(av,g);
     886        8101 :     b = FpX_Fp_mul_to_monic(b, g, p);
     887        8101 :     c = FpX_rem(a, b, p); a = b; b = c;
     888        8101 :     if (gc_needed(av,1))
     889             :     {
     890           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_gcd_check (d = %ld)",degpol(b));
     891           0 :       gerepileall(av,2,&a,&b);
     892             :     }
     893             :   }
     894         748 :   return gc_NULL(av);
     895             : }
     896             : 
     897             : static GEN
     898      584747 : FpX_extgcd_basecase(GEN a, GEN b, GEN p, GEN *ptu, GEN *ptv)
     899             : {
     900      584747 :   pari_sp av=avma;
     901      584747 :   GEN v,v1, A = a, B = b;
     902      584747 :   long vx = varn(a);
     903      584747 :   if (!lgpol(b))
     904             :   {
     905           0 :     if (ptu) *ptu = pol_1(vx);
     906           0 :     *ptv = pol_0(vx);
     907           0 :     return RgX_copy(a);
     908             :   }
     909      584747 :   v = pol_0(vx); v1 = pol_1(vx);
     910             :   while (1)
     911     1553185 :   {
     912     2137932 :     GEN r, q = FpX_divrem(a,b,p, &r);
     913     2137932 :     a = b; b = r;
     914     2137932 :     swap(v,v1);
     915     2137932 :     if (!lgpol(b)) break;
     916     1553185 :     v1 = FpX_sub(v1, FpX_mul(v, q, p), p);
     917     1553185 :     if (gc_needed(av,2))
     918             :     {
     919           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_extgcd (d = %ld)",degpol(a));
     920           0 :       gerepileall(av,4,&a,&b,&v,&v1);
     921             :     }
     922             :   }
     923      584747 :   if (ptu) *ptu = FpX_div(FpX_sub(a,FpX_mul(B,v,p),p),A,p);
     924      584747 :   *ptv = v;
     925      584747 :   return a;
     926             : }
     927             : 
     928             : static GEN
     929       13435 : FpX_extgcd_halfgcd(GEN x, GEN y, GEN p, GEN *ptu, GEN *ptv)
     930             : {
     931             :   GEN u, v;
     932       13435 :   GEN V = cgetg(expu(lgpol(y))+2,t_VEC);
     933       13435 :   long i, n = 0, vs = varn(x);
     934       26963 :   while (lgpol(y) >= FpX_EXTGCD_LIMIT)
     935             :   {
     936       13528 :     if (lgpol(y)<=(lgpol(x)>>1))
     937             :     {
     938           8 :       GEN r, q = FpX_divrem(x, y, p, &r);
     939           8 :       x = y; y = r;
     940           8 :       gel(V,++n) = mkmat22(pol_0(vs),pol_1(vs),pol_1(vs),FpX_neg(q,p));
     941             :     } else
     942       13520 :       gel(V,++n) = FpX_halfgcd_all(x, y, p, &x, &y);
     943             :   }
     944       13435 :   y = FpX_extgcd_basecase(x, y, p, &u, &v);
     945       13528 :   for (i = n; i>1; i--)
     946             :   {
     947          93 :     GEN R = gel(V,i);
     948          93 :     GEN u1 = FpX_addmulmul(u, v, gcoeff(R,1,1), gcoeff(R,2,1), p);
     949          93 :     GEN v1 = FpX_addmulmul(u, v, gcoeff(R,1,2), gcoeff(R,2,2), p);
     950          93 :     u = u1; v = v1;
     951             :   }
     952             :   {
     953       13435 :     GEN R = gel(V,1);
     954       13435 :     if (ptu)
     955          40 :       *ptu = FpX_addmulmul(u, v, gcoeff(R,1,1), gcoeff(R,2,1), p);
     956       13435 :     *ptv   = FpX_addmulmul(u, v, gcoeff(R,1,2), gcoeff(R,2,2), p);
     957             :   }
     958       13435 :   return y;
     959             : }
     960             : 
     961             : /* x and y in Z[X], return lift(gcd(x mod p, y mod p)). Set u and v st
     962             :  * ux + vy = gcd (mod p) */
     963             : GEN
     964     1437403 : FpX_extgcd(GEN x, GEN y, GEN p, GEN *ptu, GEN *ptv)
     965             : {
     966     1437403 :   pari_sp av = avma;
     967             :   GEN d;
     968     1437403 :   if (lgefint(p)==3)
     969             :   {
     970      852660 :     ulong pp = to_Flx(&x, &y, p);
     971      852649 :     d = Flx_extgcd(x,y, pp, ptu,ptv);
     972      852690 :     d = Flx_to_ZX(d);
     973      852629 :     if (ptu) *ptu = Flx_to_ZX(*ptu);
     974      852630 :     *ptv = Flx_to_ZX(*ptv);
     975             :   }
     976             :   else
     977             :   {
     978      584743 :     x = FpX_red(x, p);
     979      584747 :     y = FpX_red(y, p);
     980      584747 :     if (lgpol(y) >= FpX_EXTGCD_LIMIT)
     981       13435 :       d = FpX_extgcd_halfgcd(x, y, p, ptu, ptv);
     982             :     else
     983      571312 :       d = FpX_extgcd_basecase(x, y, p, ptu, ptv);
     984             :   }
     985     1437373 :   return gc_all(av, ptu?3:2, &d, ptv, ptu);
     986             : }
     987             : 
     988             : static GEN
     989         106 : FpX_halfres(GEN x, GEN y, GEN p, GEN *a, GEN *b, GEN *r)
     990             : {
     991             :   struct FpX_res res;
     992             :   GEN V;
     993             :   long dB;
     994             : 
     995         106 :   res.res  = *r;
     996         106 :   res.lc   = leading_coeff(y);
     997         106 :   res.deg0 = degpol(x);
     998         106 :   res.deg1 = degpol(y);
     999         106 :   res.off = 0;
    1000         106 :   V = FpX_halfres_i(x, y, p, a, b, &res);
    1001         106 :   dB = degpol(*b);
    1002         106 :   if (dB < degpol(y))
    1003         106 :     FpX_halfres_update(res.deg0, res.deg1, dB, p, &res);
    1004         106 :   *r = res.res;
    1005         106 :   return V;
    1006             : }
    1007             : 
    1008             : static GEN
    1009        3875 : FpX_resultant_basecase(GEN a, GEN b, GEN p)
    1010             : {
    1011        3875 :   pari_sp av = avma;
    1012             :   long da,db,dc;
    1013        3875 :   GEN c, lb, res = gen_1;
    1014             : 
    1015        3875 :   if (!signe(a) || !signe(b)) return pol_0(varn(a));
    1016             : 
    1017        3875 :   da = degpol(a);
    1018        3875 :   db = degpol(b);
    1019        3875 :   if (db > da)
    1020             :   {
    1021           0 :     swapspec(a,b, da,db);
    1022           0 :     if (both_odd(da,db)) res = subii(p, res);
    1023             :   }
    1024        3875 :   if (!da) return gc_const(av, gen_1); /* = res * a[2] ^ db, since 0 <= db <= da = 0 */
    1025       10739 :   while (db)
    1026             :   {
    1027        6864 :     lb = gel(b,db+2);
    1028        6864 :     c = FpX_rem(a,b, p);
    1029        6864 :     a = b; b = c; dc = degpol(c);
    1030        6864 :     if (dc < 0) return gc_const(av, gen_0);
    1031             : 
    1032        6864 :     if (both_odd(da,db)) res = subii(p, res);
    1033        6864 :     if (!equali1(lb)) res = Fp_mul(res, Fp_powu(lb, da - dc, p), p);
    1034        6864 :     if (gc_needed(av,2))
    1035             :     {
    1036           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_resultant (da = %ld)",da);
    1037           0 :       gerepileall(av,3, &a,&b,&res);
    1038             :     }
    1039        6864 :     da = db; /* = degpol(a) */
    1040        6864 :     db = dc; /* = degpol(b) */
    1041             :   }
    1042        3875 :   return gerepileuptoint(av, Fp_mul(res, Fp_powu(gel(b,2), da, p), p));
    1043             : }
    1044             : 
    1045             : GEN
    1046      416359 : FpX_resultant(GEN x, GEN y, GEN p)
    1047             : {
    1048      416359 :   pari_sp av = avma;
    1049             :   long dx, dy;
    1050      416359 :   GEN res = gen_1;
    1051      416359 :   if (!signe(x) || !signe(y)) return gen_0;
    1052      416359 :   if (lgefint(p) == 3)
    1053             :   {
    1054      412484 :     pari_sp av = avma;
    1055      412484 :     ulong pp = to_Flx(&x, &y, p);
    1056      412484 :     ulong res = Flx_resultant(x, y, pp);
    1057      412484 :     return gc_utoi(av, res);
    1058             :   }
    1059        3875 :   dx = degpol(x); dy = degpol(y);
    1060        3875 :   if (dx < dy)
    1061             :   {
    1062           0 :     swap(x,y);
    1063           0 :     if (both_odd(dx, dy))
    1064           0 :       res = Fp_neg(res, p);
    1065             :   }
    1066        3882 :   while (lgpol(y) >= FpX_GCD_LIMIT)
    1067             :   {
    1068           7 :     if (lgpol(y)<=(lgpol(x)>>1))
    1069             :     {
    1070           0 :       GEN r = FpX_rem(x, y, p);
    1071           0 :       long dx = degpol(x), dy = degpol(y), dr = degpol(r);
    1072           0 :       GEN ly = gel(y,dy+2);
    1073           0 :       if (!equali1(ly)) res = Fp_mul(res, Fp_powu(ly, dx - dr, p), p);
    1074           0 :       if (both_odd(dx, dy))
    1075           0 :         res = Fp_neg(res, p);
    1076           0 :       x = y; y = r;
    1077             :     }
    1078           7 :     (void) FpX_halfres(x, y, p, &x, &y, &res);
    1079           7 :     if (gc_needed(av,2))
    1080             :     {
    1081           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpX_res (y = %ld)",degpol(y));
    1082           0 :       gerepileall(av,3,&x,&y,&res);
    1083             :     }
    1084             :   }
    1085        3875 :   return gerepileuptoint(av, Fp_mul(res, FpX_resultant_basecase(x, y, p), p));
    1086             : }
    1087             : 
    1088             : /* If resultant is 0, *ptU and *ptV are not set */
    1089             : static GEN
    1090          24 : FpX_extresultant_basecase(GEN a, GEN b, GEN p, GEN *ptU, GEN *ptV)
    1091             : {
    1092          24 :   pari_sp av = avma;
    1093          24 :   GEN z,q,u,v, x = a, y = b;
    1094          24 :   GEN lb, res = gen_1;
    1095             :   long dx, dy, dz;
    1096          24 :   long vs = varn(a);
    1097             : 
    1098          24 :   u = pol_0(vs);
    1099          24 :   v = pol_1(vs); /* v = 1 */
    1100          24 :   dx = degpol(x);
    1101          24 :   dy = degpol(y);
    1102         281 :   while (dy)
    1103             :   { /* b u = x (a), b v = y (a) */
    1104         257 :     lb = gel(y,dy+2);
    1105         257 :     q = FpX_divrem(x,y, p, &z);
    1106         257 :     x = y; y = z; /* (x,y) = (y, x - q y) */
    1107         257 :     dz = degpol(z); if (dz < 0) return gc_const(av,gen_0);
    1108         257 :     z = FpX_sub(u, FpX_mul(q,v, p), p);
    1109         257 :     u = v; v = z; /* (u,v) = (v, u - q v) */
    1110             : 
    1111         257 :     if (both_odd(dx,dy)) res = Fp_neg(res, p);
    1112         257 :     if (!equali1(lb)) res = Fp_mul(res, Fp_powu(lb, dx-dz, p), p);
    1113         257 :     dx = dy; /* = degpol(x) */
    1114         257 :     dy = dz; /* = degpol(y) */
    1115             :   }
    1116          24 :   res = Fp_mul(res, Fp_powu(gel(y,2), dx, p), p);
    1117          24 :   lb = Fp_mul(res, Fp_inv(gel(y,2),p), p);
    1118          24 :   v = FpX_Fp_mul(v, lb, p);
    1119          24 :   u = Fp_FpX_sub(res, FpX_mul(b,v,p), p);
    1120          24 :   u = FpX_div(u,a,p); /* = (res - b v) / a */
    1121          24 :   *ptU = u;
    1122          24 :   *ptV = v;
    1123          24 :   return res;
    1124             : }
    1125             : 
    1126             : GEN
    1127          77 : FpX_extresultant(GEN x, GEN y, GEN p, GEN *ptU, GEN *ptV)
    1128             : {
    1129          77 :   pari_sp av=avma;
    1130             :   GEN u, v, R;
    1131          77 :   GEN res = gen_1, res1;
    1132          77 :   long dx = degpol(x), dy = degpol(y);
    1133          77 :   if (lgefint(p) == 3)
    1134             :   {
    1135          53 :     pari_sp av = avma;
    1136          53 :     ulong pp = to_Flx(&x, &y, p);
    1137          53 :     ulong resp = Flx_extresultant(x, y, pp, &u, &v);
    1138          53 :     if (!resp) return gc_const(av, gen_0);
    1139          53 :     res = utoi(resp);
    1140          53 :     *ptU = Flx_to_ZX(u); *ptV = Flx_to_ZX(v);
    1141          53 :     return gc_all(av, 3, &res, ptU, ptV);
    1142             :   }
    1143          24 :   if (dy > dx)
    1144             :   {
    1145           8 :     swap(x,y); lswap(dx,dy);
    1146           8 :     if (both_odd(dx,dy)) res = Fp_neg(res,p);
    1147           8 :     R = matJ2_FpXM(x[1]);
    1148          16 :   } else R = matid2_FpXM(x[1]);
    1149          24 :   if (dy < 0) return gen_0;
    1150         123 :   while (lgpol(y) >= FpX_EXTGCD_LIMIT)
    1151             :   {
    1152             :     GEN M;
    1153          99 :     if (lgpol(y)<=(lgpol(x)>>1))
    1154             :     {
    1155           8 :       GEN r, q = FpX_divrem(x, y, p, &r);
    1156           8 :       long dx = degpol(x), dy = degpol(y), dr = degpol(r);
    1157           8 :       GEN ly = gel(y,dy+2);
    1158           8 :       if (!equali1(ly)) res = Fp_mul(res, Fp_powu(ly, dx - dr, p), p);
    1159           8 :       if (both_odd(dx, dy))
    1160           0 :         res = Fp_neg(res, p);
    1161           8 :       x = y; y = r;
    1162           8 :       R = FpX_FpXM_qmul(q, R, p);
    1163             :     }
    1164          99 :     M = FpX_halfres(x, y, p, &x, &y, &res);
    1165          99 :     if (!signe(res)) return gc_const(av, gen_0);
    1166          99 :     R = FpXM_mul2(M, R, p);
    1167          99 :     gerepileall(av,4,&x,&y,&R,&res);
    1168             :   }
    1169          24 :   res1 = FpX_extresultant_basecase(x,y,p,&u,&v);
    1170          24 :   if (!signe(res1)) return gc_const(av, gen_0);
    1171          24 :   *ptU = FpX_Fp_mul(FpX_addmulmul(u, v, gcoeff(R,1,1), gcoeff(R,2,1), p), res, p);
    1172          24 :   *ptV = FpX_Fp_mul(FpX_addmulmul(u, v, gcoeff(R,1,2), gcoeff(R,2,2), p), res, p);
    1173          24 :   res = Fp_mul(res1,res,p);
    1174          24 :   return gc_all(av, 3, &res, ptU, ptV);
    1175             : }
    1176             : 
    1177             : GEN
    1178      177509 : FpX_rescale(GEN P, GEN h, GEN p)
    1179             : {
    1180      177509 :   long i, l = lg(P);
    1181      177509 :   GEN Q = cgetg(l,t_POL), hi = h;
    1182      177509 :   gel(Q,l-1) = gel(P,l-1);
    1183      361958 :   for (i=l-2; i>=2; i--)
    1184             :   {
    1185      361956 :     gel(Q,i) = Fp_mul(gel(P,i), hi, p);
    1186      361960 :     if (i == 2) break;
    1187      184450 :     hi = Fp_mul(hi,h, p);
    1188             :   }
    1189      177512 :   Q[1] = P[1]; return Q;
    1190             : }
    1191             : 
    1192             : GEN
    1193     1628400 : FpX_deriv(GEN x, GEN p) { return FpX_red(ZX_deriv(x), p); }
    1194             : 
    1195             : /* Compute intformal(x^n*S)/x^(n+1) */
    1196             : static GEN
    1197       55491 : FpX_integXn(GEN x, long n, GEN p)
    1198             : {
    1199       55491 :   long i, lx = lg(x);
    1200             :   GEN y;
    1201       55491 :   if (lx == 2) return ZX_copy(x);
    1202       54226 :   y = cgetg(lx, t_POL); y[1] = x[1];
    1203      192971 :   for (i=2; i<lx; i++)
    1204             :   {
    1205      138745 :     GEN xi = gel(x,i);
    1206      138745 :     if (!signe(xi))
    1207           0 :       gel(y,i) = gen_0;
    1208             :     else
    1209             :     {
    1210      138745 :       ulong j = n+i-1;
    1211      138745 :       ulong d = ugcd(j, umodiu(xi, j));
    1212      138745 :       if (d==1)
    1213       89567 :         gel(y,i) = Fp_divu(xi, j, p);
    1214             :       else
    1215       49178 :         gel(y,i) = Fp_divu(diviuexact(xi, d), j/d, p);
    1216             :     }
    1217             :   }
    1218       54226 :   return ZX_renormalize(y, lx);;
    1219             : }
    1220             : 
    1221             : GEN
    1222           0 : FpX_integ(GEN x, GEN p)
    1223             : {
    1224           0 :   long i, lx = lg(x);
    1225             :   GEN y;
    1226           0 :   if (lx == 2) return ZX_copy(x);
    1227           0 :   y = cgetg(lx+1, t_POL); y[1] = x[1];
    1228           0 :   gel(y,2) = gen_0;
    1229           0 :   for (i=3; i<=lx; i++)
    1230           0 :     gel(y,i) = signe(gel(x,i-1))? Fp_divu(gel(x,i-1), i-2, p): gen_0;
    1231           0 :   return ZX_renormalize(y, lx+1);;
    1232             : }
    1233             : 
    1234             : INLINE GEN
    1235      531072 : FpXn_recip(GEN P, long n)
    1236      531072 : { return RgXn_recip_shallow(P, n); }
    1237             : 
    1238             : GEN
    1239      520228 : FpX_Newton(GEN P, long n, GEN p)
    1240             : {
    1241      520228 :   pari_sp av = avma;
    1242      520228 :   GEN dP = FpX_deriv(P, p);
    1243      520223 :   GEN Q = FpXn_recip(FpX_div(FpX_shift(dP,n), P, p), n);
    1244      520247 :   return gerepilecopy(av, Q);
    1245             : }
    1246             : 
    1247             : GEN
    1248       11334 : FpX_fromNewton(GEN P, GEN p)
    1249             : {
    1250       11334 :   pari_sp av = avma;
    1251       11334 :   if (lgefint(p)==3)
    1252             :   {
    1253         497 :     ulong pp = p[2];
    1254         497 :     GEN Q = Flx_fromNewton(ZX_to_Flx(P, pp), pp);
    1255         497 :     return gerepileupto(av, Flx_to_ZX(Q));
    1256             :   } else
    1257             :   {
    1258       10837 :     long n = itos(modii(constant_coeff(P), p))+1;
    1259       10837 :     GEN z = FpX_neg(FpX_shift(P,-1),p);
    1260       10837 :     GEN Q = FpXn_recip(FpXn_expint(z, n, p), n);
    1261       10837 :     return gerepilecopy(av, Q);
    1262             :   }
    1263             : }
    1264             : 
    1265             : GEN
    1266         158 : FpX_invLaplace(GEN x, GEN p)
    1267             : {
    1268         158 :   pari_sp av = avma;
    1269         158 :   long i, d = degpol(x);
    1270             :   GEN t, y;
    1271         158 :   if (d <= 1) return gcopy(x);
    1272         158 :   t = Fp_inv(factorial_Fp(d, p), p);
    1273         158 :   y = cgetg(d+3, t_POL);
    1274         158 :   y[1] = x[1];
    1275        1328 :   for (i=d; i>=2; i--)
    1276             :   {
    1277        1170 :     gel(y,i+2) = Fp_mul(gel(x,i+2), t, p);
    1278        1170 :     t = Fp_mulu(t, i, p);
    1279             :   }
    1280         158 :   gel(y,3) = gel(x,3);
    1281         158 :   gel(y,2) = gel(x,2);
    1282         158 :   return gerepilecopy(av, y);
    1283             : }
    1284             : 
    1285             : GEN
    1286         576 : FpX_Laplace(GEN x, GEN p)
    1287             : {
    1288         576 :   pari_sp av = avma;
    1289         576 :   long i, d = degpol(x);
    1290         576 :   GEN t = gen_1;
    1291             :   GEN y;
    1292         576 :   if (d <= 1) return gcopy(x);
    1293         576 :   y = cgetg(d+3, t_POL);
    1294         576 :   y[1] = x[1];
    1295         576 :   gel(y,2) = gel(x,2);
    1296         576 :   gel(y,3) = gel(x,3);
    1297       29049 :   for (i=2; i<=d; i++)
    1298             :   {
    1299       28473 :     t = Fp_mulu(t, i, p);
    1300       28473 :     gel(y,i+2) = Fp_mul(gel(x,i+2), t, p);
    1301             :   }
    1302         576 :   return gerepilecopy(av, y);
    1303             : }
    1304             : 
    1305             : int
    1306       40921 : FpX_is_squarefree(GEN f, GEN p)
    1307             : {
    1308       40921 :   pari_sp av = avma;
    1309       40921 :   GEN z = FpX_gcd(f,FpX_deriv(f,p),p);
    1310       40918 :   set_avma(av);
    1311       40918 :   return degpol(z)==0;
    1312             : }
    1313             : 
    1314             : GEN
    1315      259586 : random_FpX(long d1, long v, GEN p)
    1316             : {
    1317      259586 :   long i, d = d1+2;
    1318      259586 :   GEN y = cgetg(d,t_POL); y[1] = evalsigne(1) | evalvarn(v);
    1319      880803 :   for (i=2; i<d; i++) gel(y,i) = randomi(p);
    1320      259584 :   return FpX_renormalize(y,d);
    1321             : }
    1322             : 
    1323             : GEN
    1324        8004 : FpX_dotproduct(GEN x, GEN y, GEN p)
    1325             : {
    1326        8004 :   long i, l = minss(lg(x), lg(y));
    1327             :   pari_sp av;
    1328             :   GEN c;
    1329        8004 :   if (l == 2) return gen_0;
    1330        7927 :   av = avma; c = mulii(gel(x,2),gel(y,2));
    1331      613393 :   for (i=3; i<l; i++) c = addii(c, mulii(gel(x,i),gel(y,i)));
    1332        7927 :   return gerepileuptoint(av, modii(c,p));
    1333             : }
    1334             : 
    1335             : /* Evaluation in Fp
    1336             :  * x a ZX and y an Fp, return x(y) mod p
    1337             :  *
    1338             :  * If p is very large (several longs) and x has small coefficients(<<p),
    1339             :  * then Brent & Kung algorithm is faster. */
    1340             : GEN
    1341      963718 : FpX_eval(GEN x,GEN y,GEN p)
    1342             : {
    1343             :   pari_sp av;
    1344             :   GEN p1,r,res;
    1345      963718 :   long j, i=lg(x)-1;
    1346      963718 :   if (i<=2 || !signe(y))
    1347      181832 :     return (i==1)? gen_0: modii(gel(x,2),p);
    1348      781886 :   res=cgeti(lgefint(p));
    1349      781885 :   av=avma; p1=gel(x,i);
    1350             :   /* specific attention to sparse polynomials (see poleval)*/
    1351             :   /*You've guessed it! It's a copy-paste(tm)*/
    1352     3403614 :   for (i--; i>=2; i=j-1)
    1353             :   {
    1354     3699497 :     for (j=i; !signe(gel(x,j)); j--)
    1355     1077774 :       if (j==2)
    1356             :       {
    1357      162138 :         if (i!=j) y = Fp_powu(y,i-j+1,p);
    1358      162138 :         p1=mulii(p1,y);
    1359      162129 :         goto fppoleval;/*sorry break(2) no implemented*/
    1360             :       }
    1361     2621723 :     r = (i==j)? y: Fp_powu(y,i-j+1,p);
    1362     2621721 :     p1 = Fp_addmul(gel(x,j), p1, r, p);
    1363     2621728 :     if ((i & 7) == 0) { affii(p1, res); p1 = res; set_avma(av); }
    1364             :   }
    1365      619753 :  fppoleval:
    1366      781882 :   modiiz(p1,p,res); return gc_const(av, res);
    1367             : }
    1368             : 
    1369             : /* Tz=Tx*Ty where Tx and Ty coprime
    1370             :  * return lift(chinese(Mod(x*Mod(1,p),Tx*Mod(1,p)),Mod(y*Mod(1,p),Ty*Mod(1,p))))
    1371             :  * if Tz is NULL it is computed
    1372             :  * As we do not return it, and the caller will frequently need it,
    1373             :  * it must compute it and pass it.
    1374             :  */
    1375             : GEN
    1376           0 : FpX_chinese_coprime(GEN x,GEN y,GEN Tx,GEN Ty,GEN Tz,GEN p)
    1377             : {
    1378           0 :   pari_sp av = avma;
    1379             :   GEN ax,p1;
    1380           0 :   ax = FpX_mul(FpXQ_inv(Tx,Ty,p), Tx,p);
    1381           0 :   p1 = FpX_mul(ax, FpX_sub(y,x,p),p);
    1382           0 :   p1 = FpX_add(x,p1,p);
    1383           0 :   if (!Tz) Tz=FpX_mul(Tx,Ty,p);
    1384           0 :   p1 = FpX_rem(p1,Tz,p);
    1385           0 :   return gerepileupto(av,p1);
    1386             : }
    1387             : 
    1388             : /* disc P = (-1)^(n(n-1)/2) lc(P)^(n - deg P' - 2) Res(P,P'), n = deg P */
    1389             : GEN
    1390          42 : FpX_disc(GEN P, GEN p)
    1391             : {
    1392          42 :   pari_sp av = avma;
    1393          42 :   GEN L, dP = FpX_deriv(P,p), D = FpX_resultant(P, dP, p);
    1394             :   long dd;
    1395          42 :   if (!signe(D)) return gen_0;
    1396          35 :   dd = degpol(P) - 2 - degpol(dP); /* >= -1; > -1 iff p | deg(P) */
    1397          35 :   L = leading_coeff(P);
    1398          35 :   if (dd && !equali1(L))
    1399           7 :     D = (dd == -1)? Fp_div(D,L,p): Fp_mul(D, Fp_powu(L, dd, p), p);
    1400          35 :   if (degpol(P) & 2) D = Fp_neg(D ,p);
    1401          35 :   return gerepileuptoint(av, D);
    1402             : }
    1403             : 
    1404             : GEN
    1405       93114 : FpV_roots_to_pol(GEN V, GEN p, long v)
    1406             : {
    1407       93114 :   pari_sp ltop=avma;
    1408             :   long i;
    1409       93114 :   GEN g=cgetg(lg(V),t_VEC);
    1410      402498 :   for(i=1;i<lg(V);i++)
    1411      309384 :     gel(g,i) = deg1pol_shallow(gen_1,modii(negi(gel(V,i)),p),v);
    1412       93114 :   return gerepileupto(ltop,FpXV_prod(g,p));
    1413             : }
    1414             : 
    1415             : /* invert all elements of x mod p using Montgomery's multi-inverse trick.
    1416             :  * Not stack-clean. */
    1417             : GEN
    1418       34038 : FpV_inv(GEN x, GEN p)
    1419             : {
    1420       34038 :   long i, lx = lg(x);
    1421       34038 :   GEN u, y = cgetg(lx, t_VEC);
    1422             : 
    1423       34038 :   gel(y,1) = gel(x,1);
    1424      471420 :   for (i=2; i<lx; i++) gel(y,i) = Fp_mul(gel(y,i-1), gel(x,i), p);
    1425             : 
    1426       34037 :   u = Fp_inv(gel(y,--i), p);
    1427      471419 :   for ( ; i > 1; i--)
    1428             :   {
    1429      437381 :     gel(y,i) = Fp_mul(u, gel(y,i-1), p);
    1430      437381 :     u = Fp_mul(u, gel(x,i), p); /* u = 1 / (x[1] ... x[i-1]) */
    1431             :   }
    1432       34038 :   gel(y,1) = u; return y;
    1433             : }
    1434             : GEN
    1435           0 : FqV_inv(GEN x, GEN T, GEN p)
    1436             : {
    1437           0 :   long i, lx = lg(x);
    1438           0 :   GEN u, y = cgetg(lx, t_VEC);
    1439             : 
    1440           0 :   gel(y,1) = gel(x,1);
    1441           0 :   for (i=2; i<lx; i++) gel(y,i) = Fq_mul(gel(y,i-1), gel(x,i), T,p);
    1442             : 
    1443           0 :   u = Fq_inv(gel(y,--i), T,p);
    1444           0 :   for ( ; i > 1; i--)
    1445             :   {
    1446           0 :     gel(y,i) = Fq_mul(u, gel(y,i-1), T,p);
    1447           0 :     u = Fq_mul(u, gel(x,i), T,p); /* u = 1 / (x[1] ... x[i-1]) */
    1448             :   }
    1449           0 :   gel(y,1) = u; return y;
    1450             : }
    1451             : 
    1452             : /***********************************************************************/
    1453             : /**                                                                   **/
    1454             : /**                      Barrett reduction                            **/
    1455             : /**                                                                   **/
    1456             : /***********************************************************************/
    1457             : 
    1458             : static GEN
    1459        3324 : FpX_invBarrett_basecase(GEN T, GEN p)
    1460             : {
    1461        3324 :   long i, l=lg(T)-1, lr = l-1, k;
    1462        3324 :   GEN r=cgetg(lr, t_POL); r[1]=T[1];
    1463        3324 :   gel(r,2) = gen_1;
    1464      168480 :   for (i=3; i<lr; i++)
    1465             :   {
    1466      165156 :     pari_sp av = avma;
    1467      165156 :     GEN u = gel(T,l-i+2);
    1468     4517183 :     for (k=3; k<i; k++)
    1469     4352027 :       u = addii(u, mulii(gel(T,l-i+k), gel(r,k)));
    1470      165156 :     gel(r,i) = gerepileupto(av, modii(negi(u), p));
    1471             :   }
    1472        3324 :   return FpX_renormalize(r,lr);
    1473             : }
    1474             : 
    1475             : /* Return new lgpol */
    1476             : static long
    1477      458035 : ZX_lgrenormalizespec(GEN x, long lx)
    1478             : {
    1479             :   long i;
    1480      823608 :   for (i = lx-1; i>=0; i--)
    1481      823610 :     if (signe(gel(x,i))) break;
    1482      458035 :   return i+1;
    1483             : }
    1484             : 
    1485             : INLINE GEN
    1486      431797 : FpX_recipspec(GEN x, long l, long n)
    1487             : {
    1488      431797 :   return RgX_recipspec_shallow(x, l, n);
    1489             : }
    1490             : 
    1491             : static GEN
    1492        1510 : FpX_invBarrett_Newton(GEN T, GEN p)
    1493             : {
    1494        1510 :   pari_sp av = avma;
    1495        1510 :   long nold, lx, lz, lq, l = degpol(T), i, lQ;
    1496        1510 :   GEN q, y, z, x = cgetg(l+2, t_POL) + 2;
    1497        1510 :   ulong mask = quadratic_prec_mask(l-2); /* assume l > 2 */
    1498      598582 :   for (i=0;i<l;i++) gel(x,i) = gen_0;
    1499        1510 :   q = FpX_recipspec(T+2,l+1,l+1); lQ = lgpol(q); q+=2;
    1500             :   /* We work on _spec_ FpX's, all the l[xzq] below are lgpol's */
    1501             : 
    1502             :   /* initialize */
    1503        1510 :   gel(x,0) = Fp_inv(gel(q,0), p);
    1504        1510 :   if (lQ>1) gel(q,1) = Fp_red(gel(q,1), p);
    1505        1510 :   if (lQ>1 && signe(gel(q,1)))
    1506        1123 :   {
    1507        1123 :     GEN u = gel(q, 1);
    1508        1123 :     if (!equali1(gel(x,0))) u = Fp_mul(u, Fp_sqr(gel(x,0), p), p);
    1509        1123 :     gel(x,1) = Fp_neg(u, p); lx = 2;
    1510             :   }
    1511             :   else
    1512         387 :     lx = 1;
    1513        1510 :   nold = 1;
    1514       13582 :   for (; mask > 1; )
    1515             :   { /* set x -= x(x*q - 1) + O(t^(nnew + 1)), knowing x*q = 1 + O(t^(nold+1)) */
    1516       12098 :     long i, lnew, nnew = nold << 1;
    1517             : 
    1518       12098 :     if (mask & 1) nnew--;
    1519       12098 :     mask >>= 1;
    1520             : 
    1521       12098 :     lnew = nnew + 1;
    1522       12098 :     lq = ZX_lgrenormalizespec(q, minss(lQ,lnew));
    1523       12096 :     z = FpX_mulspec(x, q, p, lx, lq); /* FIXME: high product */
    1524       12102 :     lz = lgpol(z); if (lz > lnew) lz = lnew;
    1525       12102 :     z += 2;
    1526             :     /* subtract 1 [=>first nold words are 0]: renormalize so that z(0) != 0 */
    1527       84207 :     for (i = nold; i < lz; i++) if (signe(gel(z,i))) break;
    1528       12102 :     nold = nnew;
    1529       12102 :     if (i >= lz) continue; /* z-1 = 0(t^(nnew + 1)) */
    1530             : 
    1531             :     /* z + i represents (x*q - 1) / t^i */
    1532        9559 :     lz = ZX_lgrenormalizespec (z+i, lz-i);
    1533        9559 :     z = FpX_mulspec(x, z+i, p, lx, lz); /* FIXME: low product */
    1534        9556 :     lz = lgpol(z); z += 2;
    1535        9556 :     if (lz > lnew-i) lz = ZX_lgrenormalizespec(z, lnew-i);
    1536             : 
    1537        9556 :     lx = lz+ i;
    1538        9556 :     y  = x + i; /* x -= z * t^i, in place */
    1539      431308 :     for (i = 0; i < lz; i++) gel(y,i) = Fp_neg(gel(z,i), p);
    1540             :   }
    1541        1484 :   x -= 2; setlg(x, lx + 2); x[1] = T[1];
    1542        1510 :   return gerepilecopy(av, x);
    1543             : }
    1544             : 
    1545             : /* 1/polrecip(T)+O(x^(deg(T)-1)) */
    1546             : GEN
    1547        4887 : FpX_invBarrett(GEN T, GEN p)
    1548             : {
    1549        4887 :   pari_sp ltop = avma;
    1550        4887 :   long l = lg(T);
    1551             :   GEN r;
    1552        4887 :   if (l<5) return pol_0(varn(T));
    1553        4834 :   if (l<=FpX_INVBARRETT_LIMIT)
    1554             :   {
    1555        3324 :     GEN c = gel(T,l-1), ci=gen_1;
    1556        3324 :     if (!equali1(c))
    1557             :     {
    1558          14 :       ci = Fp_inv(c, p);
    1559          14 :       T = FpX_Fp_mul(T, ci, p);
    1560          14 :       r = FpX_invBarrett_basecase(T, p);
    1561          14 :       r = FpX_Fp_mul(r, ci, p);
    1562             :     } else
    1563        3310 :       r = FpX_invBarrett_basecase(T, p);
    1564             :   }
    1565             :   else
    1566        1510 :     r = FpX_invBarrett_Newton(T, p);
    1567        4834 :   return gerepileupto(ltop, r);
    1568             : }
    1569             : 
    1570             : GEN
    1571      958734 : FpX_get_red(GEN T, GEN p)
    1572             : {
    1573      958734 :   if (typ(T)==t_POL && lg(T)>FpX_BARRETT_LIMIT)
    1574        4050 :     retmkvec2(FpX_invBarrett(T,p),T);
    1575      954684 :   return T;
    1576             : }
    1577             : 
    1578             : /* Compute x mod T where 2 <= degpol(T) <= l+1 <= 2*(degpol(T)-1)
    1579             :  * and mg is the Barrett inverse of T. */
    1580             : static GEN
    1581      213618 : FpX_divrem_Barrettspec(GEN x, long l, GEN mg, GEN T, GEN p, GEN *pr)
    1582             : {
    1583             :   GEN q, r;
    1584      213618 :   long lt = degpol(T); /*We discard the leading term*/
    1585             :   long ld, lm, lT, lmg;
    1586      213618 :   ld = l-lt;
    1587      213618 :   lm = minss(ld, lgpol(mg));
    1588      213618 :   lT  = ZX_lgrenormalizespec(T+2,lt);
    1589      213618 :   lmg = ZX_lgrenormalizespec(mg+2,lm);
    1590      213618 :   q = FpX_recipspec(x+lt,ld,ld);              /* q = rec(x)     lq<=ld*/
    1591      213619 :   q = FpX_mulspec(q+2,mg+2,p,lgpol(q),lmg);    /* q = rec(x) * mg lq<=ld+lm*/
    1592      213618 :   q = FpX_recipspec(q+2,minss(ld,lgpol(q)),ld);/* q = rec (rec(x) * mg) lq<=ld*/
    1593      213620 :   if (!pr) return q;
    1594      213620 :   r = FpX_mulspec(q+2,T+2,p,lgpol(q),lT);      /* r = q*pol        lr<=ld+lt*/
    1595      213618 :   r = FpX_subspec(x,r+2,p,lt,minss(lt,lgpol(r)));/* r = x - r   lr<=lt */
    1596      213619 :   if (pr == ONLY_REM) return r;
    1597        1330 :   *pr = r; return q;
    1598             : }
    1599             : 
    1600             : static GEN
    1601      212871 : FpX_divrem_Barrett(GEN x, GEN mg, GEN T, GEN p, GEN *pr)
    1602             : {
    1603      212871 :   GEN q = NULL, r = FpX_red(x, p);
    1604      212872 :   long l = lgpol(r), lt = degpol(T), lm = 2*lt-1, v = varn(T);
    1605             :   long i;
    1606      212872 :   if (l <= lt)
    1607             :   {
    1608           0 :     if (pr == ONLY_REM) return r;
    1609           0 :     if (pr == ONLY_DIVIDES) return signe(r)? NULL: pol_0(v);
    1610           0 :     if (pr) *pr = r;
    1611           0 :     return pol_0(v);
    1612             :   }
    1613      212872 :   if (lt <= 1)
    1614          53 :     return FpX_divrem_basecase(r,T,p,pr);
    1615      212819 :   if (pr != ONLY_REM && l>lm)
    1616             :   {
    1617         497 :     q = cgetg(l-lt+2, t_POL); q[1] = T[1];
    1618      905007 :     for (i=0;i<l-lt;i++) gel(q+2,i) = gen_0;
    1619             :   }
    1620      213619 :   while (l>lm)
    1621             :   {
    1622         800 :     GEN zr, zq = FpX_divrem_Barrettspec(r+2+l-lm,lm,mg,T,p,&zr);
    1623         800 :     long lz = lgpol(zr);
    1624         800 :     if (pr != ONLY_REM)
    1625             :     {
    1626         626 :       long lq = lgpol(zq);
    1627      464768 :       for(i=0; i<lq; i++) gel(q+2+l-lm,i) = gel(zq,2+i);
    1628             :     }
    1629      475648 :     for(i=0; i<lz; i++) gel(r+2+l-lm,i) = gel(zr,2+i);
    1630         800 :     l = l-lm+lz;
    1631             :   }
    1632      212819 :   if (pr == ONLY_REM)
    1633             :   {
    1634      212288 :     if (l > lt)
    1635      212288 :       r = FpX_divrem_Barrettspec(r+2, l, mg, T, p, ONLY_REM);
    1636             :     else
    1637           0 :       r = FpX_renormalize(r, l+2);
    1638      212289 :     setvarn(r, v); return r;
    1639             :   }
    1640         531 :   if (l > lt)
    1641             :   {
    1642         530 :     GEN zq = FpX_divrem_Barrettspec(r+2,l,mg,T,p, pr? &r: NULL);
    1643         530 :     if (!q) q = zq;
    1644             :     else
    1645             :     {
    1646         496 :       long lq = lgpol(zq);
    1647      440483 :       for(i=0; i<lq; i++) gel(q+2,i) = gel(zq,2+i);
    1648             :     }
    1649             :   }
    1650           1 :   else if (pr)
    1651           1 :     r = FpX_renormalize(r, l+2);
    1652         531 :   setvarn(q, v); q = FpX_renormalize(q, lg(q));
    1653         531 :   if (pr == ONLY_DIVIDES) return signe(r)? NULL: q;
    1654         531 :   if (pr) { setvarn(r, v); *pr = r; }
    1655         531 :   return q;
    1656             : }
    1657             : 
    1658             : GEN
    1659    14366746 : FpX_divrem(GEN x, GEN T, GEN p, GEN *pr)
    1660             : {
    1661             :   GEN B, y;
    1662             :   long dy, dx, d;
    1663    14366746 :   if (pr==ONLY_REM) return FpX_rem(x, T, p);
    1664    14366746 :   y = get_FpX_red(T, &B);
    1665    14366735 :   dy = degpol(y); dx = degpol(x); d = dx-dy;
    1666    14366658 :   if (!B && d+3 < FpX_DIVREM_BARRETT_LIMIT)
    1667    14364776 :     return FpX_divrem_basecase(x,y,p,pr);
    1668        1882 :   else if (lgefint(p)==3)
    1669             :   {
    1670        1318 :     pari_sp av = avma;
    1671        1318 :     ulong pp = to_Flxq(&x, &T, p);
    1672        1318 :     GEN z = Flx_divrem(x, T, pp, pr);
    1673        1318 :     if (!z) return gc_NULL(av);
    1674        1318 :     if (!pr || pr == ONLY_DIVIDES)
    1675          59 :       return Flx_to_ZX_inplace(gerepileuptoleaf(av, z));
    1676        1259 :     z = Flx_to_ZX(z);
    1677        1259 :     *pr = Flx_to_ZX(*pr);
    1678        1259 :     return gc_all(av, 2, &z, pr);
    1679             :   } else
    1680             :   {
    1681         564 :     pari_sp av = avma;
    1682         564 :     GEN mg = B? B: FpX_invBarrett(y, p);
    1683         564 :     GEN z = FpX_divrem_Barrett(x,mg,y,p,pr);
    1684         564 :     if (!z) return gc_NULL(av);
    1685         564 :     if (!pr || pr==ONLY_DIVIDES) return gerepilecopy(av, z);
    1686         564 :     return gc_all(av, 2, &z, pr);
    1687             :   }
    1688             : }
    1689             : 
    1690             : GEN
    1691    71547038 : FpX_rem(GEN x, GEN T, GEN p)
    1692             : {
    1693    71547038 :   GEN B, y = get_FpX_red(T, &B);
    1694    71566070 :   long dy = degpol(y), dx = degpol(x), d = dx-dy;
    1695    71587072 :   if (d < 0) return FpX_red(x,p);
    1696    52455608 :   if (!B && d+3 < FpX_REM_BARRETT_LIMIT)
    1697    52203280 :     return FpX_divrem_basecase(x,y,p,ONLY_REM);
    1698      252328 :   else if (lgefint(p)==3)
    1699             :   {
    1700       40021 :     pari_sp av = avma;
    1701       40021 :     ulong pp = to_Flxq(&x, &T, p);
    1702       40023 :     return Flx_to_ZX_inplace(gerepileuptoleaf(av, Flx_rem(x, T, pp)));
    1703             :   } else
    1704             :   {
    1705      212307 :     pari_sp av = avma;
    1706      212307 :     GEN mg = B? B: FpX_invBarrett(y, p);
    1707      212307 :     return gerepileupto(av, FpX_divrem_Barrett(x, mg, y, p, ONLY_REM));
    1708             :   }
    1709             : }
    1710             : 
    1711             : static GEN
    1712       32198 : FpXV_producttree_dbl(GEN t, long n, GEN p)
    1713             : {
    1714       32198 :   long i, j, k, m = n==1 ? 1: expu(n-1)+1;
    1715       32198 :   GEN T = cgetg(m+1, t_VEC);
    1716       32198 :   gel(T,1) = t;
    1717       63523 :   for (i=2; i<=m; i++)
    1718             :   {
    1719       31325 :     GEN u = gel(T, i-1);
    1720       31325 :     long n = lg(u)-1;
    1721       31325 :     GEN t = cgetg(((n+1)>>1)+1, t_VEC);
    1722      102284 :     for (j=1, k=1; k<n; j++, k+=2)
    1723       70959 :       gel(t, j) = FpX_mul(gel(u, k), gel(u, k+1), p);
    1724       31325 :     gel(T, i) = t;
    1725             :   }
    1726       32198 :   return T;
    1727             : }
    1728             : 
    1729             : static GEN
    1730       31611 : FpV_producttree(GEN xa, GEN s, GEN p, long vs)
    1731             : {
    1732       31611 :   long n = lg(xa)-1;
    1733       31611 :   long j, k, ls = lg(s);
    1734       31611 :   GEN t = cgetg(ls, t_VEC);
    1735      131912 :   for (j=1, k=1; j<ls; k+=s[j++])
    1736      100301 :     gel(t, j) = s[j] == 1 ?
    1737      100302 :              deg1pol_shallow(gen_1, Fp_neg(gel(xa,k), p), vs):
    1738       61738 :              deg2pol_shallow(gen_1,
    1739       61739 :                Fp_neg(Fp_add(gel(xa,k), gel(xa,k+1), p), p),
    1740       61739 :                Fp_mul(gel(xa,k), gel(xa,k+1), p), vs);
    1741       31610 :   return FpXV_producttree_dbl(t, n, p);
    1742             : }
    1743             : 
    1744             : static GEN
    1745       32199 : FpX_FpXV_multirem_dbl_tree(GEN P, GEN T, GEN p)
    1746             : {
    1747             :   long i,j,k;
    1748       32199 :   long m = lg(T)-1;
    1749             :   GEN t;
    1750       32199 :   GEN Tp = cgetg(m+1, t_VEC);
    1751       32200 :   gel(Tp, m) = mkvec(P);
    1752       63524 :   for (i=m-1; i>=1; i--)
    1753             :   {
    1754       31325 :     GEN u = gel(T, i);
    1755       31325 :     GEN v = gel(Tp, i+1);
    1756       31325 :     long n = lg(u)-1;
    1757       31325 :     t = cgetg(n+1, t_VEC);
    1758      102284 :     for (j=1, k=1; k<n; j++, k+=2)
    1759             :     {
    1760       70959 :       gel(t, k)   = FpX_rem(gel(v, j), gel(u, k), p);
    1761       70959 :       gel(t, k+1) = FpX_rem(gel(v, j), gel(u, k+1), p);
    1762             :     }
    1763       31325 :     gel(Tp, i) = t;
    1764             :   }
    1765       32199 :   return Tp;
    1766             : }
    1767             : 
    1768             : static GEN
    1769       31611 : FpX_FpV_multieval_tree(GEN P, GEN xa, GEN T, GEN p)
    1770             : {
    1771       31611 :   pari_sp av = avma;
    1772             :   long j,k;
    1773       31611 :   GEN Tp = FpX_FpXV_multirem_dbl_tree(P, T, p);
    1774       31611 :   GEN R = cgetg(lg(xa), t_VEC);
    1775       31610 :   GEN u = gel(T, 1);
    1776       31610 :   GEN v = gel(Tp, 1);
    1777       31610 :   long n = lg(u)-1;
    1778      131914 :   for (j=1, k=1; j<=n; j++)
    1779             :   {
    1780      100302 :     long c, d = degpol(gel(u,j));
    1781      262343 :     for (c=1; c<=d; c++, k++)
    1782      162039 :       gel(R,k) = FpX_eval(gel(v, j), gel(xa,k), p);
    1783             :   }
    1784       31612 :   return gerepileupto(av, R);
    1785             : }
    1786             : 
    1787             : static GEN
    1788          15 : FpVV_polint_tree(GEN T, GEN R, GEN s, GEN xa, GEN ya, GEN p, long vs)
    1789             : {
    1790          15 :   pari_sp av = avma;
    1791          15 :   long m = lg(T)-1;
    1792          15 :   long i, j, k, ls = lg(s);
    1793          15 :   GEN Tp = cgetg(m+1, t_VEC);
    1794          15 :   GEN t = cgetg(ls, t_VEC);
    1795         241 :   for (j=1, k=1; j<ls; k+=s[j++])
    1796         226 :     if (s[j]==2)
    1797             :     {
    1798          58 :       GEN a = Fp_mul(gel(ya,k), gel(R,k), p);
    1799          58 :       GEN b = Fp_mul(gel(ya,k+1), gel(R,k+1), p);
    1800          58 :       gel(t, j) = deg1pol_shallow(Fp_add(a, b, p),
    1801          58 :               Fp_neg(Fp_add(Fp_mul(gel(xa,k), b, p ),
    1802          58 :               Fp_mul(gel(xa,k+1), a, p), p), p), vs);
    1803             :     }
    1804             :     else
    1805         168 :       gel(t, j) = scalarpol(Fp_mul(gel(ya,k), gel(R,k), p), vs);
    1806          15 :   gel(Tp, 1) = t;
    1807          72 :   for (i=2; i<=m; i++)
    1808             :   {
    1809          57 :     GEN u = gel(T, i-1);
    1810          57 :     GEN t = cgetg(lg(gel(T,i)), t_VEC);
    1811          57 :     GEN v = gel(Tp, i-1);
    1812          57 :     long n = lg(v)-1;
    1813         268 :     for (j=1, k=1; k<n; j++, k+=2)
    1814         211 :       gel(t, j) = FpX_add(ZX_mul(gel(u, k), gel(v, k+1)),
    1815         211 :                           ZX_mul(gel(u, k+1), gel(v, k)), p);
    1816          57 :     gel(Tp, i) = t;
    1817             :   }
    1818          15 :   return gerepilecopy(av, gmael(Tp,m,1));
    1819             : }
    1820             : 
    1821             : GEN
    1822           0 : FpX_FpV_multieval(GEN P, GEN xa, GEN p)
    1823             : {
    1824           0 :   pari_sp av = avma;
    1825           0 :   GEN s = producttree_scheme(lg(xa)-1);
    1826           0 :   GEN T = FpV_producttree(xa, s, p, varn(P));
    1827           0 :   return gerepileupto(av, FpX_FpV_multieval_tree(P, xa, T, p));
    1828             : }
    1829             : 
    1830             : GEN
    1831          22 : FpV_polint(GEN xa, GEN ya, GEN p, long vs)
    1832             : {
    1833          22 :   pari_sp av = avma;
    1834             :   GEN s, T, P, R;
    1835             :   long m;
    1836          22 :   if (lgefint(p) == 3)
    1837             :   {
    1838           7 :     ulong pp = p[2];
    1839           7 :     P = Flv_polint(ZV_to_Flv(xa, pp), ZV_to_Flv(ya, pp), pp, evalvarn(vs));
    1840           7 :     return gerepileupto(av, Flx_to_ZX(P));
    1841             :   }
    1842          15 :   s = producttree_scheme(lg(xa)-1);
    1843          15 :   T = FpV_producttree(xa, s, p, vs);
    1844          15 :   m = lg(T)-1;
    1845          15 :   P = FpX_deriv(gmael(T, m, 1), p);
    1846          15 :   R = FpV_inv(FpX_FpV_multieval_tree(P, xa, T, p), p);
    1847          15 :   return gerepileupto(av, FpVV_polint_tree(T, R, s, xa, ya, p, vs));
    1848             : }
    1849             : 
    1850             : GEN
    1851           0 : FpV_FpM_polint(GEN xa, GEN ya, GEN p, long vs)
    1852             : {
    1853           0 :   pari_sp av = avma;
    1854           0 :   GEN s = producttree_scheme(lg(xa)-1);
    1855           0 :   GEN T = FpV_producttree(xa, s, p, vs);
    1856           0 :   long i, m = lg(T)-1, l = lg(ya)-1;
    1857           0 :   GEN P = FpX_deriv(gmael(T, m, 1), p);
    1858           0 :   GEN R = FpV_inv(FpX_FpV_multieval_tree(P, xa, T, p), p);
    1859           0 :   GEN M = cgetg(l+1, t_VEC);
    1860           0 :   for (i=1; i<=l; i++)
    1861           0 :     gel(M,i) = FpVV_polint_tree(T, R, s, xa, gel(ya,i), p, vs);
    1862           0 :   return gerepileupto(av, M);
    1863             : }
    1864             : 
    1865             : GEN
    1866       31596 : FpV_invVandermonde(GEN L, GEN den, GEN p)
    1867             : {
    1868       31596 :   pari_sp av = avma;
    1869       31596 :   long i, n = lg(L);
    1870             :   GEN M, R;
    1871       31596 :   GEN s = producttree_scheme(n-1);
    1872       31596 :   GEN tree = FpV_producttree(L, s, p, 0);
    1873       31595 :   long m = lg(tree)-1;
    1874       31595 :   GEN T = gmael(tree, m, 1);
    1875       31595 :   R = FpV_inv(FpX_FpV_multieval_tree(FpX_deriv(T, p), L, tree, p), p);
    1876       31596 :   if (den) R = FpC_Fp_mul(R, den, p);
    1877       31595 :   M = cgetg(n, t_MAT);
    1878      193351 :   for (i = 1; i < n; i++)
    1879             :   {
    1880      161755 :     GEN P = FpX_Fp_mul(FpX_div_by_X_x(T, gel(L,i), p, NULL), gel(R,i), p);
    1881      161754 :     gel(M,i) = RgX_to_RgC(P, n-1);
    1882             :   }
    1883       31596 :   return gerepilecopy(av, M);
    1884             : }
    1885             : 
    1886             : static GEN
    1887         588 : FpXV_producttree(GEN xa, GEN s, GEN p)
    1888             : {
    1889         588 :   long n = lg(xa)-1;
    1890         588 :   long j, k, ls = lg(s);
    1891         588 :   GEN t = cgetg(ls, t_VEC);
    1892        3444 :   for (j=1, k=1; j<ls; k+=s[j++])
    1893        2856 :     gel(t, j) = s[j] == 1 ?
    1894        2856 :              gel(xa,k): FpX_mul(gel(xa,k),gel(xa,k+1),p);
    1895         588 :   return FpXV_producttree_dbl(t, n, p);
    1896             : }
    1897             : 
    1898             : static GEN
    1899         588 : FpX_FpXV_multirem_tree(GEN P, GEN xa, GEN T, GEN s, GEN p)
    1900             : {
    1901         588 :   pari_sp av = avma;
    1902         588 :   long j, k, ls = lg(s);
    1903         588 :   GEN Tp = FpX_FpXV_multirem_dbl_tree(P, T, p);
    1904         588 :   GEN R = cgetg(lg(xa), t_VEC);
    1905         588 :   GEN v = gel(Tp, 1);
    1906        3444 :   for (j=1, k=1; j<ls; k+=s[j++])
    1907             :   {
    1908        2856 :     gel(R,k) = FpX_rem(gel(v, j), gel(xa,k), p);
    1909        2856 :     if (s[j] == 2)
    1910        1050 :       gel(R,k+1) = FpX_rem(gel(v, j), gel(xa,k+1), p);
    1911             :   }
    1912         588 :   return gerepileupto(av, R);
    1913             : }
    1914             : 
    1915             : GEN
    1916           0 : FpX_FpXV_multirem(GEN P, GEN xa, GEN p)
    1917             : {
    1918           0 :   pari_sp av = avma;
    1919           0 :   GEN s = producttree_scheme(lg(xa)-1);
    1920           0 :   GEN T = FpXV_producttree(xa, s, p);
    1921           0 :   return gerepileupto(av, FpX_FpXV_multirem_tree(P, xa, T, s, p));
    1922             : }
    1923             : 
    1924             : /* T = ZV_producttree(P), R = ZV_chinesetree(P,T) */
    1925             : static GEN
    1926         588 : FpXV_chinese_tree(GEN A, GEN P, GEN T, GEN R, GEN s, GEN p)
    1927             : {
    1928         588 :   long m = lg(T)-1, ls = lg(s);
    1929             :   long i,j,k;
    1930         588 :   GEN Tp = cgetg(m+1, t_VEC);
    1931         588 :   GEN M = gel(T, 1);
    1932         588 :   GEN t = cgetg(lg(M), t_VEC);
    1933        3444 :   for (j=1, k=1; j<ls; k+=s[j++])
    1934        2856 :     if (s[j] == 2)
    1935             :     {
    1936        1050 :       pari_sp av = avma;
    1937        1050 :       GEN a = FpX_mul(gel(A,k), gel(R,k), p), b = FpX_mul(gel(A,k+1), gel(R,k+1), p);
    1938        1050 :       GEN tj = FpX_rem(FpX_add(FpX_mul(gel(P,k), b, p),
    1939        1050 :             FpX_mul(gel(P,k+1), a, p), p), gel(M,j), p);
    1940        1050 :       gel(t, j) = gerepileupto(av, tj);
    1941             :     }
    1942             :     else
    1943        1806 :       gel(t, j) = FpX_rem(FpX_mul(gel(A,k), gel(R,k), p), gel(M, j), p);
    1944         588 :   gel(Tp, 1) = t;
    1945        1890 :   for (i=2; i<=m; i++)
    1946             :   {
    1947        1302 :     GEN u = gel(T, i-1), M = gel(T, i);
    1948        1302 :     GEN t = cgetg(lg(M), t_VEC);
    1949        1302 :     GEN v = gel(Tp, i-1);
    1950        1302 :     long n = lg(v)-1;
    1951        3570 :     for (j=1, k=1; k<n; j++, k+=2)
    1952             :     {
    1953        2268 :       pari_sp av = avma;
    1954        2268 :       gel(t, j) = gerepileupto(av, FpX_rem(FpX_add(FpX_mul(gel(u, k), gel(v, k+1), p),
    1955        2268 :               FpX_mul(gel(u, k+1), gel(v, k), p), p), gel(M, j), p));
    1956             :     }
    1957        1302 :     if (k==n) gel(t, j) = gel(v, k);
    1958        1302 :     gel(Tp, i) = t;
    1959             :   }
    1960         588 :   return gmael(Tp,m,1);
    1961             : }
    1962             : 
    1963             : static GEN
    1964         588 : FpXV_sqr(GEN x, GEN p)
    1965        4494 : { pari_APPLY_type(t_VEC, FpX_sqr(gel(x,i), p)) }
    1966             : 
    1967             : static GEN
    1968        7602 : FpXT_sqr(GEN x, GEN p)
    1969             : {
    1970        7602 :   if (typ(x) == t_POL)
    1971        5124 :     return FpX_sqr(x, p);
    1972        9492 :   pari_APPLY_type(t_VEC, FpXT_sqr(gel(x,i), p))
    1973             : }
    1974             : 
    1975             : static GEN
    1976         588 : FpXV_invdivexact(GEN x, GEN y, GEN p)
    1977        4494 : { pari_APPLY_type(t_VEC, FpXQ_inv(FpX_div(gel(x,i), gel(y,i),p), gel(y,i),p)) }
    1978             : 
    1979             : static GEN
    1980         588 : FpXV_chinesetree(GEN P, GEN T, GEN s, GEN p)
    1981             : {
    1982         588 :   GEN T2 = FpXT_sqr(T, p), P2 = FpXV_sqr(P, p);
    1983         588 :   GEN mod = gmael(T,lg(T)-1,1);
    1984         588 :   return FpXV_invdivexact(FpX_FpXV_multirem_tree(mod, P2, T2, s, p), P, p);
    1985             : }
    1986             : 
    1987             : static GEN
    1988         588 : gc_chinese(pari_sp av, GEN T, GEN a, GEN *pt_mod)
    1989             : {
    1990         588 :   if (!pt_mod)
    1991         588 :     return gerepileupto(av, a);
    1992             :   else
    1993             :   {
    1994           0 :     GEN mod = gmael(T, lg(T)-1, 1);
    1995           0 :     gerepileall(av, 2, &a, &mod);
    1996           0 :     *pt_mod = mod;
    1997           0 :     return a;
    1998             :   }
    1999             : }
    2000             : 
    2001             : GEN
    2002         588 : FpXV_chinese(GEN A, GEN P, GEN p, GEN *pt_mod)
    2003             : {
    2004         588 :   pari_sp av = avma;
    2005         588 :   GEN s = producttree_scheme(lg(P)-1);
    2006         588 :   GEN T = FpXV_producttree(P, s, p);
    2007         588 :   GEN R = FpXV_chinesetree(P, T, s, p);
    2008         588 :   GEN a = FpXV_chinese_tree(A, P, T, R, s, p);
    2009         588 :   return gc_chinese(av, T, a, pt_mod);
    2010             : }
    2011             : 
    2012             : /***********************************************************************/
    2013             : /**                                                                   **/
    2014             : /**                              FpXQ                                 **/
    2015             : /**                                                                   **/
    2016             : /***********************************************************************/
    2017             : 
    2018             : /* FpXQ are elements of Fp[X]/(T), represented by FpX*/
    2019             : 
    2020             : GEN
    2021    17870692 : FpXQ_red(GEN x, GEN T, GEN p)
    2022             : {
    2023    17870692 :   GEN z = FpX_red(x,p);
    2024    17843462 :   return FpX_rem(z, T,p);
    2025             : }
    2026             : 
    2027             : GEN
    2028    11594958 : FpXQ_mul(GEN x,GEN y,GEN T,GEN p)
    2029             : {
    2030    11594958 :   GEN z = FpX_mul(x,y,p);
    2031    11594931 :   return FpX_rem(z, T, p);
    2032             : }
    2033             : 
    2034             : GEN
    2035     6023733 : FpXQ_sqr(GEN x, GEN T, GEN p)
    2036             : {
    2037     6023733 :   GEN z = FpX_sqr(x,p);
    2038     6023314 :   return FpX_rem(z, T, p);
    2039             : }
    2040             : 
    2041             : /* Inverse of x in Z/pZ[X]/(pol) or NULL if inverse doesn't exist
    2042             :  * return lift(1 / (x mod (p,pol))) */
    2043             : GEN
    2044     1095713 : FpXQ_invsafe(GEN x, GEN y, GEN p)
    2045             : {
    2046     1095713 :   GEN V, z = FpX_extgcd(get_FpX_mod(y), x, p, NULL, &V);
    2047     1095724 :   if (degpol(z)) return NULL;
    2048     1095722 :   z = Fp_invsafe(gel(z,2), p);
    2049     1095672 :   if (!z) return NULL;
    2050     1095672 :   return FpX_Fp_mul(V, z, p);
    2051             : }
    2052             : 
    2053             : GEN
    2054     1095713 : FpXQ_inv(GEN x,GEN T,GEN p)
    2055             : {
    2056     1095713 :   pari_sp av = avma;
    2057     1095713 :   GEN U = FpXQ_invsafe(x, T, p);
    2058     1095644 :   if (!U) pari_err_INV("FpXQ_inv",x);
    2059     1095644 :   return gerepileupto(av, U);
    2060             : }
    2061             : 
    2062             : GEN
    2063      530715 : FpXQ_div(GEN x,GEN y,GEN T,GEN p)
    2064             : {
    2065      530715 :   pari_sp av = avma;
    2066      530715 :   return gerepileupto(av, FpXQ_mul(x,FpXQ_inv(y,T,p),T,p));
    2067             : }
    2068             : 
    2069             : static GEN
    2070     2263930 : _FpXQ_add(void *data, GEN x, GEN y)
    2071             : {
    2072             :   (void) data;
    2073     2263930 :   return ZX_add(x, y);
    2074             : }
    2075             : static GEN
    2076       52941 : _FpXQ_sub(void *data, GEN x, GEN y)
    2077             : {
    2078             :   (void) data;
    2079       52941 :   return ZX_sub(x, y);
    2080             : }
    2081             : static GEN
    2082     2678725 : _FpXQ_cmul(void *data, GEN P, long a, GEN x)
    2083             : {
    2084             :   (void) data;
    2085     2678725 :   return ZX_Z_mul(x, gel(P,a+2));
    2086             : }
    2087             : static GEN
    2088     5114414 : _FpXQ_sqr(void *data, GEN x)
    2089             : {
    2090     5114414 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2091     5114414 :   return FpXQ_sqr(x, D->T, D->p);
    2092             : }
    2093             : static GEN
    2094     1609780 : _FpXQ_mul(void *data, GEN x, GEN y)
    2095             : {
    2096     1609780 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2097     1609780 :   return FpXQ_mul(x,y, D->T, D->p);
    2098             : }
    2099             : static GEN
    2100        4123 : _FpXQ_zero(void *data)
    2101             : {
    2102        4123 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2103        4123 :   return pol_0(get_FpX_var(D->T));
    2104             : }
    2105             : static GEN
    2106      887963 : _FpXQ_one(void *data)
    2107             : {
    2108      887963 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2109      887963 :   return pol_1(get_FpX_var(D->T));
    2110             : }
    2111             : static GEN
    2112      885625 : _FpXQ_red(void *data, GEN x)
    2113             : {
    2114      885625 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2115      885625 :   return FpX_red(x,D->p);
    2116             : }
    2117             : 
    2118             : static struct bb_algebra FpXQ_algebra = { _FpXQ_red, _FpXQ_add, _FpXQ_sub,
    2119             :        _FpXQ_mul, _FpXQ_sqr, _FpXQ_one, _FpXQ_zero };
    2120             : 
    2121             : const struct bb_algebra *
    2122       10199 : get_FpXQ_algebra(void **E, GEN T, GEN p)
    2123             : {
    2124       10199 :   GEN z = new_chunk(sizeof(struct _FpXQ));
    2125       10199 :   struct _FpXQ *e = (struct _FpXQ *) z;
    2126       10199 :   e->T = FpX_get_red(T, p);
    2127       10199 :   e->p  = p; *E = (void*)e;
    2128       10199 :   return &FpXQ_algebra;
    2129             : }
    2130             : 
    2131             : static GEN
    2132           0 : _FpX_red(void *E, GEN x)
    2133           0 : { struct _FpX *D = (struct _FpX*)E; return FpX_red(x,D->p); }
    2134             : 
    2135             : static GEN
    2136           0 : _FpX_zero(void *E)
    2137           0 : { struct _FpX *D = (struct _FpX *)E; return pol_0(D->v); }
    2138             : 
    2139             : 
    2140             : static struct bb_algebra FpX_algebra = { _FpX_red, _FpXQ_add, _FpXQ_sub,
    2141             :        _FpX_mul, _FpX_sqr, _FpX_one, _FpX_zero };
    2142             : 
    2143             : const struct bb_algebra *
    2144           0 : get_FpX_algebra(void **E, GEN p, long v)
    2145             : {
    2146           0 :   GEN z = new_chunk(sizeof(struct _FpX));
    2147           0 :   struct _FpX *e = (struct _FpX *) z;
    2148           0 :   e->p  = p; e->v = v; *E = (void*)e;
    2149           0 :   return &FpX_algebra;
    2150             : }
    2151             : 
    2152             : /* x,pol in Z[X], p in Z, n in Z, compute lift(x^n mod (p, pol)) */
    2153             : GEN
    2154      913565 : FpXQ_pow(GEN x, GEN n, GEN T, GEN p)
    2155             : {
    2156             :   struct _FpXQ D;
    2157             :   pari_sp av;
    2158      913565 :   long s = signe(n);
    2159             :   GEN y;
    2160      913565 :   if (!s) return pol_1(varn(x));
    2161      912476 :   if (is_pm1(n)) /* +/- 1 */
    2162       37058 :     return (s < 0)? FpXQ_inv(x,T,p): FpXQ_red(x,T,p);
    2163      875417 :   av = avma;
    2164      875417 :   if (!is_bigint(p))
    2165             :   {
    2166      658971 :     ulong pp = to_Flxq(&x, &T, p);
    2167      658968 :     y = Flxq_pow(x, n, T, pp);
    2168      658959 :     return Flx_to_ZX_inplace(gerepileuptoleaf(av, y));
    2169             :   }
    2170      216447 :   if (s < 0) x = FpXQ_inv(x,T,p);
    2171      216447 :   D.p = p; D.T = FpX_get_red(T,p);
    2172      216447 :   y = gen_pow_i(x, n, (void*)&D, &_FpXQ_sqr, &_FpXQ_mul);
    2173      216447 :   return gerepilecopy(av, y);
    2174             : }
    2175             : 
    2176             : GEN /*Assume n is very small*/
    2177      604353 : FpXQ_powu(GEN x, ulong n, GEN T, GEN p)
    2178             : {
    2179             :   struct _FpXQ D;
    2180             :   pari_sp av;
    2181             :   GEN y;
    2182      604353 :   if (!n) return pol_1(varn(x));
    2183      604353 :   if (n==1) return FpXQ_red(x,T,p);
    2184      205326 :   av = avma;
    2185      205326 :   if (!is_bigint(p))
    2186             :   {
    2187      196801 :     ulong pp = to_Flxq(&x, &T, p);
    2188      196805 :     y = Flxq_powu(x, n, T, pp);
    2189      196801 :     return Flx_to_ZX_inplace(gerepileuptoleaf(av, y));
    2190             :   }
    2191        8533 :   D.T = FpX_get_red(T, p); D.p = p;
    2192        8533 :   y = gen_powu_i(x, n, (void*)&D, &_FpXQ_sqr, &_FpXQ_mul);
    2193        8533 :   return gerepilecopy(av, y);
    2194             : }
    2195             : 
    2196             : /* generates the list of powers of x of degree 0,1,2,...,l*/
    2197             : GEN
    2198      383515 : FpXQ_powers(GEN x, long l, GEN T, GEN p)
    2199             : {
    2200             :   struct _FpXQ D;
    2201             :   int use_sqr;
    2202      383515 :   if (l>2 && lgefint(p) == 3) {
    2203      209593 :     pari_sp av = avma;
    2204      209593 :     ulong pp = to_Flxq(&x, &T, p);
    2205      209595 :     GEN z = FlxV_to_ZXV(Flxq_powers(x, l, T, pp));
    2206      209594 :     return gerepileupto(av, z);
    2207             :   }
    2208      173922 :   use_sqr = 2*degpol(x)>=get_FpX_degree(T);
    2209      173926 :   D.T = FpX_get_red(T,p); D.p = p;
    2210      173927 :   return gen_powers(x, l, use_sqr, (void*)&D, &_FpXQ_sqr, &_FpXQ_mul,&_FpXQ_one);
    2211             : }
    2212             : 
    2213             : GEN
    2214       66289 : FpXQ_matrix_pow(GEN y, long n, long m, GEN P, GEN l)
    2215             : {
    2216       66289 :   return RgXV_to_RgM(FpXQ_powers(y,m-1,P,l),n);
    2217             : }
    2218             : 
    2219             : GEN
    2220      444180 : FpX_Frobenius(GEN T, GEN p)
    2221             : {
    2222      444180 :   return FpXQ_pow(pol_x(get_FpX_var(T)), p, T, p);
    2223             : }
    2224             : 
    2225             : GEN
    2226       31493 : FpX_matFrobenius(GEN T, GEN p)
    2227             : {
    2228       31493 :   long n = get_FpX_degree(T);
    2229       31493 :   return FpXQ_matrix_pow(FpX_Frobenius(T, p), n, n, T, p);
    2230             : }
    2231             : 
    2232             : GEN
    2233      408719 : FpX_FpXQV_eval(GEN Q, GEN x, GEN T, GEN p)
    2234             : {
    2235             :   struct _FpXQ D;
    2236      408719 :   D.T = FpX_get_red(T,p); D.p = p;
    2237      408724 :   return gen_bkeval_powers(Q,degpol(Q),x,(void*)&D,&FpXQ_algebra,_FpXQ_cmul);
    2238             : }
    2239             : 
    2240             : GEN
    2241      792712 : FpX_FpXQ_eval(GEN Q, GEN x, GEN T, GEN p)
    2242             : {
    2243             :   struct _FpXQ D;
    2244             :   int use_sqr;
    2245      792712 :   if (lgefint(p) == 3)
    2246             :   {
    2247      785849 :     pari_sp av = avma;
    2248      785849 :     ulong pp = to_Flxq(&x, &T, p);
    2249      785879 :     GEN z = Flx_Flxq_eval(ZX_to_Flx(Q, pp), x, T, pp);
    2250      785873 :     return Flx_to_ZX_inplace(gerepileuptoleaf(av, z));
    2251             :   }
    2252        6863 :   use_sqr = 2*degpol(x) >= get_FpX_degree(T);
    2253        6864 :   D.T = FpX_get_red(T,p); D.p = p;
    2254        6864 :   return gen_bkeval(Q,degpol(Q),x,use_sqr,(void*)&D,&FpXQ_algebra,_FpXQ_cmul);
    2255             : }
    2256             : 
    2257             : GEN
    2258        1470 : FpXC_FpXQV_eval(GEN x, GEN v, GEN T, GEN p)
    2259        8316 : { pari_APPLY_type(t_COL, FpX_FpXQV_eval(gel(x,i), v, T, p)) }
    2260             : 
    2261             : GEN
    2262         315 : FpXM_FpXQV_eval(GEN x, GEN v, GEN T, GEN p)
    2263        1197 : { pari_APPLY_same(FpXC_FpXQV_eval(gel(x,i), v, T, p)) }
    2264             : 
    2265             : GEN
    2266         588 : FpXC_FpXQ_eval(GEN x, GEN F, GEN T, GEN p)
    2267             : {
    2268         588 :   long d = brent_kung_optpow(RgXV_maxdegree(x), lg(x)-1, 1);
    2269         588 :   GEN Fp = FpXQ_powers(F, d, T, p);
    2270         588 :   return FpXC_FpXQV_eval(x, Fp, T, p);
    2271             : }
    2272             : 
    2273             : GEN
    2274        1771 : FpXQ_autpowers(GEN aut, long f, GEN T, GEN p)
    2275             : {
    2276        1771 :   pari_sp av = avma;
    2277        1771 :   long n = get_FpX_degree(T);
    2278        1771 :   long i, nautpow = brent_kung_optpow(n-1,f-2,1);
    2279        1771 :   long v = get_FpX_var(T);
    2280             :   GEN autpow, V;
    2281        1771 :   T = FpX_get_red(T, p);
    2282        1771 :   autpow = FpXQ_powers(aut, nautpow,T,p);
    2283        1771 :   V = cgetg(f + 2, t_VEC);
    2284        1771 :   gel(V,1) = pol_x(v); if (f==0) return gerepileupto(av, V);
    2285        1771 :   gel(V,2) = gcopy(aut);
    2286        6272 :   for (i = 3; i <= f+1; i++)
    2287        4501 :     gel(V,i) = FpX_FpXQV_eval(gel(V,i-1),autpow,T,p);
    2288        1771 :   return gerepileupto(av, V);
    2289             : }
    2290             : 
    2291             : static GEN
    2292        5739 : FpXQ_autpow_sqr(void *E, GEN x)
    2293             : {
    2294        5739 :   struct _FpXQ *D = (struct _FpXQ*)E;
    2295        5739 :   return FpX_FpXQ_eval(x, x, D->T, D->p);
    2296             : }
    2297             : 
    2298             : static GEN
    2299          21 : FpXQ_autpow_msqr(void *E, GEN x)
    2300             : {
    2301          21 :   struct _FpXQ *D = (struct _FpXQ*)E;
    2302          21 :   return FpX_FpXQV_eval(FpXQ_autpow_sqr(E, x), D->aut, D->T, D->p);
    2303             : }
    2304             : 
    2305             : GEN
    2306        5026 : FpXQ_autpow(GEN x, ulong n, GEN T, GEN p)
    2307             : {
    2308        5026 :   pari_sp av = avma;
    2309             :   struct _FpXQ D;
    2310             :   long d;
    2311        5026 :   if (n==0) return FpX_rem(pol_x(varn(x)), T, p);
    2312        5026 :   if (n==1) return FpX_rem(x, T, p);
    2313        5026 :   D.T = FpX_get_red(T, p); D.p = p;
    2314        5026 :   d = brent_kung_optpow(degpol(T), hammingl(n)-1, 1);
    2315        5026 :   D.aut = FpXQ_powers(x, d, T, p);
    2316        5026 :   x = gen_powu_fold(x,n,(void*)&D,FpXQ_autpow_sqr,FpXQ_autpow_msqr);
    2317        5026 :   return gerepilecopy(av, x);
    2318             : }
    2319             : 
    2320             : static GEN
    2321         360 : FpXQ_auttrace_mul(void *E, GEN x, GEN y)
    2322             : {
    2323         360 :   struct _FpXQ *D = (struct _FpXQ*)E;
    2324         360 :   GEN T = D->T, p = D->p;
    2325         360 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    2326         360 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    2327         360 :   ulong d = brent_kung_optpow(maxss(degpol(phi2),degpol(a2)),2,1);
    2328         360 :   GEN V1 = FpXQ_powers(phi1, d, T, p);
    2329         360 :   GEN phi3 = FpX_FpXQV_eval(phi2, V1, T, p);
    2330         360 :   GEN aphi = FpX_FpXQV_eval(a2, V1, T, p);
    2331         360 :   GEN a3 = FpX_add(a1, aphi, p);
    2332         360 :   return mkvec2(phi3, a3);
    2333             : }
    2334             : 
    2335             : static GEN
    2336         317 : FpXQ_auttrace_sqr(void *E, GEN x)
    2337         317 : { return FpXQ_auttrace_mul(E, x, x); }
    2338             : 
    2339             : GEN
    2340         437 : FpXQ_auttrace(GEN x, ulong n, GEN T, GEN p)
    2341             : {
    2342         437 :   pari_sp av = avma;
    2343             :   struct _FpXQ D;
    2344         437 :   D.T = FpX_get_red(T, p); D.p = p;
    2345         437 :   x = gen_powu_i(x,n,(void*)&D,FpXQ_auttrace_sqr,FpXQ_auttrace_mul);
    2346         437 :   return gerepilecopy(av, x);
    2347             : }
    2348             : 
    2349             : static GEN
    2350        6080 : FpXQ_autsum_mul(void *E, GEN x, GEN y)
    2351             : {
    2352        6080 :   struct _FpXQ *D = (struct _FpXQ*)E;
    2353        6080 :   GEN T = D->T, p = D->p;
    2354        6080 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    2355        6080 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    2356        6080 :   ulong d = brent_kung_optpow(maxss(degpol(phi2),degpol(a2)),2,1);
    2357        6080 :   GEN V1 = FpXQ_powers(phi1, d, T, p);
    2358        6080 :   GEN phi3 = FpX_FpXQV_eval(phi2, V1, T, p);
    2359        6080 :   GEN aphi = FpX_FpXQV_eval(a2, V1, T, p);
    2360        6080 :   GEN a3 = FpXQ_mul(a1, aphi, T, p);
    2361        6080 :   return mkvec2(phi3, a3);
    2362             : }
    2363             : static GEN
    2364        4459 : FpXQ_autsum_sqr(void *E, GEN x)
    2365        4459 : { return FpXQ_autsum_mul(E, x, x); }
    2366             : 
    2367             : GEN
    2368        4389 : FpXQ_autsum(GEN x, ulong n, GEN T, GEN p)
    2369             : {
    2370        4389 :   pari_sp av = avma;
    2371             :   struct _FpXQ D;
    2372        4389 :   D.T = FpX_get_red(T, p); D.p = p;
    2373        4389 :   x = gen_powu_i(x,n,(void*)&D,FpXQ_autsum_sqr,FpXQ_autsum_mul);
    2374        4389 :   return gerepilecopy(av, x);
    2375             : }
    2376             : 
    2377             : static GEN
    2378         315 : FpXQM_autsum_mul(void *E, GEN x, GEN y)
    2379             : {
    2380         315 :   struct _FpXQ *D = (struct _FpXQ*)E;
    2381         315 :   GEN T = D->T, p = D->p;
    2382         315 :   GEN phi1 = gel(x,1), a1 = gel(x,2);
    2383         315 :   GEN phi2 = gel(y,1), a2 = gel(y,2);
    2384         315 :   long g = lg(a2)-1, dT = get_FpX_degree(T);
    2385         315 :   ulong d = brent_kung_optpow(dT-1, g*g+1, 1);
    2386         315 :   GEN V1 = FpXQ_powers(phi1, d, T, p);
    2387         315 :   GEN phi3 = FpX_FpXQV_eval(phi2, V1, T, p);
    2388         315 :   GEN aphi = FpXM_FpXQV_eval(a2, V1, T, p);
    2389         315 :   GEN a3 = FqM_mul(a1, aphi, T, p);
    2390         315 :   return mkvec2(phi3, a3);
    2391             : }
    2392             : static GEN
    2393         217 : FpXQM_autsum_sqr(void *E, GEN x)
    2394         217 : { return FpXQM_autsum_mul(E, x, x); }
    2395             : 
    2396             : GEN
    2397         147 : FpXQM_autsum(GEN x, ulong n, GEN T, GEN p)
    2398             : {
    2399         147 :   pari_sp av = avma;
    2400             :   struct _FpXQ D;
    2401         147 :   D.T = FpX_get_red(T, p); D.p = p;
    2402         147 :   x = gen_powu_i(x, n, (void*)&D, FpXQM_autsum_sqr, FpXQM_autsum_mul);
    2403         147 :   return gerepilecopy(av, x);
    2404             : }
    2405             : 
    2406             : static long
    2407        6257 : bounded_order(GEN p, GEN b, long k)
    2408             : {
    2409             :   long i;
    2410        6257 :   GEN a=modii(p,b);
    2411       13393 :   for(i=1;i<k;i++)
    2412             :   {
    2413       12269 :     if (equali1(a))
    2414        5133 :       return i;
    2415        7136 :     a = Fp_mul(a,p,b);
    2416             :   }
    2417        1124 :   return 0;
    2418             : }
    2419             : 
    2420             : /* n = (p^d-a)\b
    2421             :  * b = bb*p^vb
    2422             :  * p^k = 1 [bb]
    2423             :  * d = m*k+r+vb
    2424             :  * u = (p^k-1)/bb;
    2425             :  * v = (p^(r+vb)-a)/b;
    2426             :  * w = (p^(m*k)-1)/(p^k-1)
    2427             :  * n = p^r*w*u+v
    2428             :  * w*u = p^vb*(p^(m*k)-1)/b
    2429             :  * n = p^(r+vb)*(p^(m*k)-1)/b+(p^(r+vb)-a)/b */
    2430             : static GEN
    2431      195309 : FpXQ_pow_Frobenius(GEN x, GEN n, GEN aut, GEN T, GEN p)
    2432             : {
    2433      195309 :   pari_sp av=avma;
    2434      195309 :   long d = get_FpX_degree(T);
    2435      195309 :   GEN an = absi_shallow(n), z, q;
    2436      195309 :   if (cmpii(an,p)<0 || cmpis(an,d)<=0) return FpXQ_pow(x, n, T, p);
    2437        6278 :   q = powiu(p, d);
    2438        6278 :   if (dvdii(q, n))
    2439             :   {
    2440           0 :     long vn = logint(an,p);
    2441           0 :     GEN autvn = vn==1 ? aut: FpXQ_autpow(aut,vn,T,p);
    2442           0 :     z = FpX_FpXQ_eval(x,autvn,T,p);
    2443             :   } else
    2444             :   {
    2445        6278 :     GEN b = diviiround(q, an), a = subii(q, mulii(an,b));
    2446             :     GEN bb, u, v, autk;
    2447        6278 :     long vb = Z_pvalrem(b,p,&bb);
    2448        6278 :     long m, r, k = is_pm1(bb) ? 1 : bounded_order(p,bb,d);
    2449        6278 :     if (!k || d-vb<k) return FpXQ_pow(x,n, T, p);
    2450        5154 :     m = (d-vb)/k; r = (d-vb)%k;
    2451        5154 :     u = diviiexact(subiu(powiu(p,k),1),bb);
    2452        5154 :     v = diviiexact(subii(powiu(p,r+vb),a),b);
    2453        5154 :     autk = k==1 ? aut: FpXQ_autpow(aut,k,T,p);
    2454        5154 :     if (r)
    2455             :     {
    2456         779 :       GEN autr = r==1 ? aut: FpXQ_autpow(aut,r,T,p);
    2457         779 :       z = FpX_FpXQ_eval(x,autr,T,p);
    2458        4375 :     } else z = x;
    2459        5154 :     if (m > 1) z = gel(FpXQ_autsum(mkvec2(autk, z), m, T, p), 2);
    2460        5154 :     if (!is_pm1(u)) z = FpXQ_pow(z, u, T, p);
    2461        5154 :     if (signe(v)) z = FpXQ_mul(z, FpXQ_pow(x, v, T, p), T, p);
    2462             :   }
    2463        5154 :   return gerepileupto(av,signe(n)>0 ? z : FpXQ_inv(z,T,p));
    2464             : }
    2465             : 
    2466             : /* assume T irreducible mod p */
    2467             : int
    2468      400726 : FpXQ_issquare(GEN x, GEN T, GEN p)
    2469             : {
    2470             :   pari_sp av;
    2471      400726 :   if (lg(x) == 2 || absequalui(2, p)) return 1;
    2472      400712 :   if (lg(x) == 3) return Fq_issquare(gel(x,2), T, p);
    2473      363643 :   av = avma; /* Ng = g^((q-1)/(p-1)) */
    2474      363643 :   return gc_bool(av, kronecker(FpXQ_norm(x,T,p), p) != -1);
    2475             : }
    2476             : int
    2477     1335928 : Fp_issquare(GEN x, GEN p)
    2478     1335928 : { return absequalui(2, p) || kronecker(x, p) != -1; }
    2479             : /* assume T irreducible mod p */
    2480             : int
    2481     1631123 : Fq_issquare(GEN x, GEN T, GEN p)
    2482             : {
    2483     1631123 :   if (typ(x) != t_INT) return FpXQ_issquare(x, T, p);
    2484     1233810 :   return (T && ! odd(get_FpX_degree(T))) || Fp_issquare(x, p);
    2485             : }
    2486             : 
    2487             : long
    2488          70 : Fq_ispower(GEN x, GEN K, GEN T, GEN p)
    2489             : {
    2490          70 :   pari_sp av = avma;
    2491             :   long d;
    2492             :   GEN Q;
    2493          70 :   if (equaliu(K,2)) return Fq_issquare(x, T, p);
    2494           0 :   if (!T) return Fp_ispower(x, K, p);
    2495           0 :   d = get_FpX_degree(T);
    2496           0 :   if (typ(x) == t_INT && !umodui(d, K)) return 1;
    2497           0 :   Q = subiu(powiu(p,d), 1);
    2498           0 :   Q = diviiexact(Q, gcdii(Q, K));
    2499           0 :   d = gequal1(Fq_pow(x, Q, T,p));
    2500           0 :   return gc_long(av, d);
    2501             : }
    2502             : 
    2503             : /* discrete log in FpXQ for a in Fp^*, g in FpXQ^* of order ord */
    2504             : GEN
    2505      544479 : Fp_FpXQ_log(GEN a, GEN g, GEN o, GEN T, GEN p)
    2506             : {
    2507      544479 :   pari_sp av = avma;
    2508             :   GEN q,n_q,ord,ordp, op;
    2509             : 
    2510      544479 :   if (equali1(a)) return gen_0;
    2511             :   /* p > 2 */
    2512             : 
    2513        7302 :   ordp = subiu(p, 1); /* even */
    2514        7302 :   ord  = get_arith_Z(o);
    2515        7274 :   if (!ord) ord = T? subiu(powiu(p, get_FpX_degree(T)), 1): ordp;
    2516        7274 :   if (equalii(a, ordp)) /* -1 */
    2517        5347 :     return gerepileuptoint(av, shifti(ord,-1));
    2518        1927 :   ordp = gcdii(ordp,ord);
    2519        1927 :   op = typ(o)==t_MAT ? famat_Z_gcd(o,ordp) : ordp;
    2520             : 
    2521        1927 :   q = NULL;
    2522        1927 :   if (T)
    2523             :   { /* we want < g > = Fp^* */
    2524        1927 :     if (!equalii(ord,ordp)) {
    2525        1903 :       q = diviiexact(ord,ordp);
    2526        1903 :       g = FpXQ_pow(g,q,T,p);
    2527             :     }
    2528        1927 :     g = constant_coeff(g);
    2529             :   }
    2530        1927 :   n_q = Fp_log(a,g,op,p);
    2531        1927 :   if (lg(n_q)==1) return gerepileuptoleaf(av, n_q);
    2532        1927 :   if (q) n_q = mulii(q, n_q);
    2533        1927 :   return gerepileuptoint(av, n_q);
    2534             : }
    2535             : 
    2536             : static GEN
    2537      179999 : _FpXQ_pow(void *data, GEN x, GEN n)
    2538             : {
    2539      179999 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2540      179999 :   return FpXQ_pow_Frobenius(x,n, D->aut, D->T, D->p);
    2541             : }
    2542             : 
    2543             : static GEN
    2544        1968 : _FpXQ_rand(void *data)
    2545             : {
    2546        1968 :   pari_sp av=avma;
    2547        1968 :   struct _FpXQ *D = (struct _FpXQ*)data;
    2548             :   GEN z;
    2549             :   do
    2550             :   {
    2551        1968 :     set_avma(av);
    2552        1968 :     z=random_FpX(get_FpX_degree(D->T),get_FpX_var(D->T),D->p);
    2553        1968 :   } while (!signe(z));
    2554        1968 :   return z;
    2555             : }
    2556             : 
    2557             : static GEN
    2558         618 : _FpXQ_easylog(void *E, GEN a, GEN g, GEN ord)
    2559             : {
    2560         618 :   struct _FpXQ *s=(struct _FpXQ*) E;
    2561         618 :   if (degpol(a)) return NULL;
    2562         539 :   return Fp_FpXQ_log(constant_coeff(a),g,ord,s->T,s->p);
    2563             : }
    2564             : 
    2565             : static const struct bb_group FpXQ_star={_FpXQ_mul,_FpXQ_pow,_FpXQ_rand,hash_GEN,ZX_equal,ZX_equal1,_FpXQ_easylog};
    2566             : 
    2567             : const struct bb_group *
    2568        3040 : get_FpXQ_star(void **E, GEN T, GEN p)
    2569             : {
    2570        3040 :   struct _FpXQ *e = (struct _FpXQ *) stack_malloc(sizeof(struct _FpXQ));
    2571        3040 :   e->T = T; e->p  = p; e->aut =  FpX_Frobenius(T, p);
    2572        3040 :   *E = (void*)e; return &FpXQ_star;
    2573             : }
    2574             : 
    2575             : GEN
    2576        1813 : FpXQ_order(GEN a, GEN ord, GEN T, GEN p)
    2577             : {
    2578        1813 :   if (lgefint(p)==3)
    2579             :   {
    2580           0 :     pari_sp av=avma;
    2581           0 :     ulong pp = to_Flxq(&a, &T, p);
    2582           0 :     GEN z = Flxq_order(a, ord, T, pp);
    2583           0 :     return gerepileuptoint(av,z);
    2584             :   }
    2585             :   else
    2586             :   {
    2587             :     void *E;
    2588        1813 :     const struct bb_group *S = get_FpXQ_star(&E,T,p);
    2589        1813 :     return gen_order(a,ord,E,S);
    2590             :   }
    2591             : }
    2592             : 
    2593             : GEN
    2594      708171 : FpXQ_log(GEN a, GEN g, GEN ord, GEN T, GEN p)
    2595             : {
    2596      708171 :   pari_sp av=avma;
    2597      708171 :   if (lgefint(p)==3)
    2598             :   {
    2599      708036 :     if (uel(p,2) == 2)
    2600             :     {
    2601      543686 :       GEN z = F2xq_log(ZX_to_F2x(a), ZX_to_F2x(g), ord,
    2602             :                                      ZX_to_F2x(get_FpX_mod(T)));
    2603      543686 :       return gerepileuptoleaf(av, z);
    2604             :     }
    2605             :     else
    2606             :     {
    2607      164350 :       ulong pp = to_Flxq(&a, &T, p);
    2608      164352 :       GEN z = Flxq_log(a, ZX_to_Flx(g, pp), ord, T, pp);
    2609      164352 :       return gerepileuptoleaf(av, z);
    2610             :     }
    2611             :   }
    2612             :   else
    2613             :   {
    2614             :     void *E;
    2615         135 :     const struct bb_group *S = get_FpXQ_star(&E,T,p);
    2616         135 :     GEN z = gen_PH_log(a,g,ord,E,S);
    2617         107 :     return gerepileuptoleaf(av, z);
    2618             :   }
    2619             : }
    2620             : 
    2621             : GEN
    2622     2193766 : Fq_log(GEN a, GEN g, GEN ord, GEN T, GEN p)
    2623             : {
    2624     2193766 :   if (!T) return Fp_log(a,g,ord,p);
    2625     1252058 :   if (typ(g) == t_INT)
    2626             :   {
    2627           0 :     if (typ(a) == t_POL)
    2628             :     {
    2629           0 :       if (degpol(a)) return cgetg(1,t_VEC);
    2630           0 :       a = gel(a,2);
    2631             :     }
    2632           0 :     return Fp_log(a,g,ord,p);
    2633             :   }
    2634     1252058 :   return typ(a) == t_INT? Fp_FpXQ_log(a,g,ord,T,p): FpXQ_log(a,g,ord,T,p);
    2635             : }
    2636             : 
    2637             : GEN
    2638        1435 : FpXQ_sqrtn(GEN a, GEN n, GEN T, GEN p, GEN *zeta)
    2639             : {
    2640        1435 :   pari_sp av = avma;
    2641             :   GEN z;
    2642        1435 :   if (!signe(a))
    2643             :   {
    2644         140 :     long v=varn(a);
    2645         140 :     if (signe(n) < 0) pari_err_INV("FpXQ_sqrtn",a);
    2646         133 :     if (zeta) *zeta=pol_1(v);
    2647         133 :     return pol_0(v);
    2648             :   }
    2649        1295 :   if (lgefint(p)==3)
    2650             :   {
    2651         203 :     if (uel(p,2) == 2)
    2652             :     {
    2653          14 :       z = F2xq_sqrtn(ZX_to_F2x(a), n, ZX_to_F2x(get_FpX_mod(T)), zeta);
    2654          14 :       if (!z) return NULL;
    2655          14 :       z = F2x_to_ZX(z);
    2656          14 :       if (!zeta) return gerepileuptoleaf(av, z);
    2657           7 :       *zeta=F2x_to_ZX(*zeta);
    2658             :     } else
    2659             :     {
    2660         189 :       ulong pp = to_Flxq(&a, &T, p);
    2661         189 :       z = Flxq_sqrtn(a, n, T, pp, zeta);
    2662         189 :       if (!z) return NULL;
    2663         189 :       if (!zeta) return Flx_to_ZX_inplace(gerepileuptoleaf(av, z));
    2664          63 :       z = Flx_to_ZX(z);
    2665          63 :       *zeta=Flx_to_ZX(*zeta);
    2666             :     }
    2667             :   }
    2668             :   else
    2669             :   {
    2670             :     void *E;
    2671        1092 :     const struct bb_group *S = get_FpXQ_star(&E,T,p);
    2672        1092 :     GEN o = subiu(powiu(p,get_FpX_degree(T)),1);
    2673        1092 :     z = gen_Shanks_sqrtn(a,n,o,zeta,E,S);
    2674        2064 :     if (!z) return NULL;
    2675        1029 :     if (!zeta) return gerepileupto(av, z);
    2676             :   }
    2677         127 :   return gc_all(av, 2, &z,zeta);
    2678             : }
    2679             : 
    2680             : static GEN
    2681       19360 : Fp2_norm(GEN x, GEN D, GEN p)
    2682             : {
    2683       19360 :   GEN a = gel(x,1), b = gel(x,2);
    2684       19360 :   if (signe(b)==0) return Fp_sqr(a,p);
    2685       19360 :   return Fp_sub(sqri(a), mulii(D, Fp_sqr(b, p)), p);
    2686             : }
    2687             : 
    2688             : static GEN
    2689       19791 : Fp2_sqrt(GEN z, GEN D, GEN p)
    2690             : {
    2691       19791 :   GEN a = gel(z,1), b = gel(z,2), as2, u, v, s;
    2692       19791 :   GEN y = Fp_2gener_i(D, p);
    2693       19791 :   if (signe(b)==0)
    2694         431 :     return kronecker(a, p)==1 ? mkvec2(Fp_sqrt_i(a, y, p), gen_0)
    2695         431 :                               : mkvec2(gen_0,Fp_sqrt_i(Fp_div(a, D, p), y, p));
    2696       19360 :   s = Fp_sqrt_i(Fp2_norm(z, D, p), y, p);
    2697       19360 :   if(!s) return NULL;
    2698       18950 :   as2 = Fp_halve(Fp_add(a, s, p), p);
    2699       18950 :   if (kronecker(as2, p)==-1) as2 = Fp_sub(as2,s,p);
    2700       18950 :   u = Fp_sqrt_i(as2, y, p);
    2701       18950 :   v = Fp_div(b, Fp_double(u, p), p);
    2702       18950 :   return mkvec2(u,v);
    2703             : }
    2704             : 
    2705             : GEN
    2706       80789 : FpXQ_sqrt(GEN z, GEN T, GEN p)
    2707             : {
    2708       80789 :    pari_sp av = avma;
    2709       80789 :   long d = get_FpX_degree(T);
    2710       80789 :   if (lgefint(p)==3)
    2711             :   {
    2712       60266 :     if (uel(p,2) == 2)
    2713             :     {
    2714        5320 :       GEN r = F2xq_sqrt(ZX_to_F2x(z), ZX_to_F2x(get_FpX_mod(T)));
    2715        5320 :       return gerepileupto(av, F2x_to_ZX(r));
    2716             :     } else
    2717             :     {
    2718       54946 :       ulong pp = to_Flxq(&z, &T, p);
    2719       54946 :       z = Flxq_sqrt(z, T, pp);
    2720       54946 :       if (!z) return NULL;
    2721       52178 :       return gerepileupto(av, Flx_to_ZX(z));
    2722             :     }
    2723             :   }
    2724       20523 :   if (d==2)
    2725             :   {
    2726       19791 :     GEN P = get_FpX_mod(T);
    2727       19791 :     GEN c = gel(P,2), b = gel(P,3), a = gel(P,4), b2 = Fp_halve(b, p);
    2728       19791 :     GEN t = Fp_div(b2, a, p);
    2729       19791 :     GEN D = Fp_sub(Fp_sqr(b2, p), Fp_mul(a, c, p), p);
    2730       19791 :     GEN x = degpol(z)<1 ? constant_coeff(z): Fp_sub(gel(z,2), Fp_mul(gel(z,3), t, p), p);
    2731       19791 :     GEN y = degpol(z)<1 ? gen_0: gel(z,3);
    2732       19791 :     GEN r = Fp2_sqrt(mkvec2(x, y), D, p), s;
    2733       19791 :     if (!r) return gc_NULL(av);
    2734       19381 :     s = deg1pol_shallow(gel(r,2),Fp_add(gel(r,1), Fp_mul(gel(r,2),t,p), p), varn(P));
    2735       19381 :     return gerepilecopy(av, s);
    2736             :   }
    2737         732 :   if (lgpol(z)<=1 && odd(d))
    2738             :   {
    2739           8 :     pari_sp av = avma;
    2740           8 :     GEN s = Fp_sqrt(constant_coeff(z), p);
    2741           8 :     if (!s) return gc_NULL(av);
    2742           8 :     return gerepilecopy(av, scalarpol_shallow(s, get_FpX_var(T)));
    2743             :   }
    2744         724 :   return FpXQ_sqrtn(z, gen_2, T, p, NULL);
    2745             : }
    2746             : 
    2747             : GEN
    2748      363651 : FpXQ_norm(GEN x, GEN TB, GEN p)
    2749             : {
    2750      363651 :   pari_sp av = avma;
    2751      363651 :   GEN T = get_FpX_mod(TB);
    2752      363651 :   GEN y = FpX_resultant(T, x, p);
    2753      363651 :   GEN L = leading_coeff(T);
    2754      363651 :   if (gequal1(L) || signe(x)==0) return y;
    2755           0 :   return gerepileupto(av, Fp_div(y, Fp_pows(L, degpol(x), p), p));
    2756             : }
    2757             : 
    2758             : GEN
    2759       21075 : FpXQ_trace(GEN x, GEN TB, GEN p)
    2760             : {
    2761       21075 :   pari_sp av = avma;
    2762       21075 :   GEN T = get_FpX_mod(TB);
    2763       21075 :   GEN dT = FpX_deriv(T,p);
    2764       21075 :   long n = degpol(dT);
    2765       21075 :   GEN z = FpXQ_mul(x, dT, TB, p);
    2766       21075 :   if (degpol(z)<n) return gc_const(av, gen_0);
    2767       19892 :   return gerepileuptoint(av, Fp_div(gel(z,2+n), gel(T,3+n),p));
    2768             : }
    2769             : 
    2770             : GEN
    2771          15 : FpXQ_charpoly(GEN x, GEN T, GEN p)
    2772             : {
    2773          15 :   pari_sp ltop=avma;
    2774          15 :   long vT, v = fetch_var();
    2775             :   GEN R;
    2776          15 :   T = leafcopy(get_FpX_mod(T));
    2777          15 :   vT = varn(T); setvarn(T, v);
    2778          15 :   x = leafcopy(x); setvarn(x, v);
    2779          15 :   R = FpX_FpXY_resultant(T, deg1pol_shallow(gen_1,FpX_neg(x,p),vT),p);
    2780          15 :   (void)delete_var(); return gerepileupto(ltop,R);
    2781             : }
    2782             : 
    2783             : /* Computing minimal polynomial :                         */
    2784             : /* cf Shoup 'Efficient Computation of Minimal Polynomials */
    2785             : /*          in Algebraic Extensions of Finite Fields'     */
    2786             : 
    2787             : /* Let v a linear form, return the linear form z->v(tau*z)
    2788             :    that is, v*(M_tau) */
    2789             : 
    2790             : static GEN
    2791        1022 : FpXQ_transmul_init(GEN tau, GEN T, GEN p)
    2792             : {
    2793             :   GEN bht;
    2794        1022 :   GEN h, Tp = get_FpX_red(T, &h);
    2795        1022 :   long n = degpol(Tp), vT = varn(Tp);
    2796        1022 :   GEN ft = FpX_recipspec(Tp+2, n+1, n+1);
    2797        1022 :   GEN bt = FpX_recipspec(tau+2, lgpol(tau), n);
    2798        1022 :   setvarn(ft, vT); setvarn(bt, vT);
    2799        1022 :   if (h)
    2800          14 :     bht = FpXn_mul(bt, h, n-1, p);
    2801             :   else
    2802             :   {
    2803        1008 :     GEN bh = FpX_div(FpX_shift(tau, n-1), T, p);
    2804        1008 :     bht = FpX_recipspec(bh+2, lgpol(bh), n-1);
    2805        1008 :     setvarn(bht, vT);
    2806             :   }
    2807        1022 :   return mkvec3(bt, bht, ft);
    2808             : }
    2809             : 
    2810             : static GEN
    2811        2643 : FpXQ_transmul(GEN tau, GEN a, long n, GEN p)
    2812             : {
    2813        2643 :   pari_sp ltop = avma;
    2814             :   GEN t1, t2, t3, vec;
    2815        2643 :   GEN bt = gel(tau, 1), bht = gel(tau, 2), ft = gel(tau, 3);
    2816        2643 :   if (signe(a)==0) return pol_0(varn(a));
    2817        2608 :   t2 = FpX_shift(FpX_mul(bt, a, p),1-n);
    2818        2608 :   if (signe(bht)==0) return gerepilecopy(ltop, t2);
    2819        2076 :   t1 = FpX_shift(FpX_mul(ft, a, p),-n);
    2820        2076 :   t3 = FpXn_mul(t1, bht, n-1, p);
    2821        2076 :   vec = FpX_sub(t2, FpX_shift(t3, 1), p);
    2822        2076 :   return gerepileupto(ltop, vec);
    2823             : }
    2824             : 
    2825             : GEN
    2826       13341 : FpXQ_minpoly(GEN x, GEN T, GEN p)
    2827             : {
    2828       13341 :   pari_sp ltop = avma;
    2829             :   long vT, n;
    2830             :   GEN v_x, g, tau;
    2831       13341 :   if (lgefint(p)==3)
    2832             :   {
    2833       12830 :     ulong pp = to_Flxq(&x, &T, p);
    2834       12830 :     GEN g = Flxq_minpoly(x, T, pp);
    2835       12830 :     return gerepileupto(ltop, Flx_to_ZX(g));
    2836             :   }
    2837         511 :   vT = get_FpX_var(T);
    2838         511 :   n = get_FpX_degree(T);
    2839         511 :   g = pol_1(vT);
    2840         511 :   tau = pol_1(vT);
    2841         511 :   T = FpX_get_red(T, p);
    2842         511 :   x = FpXQ_red(x, T, p);
    2843         511 :   v_x = FpXQ_powers(x, usqrt(2*n), T, p);
    2844        1022 :   while(signe(tau) != 0)
    2845             :   {
    2846             :     long i, j, m, k1;
    2847             :     GEN M, v, tr;
    2848             :     GEN g_prime, c;
    2849         511 :     if (degpol(g) == n) { tau = pol_1(vT); g = pol_1(vT); }
    2850         511 :     v = random_FpX(n, vT, p);
    2851         511 :     tr = FpXQ_transmul_init(tau, T, p);
    2852         511 :     v = FpXQ_transmul(tr, v, n, p);
    2853         511 :     m = 2*(n-degpol(g));
    2854         511 :     k1 = usqrt(m);
    2855         511 :     tr = FpXQ_transmul_init(gel(v_x,k1+1), T, p);
    2856         511 :     c = cgetg(m+2,t_POL);
    2857         511 :     c[1] = evalsigne(1)|evalvarn(vT);
    2858        2643 :     for (i=0; i<m; i+=k1)
    2859             :     {
    2860        2132 :       long mj = minss(m-i, k1);
    2861       10136 :       for (j=0; j<mj; j++)
    2862        8004 :         gel(c,m+1-(i+j)) = FpX_dotproduct(v, gel(v_x,j+1), p);
    2863        2132 :       v = FpXQ_transmul(tr, v, n, p);
    2864             :     }
    2865         511 :     c = FpX_renormalize(c, m+2);
    2866             :     /* now c contains <v,x^i> , i = 0..m-1  */
    2867         511 :     M = FpX_halfgcd(pol_xn(m, vT), c, p);
    2868         511 :     g_prime = gmael(M, 2, 2);
    2869         511 :     if (degpol(g_prime) < 1) continue;
    2870         511 :     g = FpX_mul(g, g_prime, p);
    2871         511 :     tau = FpXQ_mul(tau, FpX_FpXQV_eval(g_prime, v_x, T, p), T, p);
    2872             :   }
    2873         511 :   g = FpX_normalize(g,p);
    2874         511 :   return gerepilecopy(ltop,g);
    2875             : }
    2876             : 
    2877             : GEN
    2878           8 : FpXQ_conjvec(GEN x, GEN T, GEN p)
    2879             : {
    2880           8 :   pari_sp av=avma;
    2881             :   long i;
    2882           8 :   long n = get_FpX_degree(T), v = varn(x);
    2883           8 :   GEN M = FpX_matFrobenius(T, p);
    2884           8 :   GEN z = cgetg(n+1,t_COL);
    2885           8 :   gel(z,1) = RgX_to_RgC(x,n);
    2886          17 :   for (i=2; i<=n; i++) gel(z,i) = FpM_FpC_mul(M,gel(z,i-1),p);
    2887           8 :   gel(z,1) = x;
    2888          17 :   for (i=2; i<=n; i++) gel(z,i) = RgV_to_RgX(gel(z,i),v);
    2889           8 :   return gerepilecopy(av,z);
    2890             : }
    2891             : 
    2892             : /* p prime, p_1 = p-1, q = p^deg T, Lp = cofactors of some prime divisors
    2893             :  * l_p of p-1, Lq = cofactors of some prime divisors l_q of q-1, return a
    2894             :  * g in Fq such that
    2895             :  * - Ng generates all l_p-Sylows of Fp^*
    2896             :  * - g generates all l_q-Sylows of Fq^* */
    2897             : static GEN
    2898       83392 : gener_FpXQ_i(GEN T, GEN p, GEN p_1, GEN Lp, GEN Lq)
    2899             : {
    2900             :   pari_sp av;
    2901       83392 :   long vT = varn(T), f = degpol(T), l = lg(Lq);
    2902       83392 :   GEN F = FpX_Frobenius(T, p);
    2903       83392 :   int p_is_2 = is_pm1(p_1);
    2904      168447 :   for (av = avma;; set_avma(av))
    2905       85055 :   {
    2906      168447 :     GEN t, g = random_FpX(f, vT, p);
    2907             :     long i;
    2908      168447 :     if (degpol(g) < 1) continue;
    2909      108126 :     if (p_is_2)
    2910       55758 :       t = g;
    2911             :     else
    2912             :     {
    2913       52368 :       t = FpX_resultant(T, g, p); /* Ng = g^((q-1)/(p-1)), assuming T monic */
    2914       52368 :       if (kronecker(t, p) == 1) continue;
    2915       31475 :       if (lg(Lp) > 1 && !is_gener_Fp(t, p, p_1, Lp)) continue;
    2916       30113 :       t = FpXQ_pow(g, shifti(p_1,-1), T, p);
    2917             :     }
    2918       98701 :     for (i = 1; i < l; i++)
    2919             :     {
    2920       15310 :       GEN a = FpXQ_pow_Frobenius(t, gel(Lq,i), F, T, p);
    2921       15310 :       if (!degpol(a) && equalii(gel(a,2), p_1)) break;
    2922             :     }
    2923       85869 :     if (i == l) return g;
    2924             :   }
    2925             : }
    2926             : 
    2927             : GEN
    2928        7016 : gener_FpXQ(GEN T, GEN p, GEN *po)
    2929             : {
    2930        7016 :   long i, j, f = get_FpX_degree(T);
    2931             :   GEN g, Lp, Lq, p_1, q_1, N, o;
    2932        7016 :   pari_sp av = avma;
    2933             : 
    2934        7016 :   p_1 = subiu(p,1);
    2935        7016 :   if (f == 1) {
    2936             :     GEN Lp, fa;
    2937           7 :     o = p_1;
    2938           7 :     fa = Z_factor(o);
    2939           7 :     Lp = gel(fa,1);
    2940           7 :     Lp = vecslice(Lp, 2, lg(Lp)-1); /* remove 2 for efficiency */
    2941             : 
    2942           7 :     g = cgetg(3, t_POL);
    2943           7 :     g[1] = evalsigne(1) | evalvarn(get_FpX_var(T));
    2944           7 :     gel(g,2) = pgener_Fp_local(p, Lp);
    2945           7 :     if (po) *po = mkvec2(o, fa);
    2946           7 :     return g;
    2947             :   }
    2948        7009 :   if (lgefint(p) == 3)
    2949             :   {
    2950        6972 :     ulong pp = to_Flxq(NULL, &T, p);
    2951        6972 :     g = gener_Flxq(T, pp, po);
    2952        6972 :     if (!po) return Flx_to_ZX_inplace(gerepileuptoleaf(av, g));
    2953        6972 :     g = Flx_to_ZX(g); return gc_all(av, 2, &g, po);
    2954             :   }
    2955             :   /* p now odd */
    2956          37 :   q_1 = subiu(powiu(p,f), 1);
    2957          37 :   N = diviiexact(q_1, p_1);
    2958          37 :   Lp = odd_prime_divisors(p_1);
    2959         168 :   for (i=lg(Lp)-1; i; i--) gel(Lp,i) = diviiexact(p_1, gel(Lp,i));
    2960          37 :   o = factor_pn_1(p,f);
    2961          37 :   Lq = leafcopy( gel(o, 1) );
    2962         353 :   for (i = j = 1; i < lg(Lq); i++)
    2963             :   {
    2964         316 :     if (dvdii(p_1, gel(Lq,i))) continue;
    2965         148 :     gel(Lq,j++) = diviiexact(N, gel(Lq,i));
    2966             :   }
    2967          37 :   setlg(Lq, j);
    2968          37 :   g = gener_FpXQ_i(get_FpX_mod(T), p, p_1, Lp, Lq);
    2969          37 :   if (!po) g = gerepilecopy(av, g);
    2970             :   else {
    2971          21 :     *po = mkvec2(q_1, o);
    2972          21 :     gerepileall(av, 2, &g, po);
    2973             :   }
    2974          37 :   return g;
    2975             : }
    2976             : 
    2977             : GEN
    2978       83355 : gener_FpXQ_local(GEN T, GEN p, GEN L)
    2979             : {
    2980             :   GEN Lp, Lq, p_1, q_1, N, Q;
    2981             :   long f, i, ip, iq, l;
    2982             : 
    2983       83355 :   T = get_FpX_mod(T);
    2984       83355 :   f = degpol(T);
    2985       83356 :   if (f == 1) return pgener_Fp_local(p, L);
    2986       83356 :   l = lg(L);
    2987       83356 :   p_1 = subiu(p,1);
    2988       83354 :   q_1 = subiu(powiu(p,f), 1);
    2989       83352 :   N = diviiexact(q_1, p_1);
    2990             : 
    2991       83351 :   Q = is_pm1(p_1)? gen_1: shifti(p_1,-1);
    2992       83355 :   Lp = cgetg(l, t_VEC); ip = 1;
    2993       83351 :   Lq = cgetg(l, t_VEC); iq = 1;
    2994       98866 :   for (i=1; i < l; i++)
    2995             :   {
    2996       15512 :     GEN a, b, ell = gel(L,i);
    2997       15512 :     if (absequaliu(ell,2)) continue;
    2998       15232 :     a = dvmdii(Q, ell, &b);
    2999       15232 :     if (b == gen_0)
    3000        2555 :       gel(Lp,ip++) = a;
    3001             :     else
    3002       12677 :       gel(Lq,iq++) = diviiexact(N,ell);
    3003             :   }
    3004       83354 :   setlg(Lp, ip);
    3005       83355 :   setlg(Lq, iq);
    3006       83355 :   return gener_FpXQ_i(T, p, p_1, Lp, Lq);
    3007             : }
    3008             : 
    3009             : /***********************************************************************/
    3010             : /**                                                                   **/
    3011             : /**                              FpXn                                 **/
    3012             : /**                                                                   **/
    3013             : /***********************************************************************/
    3014             : 
    3015             : GEN
    3016     2559688 : FpXn_mul(GEN a, GEN b, long n, GEN p)
    3017             : {
    3018     2559688 :   return FpX_red(ZXn_mul(a, b, n), p);
    3019             : }
    3020             : 
    3021             : GEN
    3022           0 : FpXn_sqr(GEN a, long n, GEN p)
    3023             : {
    3024           0 :   return FpX_red(ZXn_sqr(a, n), p);
    3025             : }
    3026             : 
    3027             : /* (f*g) \/ x^n */
    3028             : static GEN
    3029      114901 : FpX_mulhigh_i(GEN f, GEN g, long n, GEN p)
    3030             : {
    3031      114901 :   return FpX_shift(FpX_mul(f,g, p),-n);
    3032             : }
    3033             : 
    3034             : static GEN
    3035       59410 : FpXn_mulhigh(GEN f, GEN g, long n2, long n, GEN p)
    3036             : {
    3037       59410 :   GEN F = RgX_blocks(f, n2, 2), fl = gel(F,1), fh = gel(F,2);
    3038       59410 :   return FpX_add(FpX_mulhigh_i(fl, g, n2, p), FpXn_mul(fh, g, n - n2, p), p);
    3039             : }
    3040             : 
    3041             : GEN
    3042        6412 : FpXn_div(GEN g, GEN f, long e, GEN p)
    3043             : {
    3044        6412 :   pari_sp av = avma, av2;
    3045             :   ulong mask;
    3046             :   GEN W, a;
    3047        6412 :   long v = varn(f), n = 1;
    3048             : 
    3049        6412 :   if (!signe(f)) pari_err_INV("FpXn_inv",f);
    3050        6412 :   a = Fp_inv(gel(f,2), p);
    3051        6412 :   if (e == 1 && !g) return scalarpol(a, v);
    3052        6412 :   else if (e == 2 && !g)
    3053             :   {
    3054             :     GEN b;
    3055           0 :     if (degpol(f) <= 0) return scalarpol(a, v);
    3056           0 :     b = Fp_neg(gel(f,3),p);
    3057           0 :     if (signe(b)==0) return scalarpol(a, v);
    3058           0 :     if (!is_pm1(a)) b = Fp_mul(b, Fp_sqr(a, p), p);
    3059           0 :     W = deg1pol_shallow(b, a, v);
    3060           0 :     return gerepilecopy(av, W);
    3061             :   }
    3062        6412 :   W = scalarpol_shallow(Fp_inv(gel(f,2), p),v);
    3063        6412 :   mask = quadratic_prec_mask(e);
    3064        6412 :   av2 = avma;
    3065       27580 :   for (;mask>1;)
    3066             :   {
    3067             :     GEN u, fr;
    3068       21168 :     long n2 = n;
    3069       21168 :     n<<=1; if (mask & 1) n--;
    3070       21168 :     mask >>= 1;
    3071       21168 :     fr = FpXn_red(f, n);
    3072       21168 :     if (mask>1 || !g)
    3073             :     {
    3074       21168 :       u = FpXn_mul(W, FpXn_mulhigh(fr, W, n2, n, p), n-n2, p);
    3075       21168 :       W = FpX_sub(W, FpX_shift(u, n2), p);
    3076             :     }
    3077             :     else
    3078             :     {
    3079           0 :       GEN y = FpXn_mul(g, W, n, p), yt =  FpXn_red(y, n-n2);
    3080           0 :       u = FpXn_mul(yt, FpXn_mulhigh(fr,  W, n2, n, p), n-n2, p);
    3081           0 :       W = FpX_sub(y, FpX_shift(u, n2), p);
    3082             :     }
    3083       21168 :     if (gc_needed(av2,2))
    3084             :     {
    3085           0 :       if(DEBUGMEM>1) pari_warn(warnmem,"FpXn_inv, e = %ld", n);
    3086           0 :       W = gerepileupto(av2, W);
    3087             :     }
    3088             :   }
    3089        6412 :   return gerepileupto(av, W);
    3090             : }
    3091             : 
    3092             : GEN
    3093        6412 : FpXn_inv(GEN f, long e, GEN p)
    3094        6412 : { return FpXn_div(NULL, f, e, p); }
    3095             : 
    3096             : GEN
    3097       17249 : FpXn_expint(GEN h, long e, GEN p)
    3098             : {
    3099       17249 :   pari_sp av = avma, av2;
    3100       17249 :   long v = varn(h), n=1;
    3101       17249 :   GEN f = pol_1(v), g = pol_1(v);
    3102       17249 :   ulong mask = quadratic_prec_mask(e);
    3103       17249 :   av2 = avma;
    3104       55491 :   for (;mask>1;)
    3105             :   {
    3106             :     GEN u, w;
    3107       55491 :     long n2 = n;
    3108       55491 :     n<<=1; if (mask & 1) n--;
    3109       55491 :     mask >>= 1;
    3110       55491 :     u = FpXn_mul(g, FpX_mulhigh_i(f, FpXn_red(h, n2-1), n2-1, p), n-n2, p);
    3111       55491 :     u = FpX_add(u, FpX_shift(FpXn_red(h, n-1), 1-n2), p);
    3112       55491 :     w = FpXn_mul(f, FpX_integXn(u, n2-1, p), n-n2, p);
    3113       55491 :     f = FpX_add(f, FpX_shift(w, n2), p);
    3114       55491 :     if (mask<=1) break;
    3115       38242 :     u = FpXn_mul(g, FpXn_mulhigh(f, g, n2, n, p), n-n2, p);
    3116       38242 :     g = FpX_sub(g, FpX_shift(u, n2), p);
    3117       38242 :     if (gc_needed(av2,2))
    3118             :     {
    3119           0 :       if (DEBUGMEM>1) pari_warn(warnmem,"FpXn_exp, e = %ld", n);
    3120           0 :       gerepileall(av2, 2, &f, &g);
    3121             :     }
    3122             :   }
    3123       17249 :   return gerepileupto(av, f);
    3124             : }
    3125             : 
    3126             : GEN
    3127           0 : FpXn_exp(GEN h, long e, GEN p)
    3128             : {
    3129           0 :   if (signe(h)==0 || degpol(h)<1 || !gequal0(gel(h,2)))
    3130           0 :     pari_err_DOMAIN("FpXn_exp","valuation", "<", gen_1, h);
    3131           0 :   return FpXn_expint(FpX_deriv(h, p), e, p);
    3132             : }

Generated by: LCOV version 1.16