1 /* Copyright (C) 2000-2005  The PARI group.
2 
3 This file is part of the PARI/GP package.
4 
5 PARI/GP is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 2 of the License, or (at your option) any later
8 version. It is distributed in the hope that it will be useful, but WITHOUT
9 ANY WARRANTY WHATSOEVER.
10 
11 Check the License for details. You should have received a copy of it, along
12 with the package; see the file 'COPYING'. If not, write to the Free Software
13 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
14 
15 /***********************************************************************/
16 /**                                                                   **/
17 /**               ARITHMETIC OPERATIONS ON POLYNOMIALS                **/
18 /**                         (third part)                              **/
19 /**                                                                   **/
20 /***********************************************************************/
21 #include "pari.h"
22 #include "paripriv.h"
23 
24 /************************************************************************
25  **                                                                    **
26  **                      Ring membership                               **
27  **                                                                    **
28  ************************************************************************/
29 struct charact {
30   GEN q;
31   int isprime;
32 };
33 static void
char_update_prime(struct charact * S,GEN p)34 char_update_prime(struct charact *S, GEN p)
35 {
36   if (!S->isprime) { S->isprime = 1; S->q = p; }
37   if (!equalii(p, S->q)) pari_err_MODULUS("characteristic", S->q, p);
38 }
39 static void
char_update_int(struct charact * S,GEN n)40 char_update_int(struct charact *S, GEN n)
41 {
42   if (S->isprime)
43   {
44     if (dvdii(n, S->q)) return;
45     pari_err_MODULUS("characteristic", S->q, n);
46   }
47   S->q = gcdii(S->q, n);
48 }
49 static void
charact(struct charact * S,GEN x)50 charact(struct charact *S, GEN x)
51 {
52   const long tx = typ(x);
53   long i, l;
54   switch(tx)
55   {
56     case t_INTMOD:char_update_int(S, gel(x,1)); break;
57     case t_FFELT: char_update_prime(S, gel(x,4)); break;
58     case t_COMPLEX: case t_QUAD:
59     case t_POLMOD: case t_POL: case t_SER: case t_RFRAC:
60     case t_VEC: case t_COL: case t_MAT:
61       l = lg(x);
62       for (i=lontyp[tx]; i < l; i++) charact(S,gel(x,i));
63       break;
64     case t_LIST:
65       x = list_data(x);
66       if (x) charact(S, x);
67       break;
68   }
69 }
70 static void
charact_res(struct charact * S,GEN x)71 charact_res(struct charact *S, GEN x)
72 {
73   const long tx = typ(x);
74   long i, l;
75   switch(tx)
76   {
77     case t_INTMOD:char_update_int(S, gel(x,1)); break;
78     case t_FFELT: char_update_prime(S, gel(x,4)); break;
79     case t_PADIC: char_update_prime(S, gel(x,2)); break;
80     case t_COMPLEX: case t_QUAD:
81     case t_POLMOD: case t_POL: case t_SER: case t_RFRAC:
82     case t_VEC: case t_COL: case t_MAT:
83       l = lg(x);
84       for (i=lontyp[tx]; i < l; i++) charact_res(S,gel(x,i));
85       break;
86     case t_LIST:
87       x = list_data(x);
88       if (x) charact_res(S, x);
89       break;
90   }
91 }
92 GEN
characteristic(GEN x)93 characteristic(GEN x)
94 {
95   struct charact S;
96   S.q = gen_0; S.isprime = 0;
97   charact(&S, x); return S.q;
98 }
99 GEN
residual_characteristic(GEN x)100 residual_characteristic(GEN x)
101 {
102   struct charact S;
103   S.q = gen_0; S.isprime = 0;
104   charact_res(&S, x); return S.q;
105 }
106 
107 int
Rg_is_Fp(GEN x,GEN * pp)108 Rg_is_Fp(GEN x, GEN *pp)
109 {
110   GEN mod;
111   switch(typ(x))
112   {
113   case t_INTMOD:
114     mod = gel(x,1);
115     if (!*pp) *pp = mod;
116     else if (mod != *pp && !equalii(mod, *pp))
117     {
118       if (DEBUGLEVEL) pari_warn(warner,"different moduli in Rg_is_Fp");
119       return 0;
120     }
121     return 1;
122   case t_INT:
123     return 1;
124   default: return 0;
125   }
126 }
127 
128 int
RgX_is_FpX(GEN x,GEN * pp)129 RgX_is_FpX(GEN x, GEN *pp)
130 {
131   long i, lx = lg(x);
132   for (i=2; i<lx; i++)
133     if (!Rg_is_Fp(gel(x, i), pp))
134       return 0;
135   return 1;
136 }
137 
138 int
RgV_is_FpV(GEN x,GEN * pp)139 RgV_is_FpV(GEN x, GEN *pp)
140 {
141   long i, lx = lg(x);
142   for (i=1; i<lx; i++)
143     if (!Rg_is_Fp(gel(x,i), pp)) return 0;
144   return 1;
145 }
146 
147 int
RgM_is_FpM(GEN x,GEN * pp)148 RgM_is_FpM(GEN x, GEN *pp)
149 {
150   long i, lx = lg(x);
151   for (i=1; i<lx; i++)
152     if (!RgV_is_FpV(gel(x, i), pp)) return 0;
153   return 1;
154 }
155 
156 int
Rg_is_FpXQ(GEN x,GEN * pT,GEN * pp)157 Rg_is_FpXQ(GEN x, GEN *pT, GEN *pp)
158 {
159   GEN pol, mod, p;
160   switch(typ(x))
161   {
162   case t_INTMOD:
163     return Rg_is_Fp(x, pp);
164   case t_INT:
165     return 1;
166   case t_POL:
167     return RgX_is_FpX(x, pp);
168   case t_FFELT:
169     mod = x; p = FF_p_i(x);
170     if (!*pp) *pp = p;
171     if (!*pT) *pT = mod;
172     else if (typ(*pT)!=t_FFELT || !FF_samefield(*pT,mod))
173     {
174       if (DEBUGLEVEL) pari_warn(warner,"different moduli in Rg_is_FpXQ");
175       return 0;
176     }
177     return 1;
178   case t_POLMOD:
179     mod = gel(x,1); pol = gel(x, 2);
180     if (!RgX_is_FpX(mod, pp)) return 0;
181     if (typ(pol)==t_POL)
182     {
183       if (!RgX_is_FpX(pol, pp)) return 0;
184     }
185     else if (!Rg_is_Fp(pol, pp)) return 0;
186     if (!*pT) *pT = mod;
187     else if (mod != *pT && !gequal(mod, *pT))
188     {
189       if (DEBUGLEVEL) pari_warn(warner,"different moduli in Rg_is_FpXQ");
190       return 0;
191     }
192     return 1;
193 
194   default: return 0;
195   }
196 }
197 
198 int
RgX_is_FpXQX(GEN x,GEN * pT,GEN * pp)199 RgX_is_FpXQX(GEN x, GEN *pT, GEN *pp)
200 {
201   long i, lx = lg(x);
202   for (i = 2; i < lx; i++)
203     if (!Rg_is_FpXQ(gel(x,i), pT, pp)) return 0;
204   return 1;
205 }
206 
207 /************************************************************************
208  **                                                                    **
209  **                      Ring conversion                               **
210  **                                                                    **
211  ************************************************************************/
212 
213 /* p > 0 a t_INT, return lift(x * Mod(1,p)).
214  * If x is an INTMOD, assume modulus is a multiple of p. */
215 GEN
Rg_to_Fp(GEN x,GEN p)216 Rg_to_Fp(GEN x, GEN p)
217 {
218   if (lgefint(p) == 3) return utoi(Rg_to_Fl(x, uel(p,2)));
219   switch(typ(x))
220   {
221     case t_INT: return modii(x, p);
222     case t_FRAC: {
223       pari_sp av = avma;
224       GEN z = modii(gel(x,1), p);
225       if (z == gen_0) return gen_0;
226       return gerepileuptoint(av, remii(mulii(z, Fp_inv(gel(x,2), p)), p));
227     }
228     case t_PADIC: return padic_to_Fp(x, p);
229     case t_INTMOD: {
230       GEN q = gel(x,1), a = gel(x,2);
231       if (equalii(q, p)) return icopy(a);
232       if (!dvdii(q,p)) pari_err_MODULUS("Rg_to_Fp", q, p);
233       return remii(a, p);
234     }
235     default: pari_err_TYPE("Rg_to_Fp",x);
236       return NULL; /* LCOV_EXCL_LINE */
237   }
238 }
239 /* If x is a POLMOD, assume modulus is a multiple of T. */
240 GEN
Rg_to_FpXQ(GEN x,GEN T,GEN p)241 Rg_to_FpXQ(GEN x, GEN T, GEN p)
242 {
243   long ta, tx = typ(x), v = get_FpX_var(T);
244   GEN a, b;
245   if (is_const_t(tx))
246   {
247     if (tx == t_FFELT)
248     {
249       GEN z = FF_to_FpXQ(x);
250       setvarn(z, v);
251       return z;
252     }
253     return scalar_ZX(degpol(T)? Rg_to_Fp(x, p): gen_0, v);
254   }
255   switch(tx)
256   {
257     case t_POLMOD:
258       b = gel(x,1);
259       a = gel(x,2); ta = typ(a);
260       if (is_const_t(ta))
261         return scalar_ZX(degpol(T)? Rg_to_Fp(a, p): gen_0, v);
262       b = RgX_to_FpX(b, p); if (varn(b) != v) break;
263       a = RgX_to_FpX(a, p);
264       if (ZX_equal(b,get_FpX_mod(T)) || signe(FpX_rem(b,T,p))==0)
265         return FpX_rem(a, T, p);
266       break;
267     case t_POL:
268       if (varn(x) != v) break;
269       return FpX_rem(RgX_to_FpX(x,p), T, p);
270     case t_RFRAC:
271       a = Rg_to_FpXQ(gel(x,1), T,p);
272       b = Rg_to_FpXQ(gel(x,2), T,p);
273       return FpXQ_div(a,b, T,p);
274   }
275   pari_err_TYPE("Rg_to_FpXQ",x);
276   return NULL; /* LCOV_EXCL_LINE */
277 }
278 GEN
RgX_to_FpX(GEN x,GEN p)279 RgX_to_FpX(GEN x, GEN p)
280 {
281   long i, l;
282   GEN z = cgetg_copy(x, &l); z[1] = x[1];
283   for (i = 2; i < l; i++) gel(z,i) = Rg_to_Fp(gel(x,i), p);
284   return FpX_renormalize(z, l);
285 }
286 
287 GEN
RgV_to_FpV(GEN x,GEN p)288 RgV_to_FpV(GEN x, GEN p)
289 { pari_APPLY_type(t_VEC, Rg_to_Fp(gel(x,i), p)) }
290 
291 GEN
RgC_to_FpC(GEN x,GEN p)292 RgC_to_FpC(GEN x, GEN p)
293 { pari_APPLY_type(t_COL, Rg_to_Fp(gel(x,i), p)) }
294 
295 GEN
RgM_to_FpM(GEN x,GEN p)296 RgM_to_FpM(GEN x, GEN p)
297 { pari_APPLY_same(RgC_to_FpC(gel(x,i), p)) }
298 
299 GEN
RgV_to_Flv(GEN x,ulong p)300 RgV_to_Flv(GEN x, ulong p)
301 { pari_APPLY_ulong(Rg_to_Fl(gel(x,i), p)) }
302 
303 GEN
RgM_to_Flm(GEN x,ulong p)304 RgM_to_Flm(GEN x, ulong p)
305 { pari_APPLY_same(RgV_to_Flv(gel(x,i), p)) }
306 
307 GEN
RgX_to_FpXQX(GEN x,GEN T,GEN p)308 RgX_to_FpXQX(GEN x, GEN T, GEN p)
309 {
310   long i, l = lg(x);
311   GEN z = cgetg(l, t_POL); z[1] = x[1];
312   for (i = 2; i < l; i++) gel(z,i) = Rg_to_FpXQ(gel(x,i), T,p);
313   return FpXQX_renormalize(z, l);
314 }
315 GEN
RgX_to_FqX(GEN x,GEN T,GEN p)316 RgX_to_FqX(GEN x, GEN T, GEN p)
317 {
318   long i, l = lg(x);
319   GEN z = cgetg(l, t_POL); z[1] = x[1];
320   if (T)
321     for (i = 2; i < l; i++) gel(z,i) = Rg_to_FpXQ(gel(x,i), T, p);
322   else
323     for (i = 2; i < l; i++) gel(z,i) = Rg_to_Fp(gel(x,i), p);
324   return FpXQX_renormalize(z, l);
325 }
326 
327 GEN
RgC_to_FqC(GEN x,GEN T,GEN p)328 RgC_to_FqC(GEN x, GEN T, GEN p)
329 {
330   long i, l = lg(x);
331   GEN z = cgetg(l, t_COL);
332   if (T)
333     for (i = 1; i < l; i++) gel(z,i) = Rg_to_FpXQ(gel(x,i), T, p);
334   else
335     for (i = 1; i < l; i++) gel(z,i) = Rg_to_Fp(gel(x,i), p);
336   return z;
337 }
338 
339 GEN
RgM_to_FqM(GEN x,GEN T,GEN p)340 RgM_to_FqM(GEN x, GEN T, GEN p)
341 { pari_APPLY_same(RgC_to_FqC(gel(x, i), T, p)) }
342 
343 /* lg(V) > 1 */
344 GEN
FpXV_FpC_mul(GEN V,GEN W,GEN p)345 FpXV_FpC_mul(GEN V, GEN W, GEN p)
346 {
347   pari_sp av = avma;
348   long i, l = lg(V);
349   GEN z = ZX_Z_mul(gel(V,1),gel(W,1));
350   for(i=2; i<l; i++)
351   {
352     z = ZX_add(z, ZX_Z_mul(gel(V,i),gel(W,i)));
353     if ((i & 7) == 0) z = gerepileupto(av, z);
354   }
355   return gerepileupto(av, FpX_red(z,p));
356 }
357 
358 GEN
FqX_Fq_add(GEN y,GEN x,GEN T,GEN p)359 FqX_Fq_add(GEN y, GEN x, GEN T, GEN p)
360 {
361   long i, lz = lg(y);
362   GEN z;
363   if (!T) return FpX_Fp_add(y, x, p);
364   if (lz == 2) return scalarpol(x, varn(y));
365   z = cgetg(lz,t_POL); z[1] = y[1];
366   gel(z,2) = Fq_add(gel(y,2),x, T, p);
367   if (lz == 3) z = FpXX_renormalize(z,lz);
368   else
369     for(i=3;i<lz;i++) gel(z,i) = gcopy(gel(y,i));
370   return z;
371 }
372 
373 GEN
FqX_Fq_sub(GEN y,GEN x,GEN T,GEN p)374 FqX_Fq_sub(GEN y, GEN x, GEN T, GEN p)
375 {
376   long i, lz = lg(y);
377   GEN z;
378   if (!T) return FpX_Fp_sub(y, x, p);
379   if (lz == 2) return scalarpol(x, varn(y));
380   z = cgetg(lz,t_POL); z[1] = y[1];
381   gel(z,2) = Fq_sub(gel(y,2), x, T, p);
382   if (lz == 3) z = FpXX_renormalize(z,lz);
383   else
384     for(i=3;i<lz;i++) gel(z,i) = gcopy(gel(y,i));
385   return z;
386 }
387 
388 GEN
FqX_Fq_mul_to_monic(GEN P,GEN U,GEN T,GEN p)389 FqX_Fq_mul_to_monic(GEN P, GEN U, GEN T, GEN p)
390 {
391   long i, lP;
392   GEN res = cgetg_copy(P, &lP); res[1] = P[1];
393   for(i=2; i<lP-1; i++) gel(res,i) = Fq_mul(U,gel(P,i), T,p);
394   gel(res,lP-1) = gen_1; return res;
395 }
396 
397 GEN
FpXQX_normalize(GEN z,GEN T,GEN p)398 FpXQX_normalize(GEN z, GEN T, GEN p)
399 {
400   GEN lc;
401   if (lg(z) == 2) return z;
402   lc = leading_coeff(z);
403   if (typ(lc) == t_POL)
404   {
405     if (lg(lc) > 3) /* nonconstant */
406       return FqX_Fq_mul_to_monic(z, Fq_inv(lc,T,p), T,p);
407     /* constant */
408     lc = gel(lc,2);
409     z = shallowcopy(z);
410     gel(z, lg(z)-1) = lc;
411   }
412   /* lc a t_INT */
413   if (equali1(lc)) return z;
414   return FqX_Fq_mul_to_monic(z, Fp_inv(lc,p), T,p);
415 }
416 
417 GEN
FqX_eval(GEN x,GEN y,GEN T,GEN p)418 FqX_eval(GEN x, GEN y, GEN T, GEN p)
419 {
420   pari_sp av;
421   GEN p1, r;
422   long j, i=lg(x)-1;
423   if (i<=2)
424     return (i==2)? Fq_red(gel(x,2), T, p): gen_0;
425   av=avma; p1=gel(x,i);
426   /* specific attention to sparse polynomials (see poleval)*/
427   /*You've guessed it! It's a copy-paste(tm)*/
428   for (i--; i>=2; i=j-1)
429   {
430     for (j=i; !signe(gel(x,j)); j--)
431       if (j==2)
432       {
433         if (i!=j) y = Fq_pow(y,utoipos(i-j+1), T, p);
434         return gerepileupto(av, Fq_mul(p1,y, T, p));
435       }
436     r = (i==j)? y: Fq_pow(y, utoipos(i-j+1), T, p);
437     p1 = Fq_add(Fq_mul(p1,r,T,p), gel(x,j), T, p);
438   }
439   return gerepileupto(av, p1);
440 }
441 
442 GEN
FqXY_evalx(GEN Q,GEN x,GEN T,GEN p)443 FqXY_evalx(GEN Q, GEN x, GEN T, GEN p)
444 {
445   long i, lb = lg(Q);
446   GEN z;
447   if (!T) return FpXY_evalx(Q, x, p);
448   z = cgetg(lb, t_POL); z[1] = Q[1];
449   for (i=2; i<lb; i++)
450   {
451     GEN q = gel(Q,i);
452     gel(z,i) = typ(q) == t_INT? modii(q,p): FqX_eval(q, x, T, p);
453   }
454   return FpXQX_renormalize(z, lb);
455 }
456 
457 /* Q an FpXY, evaluate at (X,Y) = (x,y) */
458 GEN
FqXY_eval(GEN Q,GEN y,GEN x,GEN T,GEN p)459 FqXY_eval(GEN Q, GEN y, GEN x, GEN T, GEN p)
460 {
461   pari_sp av = avma;
462   if (!T) return FpXY_eval(Q, y, x, p);
463   return gerepileupto(av, FqX_eval(FqXY_evalx(Q, x, T, p), y, T, p));
464 }
465 
466 /* a X^d */
467 GEN
monomial(GEN a,long d,long v)468 monomial(GEN a, long d, long v)
469 {
470   long i, n;
471   GEN P;
472   if (d < 0) {
473     if (isrationalzero(a)) return pol_0(v);
474     retmkrfrac(a, pol_xn(-d, v));
475   }
476   if (gequal0(a))
477   {
478     if (isexactzero(a)) return scalarpol_shallow(a,v);
479     n = d+2; P = cgetg(n+1, t_POL);
480     P[1] = evalsigne(0) | evalvarn(v);
481   }
482   else
483   {
484     n = d+2; P = cgetg(n+1, t_POL);
485     P[1] = evalsigne(1) | evalvarn(v);
486   }
487   for (i = 2; i < n; i++) gel(P,i) = gen_0;
488   gel(P,i) = a; return P;
489 }
490 GEN
monomialcopy(GEN a,long d,long v)491 monomialcopy(GEN a, long d, long v)
492 {
493   long i, n;
494   GEN P;
495   if (d < 0) {
496     if (isrationalzero(a)) return pol_0(v);
497     retmkrfrac(gcopy(a), pol_xn(-d, v));
498   }
499   if (gequal0(a))
500   {
501     if (isexactzero(a)) return scalarpol(a,v);
502     n = d+2; P = cgetg(n+1, t_POL);
503     P[1] = evalsigne(0) | evalvarn(v);
504   }
505   else
506   {
507     n = d+2; P = cgetg(n+1, t_POL);
508     P[1] = evalsigne(1) | evalvarn(v);
509   }
510   for (i = 2; i < n; i++) gel(P,i) = gen_0;
511   gel(P,i) = gcopy(a); return P;
512 }
513 GEN
pol_x_powers(long N,long v)514 pol_x_powers(long N, long v)
515 {
516   GEN L = cgetg(N+1,t_VEC);
517   long i;
518   for (i=1; i<=N; i++) gel(L,i) = pol_xn(i-1, v);
519   return L;
520 }
521 
522 GEN
FqXQ_powers(GEN x,long l,GEN S,GEN T,GEN p)523 FqXQ_powers(GEN x, long l, GEN S, GEN T, GEN p)
524 {
525   return T ? FpXQXQ_powers(x, l, S, T, p): FpXQ_powers(x, l, S, p);
526 }
527 
528 GEN
FqXQ_matrix_pow(GEN y,long n,long m,GEN S,GEN T,GEN p)529 FqXQ_matrix_pow(GEN y, long n, long m, GEN S, GEN T, GEN p)
530 {
531   return T ? FpXQXQ_matrix_pow(y, n, m, S, T, p): FpXQ_matrix_pow(y, n, m, S, p);
532 }
533 
534 /*******************************************************************/
535 /*                                                                 */
536 /*                             Fq                                  */
537 /*                                                                 */
538 /*******************************************************************/
539 
540 GEN
Fq_add(GEN x,GEN y,GEN T,GEN p)541 Fq_add(GEN x, GEN y, GEN T/*unused*/, GEN p)
542 {
543   (void)T;
544   switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
545   {
546     case 0: return Fp_add(x,y,p);
547     case 1: return FpX_Fp_add(x,y,p);
548     case 2: return FpX_Fp_add(y,x,p);
549     case 3: return FpX_add(x,y,p);
550   }
551   return NULL;/*LCOV_EXCL_LINE*/
552 }
553 
554 GEN
Fq_sub(GEN x,GEN y,GEN T,GEN p)555 Fq_sub(GEN x, GEN y, GEN T/*unused*/, GEN p)
556 {
557   (void)T;
558   switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
559   {
560     case 0: return Fp_sub(x,y,p);
561     case 1: return FpX_Fp_sub(x,y,p);
562     case 2: return Fp_FpX_sub(x,y,p);
563     case 3: return FpX_sub(x,y,p);
564   }
565   return NULL;/*LCOV_EXCL_LINE*/
566 }
567 
568 GEN
Fq_neg(GEN x,GEN T,GEN p)569 Fq_neg(GEN x, GEN T/*unused*/, GEN p)
570 {
571   (void)T;
572   return (typ(x)==t_POL)? FpX_neg(x,p): Fp_neg(x,p);
573 }
574 
575 GEN
Fq_halve(GEN x,GEN T,GEN p)576 Fq_halve(GEN x, GEN T/*unused*/, GEN p)
577 {
578   (void)T;
579   return (typ(x)==t_POL)? FpX_halve(x,p): Fp_halve(x,p);
580 }
581 
582 /* If T==NULL do not reduce*/
583 GEN
Fq_mul(GEN x,GEN y,GEN T,GEN p)584 Fq_mul(GEN x, GEN y, GEN T, GEN p)
585 {
586   switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
587   {
588     case 0: return Fp_mul(x,y,p);
589     case 1: return FpX_Fp_mul(x,y,p);
590     case 2: return FpX_Fp_mul(y,x,p);
591     case 3: if (T) return FpXQ_mul(x,y,T,p);
592             else return FpX_mul(x,y,p);
593   }
594   return NULL;/*LCOV_EXCL_LINE*/
595 }
596 
597 /* If T==NULL do not reduce*/
598 GEN
Fq_mulu(GEN x,ulong y,GEN T,GEN p)599 Fq_mulu(GEN x, ulong y, /*unused*/GEN T, GEN p)
600 {
601   (void) T;
602   return typ(x)==t_POL ? FpX_Fp_mul(x,utoi(y),p): Fp_mulu(x, y, p);
603 }
604 
605 /* y t_INT */
606 GEN
Fq_Fp_mul(GEN x,GEN y,GEN T,GEN p)607 Fq_Fp_mul(GEN x, GEN y, GEN T/*unused*/, GEN p)
608 {
609   (void)T;
610   return (typ(x) == t_POL)? FpX_Fp_mul(x,y,p)
611                           : Fp_mul(x,y,p);
612 }
613 /* If T==NULL do not reduce*/
614 GEN
Fq_sqr(GEN x,GEN T,GEN p)615 Fq_sqr(GEN x, GEN T, GEN p)
616 {
617   if (typ(x) == t_POL)
618   {
619     if (T) return FpXQ_sqr(x,T,p);
620     else return FpX_sqr(x,p);
621   }
622   else
623     return Fp_sqr(x,p);
624 }
625 
626 GEN
Fq_neg_inv(GEN x,GEN T,GEN p)627 Fq_neg_inv(GEN x, GEN T, GEN p)
628 {
629   if (typ(x) == t_INT) return Fp_inv(Fp_neg(x,p),p);
630   return FpXQ_inv(FpX_neg(x,p),T,p);
631 }
632 
633 GEN
Fq_invsafe(GEN x,GEN pol,GEN p)634 Fq_invsafe(GEN x, GEN pol, GEN p)
635 {
636   if (typ(x) == t_INT) return Fp_invsafe(x,p);
637   return FpXQ_invsafe(x,pol,p);
638 }
639 
640 GEN
Fq_inv(GEN x,GEN pol,GEN p)641 Fq_inv(GEN x, GEN pol, GEN p)
642 {
643   if (typ(x) == t_INT) return Fp_inv(x,p);
644   return FpXQ_inv(x,pol,p);
645 }
646 
647 GEN
Fq_div(GEN x,GEN y,GEN pol,GEN p)648 Fq_div(GEN x, GEN y, GEN pol, GEN p)
649 {
650   switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
651   {
652     case 0: return Fp_div(x,y,p);
653     case 1: return FpX_Fp_div(x,y,p);
654     case 2: return FpX_Fp_mul(FpXQ_inv(y,pol,p),x,p);
655     case 3: return FpXQ_div(x,y,pol,p);
656   }
657   return NULL;/*LCOV_EXCL_LINE*/
658 }
659 
660 GEN
Fq_pow(GEN x,GEN n,GEN pol,GEN p)661 Fq_pow(GEN x, GEN n, GEN pol, GEN p)
662 {
663   if (typ(x) == t_INT) return Fp_pow(x,n,p);
664   return FpXQ_pow(x,n,pol,p);
665 }
666 
667 GEN
Fq_powu(GEN x,ulong n,GEN pol,GEN p)668 Fq_powu(GEN x, ulong n, GEN pol, GEN p)
669 {
670   if (typ(x) == t_INT) return Fp_powu(x,n,p);
671   return FpXQ_powu(x,n,pol,p);
672 }
673 
674 GEN
Fq_sqrt(GEN x,GEN T,GEN p)675 Fq_sqrt(GEN x, GEN T, GEN p)
676 {
677   if (typ(x) == t_INT)
678   {
679     if (!T || odd(get_FpX_degree(T))) return Fp_sqrt(x,p);
680     x = scalarpol_shallow(x, get_FpX_var(T));
681   }
682   return FpXQ_sqrt(x,T,p);
683 }
684 GEN
Fq_sqrtn(GEN x,GEN n,GEN T,GEN p,GEN * zeta)685 Fq_sqrtn(GEN x, GEN n, GEN T, GEN p, GEN *zeta)
686 {
687   if (typ(x) == t_INT)
688   {
689     long d;
690     if (!T) return Fp_sqrtn(x,n,p,zeta);
691     d = get_FpX_degree(T);
692     if (ugcdiu(n,d) == 1)
693     {
694       if (!zeta) return Fp_sqrtn(x,n,p,NULL);
695       /* gcd(n,p-1)=gcd(n,q-1): same number of solutions in Fp and F_q */
696       if (equalii(gcdii(subiu(p,1),n), gcdii(subiu(Fp_powu(p,d,n), 1), n)))
697         return Fp_sqrtn(x,n,p,zeta);
698     }
699     x = scalarpol(x, get_FpX_var(T)); /* left on stack */
700   }
701   return FpXQ_sqrtn(x,n,T,p,zeta);
702 }
703 
704 struct _Fq_field
705 {
706   GEN T, p;
707 };
708 
709 static GEN
_Fq_red(void * E,GEN x)710 _Fq_red(void *E, GEN x)
711 { struct _Fq_field *s = (struct _Fq_field *)E;
712   return Fq_red(x, s->T, s->p);
713 }
714 
715 static GEN
_Fq_add(void * E,GEN x,GEN y)716 _Fq_add(void *E, GEN x, GEN y)
717 {
718   (void) E;
719   switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
720   {
721     case 0: return addii(x,y);
722     case 1: return ZX_Z_add(x,y);
723     case 2: return ZX_Z_add(y,x);
724     default: return ZX_add(x,y);
725   }
726 }
727 
728 static GEN
_Fq_neg(void * E,GEN x)729 _Fq_neg(void *E, GEN x) { (void) E; return typ(x)==t_POL?ZX_neg(x):negi(x); }
730 
731 static GEN
_Fq_mul(void * E,GEN x,GEN y)732 _Fq_mul(void *E, GEN x, GEN y)
733 {
734   (void) E;
735   switch((typ(x)==t_POL)|((typ(y)==t_POL)<<1))
736   {
737     case 0: return mulii(x,y);
738     case 1: return ZX_Z_mul(x,y);
739     case 2: return ZX_Z_mul(y,x);
740     default: return ZX_mul(x,y);
741   }
742 }
743 
744 static GEN
_Fq_inv(void * E,GEN x)745 _Fq_inv(void *E, GEN x)
746 { struct _Fq_field *s = (struct _Fq_field *)E;
747   return Fq_inv(x,s->T,s->p);
748 }
749 
750 static int
_Fq_equal0(GEN x)751 _Fq_equal0(GEN x) { return signe(x)==0; }
752 
753 static GEN
_Fq_s(void * E,long x)754 _Fq_s(void *E, long x) { (void) E; return stoi(x); }
755 
756 static const struct bb_field Fq_field={_Fq_red,_Fq_add,_Fq_mul,_Fq_neg,
757                                        _Fq_inv,_Fq_equal0,_Fq_s};
758 
get_Fq_field(void ** E,GEN T,GEN p)759 const struct bb_field *get_Fq_field(void **E, GEN T, GEN p)
760 {
761   if (!T)
762     return get_Fp_field(E, p);
763   else
764   {
765     GEN z = new_chunk(sizeof(struct _Fq_field));
766     struct _Fq_field *e = (struct _Fq_field *) z;
767     e->T = T; e->p  = p; *E = (void*)e;
768     return &Fq_field;
769   }
770 }
771 
772 /*******************************************************************/
773 /*                                                                 */
774 /*                             Fq[X]                               */
775 /*                                                                 */
776 /*******************************************************************/
777 /* P(X + c) */
778 GEN
FpX_translate(GEN P,GEN c,GEN p)779 FpX_translate(GEN P, GEN c, GEN p)
780 {
781   pari_sp av = avma;
782   GEN Q, *R;
783   long i, k, n;
784 
785   if (!signe(P) || !signe(c)) return ZX_copy(P);
786   Q = leafcopy(P);
787   R = (GEN*)(Q+2); n = degpol(P);
788   for (i=1; i<=n; i++)
789   {
790     for (k=n-i; k<n; k++)
791       R[k] = Fp_add(R[k], Fp_mul(c, R[k+1], p), p);
792 
793     if (gc_needed(av,2))
794     {
795       if(DEBUGMEM>1) pari_warn(warnmem,"FpX_translate, i = %ld/%ld", i,n);
796       Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
797     }
798   }
799   return gerepilecopy(av, FpX_renormalize(Q, lg(Q)));
800 }
801 /* P(X + c), c an Fq */
802 GEN
FqX_translate(GEN P,GEN c,GEN T,GEN p)803 FqX_translate(GEN P, GEN c, GEN T, GEN p)
804 {
805   pari_sp av = avma;
806   GEN Q, *R;
807   long i, k, n;
808 
809   /* signe works for t_(INT|POL) */
810   if (!signe(P) || !signe(c)) return RgX_copy(P);
811   Q = leafcopy(P);
812   R = (GEN*)(Q+2); n = degpol(P);
813   for (i=1; i<=n; i++)
814   {
815     for (k=n-i; k<n; k++)
816       R[k] = Fq_add(R[k], Fq_mul(c, R[k+1], T, p), T, p);
817 
818     if (gc_needed(av,2))
819     {
820       if(DEBUGMEM>1) pari_warn(warnmem,"FqX_translate, i = %ld/%ld", i,n);
821       Q = gerepilecopy(av, Q); R = (GEN*)Q+2;
822     }
823   }
824   return gerepilecopy(av, FpXQX_renormalize(Q, lg(Q)));
825 }
826 
827 GEN
FqV_roots_to_pol(GEN V,GEN T,GEN p,long v)828 FqV_roots_to_pol(GEN V, GEN T, GEN p, long v)
829 {
830   pari_sp ltop = avma;
831   long k;
832   GEN W;
833   if (lgefint(p) == 3)
834   {
835     ulong pp = p[2];
836     GEN Tl = ZX_to_Flx(T, pp);
837     GEN Vl = FqV_to_FlxV(V, T, p);
838     Tl = FlxqV_roots_to_pol(Vl, Tl, pp, v);
839     return gerepileupto(ltop, FlxX_to_ZXX(Tl));
840   }
841   W = cgetg(lg(V),t_VEC);
842   for(k=1; k < lg(V); k++)
843     gel(W,k) = deg1pol_shallow(gen_1,Fq_neg(gel(V,k),T,p),v);
844   return gerepileupto(ltop, FpXQXV_prod(W, T, p));
845 }
846 
847 GEN
FqV_red(GEN x,GEN T,GEN p)848 FqV_red(GEN x, GEN T, GEN p)
849 { pari_APPLY_same(Fq_red(gel(x,i), T, p)) }
850 
851 GEN
FqC_add(GEN x,GEN y,GEN T,GEN p)852 FqC_add(GEN x, GEN y, GEN T, GEN p)
853 {
854   if (!T) return FpC_add(x, y, p);
855   pari_APPLY_type(t_COL, Fq_add(gel(x,i), gel(y,i), T, p))
856 }
857 
858 GEN
FqC_sub(GEN x,GEN y,GEN T,GEN p)859 FqC_sub(GEN x, GEN y, GEN T, GEN p)
860 {
861   if (!T) return FpC_sub(x, y, p);
862   pari_APPLY_type(t_COL, Fq_sub(gel(x,i), gel(y,i), T, p))
863 }
864 
865 GEN
FqC_Fq_mul(GEN x,GEN y,GEN T,GEN p)866 FqC_Fq_mul(GEN x, GEN y, GEN T, GEN p)
867 {
868   if (!T) return FpC_Fp_mul(x, y, p);
869   pari_APPLY_type(t_COL, Fq_mul(gel(x,i),y,T,p))
870 }
871 
872 GEN
FqC_FqV_mul(GEN x,GEN y,GEN T,GEN p)873 FqC_FqV_mul(GEN x, GEN y, GEN T, GEN p)
874 {
875   long i,j, lx=lg(x), ly=lg(y);
876   GEN z;
877   if (ly==1) return cgetg(1,t_MAT);
878   z = cgetg(ly,t_MAT);
879   for (j=1; j < ly; j++)
880   {
881     GEN zj = cgetg(lx,t_COL);
882     for (i=1; i<lx; i++) gel(zj,i) = Fq_mul(gel(x,i),gel(y,j), T, p);
883     gel(z, j) = zj;
884   }
885   return z;
886 }
887 
888 GEN
FqV_to_FlxV(GEN x,GEN T,GEN pp)889 FqV_to_FlxV(GEN x, GEN T, GEN pp)
890 {
891   long vT = evalvarn(get_FpX_var(T));
892   ulong p = pp[2];
893   pari_APPLY_type(t_VEC, typ(gel(x,i))==t_INT?  Z_to_Flx(gel(x,i), p, vT)
894                                              : ZX_to_Flx(gel(x,i), p))
895 }
896 
897 GEN
FqC_to_FlxC(GEN x,GEN T,GEN pp)898 FqC_to_FlxC(GEN x, GEN T, GEN pp)
899 {
900   long vT = evalvarn(get_FpX_var(T));
901   ulong p = pp[2];
902   pari_APPLY_type(t_COL, typ(gel(x,i))==t_INT?  Z_to_Flx(gel(x,i), p, vT)
903                                              : ZX_to_Flx(gel(x,i), p))
904 }
905 
906 GEN
FqM_to_FlxM(GEN x,GEN T,GEN p)907 FqM_to_FlxM(GEN x, GEN T, GEN p)
908 { pari_APPLY_same(FqC_to_FlxC(gel(x,i), T, p)) }
909 
910 GEN
FpXC_center(GEN x,GEN p,GEN pov2)911 FpXC_center(GEN x, GEN p, GEN pov2)
912 { pari_APPLY_type(t_COL, FpX_center(gel(x,i), p, pov2)) }
913 
914 GEN
FpXM_center(GEN x,GEN p,GEN pov2)915 FpXM_center(GEN x, GEN p, GEN pov2)
916 { pari_APPLY_same(FpXC_center(gel(x,i), p, pov2)) }
917 
918 /*******************************************************************/
919 /*                                                                 */
920 /*                          GENERIC CRT                            */
921 /*                                                                 */
922 /*******************************************************************/
923 static GEN
primelist(forprime_t * S,long n,GEN dB)924 primelist(forprime_t *S, long n, GEN dB)
925 {
926   GEN P = cgetg(n+1, t_VECSMALL);
927   long i = 1;
928   ulong p;
929   while (i <= n && (p = u_forprime_next(S)))
930     if (!dB || umodiu(dB, p)) P[i++] = p;
931   return P;
932 }
933 
934 void
gen_inccrt_i(const char * str,GEN worker,GEN dB,long n,long mmin,forprime_t * S,GEN * pH,GEN * pmod,GEN crt (GEN,GEN,GEN *),GEN center (GEN,GEN,GEN))935 gen_inccrt_i(const char *str, GEN worker, GEN dB, long n, long mmin,
936              forprime_t *S, GEN *pH, GEN *pmod, GEN crt(GEN, GEN, GEN*),
937              GEN center(GEN, GEN, GEN))
938 {
939   long m = mmin? minss(mmin, n): usqrt(n);
940   GEN  H, P, mod;
941   pari_timer ti;
942   if (DEBUGLEVEL > 4)
943   {
944     timer_start(&ti);
945     err_printf("%s: nb primes: %ld\n",str, n);
946   }
947   if (m == 1)
948   {
949     GEN P = primelist(S, n, dB);
950     GEN done = closure_callgen1(worker, P);
951     H = gel(done,1);
952     mod = gel(done,2);
953     if (!*pH && center) H = center(H, mod, shifti(mod,-1));
954     if (DEBUGLEVEL>4) timer_printf(&ti,"%s: modular", str);
955   }
956   else
957   {
958     long i, s = (n+m-1)/m, r = m - (m*s-n), di = 0;
959     struct pari_mt pt;
960     long pending = 0;
961     H = cgetg(m+1, t_VEC); P = cgetg(m+1, t_VEC);
962     mt_queue_start_lim(&pt, worker, m);
963     for (i=1; i<=m || pending; i++)
964     {
965       GEN done;
966       GEN pr = i <= m ? mkvec(primelist(S, i<=r ? s: s-1, dB)): NULL;
967       mt_queue_submit(&pt, i, pr);
968       done = mt_queue_get(&pt, NULL, &pending);
969       if (done)
970       {
971         di++;
972         gel(H, di) = gel(done,1);
973         gel(P, di) = gel(done,2);
974         if (DEBUGLEVEL>5) err_printf("%ld%% ",100*di/m);
975       }
976     }
977     mt_queue_end(&pt);
978     if (DEBUGLEVEL>5) err_printf("\n");
979     if (DEBUGLEVEL>4) timer_printf(&ti,"%s: modular", str);
980     H = crt(H, P, &mod);
981     if (DEBUGLEVEL>4) timer_printf(&ti,"%s: chinese", str);
982   }
983   if (*pH) H = crt(mkvec2(*pH, H), mkvec2(*pmod, mod), &mod);
984   *pH = H; *pmod = mod;
985 }
986 void
gen_inccrt(const char * str,GEN worker,GEN dB,long n,long mmin,forprime_t * S,GEN * pH,GEN * pmod,GEN crt (GEN,GEN,GEN *),GEN center (GEN,GEN,GEN))987 gen_inccrt(const char *str, GEN worker, GEN dB, long n, long mmin,
988            forprime_t *S, GEN *pH, GEN *pmod, GEN crt(GEN, GEN, GEN*),
989            GEN center(GEN, GEN, GEN))
990 {
991   pari_sp av = avma;
992   gen_inccrt_i(str, worker, dB, n, mmin, S, pH, pmod, crt, center);
993   gerepileall(av, 2, pH, pmod);
994 }
995 
996 GEN
gen_crt(const char * str,GEN worker,forprime_t * S,GEN dB,ulong bound,long mmin,GEN * pmod,GEN crt (GEN,GEN,GEN *),GEN center (GEN,GEN,GEN))997 gen_crt(const char *str, GEN worker, forprime_t *S, GEN dB, ulong bound, long mmin, GEN *pmod,
998         GEN crt(GEN, GEN, GEN*), GEN center(GEN, GEN, GEN))
999 {
1000   GEN mod = gen_1, H = NULL;
1001   ulong e;
1002 
1003   bound++;
1004   while (bound > (e = expi(mod)))
1005   {
1006     long n = (bound - e) / expu(S->p) + 1;
1007     gen_inccrt(str, worker, dB, n, mmin, S, &H, &mod, crt, center);
1008   }
1009   if (pmod) *pmod = mod;
1010   return H;
1011 }
1012 
1013 /*******************************************************************/
1014 /*                                                                 */
1015 /*                          MODULAR GCD                            */
1016 /*                                                                 */
1017 /*******************************************************************/
1018 /* return z = a mod q, b mod p (p,q) = 1; qinv = 1/q mod p; a in ]-q,q] */
1019 static GEN
Fl_chinese_coprime(GEN a,ulong b,GEN q,ulong p,ulong qinv,GEN pq,GEN pq2)1020 Fl_chinese_coprime(GEN a, ulong b, GEN q, ulong p, ulong qinv, GEN pq, GEN pq2)
1021 {
1022   ulong d, amod = umodiu(a, p);
1023   pari_sp av = avma;
1024   GEN ax;
1025 
1026   if (b == amod) return NULL;
1027   d = Fl_mul(Fl_sub(b, amod, p), qinv, p); /* != 0 */
1028   if (d >= 1 + (p>>1))
1029     ax = subii(a, mului(p-d, q));
1030   else
1031   {
1032     ax = addii(a, mului(d, q)); /* in ]0, pq[ assuming a in ]-q,q[ */
1033     if (cmpii(ax,pq2) > 0) ax = subii(ax,pq);
1034   }
1035   return gerepileuptoint(av, ax);
1036 }
1037 GEN
Z_init_CRT(ulong Hp,ulong p)1038 Z_init_CRT(ulong Hp, ulong p) { return stoi(Fl_center(Hp, p, p>>1)); }
1039 GEN
ZX_init_CRT(GEN Hp,ulong p,long v)1040 ZX_init_CRT(GEN Hp, ulong p, long v)
1041 {
1042   long i, l = lg(Hp), lim = (long)(p>>1);
1043   GEN H = cgetg(l, t_POL);
1044   H[1] = evalsigne(1) | evalvarn(v);
1045   for (i=2; i<l; i++)
1046     gel(H,i) = stoi(Fl_center(Hp[i], p, lim));
1047   return ZX_renormalize(H,l);
1048 }
1049 
1050 GEN
ZM_init_CRT(GEN Hp,ulong p)1051 ZM_init_CRT(GEN Hp, ulong p)
1052 {
1053   long i,j, m, l = lg(Hp), lim = (long)(p>>1);
1054   GEN c, cp, H = cgetg(l, t_MAT);
1055   if (l==1) return H;
1056   m = lgcols(Hp);
1057   for (j=1; j<l; j++)
1058   {
1059     cp = gel(Hp,j);
1060     c = cgetg(m, t_COL);
1061     gel(H,j) = c;
1062     for (i=1; i<m; i++) gel(c,i) = stoi(Fl_center(cp[i],p, lim));
1063   }
1064   return H;
1065 }
1066 
1067 int
Z_incremental_CRT(GEN * H,ulong Hp,GEN * ptq,ulong p)1068 Z_incremental_CRT(GEN *H, ulong Hp, GEN *ptq, ulong p)
1069 {
1070   GEN h, q = *ptq, qp = muliu(q,p);
1071   ulong qinv = Fl_inv(umodiu(q,p), p);
1072   int stable = 1;
1073   h = Fl_chinese_coprime(*H,Hp,q,p,qinv,qp,shifti(qp,-1));
1074   if (h) { *H = h; stable = 0; }
1075   *ptq = qp; return stable;
1076 }
1077 
1078 static int
ZX_incremental_CRT_raw(GEN * ptH,GEN Hp,GEN q,GEN qp,ulong p)1079 ZX_incremental_CRT_raw(GEN *ptH, GEN Hp, GEN q, GEN qp, ulong p)
1080 {
1081   GEN H = *ptH, h, qp2 = shifti(qp,-1);
1082   ulong qinv = Fl_inv(umodiu(q,p), p);
1083   long i, l = lg(H), lp = lg(Hp);
1084   int stable = 1;
1085 
1086   if (l < lp)
1087   { /* degree increases */
1088     GEN x = cgetg(lp, t_POL);
1089     for (i=1; i<l; i++)  x[i] = H[i];
1090     for (   ; i<lp; i++) gel(x,i) = gen_0;
1091     *ptH = H = x;
1092     stable = 0;
1093   } else if (l > lp)
1094   { /* degree decreases */
1095     GEN x = cgetg(l, t_VECSMALL);
1096     for (i=1; i<lp; i++)  x[i] = Hp[i];
1097     for (   ; i<l; i++) x[i] = 0;
1098     Hp = x; lp = l;
1099   }
1100   for (i=2; i<lp; i++)
1101   {
1102     h = Fl_chinese_coprime(gel(H,i),Hp[i],q,p,qinv,qp,qp2);
1103     if (h) { gel(H,i) = h; stable = 0; }
1104   }
1105   (void)ZX_renormalize(H,lp);
1106   return stable;
1107 }
1108 
1109 int
ZX_incremental_CRT(GEN * ptH,GEN Hp,GEN * ptq,ulong p)1110 ZX_incremental_CRT(GEN *ptH, GEN Hp, GEN *ptq, ulong p)
1111 {
1112   GEN q = *ptq, qp = muliu(q,p);
1113   int stable = ZX_incremental_CRT_raw(ptH, Hp, q, qp, p);
1114   *ptq = qp; return stable;
1115 }
1116 
1117 int
ZM_incremental_CRT(GEN * pH,GEN Hp,GEN * ptq,ulong p)1118 ZM_incremental_CRT(GEN *pH, GEN Hp, GEN *ptq, ulong p)
1119 {
1120   GEN h, H = *pH, q = *ptq, qp = muliu(q, p), qp2 = shifti(qp,-1);
1121   ulong qinv = Fl_inv(umodiu(q,p), p);
1122   long i,j, l = lg(H), m = lgcols(H);
1123   int stable = 1;
1124   for (j=1; j<l; j++)
1125     for (i=1; i<m; i++)
1126     {
1127       h = Fl_chinese_coprime(gcoeff(H,i,j), coeff(Hp,i,j),q,p,qinv,qp,qp2);
1128       if (h) { gcoeff(H,i,j) = h; stable = 0; }
1129     }
1130   *ptq = qp; return stable;
1131 }
1132 
1133 GEN
ZXM_init_CRT(GEN Hp,long deg,ulong p)1134 ZXM_init_CRT(GEN Hp, long deg, ulong p)
1135 {
1136   long i, j, k;
1137   GEN H;
1138   long m, l = lg(Hp), lim = (long)(p>>1), n;
1139   H = cgetg(l, t_MAT);
1140   if (l==1) return H;
1141   m = lgcols(Hp);
1142   n = deg + 3;
1143   for (j=1; j<l; j++)
1144   {
1145     GEN cp = gel(Hp,j);
1146     GEN c = cgetg(m, t_COL);
1147     gel(H,j) = c;
1148     for (i=1; i<m; i++)
1149     {
1150       GEN dp = gel(cp, i);
1151       long l = lg(dp);
1152       GEN d = cgetg(n, t_POL);
1153       gel(c, i) = d;
1154       d[1] = dp[1] | evalsigne(1);
1155       for (k=2; k<l; k++)
1156         gel(d,k) = stoi(Fl_center(dp[k], p, lim));
1157       for (   ; k<n; k++)
1158         gel(d,k) = gen_0;
1159     }
1160   }
1161   return H;
1162 }
1163 
1164 int
ZXM_incremental_CRT(GEN * pH,GEN Hp,GEN * ptq,ulong p)1165 ZXM_incremental_CRT(GEN *pH, GEN Hp, GEN *ptq, ulong p)
1166 {
1167   GEN v, H = *pH, q = *ptq, qp = muliu(q, p), qp2 = shifti(qp,-1);
1168   ulong qinv = Fl_inv(umodiu(q,p), p);
1169   long i,j,k, l = lg(H), m = lgcols(H), n = lg(gmael(H,1,1));
1170   int stable = 1;
1171   for (j=1; j<l; j++)
1172     for (i=1; i<m; i++)
1173     {
1174       GEN h = gmael(H,j,i), hp = gmael(Hp,j,i);
1175       long lh = lg(hp);
1176       for (k=2; k<lh; k++)
1177       {
1178         v = Fl_chinese_coprime(gel(h,k),uel(hp,k),q,p,qinv,qp,qp2);
1179         if (v) { gel(h,k) = v; stable = 0; }
1180       }
1181       for (; k<n; k++)
1182       {
1183         v = Fl_chinese_coprime(gel(h,k),0,q,p,qinv,qp,qp2);
1184         if (v) { gel(h,k) = v; stable = 0; }
1185       }
1186     }
1187   *ptq = qp; return stable;
1188 }
1189 
1190 /* record the degrees of Euclidean remainders (make them as large as
1191  * possible : smaller values correspond to a degenerate sequence) */
1192 static void
Flx_resultant_set_dglist(GEN a,GEN b,GEN dglist,ulong p)1193 Flx_resultant_set_dglist(GEN a, GEN b, GEN dglist, ulong p)
1194 {
1195   long da,db,dc, ind;
1196   pari_sp av = avma;
1197 
1198   if (lgpol(a)==0 || lgpol(b)==0) return;
1199   da = degpol(a);
1200   db = degpol(b);
1201   if (db > da)
1202   { swapspec(a,b, da,db); }
1203   else if (!da) return;
1204   ind = 0;
1205   while (db)
1206   {
1207     GEN c = Flx_rem(a,b, p);
1208     a = b; b = c; dc = degpol(c);
1209     if (dc < 0) break;
1210 
1211     ind++;
1212     if (dc > dglist[ind]) dglist[ind] = dc;
1213     if (gc_needed(av,2))
1214     {
1215       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_resultant_all");
1216       gerepileall(av, 2, &a,&b);
1217     }
1218     db = dc; /* = degpol(b) */
1219   }
1220   if (ind+1 > lg(dglist)) setlg(dglist,ind+1);
1221   set_avma(av);
1222 }
1223 /* assuming the PRS finishes on a degree 1 polynomial C0 + C1X, with
1224  * "generic" degree sequence as given by dglist, set *Ci and return
1225  * resultant(a,b). Modular version of Collins's subresultant */
1226 static ulong
Flx_resultant_all(GEN a,GEN b,long * C0,long * C1,GEN dglist,ulong p)1227 Flx_resultant_all(GEN a, GEN b, long *C0, long *C1, GEN dglist, ulong p)
1228 {
1229   long da,db,dc, ind;
1230   ulong lb, res, g = 1UL, h = 1UL, ca = 1UL, cb = 1UL;
1231   int s = 1;
1232   pari_sp av = avma;
1233 
1234   *C0 = 1; *C1 = 0;
1235   if (lgpol(a)==0 || lgpol(b)==0) return 0;
1236   da = degpol(a);
1237   db = degpol(b);
1238   if (db > da)
1239   {
1240     swapspec(a,b, da,db);
1241     if (both_odd(da,db)) s = -s;
1242   }
1243   else if (!da) return 1; /* = a[2] ^ db, since 0 <= db <= da = 0 */
1244   ind = 0;
1245   while (db)
1246   { /* sub-resultant algo., applied to ca * a and cb * b, ca,cb scalars,
1247      * da = deg a, db = deg b */
1248     GEN c = Flx_rem(a,b, p);
1249     long delta = da - db;
1250 
1251     if (both_odd(da,db)) s = -s;
1252     lb = Fl_mul(b[db+2], cb, p);
1253     a = b; b = c; dc = degpol(c);
1254     ind++;
1255     if (dc != dglist[ind]) return gc_ulong(av,0); /* degenerates */
1256     if (g == h)
1257     { /* frequent */
1258       ulong cc = Fl_mul(ca, Fl_powu(Fl_div(lb,g,p), delta+1, p), p);
1259       ca = cb;
1260       cb = cc;
1261     }
1262     else
1263     {
1264       ulong cc = Fl_mul(ca, Fl_powu(lb, delta+1, p), p);
1265       ulong ghdelta = Fl_mul(g, Fl_powu(h, delta, p), p);
1266       ca = cb;
1267       cb = Fl_div(cc, ghdelta, p);
1268     }
1269     da = db; /* = degpol(a) */
1270     db = dc; /* = degpol(b) */
1271 
1272     g = lb;
1273     if (delta == 1)
1274       h = g; /* frequent */
1275     else
1276       h = Fl_mul(h, Fl_powu(Fl_div(g,h,p), delta, p), p);
1277 
1278     if (gc_needed(av,2))
1279     {
1280       if (DEBUGMEM>1) pari_warn(warnmem,"Flx_resultant_all");
1281       gerepileall(av, 2, &a,&b);
1282     }
1283   }
1284   if (da > 1) return 0; /* Failure */
1285   /* last nonconstant polynomial has degree 1 */
1286   *C0 = Fl_mul(ca, a[2], p);
1287   *C1 = Fl_mul(ca, a[3], p);
1288   res = Fl_mul(cb, b[2], p);
1289   if (s == -1) res = p - res;
1290   return gc_ulong(av,res);
1291 }
1292 
1293 /* Q a vector of polynomials representing B in Fp[X][Y], evaluate at X = x,
1294  * Return 0 in case of degree drop. */
1295 static GEN
FlxY_evalx_drop(GEN Q,ulong x,ulong p)1296 FlxY_evalx_drop(GEN Q, ulong x, ulong p)
1297 {
1298   GEN z;
1299   long i, lb = lg(Q);
1300   ulong leadz = Flx_eval(leading_coeff(Q), x, p);
1301   long vs=mael(Q,2,1);
1302   if (!leadz) return zero_Flx(vs);
1303 
1304   z = cgetg(lb, t_VECSMALL); z[1] = vs;
1305   for (i=2; i<lb-1; i++) z[i] = Flx_eval(gel(Q,i), x, p);
1306   z[i] = leadz; return z;
1307 }
1308 
1309 GEN
FpXY_FpXQ_evaly(GEN Q,GEN y,GEN T,GEN p,long vx)1310 FpXY_FpXQ_evaly(GEN Q, GEN y, GEN T, GEN p, long vx)
1311 {
1312   pari_sp av = avma;
1313   long i, lb = lg(Q);
1314   GEN z;
1315   if (lb == 2) return pol_0(vx);
1316   z = gel(Q, lb-1);
1317   if (lb == 3 || !signe(y)) return typ(z)==t_INT? scalar_ZX(z, vx): ZX_copy(z);
1318 
1319   if (typ(z) == t_INT) z = scalar_ZX_shallow(z, vx);
1320   for (i=lb-2; i>=2; i--)
1321   {
1322     GEN c = gel(Q,i);
1323     z = FqX_Fq_mul(z, y, T, p);
1324     z = typ(c) == t_INT? FqX_Fq_add(z,c,T,p): FqX_add(z,c,T,p);
1325   }
1326   return gerepileupto(av, z);
1327 }
1328 
1329 static GEN
ZX_norml1(GEN x)1330 ZX_norml1(GEN x)
1331 {
1332   long i, l = lg(x);
1333   GEN s;
1334 
1335   if (l == 2) return gen_0;
1336   s = gel(x, l-1); /* != 0 */
1337   for (i = l-2; i > 1; i--) {
1338     GEN xi = gel(x,i);
1339     if (!signe(x)) continue;
1340     s = addii_sign(s,1, xi,1);
1341   }
1342   return s;
1343 }
1344 
1345 static GEN
L2_bound(GEN nf,GEN den,GEN * pt_roots)1346 L2_bound(GEN nf, GEN den, GEN *pt_roots)
1347 {
1348   GEN M, L, prep, T = nf_get_pol(nf), tozk = nf_get_invzk(nf);
1349   long prec = ZM_max_lg(tozk) + ZX_max_lg(T) + nbits2prec(degpol(T));
1350   (void)initgaloisborne(nf, den? den: gen_1, prec, &L, &prep, NULL);
1351   M = vandermondeinverse(L, RgX_gtofp(T,prec), den, prep);
1352   *pt_roots = L;
1353   return RgM_fpnorml2(RgM_mul(tozk,M), DEFAULTPREC);
1354 }
1355 
1356 /* Interpolate at roots of 1 and use Hadamard bound for univariate resultant:
1357  *   bound = N_2(A)^degpol B N_2(B)^degpol(A),  where
1358  *     N_2(A) = sqrt(sum (N_1(Ai))^2)
1359  * Return e such that Res(A, B) < 2^e */
1360 static GEN
RgX_RgXY_ResBound(GEN A,GEN B,long prec)1361 RgX_RgXY_ResBound(GEN A, GEN B, long prec)
1362 {
1363   pari_sp av = avma, av2;
1364   GEN a = gen_0, b = gen_0, bnd;
1365   long i , lA = lg(A), lB = lg(B);
1366   for (i=2; i<lA; i++)
1367   {
1368     a = gadd(a, gabs(gnorm(gel(A,i)), prec));
1369     if (gc_needed(av,1))
1370     {
1371       if(DEBUGMEM>1) pari_warn(warnmem,"RgX_RgXY_ResBound i = %ld",i);
1372       a = gerepileupto(av, a);
1373     }
1374   }
1375   av2 = avma;
1376   for (i=2; i<lB; i++)
1377   {
1378     GEN t = gel(B,i);
1379     if (typ(t) == t_POL) t = gnorml1(t, prec);
1380     b = gadd(b, gabs(gsqr(t), prec));
1381     if (gc_needed(av,1))
1382     {
1383       if(DEBUGMEM>1) pari_warn(warnmem,"RgX_RgXY_ResBound i = %ld",i);
1384       b = gerepileupto(av2, b);
1385     }
1386   }
1387   bnd = gsqrt(gmul(gpowgs(a, degpol(B)), gpowgs(b, degpol(A))), prec);
1388   return gerepileupto(av, bnd);
1389 }
1390 
1391 /* Interpolate at roots of 1 and use Hadamard bound for univariate resultant:
1392  *   bound = N_2(A)^degpol B N_2(B)^degpol(A),  where
1393  *     N_2(A) = sqrt(sum (N_1(Ai))^2)
1394  * Return e such that Res(A, B) < 2^e */
1395 ulong
ZX_ZXY_ResBound(GEN A,GEN B,GEN dB)1396 ZX_ZXY_ResBound(GEN A, GEN B, GEN dB)
1397 {
1398   pari_sp av = avma;
1399   GEN a = gen_0, b = gen_0;
1400   long i , lA = lg(A), lB = lg(B);
1401   double loga, logb;
1402   for (i=2; i<lA; i++)
1403   {
1404     a = addii(a, sqri(gel(A,i)));
1405     if (gc_needed(av,1))
1406     {
1407       if(DEBUGMEM>1) pari_warn(warnmem,"ZX_ZXY_ResBound i = %ld",i);
1408       a = gerepileupto(av, a);
1409     }
1410   }
1411   loga = dbllog2(a); set_avma(av);
1412   for (i=2; i<lB; i++)
1413   {
1414     GEN t = gel(B,i);
1415     if (typ(t) == t_POL) t = ZX_norml1(t);
1416     b = addii(b, sqri(t));
1417     if (gc_needed(av,1))
1418     {
1419       if(DEBUGMEM>1) pari_warn(warnmem,"ZX_ZXY_ResBound i = %ld",i);
1420       b = gerepileupto(av, b);
1421     }
1422   }
1423   logb = dbllog2(b); if (dB) logb -= 2 * dbllog2(dB);
1424   i = (long)((degpol(B) * loga + degpol(A) * logb) / 2);
1425   return gc_ulong(av, (i <= 0)? 1: 1 + (ulong)i);
1426 }
1427 /* special case B = A' */
1428 static ulong
ZX_discbound(GEN A)1429 ZX_discbound(GEN A)
1430 {
1431   pari_sp av = avma;
1432   GEN a = gen_0, b = gen_0;
1433   long i , lA = lg(A), dA = degpol(A);
1434   double loga, logb;
1435   for (i = 2; i < lA; i++)
1436   {
1437     GEN c = sqri(gel(A,i));
1438     a = addii(a, c);
1439     if (i > 2) b = addii(b, mulii(c, sqru(i-2)));
1440     if (gc_needed(av,1))
1441     {
1442       if(DEBUGMEM>1) pari_warn(warnmem,"ZX_discbound i = %ld",i);
1443       gerepileall(av, 2, &a, &b);
1444     }
1445   }
1446   loga = dbllog2(a);
1447   logb = dbllog2(b); set_avma(av);
1448   i = (long)(((dA-1) * loga + dA * logb) / 2);
1449   return (i <= 0)? 1: 1 + (ulong)i;
1450 }
1451 
1452 /* return Res(a(Y), b(n,Y)) over Fp. la = leading_coeff(a) [for efficiency] */
1453 static ulong
Flx_FlxY_eval_resultant(GEN a,GEN b,ulong n,ulong p,ulong la)1454 Flx_FlxY_eval_resultant(GEN a, GEN b, ulong n, ulong p, ulong la)
1455 {
1456   GEN ev = FlxY_evalx(b, n, p);
1457   long drop = lg(b) - lg(ev);
1458   ulong r = Flx_resultant(a, ev, p);
1459   if (drop && la != 1) r = Fl_mul(r, Fl_powu(la, drop,p),p);
1460   return r;
1461 }
1462 static GEN
FpX_FpXY_eval_resultant(GEN a,GEN b,GEN n,GEN p,GEN la,long db,long vX)1463 FpX_FpXY_eval_resultant(GEN a, GEN b, GEN n, GEN p, GEN la, long db, long vX)
1464 {
1465   GEN ev = FpXY_evaly(b, n, p, vX);
1466   long drop = db-degpol(ev);
1467   GEN r = FpX_resultant(a, ev, p);
1468   if (drop && !gequal1(la)) r = Fp_mul(r, Fp_powu(la, drop,p),p);
1469   return r;
1470 }
1471 
1472 /* assume dres := deg(Res_X(a,b), Y) <= deg(a,X) * deg(b,Y) < p */
1473 /* Return a Fly */
1474 static GEN
Flx_FlxY_resultant_polint(GEN a,GEN b,ulong p,long dres,long sx)1475 Flx_FlxY_resultant_polint(GEN a, GEN b, ulong p, long dres, long sx)
1476 {
1477   long i;
1478   ulong n, la = Flx_lead(a);
1479   GEN  x = cgetg(dres+2, t_VECSMALL);
1480   GEN  y = cgetg(dres+2, t_VECSMALL);
1481  /* Evaluate at dres+ 1 points: 0 (if dres even) and +/- n, so that P_n(X) =
1482   * P_{-n}(-X), where P_i is Lagrange polynomial: P_i(j) = delta_{i,j} */
1483   for (i=0,n = 1; i < dres; n++)
1484   {
1485     x[++i] = n;   y[i] = Flx_FlxY_eval_resultant(a,b, x[i], p,la);
1486     x[++i] = p-n; y[i] = Flx_FlxY_eval_resultant(a,b, x[i], p,la);
1487   }
1488   if (i == dres)
1489   {
1490     x[++i] = 0;   y[i] = Flx_FlxY_eval_resultant(a,b, x[i], p,la);
1491   }
1492   return Flv_polint(x,y, p, sx);
1493 }
1494 
1495 static GEN
FlxX_pseudorem(GEN x,GEN y,ulong p)1496 FlxX_pseudorem(GEN x, GEN y, ulong p)
1497 {
1498   long vx = varn(x), dx, dy, dz, i, lx, dp;
1499   pari_sp av = avma, av2;
1500 
1501   if (!signe(y)) pari_err_INV("FlxX_pseudorem",y);
1502   (void)new_chunk(2);
1503   dx=degpol(x); x = RgX_recip_shallow(x)+2;
1504   dy=degpol(y); y = RgX_recip_shallow(y)+2; dz=dx-dy; dp = dz+1;
1505   av2 = avma;
1506   for (;;)
1507   {
1508     gel(x,0) = Flx_neg(gel(x,0), p); dp--;
1509     for (i=1; i<=dy; i++)
1510       gel(x,i) = Flx_add( Flx_mul(gel(y,0), gel(x,i), p),
1511                               Flx_mul(gel(x,0), gel(y,i), p), p );
1512     for (   ; i<=dx; i++)
1513       gel(x,i) = Flx_mul(gel(y,0), gel(x,i), p);
1514     do { x++; dx--; } while (dx >= 0 && lg(gel(x,0))==2);
1515     if (dx < dy) break;
1516     if (gc_needed(av2,1))
1517     {
1518       if(DEBUGMEM>1) pari_warn(warnmem,"FlxX_pseudorem dx = %ld >= %ld",dx,dy);
1519       gerepilecoeffs(av2,x,dx+1);
1520     }
1521   }
1522   if (dx < 0) return zero_Flx(0);
1523   lx = dx+3; x -= 2;
1524   x[0]=evaltyp(t_POL) | evallg(lx);
1525   x[1]=evalsigne(1) | evalvarn(vx);
1526   x = RgX_recip_shallow(x);
1527   if (dp)
1528   { /* multiply by y[0]^dp   [beware dummy vars from FpX_FpXY_resultant] */
1529     GEN t = Flx_powu(gel(y,0), dp, p);
1530     for (i=2; i<lx; i++)
1531       gel(x,i) = Flx_mul(gel(x,i), t, p);
1532   }
1533   return gerepilecopy(av, x);
1534 }
1535 
1536 /* return a Flx */
1537 GEN
FlxX_resultant(GEN u,GEN v,ulong p,long sx)1538 FlxX_resultant(GEN u, GEN v, ulong p, long sx)
1539 {
1540   pari_sp av = avma, av2;
1541   long degq,dx,dy,du,dv,dr,signh;
1542   GEN z,g,h,r,p1;
1543 
1544   dx=degpol(u); dy=degpol(v); signh=1;
1545   if (dx < dy)
1546   {
1547     swap(u,v); lswap(dx,dy);
1548     if (both_odd(dx, dy)) signh = -signh;
1549   }
1550   if (dy < 0) return zero_Flx(sx);
1551   if (dy==0) return gerepileupto(av, Flx_powu(gel(v,2),dx,p));
1552 
1553   g = h = pol1_Flx(sx); av2 = avma;
1554   for(;;)
1555   {
1556     r = FlxX_pseudorem(u,v,p); dr = lg(r);
1557     if (dr == 2) { set_avma(av); return zero_Flx(sx); }
1558     du = degpol(u); dv = degpol(v); degq = du-dv;
1559     u = v; p1 = g; g = leading_coeff(u);
1560     switch(degq)
1561     {
1562       case 0: break;
1563       case 1:
1564         p1 = Flx_mul(h,p1, p); h = g; break;
1565       default:
1566         p1 = Flx_mul(Flx_powu(h,degq,p), p1, p);
1567         h = Flx_div(Flx_powu(g,degq,p), Flx_powu(h,degq-1,p), p);
1568     }
1569     if (both_odd(du,dv)) signh = -signh;
1570     v = FlxY_Flx_div(r, p1, p);
1571     if (dr==3) break;
1572     if (gc_needed(av2,1))
1573     {
1574       if(DEBUGMEM>1) pari_warn(warnmem,"FlxX_resultant, dr = %ld",dr);
1575       gerepileall(av2,4, &u, &v, &g, &h);
1576     }
1577   }
1578   z = gel(v,2);
1579   if (dv > 1) z = Flx_div(Flx_powu(z,dv,p), Flx_powu(h,dv-1,p), p);
1580   if (signh < 0) z = Flx_neg(z,p);
1581   return gerepileupto(av, z);
1582 }
1583 
1584 /* Warning:
1585  * This function switches between valid and invalid variable ordering*/
1586 
1587 static GEN
FlxY_to_FlyX(GEN b,long sv)1588 FlxY_to_FlyX(GEN b, long sv)
1589 {
1590   long i, n=-1;
1591   long sw = b[1]&VARNBITS;
1592   for(i=2;i<lg(b);i++) n = maxss(n,lgpol(gel(b,i)));
1593   return Flm_to_FlxX(Flm_transpose(FlxX_to_Flm(b,n)),sv,sw);
1594 }
1595 
1596 /* Return a Fly*/
1597 GEN
Flx_FlxY_resultant(GEN a,GEN b,ulong pp)1598 Flx_FlxY_resultant(GEN a, GEN b, ulong pp)
1599 {
1600   pari_sp ltop=avma;
1601   long dres = degpol(a)*degpol(b);
1602   long sx=a[1], sy=b[1]&VARNBITS;
1603   GEN z;
1604   b = FlxY_to_FlyX(b,sx);
1605   if ((ulong)dres >= pp)
1606     z = FlxX_resultant(Fly_to_FlxY(a, sy), b, pp, sx);
1607   else
1608     z = Flx_FlxY_resultant_polint(a, b, pp, (ulong)dres, sy);
1609   return gerepileupto(ltop,z);
1610 }
1611 
1612 /* return a t_POL (in variable v >= 0) whose coeffs are the coeffs of b,
1613  * in variable v. This is an incorrect PARI object if initially varn(b) << v.
1614  * We could return a vector of coeffs, but it is convenient to have degpol()
1615  * and friends available. Even in that case, it will behave nicely with all
1616  * functions treating a polynomial as a vector of coeffs (eg poleval).
1617  * FOR INTERNAL USE! */
1618 GEN
swap_vars(GEN b0,long v)1619 swap_vars(GEN b0, long v)
1620 {
1621   long i, n = RgX_degree(b0, v);
1622   GEN b, x;
1623   if (n < 0) return pol_0(v);
1624   b = cgetg(n+3, t_POL); x = b + 2;
1625   b[1] = evalsigne(1) | evalvarn(v);
1626   for (i=0; i<=n; i++) gel(x,i) = polcoef_i(b0, i, v);
1627   return b;
1628 }
1629 
1630 /* assume varn(b) << varn(a) */
1631 /* return a FpY*/
1632 GEN
FpX_FpXY_resultant(GEN a,GEN b,GEN p)1633 FpX_FpXY_resultant(GEN a, GEN b, GEN p)
1634 {
1635   long i,n,dres, db, vY = varn(b), vX = varn(a);
1636   GEN la,x,y;
1637 
1638   if (lgefint(p) == 3)
1639   {
1640     ulong pp = uel(p,2);
1641     b = ZXX_to_FlxX(b, pp, vX);
1642     a = ZX_to_Flx(a, pp);
1643     x = Flx_FlxY_resultant(a, b, pp);
1644     return Flx_to_ZX(x);
1645   }
1646   db = RgXY_degreex(b);
1647   dres = degpol(a)*degpol(b);
1648   la = leading_coeff(a);
1649   x = cgetg(dres+2, t_VEC);
1650   y = cgetg(dres+2, t_VEC);
1651  /* Evaluate at dres+ 1 points: 0 (if dres even) and +/- n, so that P_n(X) =
1652   * P_{-n}(-X), where P_i is Lagrange polynomial: P_i(j) = delta_{i,j} */
1653   for (i=0,n = 1; i < dres; n++)
1654   {
1655     gel(x,++i) = utoipos(n);
1656     gel(y,i) = FpX_FpXY_eval_resultant(a,b,gel(x,i),p,la,db,vY);
1657     gel(x,++i) = subiu(p,n);
1658     gel(y,i) = FpX_FpXY_eval_resultant(a,b,gel(x,i),p,la,db,vY);
1659   }
1660   if (i == dres)
1661   {
1662     gel(x,++i) = gen_0;
1663     gel(y,i) = FpX_FpXY_eval_resultant(a,b, gel(x,i), p,la,db,vY);
1664   }
1665   return FpV_polint(x,y, p, vY);
1666 }
1667 
1668 static GEN
FpX_composedsum(GEN P,GEN Q,GEN p)1669 FpX_composedsum(GEN P, GEN Q, GEN p)
1670 {
1671   long n = 1+ degpol(P)*degpol(Q);
1672   GEN Pl = FpX_invLaplace(FpX_Newton(P,n,p), p);
1673   GEN Ql = FpX_invLaplace(FpX_Newton(Q,n,p), p);
1674   GEN L = FpX_Laplace(FpXn_mul(Pl, Ql, n, p), p);
1675   GEN lead = Fp_mul(Fp_powu(leading_coeff(P),degpol(Q), p),
1676                     Fp_powu(leading_coeff(Q),degpol(P), p), p);
1677   GEN R = FpX_fromNewton(L, p);
1678   return FpX_Fp_mul(R, lead, p);
1679 }
1680 
1681 #if 0
1682 GEN
1683 FpX_composedprod(GEN P, GEN Q, GEN p)
1684 {
1685   long n = 1+ degpol(P)*degpol(Q);
1686   GEN L=FpX_convol(FpX_Newton(P,n,p), FpX_Newton(Q,n,p), p);
1687   return FpX_fromNewton(L, p);
1688 }
1689 #endif
1690 
1691 GEN
FpX_direct_compositum(GEN a,GEN b,GEN p)1692 FpX_direct_compositum(GEN a, GEN b, GEN p)
1693 {
1694   if (lgefint(p)==3)
1695   {
1696     pari_sp av = avma;
1697     ulong pp = p[2];
1698     GEN z = Flx_direct_compositum(ZX_to_Flx(a, pp), ZX_to_Flx(b, pp), pp);
1699     return gerepileupto(av, Flx_to_ZX(z));
1700   }
1701   return FpX_composedsum(a, b, p);
1702 }
1703 
1704 static GEN
_FpX_direct_compositum(void * E,GEN a,GEN b)1705 _FpX_direct_compositum(void *E, GEN a, GEN b)
1706 { return FpX_direct_compositum(a,b, (GEN)E); }
1707 
1708 GEN
FpXV_direct_compositum(GEN V,GEN p)1709 FpXV_direct_compositum(GEN V, GEN p)
1710 {
1711   if (lgefint(p)==3)
1712   {
1713     ulong pp = p[2];
1714     return Flx_to_ZX(FlxV_direct_compositum(ZXV_to_FlxV(V, pp), pp));
1715   }
1716   return gen_product(V, (void *)p, &_FpX_direct_compositum);
1717 }
1718 
1719 /* 0, 1, -1, 2, -2, ... */
1720 #define next_lambda(a) (a>0 ? -a : 1-a)
1721 GEN
FpX_compositum(GEN a,GEN b,GEN p)1722 FpX_compositum(GEN a, GEN b, GEN p)
1723 {
1724   long k, v = fetch_var_higher();
1725   for (k = 1;; k = next_lambda(k))
1726   {
1727     GEN x = deg1pol_shallow(gen_1, gmulsg(k, pol_x(v)), 0); /* x + k y */
1728     GEN C = FpX_FpXY_resultant(a, poleval(b,x),p);
1729     if (FpX_is_squarefree(C, p)) { (void)delete_var(); return C; }
1730   }
1731 }
1732 
1733 /* Assume A in Z[Y], B in Q[Y][X], B squarefree in (Q[Y]/(A))[X] and
1734  * Res_Y(A, B) in Z[X]. Find a small lambda (start from *lambda, use
1735  * next_lambda successively) such that C(X) = Res_Y(A(Y), B(X + lambda Y))
1736  * is squarefree, reset *lambda to the chosen value and return C. Set LERS to
1737  * the Last nonconstant polynomial in the Euclidean Remainder Sequence */
1738 static GEN
ZX_ZXY_resultant_LERS(GEN A,GEN B0,long * plambda,GEN * LERS)1739 ZX_ZXY_resultant_LERS(GEN A, GEN B0, long *plambda, GEN *LERS)
1740 {
1741   ulong bound, dp;
1742   pari_sp av = avma, av2 = 0;
1743   long lambda = *plambda, degA = degpol(A), dres = degA*degpol(B0);
1744   long stable, checksqfree, i,n, cnt, degB;
1745   long v, vX = varn(B0), vY = varn(A); /* vY < vX */
1746   GEN x, y, dglist, B, q, a, b, ev, H, H0, H1, Hp, H0p, H1p, C0, C1;
1747   forprime_t S;
1748 
1749   if (degA == 1)
1750   {
1751     GEN a1 = gel(A,3), a0 = gel(A,2);
1752     B = lambda? RgX_translate(B0, monomial(stoi(lambda), 1, vY)): B0;
1753     H = gsubst(B, vY, gdiv(gneg(a0),a1));
1754    if (!equali1(a1)) H = RgX_Rg_mul(H, powiu(a1, poldegree(B,vY)));
1755     *LERS = mkvec2(scalarpol_shallow(a0,vX), scalarpol_shallow(a1,vX));
1756     gerepileall(av, 2, &H, LERS);
1757     return H;
1758   }
1759 
1760   dglist = Hp = H0p = H1p = C0 = C1 = NULL; /* gcc -Wall */
1761   C0 = cgetg(dres+2, t_VECSMALL);
1762   C1 = cgetg(dres+2, t_VECSMALL);
1763   dglist = cgetg(dres+1, t_VECSMALL);
1764   x = cgetg(dres+2, t_VECSMALL);
1765   y = cgetg(dres+2, t_VECSMALL);
1766   B0 = leafcopy(B0);
1767   A = leafcopy(A);
1768   B = B0;
1769   v = fetch_var_higher(); setvarn(A,v);
1770   /* make sure p large enough */
1771 INIT:
1772   /* always except the first time */
1773   if (av2) { set_avma(av2); lambda = next_lambda(lambda); }
1774   if (lambda) B = RgX_translate(B0, monomial(stoi(lambda), 1, vY));
1775   B = swap_vars(B, vY); setvarn(B,v);
1776   /* B0(lambda v + x, v) */
1777   if (DEBUGLEVEL>4) err_printf("Trying lambda = %ld\n", lambda);
1778   av2 = avma;
1779 
1780   if (degA <= 3)
1781   { /* sub-resultant faster for small degrees */
1782     H = RgX_resultant_all(A,B,&q);
1783     if (typ(q) != t_POL || degpol(q)!=1) goto INIT;
1784     H0 = gel(q,2);
1785     if (typ(H0) == t_POL) setvarn(H0,vX); else H0 = scalarpol(H0,vX);
1786     H1 = gel(q,3);
1787     if (typ(H1) == t_POL) setvarn(H1,vX); else H1 = scalarpol(H1,vX);
1788     if (!ZX_is_squarefree(H)) goto INIT;
1789     goto END;
1790   }
1791 
1792   H = H0 = H1 = NULL;
1793   degB = degpol(B);
1794   bound = ZX_ZXY_ResBound(A, B, NULL);
1795   if (DEBUGLEVEL>4) err_printf("bound for resultant coeffs: 2^%ld\n",bound);
1796   dp = 1;
1797   init_modular_big(&S);
1798   for(cnt = 0, checksqfree = 1;;)
1799   {
1800     ulong p = u_forprime_next(&S);
1801     GEN Hi;
1802     a = ZX_to_Flx(A, p);
1803     b = ZXX_to_FlxX(B, p, varn(A));
1804     if (degpol(a) < degA || degpol(b) < degB) continue; /* p | lc(A)lc(B) */
1805     if (checksqfree)
1806     { /* find degree list for generic Euclidean Remainder Sequence */
1807       long goal = minss(degpol(a), degpol(b)); /* longest possible */
1808       for (n=1; n <= goal; n++) dglist[n] = 0;
1809       setlg(dglist, 1);
1810       for (n=0; n <= dres; n++)
1811       {
1812         ev = FlxY_evalx_drop(b, n, p);
1813         Flx_resultant_set_dglist(a, ev, dglist, p);
1814         if (lg(dglist)-1 == goal) break;
1815       }
1816       /* last pol in ERS has degree > 1 ? */
1817       goal = lg(dglist)-1;
1818       if (degpol(B) == 1) { if (!goal) goto INIT; }
1819       else
1820       {
1821         if (goal <= 1) goto INIT;
1822         if (dglist[goal] != 0 || dglist[goal-1] != 1) goto INIT;
1823       }
1824       if (DEBUGLEVEL>4)
1825         err_printf("Degree list for ERS (trials: %ld) = %Ps\n",n+1,dglist);
1826     }
1827 
1828     for (i=0,n = 0; i <= dres; n++)
1829     {
1830       ev = FlxY_evalx_drop(b, n, p);
1831       x[++i] = n; y[i] = Flx_resultant_all(a, ev, C0+i, C1+i, dglist, p);
1832       if (!C1[i]) i--; /* C1(i) = 0. No way to recover C0(i) */
1833     }
1834     Hi = Flv_Flm_polint(x, mkvec3(y,C0,C1), p, 0);
1835     Hp = gel(Hi,1); H0p = gel(Hi,2); H1p = gel(Hi,3);
1836     if (!H && degpol(Hp) != dres) continue;
1837     if (dp != 1) Hp = Flx_Fl_mul(Hp, Fl_powu(Fl_inv(dp,p), degA, p), p);
1838     if (checksqfree) {
1839       if (!Flx_is_squarefree(Hp, p)) goto INIT;
1840       if (DEBUGLEVEL>4) err_printf("Final lambda = %ld\n", lambda);
1841       checksqfree = 0;
1842     }
1843 
1844     if (!H)
1845     { /* initialize */
1846       q = utoipos(p); stable = 0;
1847       H = ZX_init_CRT(Hp, p,vX);
1848       H0= ZX_init_CRT(H0p, p,vX);
1849       H1= ZX_init_CRT(H1p, p,vX);
1850     }
1851     else
1852     {
1853       GEN qp = muliu(q,p);
1854       stable  = ZX_incremental_CRT_raw(&H, Hp, q,qp, p)
1855               & ZX_incremental_CRT_raw(&H0,H0p, q,qp, p)
1856               & ZX_incremental_CRT_raw(&H1,H1p, q,qp, p);
1857       q = qp;
1858     }
1859     /* could make it probabilistic for H ? [e.g if stable twice, etc]
1860      * Probabilistic anyway for H0, H1 */
1861     if (DEBUGLEVEL>5 && (stable ||  ++cnt==100))
1862     { cnt=0; err_printf("%ld%%%s ",100*expi(q)/bound,stable?"s":""); }
1863     if (stable && (ulong)expi(q) >= bound) break; /* DONE */
1864     if (gc_needed(av,2))
1865     {
1866       if (DEBUGMEM>1) pari_warn(warnmem,"ZX_ZXY_rnfequation");
1867       gerepileall(av2, 4, &H, &q, &H0, &H1);
1868     }
1869   }
1870 END:
1871   if (DEBUGLEVEL>5) err_printf(" done\n");
1872   setvarn(H, vX); (void)delete_var();
1873   *LERS = mkvec2(H0,H1);
1874   gerepileall(av, 2, &H, LERS);
1875   *plambda = lambda; return H;
1876 }
1877 
1878 GEN
ZX_ZXY_resultant_all(GEN A,GEN B,long * plambda,GEN * LERS)1879 ZX_ZXY_resultant_all(GEN A, GEN B, long *plambda, GEN *LERS)
1880 {
1881   if (LERS)
1882   {
1883     if (!plambda)
1884       pari_err_BUG("ZX_ZXY_resultant_all [LERS != NULL needs lambda]");
1885     return ZX_ZXY_resultant_LERS(A, B, plambda, LERS);
1886   }
1887   return ZX_ZXY_rnfequation(A, B, plambda);
1888 }
1889 
1890 /* If lambda = NULL, return caract(Mod(A, T)), T,A in Z[X].
1891  * Otherwise find a small lambda such that caract (Mod(A + lambda X, T)) is
1892  * squarefree */
1893 GEN
ZXQ_charpoly_sqf(GEN A,GEN T,long * lambda,long v)1894 ZXQ_charpoly_sqf(GEN A, GEN T, long *lambda, long v)
1895 {
1896   pari_sp av = avma;
1897   GEN R, a;
1898   long dA;
1899   int delvar;
1900 
1901   if (v < 0) v = 0;
1902   switch (typ(A))
1903   {
1904     case t_POL: dA = degpol(A); if (dA > 0) break;
1905       A = constant_coeff(A);
1906     default:
1907       if (lambda) { A = scalar_ZX_shallow(A,varn(T)); dA = 0; break;}
1908       return gerepileupto(av, gpowgs(gsub(pol_x(v), A), degpol(T)));
1909   }
1910   delvar = 0;
1911   if (varn(T) == 0)
1912   {
1913     long v0 = fetch_var(); delvar = 1;
1914     T = leafcopy(T); setvarn(T,v0);
1915     A = leafcopy(A); setvarn(A,v0);
1916   }
1917   R = ZX_ZXY_rnfequation(T, deg1pol_shallow(gen_1, gneg_i(A), 0), lambda);
1918   if (delvar) (void)delete_var();
1919   setvarn(R, v); a = leading_coeff(T);
1920   if (!gequal1(a)) R = gdiv(R, powiu(a, dA));
1921   return gerepileupto(av, R);
1922 }
1923 
1924 /* charpoly(Mod(A,T)), A may be in Q[X], but assume T and result are integral */
1925 GEN
ZXQ_charpoly(GEN A,GEN T,long v)1926 ZXQ_charpoly(GEN A, GEN T, long v)
1927 {
1928   return (degpol(T) < 16) ? RgXQ_charpoly(A,T,v): ZXQ_charpoly_sqf(A,T, NULL, v);
1929 }
1930 
1931 GEN
QXQ_charpoly(GEN A,GEN T,long v)1932 QXQ_charpoly(GEN A, GEN T, long v)
1933 {
1934   pari_sp av = avma;
1935   GEN den, B = Q_remove_denom(A, &den);
1936   GEN P = ZXQ_charpoly(B, T, v);
1937   return gerepilecopy(av, den ? RgX_rescale(P, ginv(den)): P);
1938 }
1939 
1940 static ulong
ZX_resultant_prime(GEN a,GEN b,GEN dB,long degA,long degB,ulong p)1941 ZX_resultant_prime(GEN a, GEN b, GEN dB, long degA, long degB, ulong p)
1942 {
1943   long dropa = degA - degpol(a), dropb = degB - degpol(b);
1944   ulong H, dp;
1945   if (dropa && dropb) return 0; /* p | lc(A), p | lc(B) */
1946   H = Flx_resultant(a, b, p);
1947   if (dropa)
1948   { /* multiply by ((-1)^deg B lc(B))^(deg A - deg a) */
1949     ulong c = b[degB+2]; /* lc(B) */
1950     if (odd(degB)) c = p - c;
1951     c = Fl_powu(c, dropa, p);
1952     if (c != 1) H = Fl_mul(H, c, p);
1953   }
1954   else if (dropb)
1955   { /* multiply by lc(A)^(deg B - deg b) */
1956     ulong c = a[degA+2]; /* lc(A) */
1957     c = Fl_powu(c, dropb, p);
1958     if (c != 1) H = Fl_mul(H, c, p);
1959   }
1960   dp = dB ? umodiu(dB, p): 1;
1961   if (dp != 1) H = Fl_mul(H, Fl_powu(Fl_inv(dp,p), degA, p), p);
1962   return H;
1963 }
1964 
1965 /* If B=NULL, assume B=A' */
1966 static GEN
ZX_resultant_slice(GEN A,GEN B,GEN dB,GEN P,GEN * mod)1967 ZX_resultant_slice(GEN A, GEN B, GEN dB, GEN P, GEN *mod)
1968 {
1969   pari_sp av = avma, av2;
1970   long degA, degB, i, n = lg(P)-1;
1971   GEN H, T;
1972 
1973   degA = degpol(A);
1974   degB = B? degpol(B): degA - 1;
1975   if (n == 1)
1976   {
1977     ulong Hp, p = uel(P,1);
1978     GEN a = ZX_to_Flx(A, p), b = B? ZX_to_Flx(B, p): Flx_deriv(a, p);
1979     Hp = ZX_resultant_prime(a, b, dB, degA, degB, p);
1980     set_avma(av); *mod = utoipos(p); return utoi(Hp);
1981   }
1982   T = ZV_producttree(P);
1983   A = ZX_nv_mod_tree(A, P, T);
1984   if (B) B = ZX_nv_mod_tree(B, P, T);
1985   H = cgetg(n+1, t_VECSMALL); av2 = avma;
1986   for(i=1; i <= n; i++, set_avma(av2))
1987   {
1988     ulong p = P[i];
1989     GEN a = gel(A,i), b = B? gel(B,i): Flx_deriv(a, p);
1990     H[i] = ZX_resultant_prime(a, b, dB, degA, degB, p);
1991   }
1992   H = ZV_chinese_tree(H, P, T, ZV_chinesetree(P,T));
1993   *mod = gmael(T, lg(T)-1, 1);
1994   gerepileall(av, 2, &H, mod);
1995   return H;
1996 }
1997 
1998 GEN
ZX_resultant_worker(GEN P,GEN A,GEN B,GEN dB)1999 ZX_resultant_worker(GEN P, GEN A, GEN B, GEN dB)
2000 {
2001   GEN V = cgetg(3, t_VEC);
2002   if (typ(B) == t_INT) B = NULL;
2003   if (!signe(dB)) dB = NULL;
2004   gel(V,1) = ZX_resultant_slice(A, B, dB, P, &gel(V,2));
2005   return V;
2006 }
2007 
2008 /* Compute Res(A, B/dB) in Z, assuming A,B in Z[X], dB in Z or NULL (= 1)
2009  * If B=NULL, take B = A' and assume deg A > 1 and 'bound' is set */
2010 GEN
ZX_resultant_all(GEN A,GEN B,GEN dB,ulong bound)2011 ZX_resultant_all(GEN A, GEN B, GEN dB, ulong bound)
2012 {
2013   pari_sp av = avma;
2014   forprime_t S;
2015   GEN  H, worker;
2016   if (B)
2017   {
2018     long a = degpol(A), b = degpol(B);
2019     if (a < 0 || b < 0) return gen_0;
2020     if (!a) return powiu(gel(A,2), b);
2021     if (!b) return powiu(gel(B,2), a);
2022     if (!bound) bound = ZX_ZXY_ResBound(A, B, dB);
2023   }
2024   worker = snm_closure(is_entry("_ZX_resultant_worker"),
2025                        mkvec3(A, B? B: gen_0, dB? dB: gen_0));
2026   init_modular_big(&S);
2027   H = gen_crt("ZX_resultant_all", worker, &S, dB, bound, 0, NULL,
2028               ZV_chinese_center, Fp_center);
2029   return gerepileuptoint(av, H);
2030 }
2031 
2032 /* A0 and B0 in Q[X] */
2033 GEN
QX_resultant(GEN A0,GEN B0)2034 QX_resultant(GEN A0, GEN B0)
2035 {
2036   GEN s, a, b, A, B;
2037   pari_sp av = avma;
2038 
2039   A = Q_primitive_part(A0, &a);
2040   B = Q_primitive_part(B0, &b);
2041   s = ZX_resultant(A, B);
2042   if (!signe(s)) { set_avma(av); return gen_0; }
2043   if (a) s = gmul(s, gpowgs(a,degpol(B)));
2044   if (b) s = gmul(s, gpowgs(b,degpol(A)));
2045   return gerepileupto(av, s);
2046 }
2047 
2048 GEN
ZX_resultant(GEN A,GEN B)2049 ZX_resultant(GEN A, GEN B) { return ZX_resultant_all(A,B,NULL,0); }
2050 
2051 GEN
QXQ_intnorm(GEN A,GEN B)2052 QXQ_intnorm(GEN A, GEN B)
2053 {
2054   GEN c, n, R, lB;
2055   long dA = degpol(A), dB = degpol(B);
2056   pari_sp av = avma;
2057   if (dA < 0) return gen_0;
2058   A = Q_primitive_part(A, &c);
2059   if (!c || typ(c) == t_INT) {
2060     n = c;
2061     R = ZX_resultant(B, A);
2062   } else {
2063     n = gel(c,1);
2064     R = ZX_resultant_all(B, A, gel(c,2), 0);
2065   }
2066   if (n && !equali1(n)) R = mulii(R, powiu(n, dB));
2067   lB = leading_coeff(B);
2068   if (!equali1(lB)) R = diviiexact(R, powiu(lB, dA));
2069   return gerepileuptoint(av, R);
2070 }
2071 
2072 GEN
QXQ_norm(GEN A,GEN B)2073 QXQ_norm(GEN A, GEN B)
2074 {
2075   GEN c, R, lB;
2076   long dA = degpol(A), dB = degpol(B);
2077   pari_sp av = avma;
2078   if (dA < 0) return gen_0;
2079   A = Q_primitive_part(A, &c);
2080   R = ZX_resultant(B, A);
2081   if (c) R = gmul(R, gpowgs(c, dB));
2082   lB = leading_coeff(B);
2083   if (!equali1(lB)) R = gdiv(R, gpowgs(lB, dA));
2084   return gerepileupto(av, R);
2085 }
2086 
2087 /* assume x has integral coefficients */
2088 GEN
ZX_disc_all(GEN x,ulong bound)2089 ZX_disc_all(GEN x, ulong bound)
2090 {
2091   pari_sp av = avma;
2092   long s, d = degpol(x);
2093   GEN l, R;
2094 
2095   if (d <= 1) return d == 1? gen_1: gen_0;
2096   s = (d & 2) ? -1: 1;
2097   l = leading_coeff(x);
2098   if (!bound) bound = ZX_discbound(x);
2099   R = ZX_resultant_all(x, NULL, NULL, bound);
2100   if (is_pm1(l))
2101   { if (signe(l) < 0) s = -s; }
2102   else
2103     R = diviiexact(R,l);
2104   if (s == -1) togglesign_safe(&R);
2105   return gerepileuptoint(av,R);
2106 }
2107 
2108 GEN
ZX_disc(GEN x)2109 ZX_disc(GEN x) { return ZX_disc_all(x,0); }
2110 
2111 static GEN
ZXQX_resultant_prime(GEN a,GEN b,GEN dB,long degA,long degB,GEN T,ulong p)2112 ZXQX_resultant_prime(GEN a, GEN b, GEN dB, long degA, long degB, GEN T, ulong p)
2113 {
2114   long dropa = degA - degpol(a), dropb = degB - degpol(b);
2115   GEN H, dp;
2116   if (dropa && dropb) return pol0_Flx(T[1]); /* p | lc(A), p | lc(B) */
2117   H = FlxqX_saferesultant(a, b, T, p);
2118   if (!H) return NULL;
2119   if (dropa)
2120   { /* multiply by ((-1)^deg B lc(B))^(deg A - deg a) */
2121     GEN c = gel(b,degB+2); /* lc(B) */
2122     if (odd(degB)) c = Flx_neg(c, p);
2123     c = Flxq_powu(c, dropa, T, p);
2124     if (!Flx_equal1(c)) H = Flxq_mul(H, c, T, p);
2125   }
2126   else if (dropb)
2127   { /* multiply by lc(A)^(deg B - deg b) */
2128     GEN c = gel(a,degA+2); /* lc(A) */
2129     c = Flxq_powu(c, dropb, T, p);
2130     if (!Flx_equal1(c)) H = Flxq_mul(H, c, T, p);
2131   }
2132   dp = dB ? ZX_to_Flx(dB, p): pol1_Flx(T[1]);
2133   if (!Flx_equal1(dp))
2134   {
2135     GEN idp = Flxq_invsafe(dp, T, p);
2136     if (!idp) return NULL;
2137     H = Flxq_mul(H, Flxq_powu(idp, degA, T, p), T, p);
2138   }
2139   return H;
2140 }
2141 
2142 /* If B=NULL, assume B=A' */
2143 static GEN
ZXQX_resultant_slice(GEN A,GEN B,GEN U,GEN dB,GEN P,GEN * mod)2144 ZXQX_resultant_slice(GEN A, GEN B, GEN U, GEN dB, GEN P, GEN *mod)
2145 {
2146   pari_sp av = avma;
2147   long degA, degB, i, n = lg(P)-1;
2148   GEN H, T;
2149   long v = varn(U), redo = 0;
2150 
2151   degA = degpol(A);
2152   degB = B? degpol(B): degA - 1;
2153   if (n == 1)
2154   {
2155     ulong p = uel(P,1);
2156     GEN a = ZXX_to_FlxX(A, p, v), b = B? ZXX_to_FlxX(B, p, v): FlxX_deriv(a, p);
2157     GEN u = ZX_to_Flx(U, p);
2158     GEN Hp = ZXQX_resultant_prime(a, b, dB, degA, degB, u, p);
2159     if (!Hp) { set_avma(av); *mod = gen_1; return pol_0(v); }
2160     Hp = gerepileupto(av, Flx_to_ZX(Hp)); *mod = utoipos(p); return Hp;
2161   }
2162   T = ZV_producttree(P);
2163   A = ZXX_nv_mod_tree(A, P, T, v);
2164   if (B) B = ZXX_nv_mod_tree(B, P, T, v);
2165   U = ZX_nv_mod_tree(U, P, T);
2166   H = cgetg(n+1, t_VEC);
2167   for(i=1; i <= n; i++)
2168   {
2169     ulong p = P[i];
2170     GEN a = gel(A,i), b = B? gel(B,i): FlxX_deriv(a, p), u = gel(U, i);
2171     GEN h = ZXQX_resultant_prime(a, b, dB, degA, degB, u, p);
2172     if (!h)
2173     {
2174       gel(H,i) = pol_0(v);
2175       P[i] = 1; redo = 1;
2176     }
2177     else
2178       gel(H,i) = h;
2179   }
2180   if (redo) T = ZV_producttree(P);
2181   H = nxV_chinese_center_tree(H, P, T, ZV_chinesetree(P, T));
2182   *mod = gmael(T, lg(T)-1, 1);
2183   gerepileall(av, 2, &H, mod);
2184   return H;
2185 }
2186 
2187 GEN
ZXQX_resultant_worker(GEN P,GEN A,GEN B,GEN T,GEN dB)2188 ZXQX_resultant_worker(GEN P, GEN A, GEN B, GEN T, GEN dB)
2189 {
2190   GEN V = cgetg(3, t_VEC);
2191   if (isintzero(B)) B = NULL;
2192   if (!signe(dB)) dB = NULL;
2193   gel(V,1) = ZXQX_resultant_slice(A, B, T, dB, P, &gel(V,2));
2194   return V;
2195 }
2196 
2197 static ulong
ZXQX_resultant_bound(GEN nf,GEN A,GEN B)2198 ZXQX_resultant_bound(GEN nf, GEN A, GEN B)
2199 {
2200   pari_sp av = avma;
2201   GEN r, M = L2_bound(nf, NULL, &r);
2202   long v = nf_get_varn(nf), i, l = lg(r);
2203   GEN a = cgetg(l, t_COL);
2204   for (i = 1; i < l; i++)
2205     gel(a, i) =  RgX_RgXY_ResBound(gsubst(A, v, gel(r,i)),
2206                                    gsubst(B, v, gel(r,i)), DEFAULTPREC);
2207   return gc_ulong(av, (ulong) dbllog2(gmul(M,RgC_fpnorml2(a, DEFAULTPREC))));
2208 }
2209 
2210 /* Compute Res(A, B/dB) in Z, assuming A,B in Z[X], dB in Z or NULL (= 1)
2211  * If B=NULL, take B = A' and assume deg A > 1 and 'bound' is set */
2212 static GEN
ZXQX_resultant_all(GEN A,GEN B,GEN T,GEN dB,ulong bound)2213 ZXQX_resultant_all(GEN A, GEN B, GEN T, GEN dB, ulong bound)
2214 {
2215   pari_sp av = avma;
2216   forprime_t S;
2217   GEN  H, worker;
2218   if (B)
2219   {
2220     long a = degpol(A), b = degpol(B);
2221     if (a < 0 || b < 0) return gen_0;
2222     if (!a) return gpowgs(gel(A,2), b);
2223     if (!b) return gpowgs(gel(B,2), a);
2224     if (!bound) bound = ZXQX_resultant_bound(nfinit(T, DEFAULTPREC), A, B);
2225   } else
2226     if (!bound) bound = ZXQX_resultant_bound(nfinit(T, DEFAULTPREC), A, RgX_deriv(A));
2227   worker = snm_closure(is_entry("_ZXQX_resultant_worker"),
2228                        mkvec4(A, B? B: gen_0, T, dB? dB: gen_0));
2229   init_modular_big(&S);
2230   H = gen_crt("ZXQX_resultant_all", worker, &S, dB, bound, 0, NULL,
2231               nxV_chinese_center, FpX_center);
2232   if (DEBUGLEVEL)
2233     err_printf("ZXQX_resultant_all: a priori bound: %lu, a posteriori: %lu\n",
2234                bound, expi(gsupnorm(H, DEFAULTPREC)));
2235   return gerepileupto(av, H);
2236 }
2237 
2238 GEN
nfX_resultant(GEN nf,GEN x,GEN y)2239 nfX_resultant(GEN nf, GEN x, GEN y)
2240 {
2241   pari_sp av = avma;
2242   GEN cx, cy, D, T = nf_get_pol(nf);
2243   ulong bound;
2244   long d = degpol(x), v = varn(T);
2245   if (d <= 1) return d == 1? pol_1(v): pol_0(v);
2246   x = Q_primitive_part(x, &cx);
2247   y = Q_primitive_part(y, &cy);
2248   bound = ZXQX_resultant_bound(nf, x, y);
2249   D = ZXQX_resultant_all(x, y, T, NULL, bound);
2250   if (cx) D = gmul(D, gpowgs(cx, degpol(y)));
2251   if (cy) D = gmul(D, gpowgs(cy, degpol(x)));
2252   return gerepileupto(av, D);
2253 }
2254 
2255 static GEN
to_ZX(GEN a,long v)2256 to_ZX(GEN a, long v) { return typ(a)==t_INT? scalarpol(a,v): a; }
2257 
2258 static GEN
ZXQX_disc_all(GEN x,GEN T,ulong bound)2259 ZXQX_disc_all(GEN x, GEN T, ulong bound)
2260 {
2261   pari_sp av = avma;
2262   long s, d = degpol(x), v = varn(T);
2263   GEN l, R;
2264 
2265   if (d <= 1) return d == 1? pol_1(v): pol_0(v);
2266   s = (d & 2) ? -1: 1;
2267   l = leading_coeff(x);
2268   R = ZXQX_resultant_all(x, NULL, T, NULL, bound);
2269   if (!gequal1(l)) R = QXQ_div(R, to_ZX(l,v), T);
2270   if (s == -1) R = RgX_neg(R);
2271   return gerepileupto(av, R);
2272 }
2273 
2274 GEN
QX_disc(GEN x)2275 QX_disc(GEN x)
2276 {
2277   pari_sp av = avma;
2278   GEN c, d = ZX_disc( Q_primitive_part(x, &c) );
2279   if (c) d = gmul(d, gpowgs(c, 2*degpol(x) - 2));
2280   return gerepileupto(av, d);
2281 }
2282 
2283 GEN
nfX_disc(GEN nf,GEN x)2284 nfX_disc(GEN nf, GEN x)
2285 {
2286   pari_sp av = avma;
2287   GEN c, D, T = nf_get_pol(nf);
2288   ulong bound;
2289   long d = degpol(x), v = varn(T);
2290   if (d <= 1) return d == 1? pol_1(v): pol_0(v);
2291   x = Q_primitive_part(x, &c);
2292   bound = ZXQX_resultant_bound(nf, x, RgX_deriv(x));
2293   D = ZXQX_disc_all(x, T, bound);
2294   if (c) D = gmul(D, gpowgs(c, 2*d - 2));
2295   return gerepileupto(av, D);
2296 }
2297 
2298 GEN
QXQ_mul(GEN x,GEN y,GEN T)2299 QXQ_mul(GEN x, GEN y, GEN T)
2300 {
2301   GEN dx, nx = Q_primitive_part(x, &dx);
2302   GEN dy, ny = Q_primitive_part(y, &dy);
2303   GEN z = ZXQ_mul(nx, ny, T);
2304   if (dx || dy)
2305   {
2306     GEN d = dx ? dy ? gmul(dx, dy): dx : dy;
2307     if (!gequal1(d)) z = ZX_Q_mul(z, d);
2308   }
2309   return z;
2310 }
2311 
2312 GEN
QXQ_sqr(GEN x,GEN T)2313 QXQ_sqr(GEN x, GEN T)
2314 {
2315   GEN dx, nx = Q_primitive_part(x, &dx);
2316   GEN z = ZXQ_sqr(nx, T);
2317   if (dx)
2318     z = ZX_Q_mul(z, gsqr(dx));
2319   return z;
2320 }
2321 
2322 static GEN
QXQ_inv_slice(GEN A,GEN B,GEN P,GEN * mod)2323 QXQ_inv_slice(GEN A, GEN B, GEN P, GEN *mod)
2324 {
2325   pari_sp av = avma;
2326   long i, n = lg(P)-1, v = varn(A), redo = 0;
2327   GEN H, T;
2328   if (n == 1)
2329   {
2330     ulong p = uel(P,1);
2331     GEN a = ZX_to_Flx(A, p), b = ZX_to_Flx(B, p);
2332     GEN U = Flxq_invsafe(a, b, p);
2333     if (!U)
2334     {
2335       set_avma(av);
2336       *mod = gen_1; return pol_0(v);
2337     }
2338     H = gerepilecopy(av, Flx_to_ZX(U));
2339     *mod = utoi(p);
2340     return H;
2341   }
2342   T = ZV_producttree(P);
2343   A = ZX_nv_mod_tree(A, P, T);
2344   B = ZX_nv_mod_tree(B, P, T);
2345   H = cgetg(n+1, t_VEC);
2346   for(i=1; i <= n; i++)
2347   {
2348     ulong p = P[i];
2349     GEN a = gel(A,i), b = gel(B,i);
2350     GEN U = Flxq_invsafe(a, b, p);
2351     if (!U)
2352     {
2353       gel(H,i) = pol_0(v);
2354       P[i] = 1; redo = 1;
2355     }
2356     else
2357       gel(H,i) = U;
2358   }
2359   if (redo) T = ZV_producttree(P);
2360   H = nxV_chinese_center_tree(H, P, T, ZV_chinesetree(P, T));
2361   *mod = gmael(T, lg(T)-1, 1);
2362   gerepileall(av, 2, &H, mod);
2363   return H;
2364 }
2365 
2366 GEN
QXQ_inv_worker(GEN P,GEN A,GEN B)2367 QXQ_inv_worker(GEN P, GEN A, GEN B)
2368 {
2369   GEN V = cgetg(3, t_VEC);
2370   gel(V,1) = QXQ_inv_slice(A, B, P, &gel(V,2));
2371   return V;
2372 }
2373 
2374 /* lift(1 / Mod(A,B)). B a ZX, A a scalar or a QX */
2375 GEN
QXQ_inv(GEN A,GEN B)2376 QXQ_inv(GEN A, GEN B)
2377 {
2378   GEN D, Ap, Bp;
2379   ulong pp;
2380   pari_sp av2, av = avma;
2381   forprime_t S;
2382   GEN worker, U, H = NULL, mod = gen_1;
2383   pari_timer ti;
2384   long k, dA, dB;
2385   if (is_scalar_t(typ(A))) return scalarpol(ginv(A), varn(B));
2386   /* A a QX, B a ZX */
2387   A = Q_primitive_part(A, &D);
2388   dA = degpol(A); dB= degpol(B);
2389   /* A, B in Z[X] */
2390   init_modular_small(&S);
2391   do {
2392     pp = u_forprime_next(&S);
2393     Ap = ZX_to_Flx(A, pp);
2394     Bp = ZX_to_Flx(B, pp);
2395   } while (degpol(Ap) != dA || degpol(Bp) != dB);
2396   if (degpol(Flx_gcd(Ap, Bp, pp)) != 0 && degpol(ZX_gcd(A,B))!=0)
2397     pari_err_INV("QXQ_inv",mkpolmod(A,B));
2398   worker = snm_closure(is_entry("_QXQ_inv_worker"), mkvec2(A, B));
2399   av2 = avma;
2400   for (k = 1; ;k *= 2)
2401   {
2402     GEN res, b, N, den;
2403     gen_inccrt_i("QXQ_inv", worker, NULL, (k+1)>>1, 0, &S, &H, &mod,
2404                  nxV_chinese_center, FpX_center);
2405     gerepileall(av2, 2, &H, &mod);
2406     b = sqrti(shifti(mod,-1));
2407     if (DEBUGLEVEL>5) timer_start(&ti);
2408     U = FpX_ratlift(H, mod, b, b, NULL);
2409     if (DEBUGLEVEL>5) timer_printf(&ti,"QXQ_inv: ratlift");
2410     if (!U) continue;
2411     N = Q_remove_denom(U, &den); if (!den) den = gen_1;
2412     res = Flx_rem(Flx_Fl_sub(Flx_mul(Ap, ZX_to_Flx(N,pp), pp),
2413                   umodiu(den, pp), pp), Bp, pp);
2414     if (degpol(res) >= 0) continue;
2415     res = ZX_Z_sub(ZX_mul(A, N), den);
2416     res = ZX_is_monic(B) ? ZX_rem(res, B): RgX_pseudorem(res, B);
2417     if (DEBUGLEVEL>5) timer_printf(&ti,"QXQ_inv: final check");
2418     if (degpol(res)<0)
2419     {
2420       if (D) U = RgX_Rg_div(U, D);
2421       return gerepilecopy(av, U);
2422     }
2423   }
2424 }
2425 
2426 static GEN
QXQ_div_slice(GEN A,GEN B,GEN C,GEN P,GEN * mod)2427 QXQ_div_slice(GEN A, GEN B, GEN C, GEN P, GEN *mod)
2428 {
2429   pari_sp av = avma;
2430   long i, n = lg(P)-1, v = varn(A), redo = 0;
2431   GEN H, T;
2432   if (n == 1)
2433   {
2434     ulong p = uel(P,1);
2435     GEN a = ZX_to_Flx(A, p), b = ZX_to_Flx(B, p), c = ZX_to_Flx(C, p);
2436     GEN bi = Flxq_invsafe(b, c, p), U;
2437     if (!bi)
2438     {
2439       set_avma(av);
2440       *mod = gen_1; return pol_0(v);
2441     }
2442     U = Flxq_mul(a, bi, c, p);
2443     H = gerepilecopy(av, Flx_to_ZX(U));
2444     *mod = utoi(p);
2445     return H;
2446   }
2447   T = ZV_producttree(P);
2448   A = ZX_nv_mod_tree(A, P, T);
2449   B = ZX_nv_mod_tree(B, P, T);
2450   C = ZX_nv_mod_tree(C, P, T);
2451   H = cgetg(n+1, t_VEC);
2452   for(i=1; i <= n; i++)
2453   {
2454     ulong p = P[i];
2455     GEN a = gel(A,i), b = gel(B,i), c = gel(C, i);
2456     GEN bi = Flxq_invsafe(b, c, p);
2457     if (!bi)
2458     {
2459       gel(H,i) = pol_0(v);
2460       P[i] = 1; redo = 1;
2461     }
2462     else
2463       gel(H,i) = Flxq_mul(a, bi, c, p);
2464   }
2465   if (redo) T = ZV_producttree(P);
2466   H = nxV_chinese_center_tree(H, P, T, ZV_chinesetree(P, T));
2467   *mod = gmael(T, lg(T)-1, 1);
2468   gerepileall(av, 2, &H, mod);
2469   return H;
2470 }
2471 
2472 GEN
QXQ_div_worker(GEN P,GEN A,GEN B,GEN C)2473 QXQ_div_worker(GEN P, GEN A, GEN B, GEN C)
2474 {
2475   GEN V = cgetg(3, t_VEC);
2476   gel(V,1) = QXQ_div_slice(A, B, C, P, &gel(V,2));
2477   return V;
2478 }
2479 
2480 /* lift(Mod(A/B, C)). C a ZX, A, B a scalar or a QX */
2481 GEN
QXQ_div(GEN A,GEN B,GEN C)2482 QXQ_div(GEN A, GEN B, GEN C)
2483 {
2484   GEN DA, DB, Ap, Bp, Cp;
2485   ulong pp;
2486   pari_sp av2, av = avma;
2487   forprime_t S;
2488   GEN worker, U, H = NULL, mod = gen_1;
2489   pari_timer ti;
2490   long k, dA, dB, dC;
2491   if (is_scalar_t(typ(A))) return scalarpol(ginv(A), varn(B));
2492   /* A a QX, B a ZX */
2493   A = Q_primitive_part(A, &DA);
2494   B = Q_primitive_part(B, &DB);
2495   dA = degpol(A); dB = degpol(B); dC = degpol(C);
2496   /* A, B in Z[X] */
2497   init_modular_small(&S);
2498   do {
2499     pp = u_forprime_next(&S);
2500     Ap = ZX_to_Flx(A, pp);
2501     Bp = ZX_to_Flx(B, pp);
2502     Cp = ZX_to_Flx(C, pp);
2503   } while (degpol(Ap) != dA || degpol(Bp) != dB || degpol(Cp) != dC);
2504   if (degpol(Flx_gcd(Bp, Cp, pp)) != 0 && degpol(ZX_gcd(B,C))!=0)
2505     pari_err_INV("QXQ_div",mkpolmod(B,C));
2506   worker = snm_closure(is_entry("_QXQ_div_worker"), mkvec3(A, B, C));
2507   av2 = avma;
2508   for (k = 1; ;k *= 2)
2509   {
2510     GEN res, b, N, den;
2511     gen_inccrt_i("QXQ_div", worker, NULL, (k+1)>>1, 0, &S, &H, &mod,
2512                  nxV_chinese_center, FpX_center);
2513     gerepileall(av2, 2, &H, &mod);
2514     b = sqrti(shifti(mod,-1));
2515     if (DEBUGLEVEL>5) timer_start(&ti);
2516     U = FpX_ratlift(H, mod, b, b, NULL);
2517     if (DEBUGLEVEL>5) timer_printf(&ti,"QXQ_div: ratlift");
2518     if (!U) continue;
2519     N = Q_remove_denom(U, &den); if (!den) den = gen_1;
2520     res = Flx_rem(Flx_sub(Flx_mul(Bp, ZX_to_Flx(N,pp), pp),
2521                           Flx_Fl_mul(Ap, umodiu(den, pp), pp), pp), Cp, pp);
2522     if (degpol(res) >= 0) continue;
2523     res = ZX_sub(ZX_mul(B, N), ZX_Z_mul(A,den));
2524     res = ZX_is_monic(C) ? ZX_rem(res, C): RgX_pseudorem(res, C);
2525     if (DEBUGLEVEL>5) timer_printf(&ti,"QXQ_div: final check");
2526     if (degpol(res)<0)
2527     {
2528       if (DA && DB) U = RgX_Rg_mul(U, gdiv(DA,DB));
2529       else if (DA) U = RgX_Rg_mul(U, DA);
2530       else if (DB) U = RgX_Rg_div(U, DB);
2531       return gerepilecopy(av, U);
2532     }
2533   }
2534 }
2535 
2536 /************************************************************************
2537  *                                                                      *
2538  *                           ZXQ_minpoly                                *
2539  *                                                                      *
2540  ************************************************************************/
2541 
2542 static GEN
ZXQ_minpoly_slice(GEN A,GEN B,long d,GEN P,GEN * mod)2543 ZXQ_minpoly_slice(GEN A, GEN B, long d, GEN P, GEN *mod)
2544 {
2545   pari_sp av = avma;
2546   long i, n = lg(P)-1, v = evalvarn(varn(B));
2547   GEN H, T;
2548   if (n == 1)
2549   {
2550     ulong p = uel(P,1);
2551     GEN a = ZX_to_Flx(A, p), b = ZX_to_Flx(B, p);
2552     GEN Hp = Flxq_minpoly(a, b, p);
2553     if (degpol(Hp) != d) { p = 1; Hp = pol0_Flx(v); }
2554     H = Flx_to_ZX(Hp);
2555     *mod = utoi(p);
2556     gerepileall(av, 2, &H, mod);
2557     return H;
2558   }
2559   T = ZV_producttree(P);
2560   A = ZX_nv_mod_tree(A, P, T);
2561   B = ZX_nv_mod_tree(B, P, T);
2562   H = cgetg(n+1, t_VEC);
2563   for(i=1; i <= n; i++)
2564   {
2565     ulong p = P[i];
2566     GEN a = gel(A,i), b = gel(B,i);
2567     GEN m = Flxq_minpoly(a, b, p);
2568     if (degpol(m) != d) { P[i] = 1; m = pol0_Flx(v); }
2569     gel(H, i) = m;
2570   }
2571   H = nxV_chinese_center_tree(H, P, T, ZV_chinesetree(P, T));
2572   *mod = gmael(T, lg(T)-1, 1);
2573   gerepileall(av, 2, &H, mod);
2574   return H;
2575 }
2576 
2577 GEN
ZXQ_minpoly_worker(GEN P,GEN A,GEN B,long d)2578 ZXQ_minpoly_worker(GEN P, GEN A, GEN B, long d)
2579 {
2580   GEN V = cgetg(3, t_VEC);
2581   gel(V,1) = ZXQ_minpoly_slice(A, B, d, P, &gel(V,2));
2582   return V;
2583 }
2584 
2585 GEN
ZXQ_minpoly(GEN A,GEN B,long d,ulong bound)2586 ZXQ_minpoly(GEN A, GEN B, long d, ulong bound)
2587 {
2588   pari_sp av = avma;
2589   GEN worker, H, dB;
2590   forprime_t S;
2591   B = Q_remove_denom(B, &dB);
2592   worker = strtoclosure("_ZXQ_minpoly_worker", 3, A, B, stoi(d));
2593   init_modular_big(&S);
2594   H = gen_crt("ZXQ_minpoly", worker, &S, dB, bound, 0, NULL,
2595                nxV_chinese_center, FpX_center_i);
2596   return gerepilecopy(av, H);
2597 }
2598 
2599 /************************************************************************
2600  *                                                                      *
2601  *                   ZX_ZXY_resultant                                   *
2602  *                                                                      *
2603  ************************************************************************/
2604 
2605 static GEN
ZX_ZXY_resultant_prime(GEN a,GEN b,ulong dp,ulong p,long degA,long degB,long dres,long sX)2606 ZX_ZXY_resultant_prime(GEN a, GEN b, ulong dp, ulong p,
2607                        long degA, long degB, long dres, long sX)
2608 {
2609   long dropa = degA - degpol(a), dropb = degB - degpol(b);
2610   GEN Hp = Flx_FlxY_resultant_polint(a, b, p, dres, sX);
2611   if (dropa && dropb)
2612     Hp = zero_Flx(sX);
2613   else {
2614     if (dropa)
2615     { /* multiply by ((-1)^deg B lc(B))^(deg A - deg a) */
2616       GEN c = gel(b,degB+2); /* lc(B) */
2617       if (odd(degB)) c = Flx_neg(c, p);
2618       if (!Flx_equal1(c)) {
2619         c = Flx_powu(c, dropa, p);
2620         if (!Flx_equal1(c)) Hp = Flx_mul(Hp, c, p);
2621       }
2622     }
2623     else if (dropb)
2624     { /* multiply by lc(A)^(deg B - deg b) */
2625       ulong c = uel(a, degA+2); /* lc(A) */
2626       c = Fl_powu(c, dropb, p);
2627       if (c != 1) Hp = Flx_Fl_mul(Hp, c, p);
2628     }
2629   }
2630   if (dp != 1) Hp = Flx_Fl_mul(Hp, Fl_powu(Fl_inv(dp,p), degA, p), p);
2631   return Hp;
2632 }
2633 
2634 static GEN
ZX_ZXY_resultant_slice(GEN A,GEN B,GEN dB,long degA,long degB,long dres,GEN P,GEN * mod,long sX,long vY)2635 ZX_ZXY_resultant_slice(GEN A, GEN B, GEN dB, long degA, long degB, long dres,
2636                        GEN P, GEN *mod, long sX, long vY)
2637 {
2638   pari_sp av = avma;
2639   long i, n = lg(P)-1;
2640   GEN H, T, D;
2641   if (n == 1)
2642   {
2643     ulong p = uel(P,1);
2644     ulong dp = dB ? umodiu(dB, p): 1;
2645     GEN a = ZX_to_Flx(A, p), b = ZXX_to_FlxX(B, p, vY);
2646     GEN Hp = ZX_ZXY_resultant_prime(a, b, dp, p, degA, degB, dres, sX);
2647     H = Flx_to_ZX(Hp);
2648     *mod = utoi(p);
2649     gerepileall(av, 2, &H, mod);
2650     return H;
2651   }
2652   T = ZV_producttree(P);
2653   A = ZX_nv_mod_tree(A, P, T);
2654   B = ZXX_nv_mod_tree(B, P, T, vY);
2655   D = dB ? Z_ZV_mod_tree(dB, P, T): NULL;
2656   H = cgetg(n+1, t_VEC);
2657   for(i=1; i <= n; i++)
2658   {
2659     ulong p = P[i];
2660     GEN a = gel(A,i), b = gel(B,i);
2661     ulong dp = D ? uel(D, i): 1;
2662     gel(H,i) = ZX_ZXY_resultant_prime(a, b, dp, p, degA, degB, dres, sX);
2663   }
2664   H = nxV_chinese_center_tree(H, P, T, ZV_chinesetree(P, T));
2665   *mod = gmael(T, lg(T)-1, 1);
2666   gerepileall(av, 2, &H, mod);
2667   return H;
2668 }
2669 
2670 GEN
ZX_ZXY_resultant_worker(GEN P,GEN A,GEN B,GEN dB,GEN v)2671 ZX_ZXY_resultant_worker(GEN P, GEN A, GEN B, GEN dB, GEN v)
2672 {
2673   GEN V = cgetg(3, t_VEC);
2674   if (isintzero(dB)) dB = NULL;
2675   gel(V,1) = ZX_ZXY_resultant_slice(A, B, dB, v[1], v[2], v[3], P, &gel(V,2), v[4], v[5]);
2676   return V;
2677 }
2678 
2679 GEN
ZX_ZXY_resultant(GEN A,GEN B)2680 ZX_ZXY_resultant(GEN A, GEN B)
2681 {
2682   pari_sp av = avma;
2683   forprime_t S;
2684   ulong bound;
2685   long v = fetch_var_higher();
2686   long degA = degpol(A), degB, dres = degA * degpol(B);
2687   long vX = varn(B), vY = varn(A); /* assume vY has lower priority */
2688   long sX = evalvarn(vX);
2689   GEN worker, H, dB;
2690   B = Q_remove_denom(B, &dB);
2691   if (!dB) B = leafcopy(B);
2692   A = leafcopy(A); setvarn(A,v);
2693   B = swap_vars(B, vY); setvarn(B,v); degB = degpol(B);
2694   bound = ZX_ZXY_ResBound(A, B, dB);
2695   if (DEBUGLEVEL>4) err_printf("bound for resultant coeffs: 2^%ld\n",bound);
2696   worker = snm_closure(is_entry("_ZX_ZXY_resultant_worker"),
2697                        mkvec4(A, B, dB? dB: gen_0,
2698                               mkvecsmall5(degA, degB, dres, sX, vY)));
2699   init_modular_big(&S);
2700   H = gen_crt("ZX_ZXY_resultant_all", worker, &S, dB, bound, 0, NULL,
2701                nxV_chinese_center, FpX_center_i);
2702   setvarn(H, vX); (void)delete_var();
2703   return gerepilecopy(av, H);
2704 }
2705 
2706 static long
ZX_ZXY_rnfequation_lambda(GEN A,GEN B0,long lambda)2707 ZX_ZXY_rnfequation_lambda(GEN A, GEN B0, long lambda)
2708 {
2709   pari_sp av = avma;
2710   long degA = degpol(A), degB, dres = degA*degpol(B0);
2711   long v = fetch_var_higher();
2712   long vX = varn(B0), vY = varn(A); /* assume vY has lower priority */
2713   long sX = evalvarn(vX);
2714   GEN dB, B, a, b, Hp;
2715   forprime_t S;
2716 
2717   B0 = Q_remove_denom(B0, &dB);
2718   if (!dB) B0 = leafcopy(B0);
2719   A = leafcopy(A);
2720   B = B0;
2721   setvarn(A,v);
2722 INIT:
2723   if (lambda) B = RgX_translate(B0, monomial(stoi(lambda), 1, vY));
2724   B = swap_vars(B, vY); setvarn(B,v);
2725   /* B0(lambda v + x, v) */
2726   if (DEBUGLEVEL>4) err_printf("Trying lambda = %ld\n", lambda);
2727 
2728   degB = degpol(B);
2729   init_modular_big(&S);
2730   while (1)
2731   {
2732     ulong p = u_forprime_next(&S);
2733     ulong dp = dB ? umodiu(dB, p): 1;
2734     if (!dp) continue;
2735     a = ZX_to_Flx(A, p);
2736     b = ZXX_to_FlxX(B, p, v);
2737     Hp = ZX_ZXY_resultant_prime(a, b, dp, p, degA, degB, dres, sX);
2738     if (degpol(Hp) != dres) continue;
2739     if (dp != 1) Hp = Flx_Fl_mul(Hp, Fl_powu(Fl_inv(dp,p), degA, p), p);
2740     if (!Flx_is_squarefree(Hp, p)) { lambda = next_lambda(lambda); goto INIT; }
2741     if (DEBUGLEVEL>4) err_printf("Final lambda = %ld\n", lambda);
2742     (void)delete_var(); return gc_long(av,lambda);
2743   }
2744 }
2745 
2746 GEN
ZX_ZXY_rnfequation(GEN A,GEN B,long * lambda)2747 ZX_ZXY_rnfequation(GEN A, GEN B, long *lambda)
2748 {
2749   if (lambda)
2750   {
2751     *lambda = ZX_ZXY_rnfequation_lambda(A, B, *lambda);
2752     if (*lambda) B = RgX_translate(B, monomial(stoi(*lambda), 1, varn(A)));
2753   }
2754   return ZX_ZXY_resultant(A,B);
2755 }
2756 
2757 static GEN
ZX_direct_compositum_slice(GEN A,GEN B,GEN P,GEN * mod)2758 ZX_direct_compositum_slice(GEN A, GEN B, GEN P, GEN *mod)
2759 {
2760   pari_sp av = avma;
2761   long i, n = lg(P)-1;
2762   GEN H, T;
2763   if (n == 1)
2764   {
2765     ulong p = uel(P,1);
2766     GEN a = ZX_to_Flx(A, p), b = ZX_to_Flx(B, p);
2767     GEN Hp = Flx_direct_compositum(a, b, p);
2768     H = gerepileupto(av, Flx_to_ZX(Hp));
2769     *mod = utoi(p);
2770     return H;
2771   }
2772   T = ZV_producttree(P);
2773   A = ZX_nv_mod_tree(A, P, T);
2774   B = ZX_nv_mod_tree(B, P, T);
2775   H = cgetg(n+1, t_VEC);
2776   for(i=1; i <= n; i++)
2777   {
2778     ulong p = P[i];
2779     GEN a = gel(A,i), b = gel(B,i);
2780     gel(H,i) = Flx_direct_compositum(a, b, p);
2781   }
2782   H = nxV_chinese_center_tree(H, P, T, ZV_chinesetree(P, T));
2783   *mod = gmael(T, lg(T)-1, 1);
2784   gerepileall(av, 2, &H, mod);
2785   return H;
2786 }
2787 
2788 GEN
ZX_direct_compositum_worker(GEN P,GEN A,GEN B)2789 ZX_direct_compositum_worker(GEN P, GEN A, GEN B)
2790 {
2791   GEN V = cgetg(3, t_VEC);
2792   gel(V,1) = ZX_direct_compositum_slice(A, B, P, &gel(V,2));
2793   return V;
2794 }
2795 
2796 /* Assume A,B irreducible (in particular squarefree) and define linearly
2797  * disjoint extensions: no factorisation needed */
2798 static GEN
ZX_direct_compositum(GEN A,GEN B,GEN lead)2799 ZX_direct_compositum(GEN A, GEN B, GEN lead)
2800 {
2801   pari_sp av = avma;
2802   forprime_t S;
2803   ulong bound;
2804   GEN H, worker, mod;
2805   bound = ZX_ZXY_ResBound(A, poleval(B,deg1pol(gen_1,pol_x(1),0)), NULL);
2806   worker = snm_closure(is_entry("_ZX_direct_compositum_worker"), mkvec2(A,B));
2807   init_modular_big(&S);
2808   H = gen_crt("ZX_direct_compositum", worker, &S, lead, bound, 0, &mod,
2809               nxV_chinese_center, FpX_center);
2810   return gerepileupto(av, H);
2811 }
2812 
2813 static long
ZX_compositum_lambda(GEN A,GEN B,GEN lead,long lambda)2814 ZX_compositum_lambda(GEN A, GEN B, GEN lead, long lambda)
2815 {
2816   pari_sp av = avma;
2817   forprime_t S;
2818   ulong p;
2819   init_modular_big(&S);
2820   p = u_forprime_next(&S);
2821   while (1)
2822   {
2823     GEN Hp, a;
2824     if (DEBUGLEVEL>4) err_printf("Trying lambda = %ld\n", lambda);
2825     if (lead && dvdiu(lead,p)) { p = u_forprime_next(&S); continue; }
2826     a = ZX_to_Flx(ZX_rescale(A, stoi(-lambda)), p);
2827     Hp = Flx_direct_compositum(a, ZX_to_Flx(B, p), p);
2828     if (!Flx_is_squarefree(Hp, p)) { lambda = next_lambda(lambda); continue; }
2829     if (DEBUGLEVEL>4) err_printf("Final lambda = %ld\n", lambda);
2830     return gc_long(av, lambda);
2831   }
2832 }
2833 
2834 GEN
ZX_compositum(GEN A,GEN B,long * lambda)2835 ZX_compositum(GEN A, GEN B, long *lambda)
2836 {
2837   GEN lead  = mulii(leading_coeff(A),leading_coeff(B));
2838   if (lambda)
2839   {
2840     *lambda = ZX_compositum_lambda(A, B, lead, *lambda);
2841     A = ZX_rescale(A, stoi(-*lambda));
2842   }
2843   return ZX_direct_compositum(A, B, lead);
2844 }
2845 
2846 GEN
ZX_compositum_disjoint(GEN A,GEN B)2847 ZX_compositum_disjoint(GEN A, GEN B)
2848 { return ZX_compositum(A, B, NULL); }
2849 
2850 static GEN
ZXQX_direct_compositum_slice(GEN A,GEN B,GEN C,GEN P,GEN * mod)2851 ZXQX_direct_compositum_slice(GEN A, GEN B, GEN C, GEN P, GEN *mod)
2852 {
2853   pari_sp av = avma;
2854   long i, n = lg(P)-1, dC = degpol(C), v = varn(C);
2855   GEN H, T;
2856   if (n == 1)
2857   {
2858     ulong p = uel(P,1);
2859     GEN a = ZXX_to_FlxX(A, p, v), b = ZXX_to_FlxX(B, p, v);
2860     GEN c = ZX_to_Flx(C, p);
2861     GEN Hp = FlxX_to_Flm(FlxqX_direct_compositum(a, b, c, p), dC);
2862     H = gerepileupto(av, Flm_to_ZM(Hp));
2863     *mod = utoi(p);
2864     return H;
2865   }
2866   T = ZV_producttree(P);
2867   A = ZXX_nv_mod_tree(A, P, T, v);
2868   B = ZXX_nv_mod_tree(B, P, T, v);
2869   C = ZX_nv_mod_tree(C, P, T);
2870   H = cgetg(n+1, t_VEC);
2871   for(i=1; i <= n; i++)
2872   {
2873     ulong p = P[i];
2874     GEN a = gel(A,i), b = gel(B,i), c = gel(C,i);
2875     gel(H,i) = FlxX_to_Flm(FlxqX_direct_compositum(a, b, c, p), dC);
2876   }
2877   H = nmV_chinese_center_tree_seq(H, P, T, ZV_chinesetree(P, T));
2878   *mod = gmael(T, lg(T)-1, 1);
2879   gerepileall(av, 2, &H, mod);
2880   return H;
2881 }
2882 
2883 GEN
ZXQX_direct_compositum_worker(GEN P,GEN A,GEN B,GEN C)2884 ZXQX_direct_compositum_worker(GEN P, GEN A, GEN B, GEN C)
2885 {
2886   GEN V = cgetg(3, t_VEC);
2887   gel(V,1) = ZXQX_direct_compositum_slice(A, B, C, P, &gel(V,2));
2888   return V;
2889 }
2890 
2891 static GEN
ZXQX_direct_compositum(GEN A,GEN B,GEN T,ulong bound)2892 ZXQX_direct_compositum(GEN A, GEN B, GEN T, ulong bound)
2893 {
2894   pari_sp av = avma;
2895   forprime_t S;
2896   GEN H, worker, mod;
2897   GEN lead = mulii(Q_content(leading_coeff(A)), Q_content(leading_coeff(B)));
2898   worker = snm_closure(is_entry("_ZXQX_direct_compositum_worker")
2899                       , mkvec3(A,B,T));
2900   init_modular_big(&S);
2901   H = gen_crt("ZXQX_direct_compositum", worker, &S, lead, bound, 0, &mod,
2902               nmV_chinese_center, FpM_center);
2903   if (DEBUGLEVEL > 4)
2904     err_printf("nfcompositum: a priori bound: %lu, a posteriori: %lu\n",
2905                bound, expi(gsupnorm(H, DEFAULTPREC)));
2906   return gerepilecopy(av, RgM_to_RgXX(H, varn(A), varn(T)));
2907 }
2908 
2909 static long
ZXQX_direct_compositum_bound(GEN nf,GEN A,GEN B)2910 ZXQX_direct_compositum_bound(GEN nf, GEN A, GEN B)
2911 {
2912   pari_sp av = avma;
2913   GEN r, M = L2_bound(nf, NULL, &r);
2914   long v = nf_get_varn(nf), i, l = lg(r);
2915   GEN a = cgetg(l, t_COL);
2916   for (i = 1; i < l; i++)
2917     gel(a, i) =  RgX_RgXY_ResBound(gsubst(A, v, gel(r,i)),
2918                  poleval(gsubst(B, v, gel(r,i)),
2919                          deg1pol(gen_1, pol_x(1), 0)), DEFAULTPREC);
2920   return gc_long(av, (long) dbllog2(gmul(M,RgC_fpnorml2(a, DEFAULTPREC))));
2921 }
2922 
2923 GEN
nf_direct_compositum(GEN nf,GEN A,GEN B)2924 nf_direct_compositum(GEN nf, GEN A, GEN B)
2925 {
2926   ulong bnd = ZXQX_direct_compositum_bound(nf, A, B);
2927   return ZXQX_direct_compositum(A, B, nf_get_pol(nf), bnd);
2928 }
2929 
2930 /************************************************************************
2931  *                                                                      *
2932  *                   IRREDUCIBLE POLYNOMIAL / Fp                        *
2933  *                                                                      *
2934  ************************************************************************/
2935 
2936 /* irreducible (unitary) polynomial of degree n over Fp */
2937 GEN
ffinit_rand(GEN p,long n)2938 ffinit_rand(GEN p,long n)
2939 {
2940   for(;;) {
2941     pari_sp av = avma;
2942     GEN pol = ZX_add(pol_xn(n, 0), random_FpX(n-1,0, p));
2943     if (FpX_is_irred(pol, p)) return pol;
2944     set_avma(av);
2945   }
2946 }
2947 
2948 /* return an extension of degree 2^l of F_2, assume l > 0
2949  * Not stack clean. */
2950 static GEN
ffinit_Artin_Schreier_2(long l)2951 ffinit_Artin_Schreier_2(long l)
2952 {
2953   GEN Q, T, S;
2954   long i, v;
2955 
2956   if (l == 1) return mkvecsmall4(0,1,1,1); /*x^2 + x + 1*/
2957   v = fetch_var_higher();
2958   S = mkvecsmall5(0, 0, 0, 1, 1); /* y(y^2 + y) */
2959   Q = mkpoln(3, pol1_Flx(0), pol1_Flx(0), S); /* x^2 + x + y(y^2+y) */
2960   setvarn(Q, v);
2961 
2962   /* x^4+x+1, irred over F_2, minimal polynomial of a root of Q */
2963   T = mkvecsmalln(6,evalvarn(v),1UL,1UL,0UL,0UL,1UL);
2964   /* Q = x^2 + x + a(y) irred. over K = F2[y] / (T(y))
2965    * ==> x^2 + x + a(y) b irred. over K for any root b of Q
2966    * ==> x^2 + x + (b^2+b)b */
2967   for (i=2; i<l; i++) T = Flx_FlxY_resultant(T, Q, 2); /* minpoly(b) / F2*/
2968   (void)delete_var(); T[1] = 0; return T;
2969 }
2970 
2971 /* return an extension of degree p^l of F_p, assume l > 0
2972  * Not stack clean. */
2973 GEN
ffinit_Artin_Schreier(ulong p,long l)2974 ffinit_Artin_Schreier(ulong p, long l)
2975 {
2976   long i, v;
2977   GEN Q, R, S, T, xp;
2978   if (p==2) return ffinit_Artin_Schreier_2(l);
2979   xp = polxn_Flx(p,0); /* x^p */
2980   T = Flx_sub(xp, mkvecsmall3(0,1,1),p); /* x^p - x - 1 */
2981   if (l == 1) return T;
2982 
2983   v = evalvarn(fetch_var_higher());
2984   xp[1] = v;
2985   R = Flx_sub(polxn_Flx(2*p-1,0), polxn_Flx(p,0),p);
2986   S = Flx_sub(xp, polx_Flx(0), p);
2987   Q = FlxX_Flx_sub(Flx_to_FlxX(S, v), R, p); /* x^p - x - (y^(2p-1)-y^p) */
2988   for (i = 2; i <= l; ++i) T = Flx_FlxY_resultant(T, Q, p);
2989   (void)delete_var(); T[1] = 0; return T;
2990 }
2991 
2992 static long
flinit_check(ulong p,long n,long l)2993 flinit_check(ulong p, long n, long l)
2994 {
2995   ulong q;
2996   if (!uisprime(n)) return 0;
2997   q = p % n; if (!q) return 0;
2998   return ugcd((n-1)/Fl_order(q, n-1, n), l) == 1;
2999 }
3000 
3001 static GEN
flinit(ulong p,long l)3002 flinit(ulong p, long l)
3003 {
3004   ulong n = 1+l;
3005   while (!flinit_check(p,n,l)) n += l;
3006   if (DEBUGLEVEL>=4) err_printf("FFInit: using polsubcyclo(%ld, %ld)\n",n,l);
3007   return ZX_to_Flx(polsubcyclo(n,l,0), p);
3008 }
3009 
3010 static GEN
ffinit_fact_Flx(ulong p,long n)3011 ffinit_fact_Flx(ulong p, long n)
3012 {
3013   GEN P, F = factoru_pow(n), Fp = gel(F,1), Fe = gel(F,2), Fm = gel(F,3);
3014   long i, l = lg(Fm);
3015   P = cgetg(l, t_VEC);
3016   for (i = 1; i < l; ++i)
3017     gel(P,i) = p==uel(Fp,i) ?
3018                  ffinit_Artin_Schreier(uel(Fp,i), Fe[i])
3019                : flinit(p, uel(Fm,i));
3020   return FlxV_direct_compositum(P, p);
3021 }
3022 
3023 static GEN
init_Flxq_i(ulong p,long n,long sv)3024 init_Flxq_i(ulong p, long n, long sv)
3025 {
3026   GEN P;
3027   if (n == 1) return polx_Flx(sv);
3028   if (flinit_check(p, n+1, n))
3029   {
3030     P = const_vecsmall(n+2,1);
3031     P[1] = sv; return P;
3032   }
3033   P = ffinit_fact_Flx(p,n);
3034   P[1] = sv; return P;
3035 }
3036 
3037 GEN
init_Flxq(ulong p,long n,long v)3038 init_Flxq(ulong p, long n, long v)
3039 {
3040   pari_sp av = avma;
3041   return gerepileupto(av, init_Flxq_i(p, n, v));
3042 }
3043 
3044 /* check if polsubcyclo(n,l,0) is irreducible modulo p */
3045 static long
fpinit_check(GEN p,long n,long l)3046 fpinit_check(GEN p, long n, long l)
3047 {
3048   ulong q;
3049   if (!uisprime(n)) return 0;
3050   q = umodiu(p,n); if (!q) return 0;
3051   return ugcd((n-1)/Fl_order(q, n-1, n), l) == 1;
3052 }
3053 
3054 /* let k=2 if p%4==1, and k=4 else and assume k*p does not divide l.
3055  * Return an irreducible polynomial of degree l over F_p.
3056  * Variant of Adleman and Lenstra "Finding irreducible polynomials over
3057  * finite fields", ACM, 1986 (5) 350--355.
3058  * Not stack clean */
3059 static GEN
fpinit(GEN p,long l)3060 fpinit(GEN p, long l)
3061 {
3062   ulong n = 1+l;
3063   while (!fpinit_check(p,n,l)) n += l;
3064   if (DEBUGLEVEL>=4) err_printf("FFInit: using polsubcyclo(%ld, %ld)\n",n,l);
3065   return FpX_red(polsubcyclo(n,l,0),p);
3066 }
3067 
3068 static GEN
ffinit_fact(GEN p,long n)3069 ffinit_fact(GEN p, long n)
3070 {
3071   GEN P, F = factoru_pow(n), Fp = gel(F,1), Fe = gel(F,2), Fm = gel(F,3);
3072   long i, l = lg(Fm);
3073   P = cgetg(l, t_VEC);
3074   for (i = 1; i < l; ++i)
3075     gel(P,i) = absequaliu(p, Fp[i]) ?
3076                  Flx_to_ZX(ffinit_Artin_Schreier(Fp[i], Fe[i]))
3077                : fpinit(p, Fm[i]);
3078   return FpXV_direct_compositum(P, p);
3079 }
3080 
3081 static GEN
init_Fq_i(GEN p,long n,long v)3082 init_Fq_i(GEN p, long n, long v)
3083 {
3084   GEN P;
3085   if (n <= 0) pari_err_DOMAIN("ffinit", "degree", "<=", gen_0, stoi(n));
3086   if (typ(p) != t_INT) pari_err_TYPE("ffinit",p);
3087   if (signe(p) <= 0) pari_err_PRIME("ffinit",p);
3088   if (v < 0) v = 0;
3089   if (n == 1) return pol_x(v);
3090   if (lgefint(p) == 3)
3091     return Flx_to_ZX(init_Flxq_i(p[2], n, evalvarn(v)));
3092   if (fpinit_check(p, n+1, n)) return polcyclo(n+1, v);
3093   P = ffinit_fact(p,n);
3094   setvarn(P, v); return P;
3095 }
3096 GEN
init_Fq(GEN p,long n,long v)3097 init_Fq(GEN p, long n, long v)
3098 {
3099   pari_sp av = avma;
3100   return gerepileupto(av, init_Fq_i(p, n, v));
3101 }
3102 GEN
ffinit(GEN p,long n,long v)3103 ffinit(GEN p, long n, long v)
3104 {
3105   pari_sp av = avma;
3106   return gerepileupto(av, FpX_to_mod(init_Fq_i(p, n, v), p));
3107 }
3108 
3109 GEN
ffnbirred(GEN p,long n)3110 ffnbirred(GEN p, long n)
3111 {
3112   pari_sp av = avma;
3113   GEN s = powiu(p,n), F = factoru(n), D = divisorsu_moebius(gel(F, 1));
3114   long j, l = lg(D);
3115   for (j = 2; j < l; j++) /* skip d = 1 */
3116   {
3117     long md = D[j]; /* mu(d) * d, d squarefree */
3118     GEN pd = powiu(p, n / labs(md)); /* p^{n/d} */
3119     s = md > 0? addii(s, pd): subii(s,pd);
3120   }
3121   return gerepileuptoint(av, diviuexact(s, n));
3122 }
3123 
3124 GEN
ffsumnbirred(GEN p,long n)3125 ffsumnbirred(GEN p, long n)
3126 {
3127   pari_sp av = avma, av2;
3128   GEN q, t = p, v = vecfactoru(1, n);
3129   long i;
3130   q = cgetg(n+1,t_VEC); gel(q,1) = p;
3131   for (i=2; i<=n; i++) gel(q,i) = mulii(gel(q,i-1), p);
3132   av2 = avma;
3133   for (i=2; i<=n; i++)
3134   {
3135     GEN s = gel(q,i), F = gel(v,i), D = divisorsu_moebius(gel(F,1));
3136     long j, l = lg(D);
3137     for (j = 2; j < l; j++) /* skip 1 */
3138     {
3139       long md = D[j];
3140       GEN pd = gel(q, i / labs(md)); /* p^{i/d} */
3141       s = md > 0? addii(s, pd): subii(s, pd);
3142     }
3143     t = gerepileuptoint(av2, addii(t, diviuexact(s, i)));
3144   }
3145   return gerepileuptoint(av, t);
3146 }
3147 
3148 GEN
ffnbirred0(GEN p,long n,long flag)3149 ffnbirred0(GEN p, long n, long flag)
3150 {
3151   if (typ(p) != t_INT) pari_err_TYPE("ffnbirred", p);
3152   if (n <= 0) pari_err_DOMAIN("ffnbirred", "degree", "<=", gen_0, stoi(n));
3153   switch(flag)
3154   {
3155     case 0: return ffnbirred(p, n);
3156     case 1: return ffsumnbirred(p, n);
3157   }
3158   pari_err_FLAG("ffnbirred");
3159   return NULL; /* LCOV_EXCL_LINE */
3160 }
3161 
3162 static void
checkmap(GEN m,const char * s)3163 checkmap(GEN m, const char *s)
3164 {
3165   if (typ(m)!=t_VEC || lg(m)!=3 || typ(gel(m,1))!=t_FFELT)
3166     pari_err_TYPE(s,m);
3167 }
3168 
3169 GEN
ffembed(GEN a,GEN b)3170 ffembed(GEN a, GEN b)
3171 {
3172   pari_sp av = avma;
3173   GEN p, Ta, Tb, g, r = NULL;
3174   if (typ(a)!=t_FFELT) pari_err_TYPE("ffembed",a);
3175   if (typ(b)!=t_FFELT) pari_err_TYPE("ffembed",b);
3176   p = FF_p_i(a); g = FF_gen(a);
3177   if (!equalii(p, FF_p_i(b))) pari_err_MODULUS("ffembed",a,b);
3178   Ta = FF_mod(a);
3179   Tb = FF_mod(b);
3180   if (degpol(Tb)%degpol(Ta)!=0)
3181     pari_err_DOMAIN("ffembed",GENtostr_raw(a),"is not a subfield of",b,a);
3182   r = gel(FFX_roots(Ta, b), 1);
3183   return gerepilecopy(av, mkvec2(g,r));
3184 }
3185 
3186 GEN
ffextend(GEN a,GEN P,long v)3187 ffextend(GEN a, GEN P, long v)
3188 {
3189   pari_sp av = avma;
3190   long n;
3191   GEN p, T, R, g, m;
3192   if (typ(a)!=t_FFELT) pari_err_TYPE("ffextend",a);
3193   T = a; p = FF_p_i(a);
3194   if (typ(P)!=t_POL || !RgX_is_FpXQX(P,&T,&p)) pari_err_TYPE("ffextend", P);
3195   if (!FF_samefield(a, T)) pari_err_MODULUS("ffextend",a,T);
3196   if (v < 0) v = varn(P);
3197   n = FF_f(T) * degpol(P); R = ffinit(p, n, v); g = ffgen(R, v);
3198   m = ffembed(a, g);
3199   R = FFX_roots(ffmap(m, P),g);
3200   return gerepilecopy(av, mkvec2(gel(R,1), m));
3201 }
3202 
3203 GEN
fffrobenius(GEN a,long n)3204 fffrobenius(GEN a, long n)
3205 {
3206   if (typ(a)!=t_FFELT) pari_err_TYPE("fffrobenius",a);
3207   retmkvec2(FF_gen(a), FF_Frobenius(a, n));
3208 }
3209 
3210 GEN
ffinvmap(GEN m)3211 ffinvmap(GEN m)
3212 {
3213   pari_sp av = avma;
3214   long i, l;
3215   GEN T, F, a, g, r, f = NULL;
3216   checkmap(m, "ffinvmap");
3217   a = gel(m,1); r = gel(m,2);
3218   if (typ(r) != t_FFELT)
3219    pari_err_TYPE("ffinvmap", m);
3220   g = FF_gen(a);
3221   T = FF_mod(r);
3222   F = gel(FFX_factor(T, a), 1);
3223   l = lg(F);
3224   for(i=1; i<l; i++)
3225   {
3226     GEN s = FFX_rem(FF_to_FpXQ_i(r), gel(F, i), a);
3227     if (degpol(s)==0 && gequal(constant_coeff(s),g)) { f = gel(F, i); break; }
3228   }
3229   if (f==NULL) pari_err_TYPE("ffinvmap", m);
3230   if (degpol(f)==1) f = FF_neg_i(gel(f,2));
3231   return gerepilecopy(av, mkvec2(FF_gen(r),f));
3232 }
3233 
3234 static GEN
ffpartmapimage(const char * s,GEN r)3235 ffpartmapimage(const char *s, GEN r)
3236 {
3237    GEN a = NULL, p = NULL;
3238    if (typ(r)==t_POL && degpol(r) >= 1
3239       && RgX_is_FpXQX(r,&a,&p) && a && typ(a)==t_FFELT) return a;
3240    pari_err_TYPE(s, r);
3241    return NULL; /* LCOV_EXCL_LINE */
3242 }
3243 
3244 static GEN
ffeltmap_i(GEN m,GEN x)3245 ffeltmap_i(GEN m, GEN x)
3246 {
3247    GEN r = gel(m,2);
3248    if (!FF_samefield(x, gel(m,1)))
3249      pari_err_DOMAIN("ffmap","m","domain does not contain", x, r);
3250    if (typ(r)==t_FFELT)
3251      return FF_map(r, x);
3252    else
3253      return FFX_preimage(x, r, ffpartmapimage("ffmap", r));
3254 }
3255 
3256 static GEN
ffmap_i(GEN m,GEN x)3257 ffmap_i(GEN m, GEN x)
3258 {
3259   GEN y;
3260   long i, lx, tx = typ(x);
3261   switch(tx)
3262   {
3263     case t_FFELT:
3264       return ffeltmap_i(m, x);
3265     case t_POL: case t_RFRAC: case t_SER:
3266     case t_VEC: case t_COL: case t_MAT:
3267       y = cgetg_copy(x, &lx);
3268       for (i=1; i<lontyp[tx]; i++) y[i] = x[1];
3269       for (i=lontyp[tx]; i<lx; i++)
3270       {
3271         GEN yi = ffmap_i(m, gel(x,i));
3272         if (!yi) return NULL;
3273         gel(y,i) = yi;
3274       }
3275       return y;
3276   }
3277   return gcopy(x);
3278 }
3279 
3280 GEN
ffmap(GEN m,GEN x)3281 ffmap(GEN m, GEN x)
3282 {
3283   pari_sp ltop = avma;
3284   GEN y;
3285   checkmap(m, "ffmap");
3286   y = ffmap_i(m, x);
3287   if (y) return y;
3288   set_avma(ltop); return cgetg(1,t_VEC);
3289 }
3290 
3291 static GEN
ffeltmaprel_i(GEN m,GEN x)3292 ffeltmaprel_i(GEN m, GEN x)
3293 {
3294    GEN g = gel(m,1), r = gel(m,2);
3295    if (!FF_samefield(x, g))
3296      pari_err_DOMAIN("ffmap","m","domain does not contain", x, r);
3297    if (typ(r)==t_FFELT)
3298      retmkpolmod(FF_map(r, x), pol_x(FF_var(g)));
3299    else
3300      retmkpolmod(FFX_preimagerel(x, r, ffpartmapimage("ffmap", r)), gcopy(r));
3301 }
3302 
3303 static GEN
ffmaprel_i(GEN m,GEN x)3304 ffmaprel_i(GEN m, GEN x)
3305 {
3306   GEN y;
3307   long i, lx, tx = typ(x);
3308   switch(tx)
3309   {
3310     case t_FFELT:
3311       return ffeltmaprel_i(m, x);
3312     case t_POL: case t_RFRAC: case t_SER:
3313     case t_VEC: case t_COL: case t_MAT:
3314       y = cgetg_copy(x, &lx);
3315       for (i=1; i<lontyp[tx]; i++) y[i] = x[1];
3316       for (i=lontyp[tx]; i<lx; i++)
3317         gel(y,i) = ffmaprel_i(m, gel(x,i));
3318       return y;
3319   }
3320   return gcopy(x);
3321 }
3322 
3323 GEN
ffmaprel(GEN m,GEN x)3324 ffmaprel(GEN m, GEN x)
3325 {
3326   checkmap(m, "ffmaprel");
3327   return ffmaprel_i(m, x);
3328 }
3329 
3330 static void
err_compo(GEN m,GEN n)3331 err_compo(GEN m, GEN n)
3332 { pari_err_DOMAIN("ffcompomap","m","domain does not contain codomain of",n,m); }
3333 
3334 GEN
ffcompomap(GEN m,GEN n)3335 ffcompomap(GEN m, GEN n)
3336 {
3337   pari_sp av = avma;
3338   GEN g = gel(n,1), r, m2, n2;
3339   checkmap(m, "ffcompomap");
3340   checkmap(n, "ffcompomap");
3341   m2 = gel(m,2); n2 = gel(n,2);
3342   switch((typ(m2)==t_POL)|((typ(n2)==t_POL)<<1))
3343   {
3344     case 0:
3345       if (!FF_samefield(gel(m,1),n2)) err_compo(m,n);
3346       r = FF_map(gel(m,2), n2);
3347       break;
3348     case 2:
3349       r = ffmap_i(m, n2);
3350       if (lg(r) == 1) err_compo(m,n);
3351       break;
3352     case 1:
3353       r = ffeltmap_i(m, n2);
3354       if (!r)
3355       {
3356         GEN a, A, R, M;
3357         long dm, dn;
3358         a = ffpartmapimage("ffcompomap",m2);
3359         A = FF_to_FpXQ_i(FF_neg(n2));
3360         setvarn(A, 1);
3361         R = deg1pol(gen_1, A, 0);
3362         setvarn(R, 0);
3363         M = gcopy(m2);
3364         setvarn(M, 1);
3365         r = polresultant0(R, M, 1, 0);
3366         dm = FF_f(gel(m,1)); dn = FF_f(gel(n,1));
3367         if (dm % dn || !FFX_ispower(r, dm/dn, a, &r)) err_compo(m,n);
3368         setvarn(r, varn(FF_mod(g)));
3369       }
3370       break;
3371     case 3:
3372     {
3373       GEN M, R, T, p, a;
3374       a = ffpartmapimage("ffcompomap",n2);
3375       if (!FF_samefield(a, gel(m,1))) err_compo(m,n);
3376       p = FF_p_i(gel(n,1));
3377       T = FF_mod(gel(n,1));
3378       setvarn(T, 1);
3379       R = RgX_to_FpXQX(n2,T,p);
3380       setvarn(R, 0);
3381       M = gcopy(m2);
3382       setvarn(M, 1);
3383       r = polresultant0(R, M, 1, 0);
3384       setvarn(r, varn(n2));
3385     }
3386   }
3387   return gerepilecopy(av, mkvec2(g,r));
3388 }
3389