/* $Id: Flx.c,v 1.74 2006/04/04 12:32:42 kb Exp $ Copyright (C) 2004 The PARI group. This file is part of the PARI/GP package. PARI/GP is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY WHATSOEVER. Check the License for details. You should have received a copy of it, along with the package; see the file 'COPYING'. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "pari.h" #include "paripriv.h" /* Not so fast arithmetic with polynomials with small coefficients. */ /***********************************************************************/ /** **/ /** Flx **/ /** **/ /***********************************************************************/ /* Flx objects are defined as follows: Let l an ulong. An Flx is a t_VECSMALL: x[0] = codeword x[1] = evalvarn(variable number) (signe is not stored). x[2] = a_0 x[3] = a_1, etc. With 0 <= a_i < l signe(x) is not valid. Use degpol(x)>=0 instead. */ #define both_odd(x,y) ((x)&(y)&1) /***********************************************************************/ /** **/ /** Conversion from Flx **/ /** **/ /***********************************************************************/ GEN Flx_to_ZX(GEN z) { long i, l = lg(z); GEN x = cgetg(l,t_POL); for (i=2; i1; i--) if (x[i]) break; stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1)); setlg(x, i+1); return x; } /*Do not renormalize. Must not use z[0],z[1]*/ static GEN Flx_red_lg_i(GEN z, long l, ulong p) { long i; ulong *y=(ulong *)z; GEN x = cgetg(l, t_VECSMALL); for (i=2; ilx) swapspec(x,y, lx,ly); lz = lx+2; z = cgetg(lz, t_VECSMALL) + 2; for (i=0; ilx) swapspec(x,y, lx,ly); lz = lx; z = cgetg(lz, t_VECSMALL); z[1]=x[1]; for (i=2; i 0, x > 0 and y >= 0 */ GEN Flx_addshift(GEN x, GEN y, ulong p, long d) { GEN xd,yd,zd = (GEN)avma; long a,lz,ny = lgpol(y), nx = lgpol(x); long vs = x[1]; x += 2; y += 2; a = ny-d; if (a <= 0) { lz = (a>nx)? ny+2: nx+d+2; (void)new_chunk(lz); xd = x+nx; yd = y+ny; while (xd > x) *--zd = *--xd; x = zd + a; while (zd > x) *--zd = 0; } else { xd = new_chunk(d); yd = y+d; x = Flx_addspec(x,yd,p, nx,a); lz = (a>nx)? ny+2: lg(x)+d; x += 2; while (xd > x) *--zd = *--xd; } while (yd > y) *--zd = *--yd; *--zd = vs; *--zd = evaltyp(t_VECSMALL) | evallg(lz); return zd; } /* shift polynomial + gerepile */ /* Do not set evalvarn*/ static GEN Flx_shiftip(pari_sp av, GEN x, long v) { long i, lx = lg(x), ly; GEN y; if (!v || lx==2) return gerepileuptoleaf(av, x); avma = av; ly = lx + v; x += lx; y = new_chunk(ly) + ly; /*cgetg could overwrite x!*/ for (i = 2; i= ny > 0 */ static GEN Flx_mulspec_basecase(GEN x, GEN y, ulong p, long nx, long ny) { long i,lz,nz; GEN z; lz = nx+ny+1; nz = lz-2; z = cgetg(lz, t_VECSMALL) + 2; /* x:y:z [i] = term of degree i */ if (u_OK_ULONG(p)) { for (i=0; i30 && maxlenghtcoeffpol(p,nb)==1) return Flx_shiftip(av,Flx_mulspec_mulii(a,b,p,na,nb), v); if (nb < Flx_MUL_LIMIT) return Flx_shiftip(av,Flx_mulspec_basecase(a,b,p,na,nb), v); i=(na>>1); n0=na-i; na=i; a0=a+n0; n0a=n0; while (n0a && !a[n0a-1]) n0a--; if (nb > n0) { GEN b0,c1,c2; long n0b; nb -= n0; b0 = b+n0; n0b = n0; while (n0b && !b[n0b-1]) n0b--; c = Flx_mulspec(a,b,p,n0a,n0b); c0 = Flx_mulspec(a0,b0,p,na,nb); c2 = Flx_addspec(a0,a,p,na,n0a); c1 = Flx_addspec(b0,b,p,nb,n0b); c1 = Flx_mul(c1,c2,p); c2 = Flx_add(c0,c,p); c2 = Flx_neg_inplace(c2,p); c2 = Flx_add(c1,c2,p); c0 = Flx_addshift(c0,c2 ,p, n0); } else { c = Flx_mulspec(a,b,p,n0a,nb); c0 = Flx_mulspec(a0,b,p,na,nb); } c0 = Flx_addshift(c0,c,p,n0); return Flx_shiftip(av,c0, v); } GEN Flx_mul(GEN x, GEN y, ulong p) { GEN z = Flx_mulspec(x+2,y+2,p, lgpol(x),lgpol(y)); z[1] = x[1]; return z; } static GEN Flx_sqrspec_basecase(GEN x, ulong p, long nx) { long i, lz, nz; ulong p1; GEN z; if (!nx) return zero_Flx(0); lz = (nx << 1) + 1, nz = lz-2; z = cgetg(lz, t_VECSMALL) + 2; if (u_OK_ULONG(p)) { for (i=0; i>1); p1 <<= 1; if ((i&1) == 0) p1 += x[i>>1] * x[i>>1]; z[i] = (p1 % p); } for ( ; i>1); p1 <<= 1; if ((i&1) == 0) p1 += x[i>>1] * x[i>>1]; z[i] = (p1 % p); } } else { for (i=0; i>1); p1 = Fl_add(p1, p1, p); if ((i&1) == 0) p1 = Fl_add(p1, Fl_mul(x[i>>1], x[i>>1], p), p); z[i] = p1; } for ( ; i>1); p1 = Fl_add(p1, p1, p); if ((i&1) == 0) p1 = Fl_add(p1, Fl_mul(x[i>>1], x[i>>1], p), p); z[i] = p1; } } z -= 2; return z; } #if 0 /* used only by Flx_sqrspec #if 0 code.*/ static GEN Flx_2_mul(GEN x, ulong p) { long i, l = lg(x); GEN z = cgetg(l, t_VECSMALL); for (i=2; i 30 && maxlenghtcoeffpol(p,na)==1) return Flx_shiftip(av, Flx_sqrspec_sqri(a,p,na), v); if (na < Flx_SQR_LIMIT) return Flx_shiftip(av, Flx_sqrspec_basecase(a,p,na), v); i=(na>>1); n0=na-i; na=i; a0=a+n0; n0a=n0; while (n0a && !a[n0a-1]) n0a--; c = Flx_sqrspec(a,p,n0a); c0= Flx_sqrspec(a0,p,na); if (p == 2) n0 *= 2; else { #if 0 c1 = Flx_2_mul(Flx_mulspec(a0,a,p, na,n0a), p); #else GEN t = Flx_addspec(a0,a,p,na,n0a); t = Flx_sqr(t,p); c1= Flx_add(c0,c, p); c1= Flx_sub(t, c1, p); #endif c0 = Flx_addshift(c0,c1,p,n0); } c0 = Flx_addshift(c0,c,p,n0); return Flx_shiftip(av,c0,v); } GEN Flx_sqr(GEN x, ulong p) { GEN z = Flx_sqrspec(x+2,p, lgpol(x)); z[1] = x[1]; return z; } GEN Flx_pow(GEN x, long n, ulong p) { GEN y = Fl_to_Flx(1,x[1]), z; long m; if (n == 0) return y; m = n; z = x; for (;;) { if (m&1) y = Flx_mul(y,z, p); m >>= 1; if (!m) return y; z = Flx_sqr(z, p); } } /* separate from Flx_divrem for maximal speed. */ GEN Flx_rem(GEN x, GEN y, ulong p) { pari_sp av; GEN z, c; long dx,dy,dz,i,j; ulong p1,inv; long vs=x[1]; dy = degpol(y); if (!dy) return zero_Flx(x[1]); dx = degpol(x); dz = dx-dy; if (dz < 0) return vecsmall_copy(x); x += 2; y += 2; inv = y[dy]; if (inv != 1UL) inv = Fl_inv(inv,p); c = cgetg(dy+3, t_VECSMALL); c[1]=vs; c += 2; av=avma; z = cgetg(dz+3, t_VECSMALL); z[1]=vs; z += 2; if (u_OK_ULONG(p)) { z[dz] = (inv*x[dx]) % p; for (i=dx-1; i>=dy; --i) { p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */ for (j=i-dy+1; j<=i && j<=dz; j++) { p1 += z[j]*y[i-j]; if (p1 & HIGHBIT) p1 %= p; } p1 %= p; z[i-dy] = p1? ((p - p1)*inv) % p: 0; } for (i=0; i=dy; --i) { p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */ for (j=i-dy+1; j<=i && j<=dz; j++) p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p); z[i-dy] = p1? Fl_mul(p - p1, inv, p): 0; } for (i=0; i=0 && !c[i]) i--; avma=av; return Flx_renormalize(c-2, i+3); } /* as FpX_divrem but working only on ulong types. ASSUME pr != ONLY_DIVIDES * if relevant, *pr is the last object on stack */ GEN Flx_divrem(GEN x, GEN y, ulong p, GEN *pr) { GEN z,q,c; long dx,dy,dz,i,j; ulong p1,inv; long sv=x[1]; if (pr == ONLY_REM) return Flx_rem(x, y, p); dy = degpol(y); if (!dy) { if (y[2] == 1UL) q = vecsmall_copy(x); else q = Flx_Fl_mul(x, Fl_inv(y[2], p), p); if (pr) *pr = zero_Flx(sv); return q; } dx = degpol(x); dz = dx-dy; if (dz < 0) { q = zero_Flx(sv); if (pr) *pr = vecsmall_copy(x); return q; } x += 2; y += 2; z = cgetg(dz + 3, t_VECSMALL); z[1] = sv; z += 2; inv = (ulong)y[dy]; if (inv != 1UL) inv = Fl_inv(inv,p); if (u_OK_ULONG(p)) { z[dz] = (inv*x[dx]) % p; for (i=dx-1; i>=dy; --i) { p1 = p - x[i]; /* compute -p1 instead of p1 (pb with ulongs otherwise) */ for (j=i-dy+1; j<=i && j<=dz; j++) { p1 += z[j]*y[i-j]; if (p1 & HIGHBIT) p1 %= p; } p1 %= p; z[i-dy] = p1? (long) ((p - p1)*inv) % p: 0; } } else { z[dz] = Fl_mul(inv, x[dx], p); for (i=dx-1; i>=dy; --i) { /* compute -p1 instead of p1 (pb with ulongs otherwise) */ p1 = p - (ulong)x[i]; for (j=i-dy+1; j<=i && j<=dz; j++) p1 = Fl_add(p1, Fl_mul(z[j],y[i-j],p), p); z[i-dy] = p1? Fl_mul(p - p1, inv, p): 0; } } q = Flx_renormalize(z-2, dz+3); if (!pr) return q; c = cgetg(dy + 3, t_VECSMALL); c[1] = sv; c += 2; if (u_OK_ULONG(p)) { for (i=0; i=0 && !c[i]) i--; c = Flx_renormalize(c-2, i+3); *pr = c; return q; } long Flx_valuation(GEN x) { long i, l=lg(x); if (l==2) return VERYBIGINT; for (i=2; i=0; i--) if (x[i]) break; return i+1; } static GEN Flx_invmontgomery_newton(GEN T, ulong p) { long i, lx, lz, lq, e, l = degpol(T); GEN E, q, y, z, x = const_vecsmall(l+1, 0) + 2; pari_sp av; y = T+2; q = Flx_recipspec(y,l,l+1) + 2; E = Newton_exponents(l-2); /* assume l > 3 */ av = avma; /* We work on _spec_ Flx's, all the l[xzq12] below are lgpol's */ q[0] = y[l]; /* initialize */ x[0] = Fl_inv(q[0], p); if (q[1]) { ulong u = q[1]; if (x[0] != 1) u = Fl_mul(u, Fl_sqr(x[0],p), p); x[1] = p - u; lx = 2; } else lx = 1; for (e = lg(E)-1; e > 1; e--, avma = av) { /* set x -= x(x*q - 1) + O(t^l2), knowing x*q = 1 + O(t^l1) */ long l2 = E[e-1] + 1, l1 = E[e] + 1; lq = Flx_lgrenormalizespec(q, l2); z = Flx_mulspec(x, q, p, lx, lq); /* FIXME: high product */ lz = lgpol(z); if (lz > l2) lz = l2; z += 2; /* subtract 1 [=>first l1-1 words are 0]: renormalize so that z(0) != 0 */ for (i = l1-1; i < lz; i++) if (z[i]) break; if (i >= lz) continue; /* z-1 = 0(t^l2) */ /* z + i represents (x*q - 1) / t^i */ z = Flx_mulspec(x, z+i, p, lx, lz-i); /* FIXME: low product */ lz = lgpol(z); z += 2; if (lz > l2-i) lz = Flx_lgrenormalizespec(z, l2-i); lx = lz+ i; y = x + i; /* x -= z * t^i, in place */ for (i = 0; i < lz; i++) y[i] = Fl_neg(z[i], p); } x -= 2; setlg(x, lx + 2); x[1] = T[1]; return Flx_shift(x,1); } /* x/polrecip(T)+O(x^deg(T)) */ GEN Flx_invmontgomery(GEN T, ulong p) { pari_sp ltop=avma; long l=lg(T); GEN r; if (l<5) return zero_Flx(T[1]); if (l lg(a)) swap(a, b); while (lgpol(b)) { c = Flx_rem(a,b,p); a = b; b = c; } return a; } GEN Flx_gcd(GEN a, GEN b, ulong p) { pari_sp av = avma; return gerepileuptoleaf(av, Flx_gcd_i(a,b,p)); } int Flx_is_squarefree(GEN z, ulong p) { pari_sp av = avma; GEN d = Flx_gcd_i(z, Flx_deriv(z,p) , p); long res= (degpol(d) == 0); avma = av; return res; } GEN Flx_extgcd(GEN a, GEN b, ulong p, GEN *ptu, GEN *ptv) { GEN q,z,u,v, x = a, y = b; u = zero_Flx(a[1]); v = Fl_to_Flx(1,a[1]); /* v = 1 */ while (lgpol(y)) { q = Flx_divrem(x,y,p,&z); x = y; y = z; /* (x,y) = (y, x - q y) */ z = Flx_sub(u, Flx_mul(q,v, p), p); u = v; v = z; /* (u,v) = (v, u - q v) */ } z = Flx_sub(x, Flx_mul(b,u,p), p); z = Flx_div(z,a,p); *ptu = z; *ptv = u; return x; } ulong Flx_resultant(GEN a, GEN b, ulong p) { long da,db,dc,cnt; ulong lb, res = 1UL; pari_sp av; GEN c; if (lgpol(a)==0 || lgpol(b)==0) return 0; da = degpol(a); db = degpol(b); if (db > da) { swapspec(a,b, da,db); if (both_odd(da,db)) res = p-res; } if (!da) return 1; /* = res * a[2] ^ db, since 0 <= db <= da = 0 */ cnt = 0; av = avma; while (db) { lb = b[db+2]; c = Flx_rem(a,b, p); a = b; b = c; dc = degpol(c); if (dc < 0) { avma = av; return 0; } if (both_odd(da,db)) res = p - res; if (lb != 1) res = Fl_mul(res, Fl_pow(lb, da - dc, p), p); if (++cnt == 4) { cnt = 0; avma = av; } da = db; /* = degpol(a) */ db = dc; /* = degpol(b) */ } avma = av; return Fl_mul(res, Fl_pow(b[2], da, p), p); } /* If resultant is 0, *ptU and *ptU are not set */ ulong Flx_extresultant(GEN a, GEN b, ulong p, GEN *ptU, GEN *ptV) { GEN z,q,u,v, x = a, y = b; ulong lb, res = 1UL; pari_sp av = avma; long dx, dy, dz; long vs=a[1]; dx = degpol(x); dy = degpol(y); if (dy > dx) { swap(x,y); lswap(dx,dy); pswap(ptU, ptV); a = x; b = y; if (both_odd(dx,dy)) res = p-res; } /* dx <= dy */ if (dx < 0) return 0; u = zero_Flx(vs); v = Fl_to_Flx(1,vs); /* v = 1 */ while (dy) { /* b u = x (a), b v = y (a) */ lb = y[dy+2]; q = Flx_divrem(x,y, p, &z); x = y; y = z; /* (x,y) = (y, x - q y) */ dz = degpol(z); if (dz < 0) { avma = av; return 0; } z = Flx_sub(u, Flx_mul(q,v, p), p); u = v; v = z; /* (u,v) = (v, u - q v) */ if (both_odd(dx,dy)) res = p - res; if (lb != 1) res = Fl_mul(res, Fl_pow(lb, dx-dz, p), p); dx = dy; /* = degpol(x) */ dy = dz; /* = degpol(y) */ } res = Fl_mul(res, Fl_pow(y[2], dx, p), p); lb = Fl_mul(res, Fl_inv(y[2],p), p); v = gerepileuptoleaf(av, Flx_Fl_mul(v, lb, p)); av = avma; u = Flx_sub(Fl_to_Flx(res,vs), Flx_mul(b,v,p), p); u = gerepileuptoleaf(av, Flx_div(u,a,p)); /* = (res - b v) / a */ *ptU = u; *ptV = v; return res; } ulong Flx_eval(GEN x, ulong y, ulong p) { ulong p1,r; long j, i=lg(x)-1; if (i<=2) return (i==2)? x[2]: 0; p1 = x[i]; /* specific attention to sparse polynomials (see poleval)*/ if (u_OK_ULONG(p)) { for (i--; i>=2; i=j-1) { for (j=i; !x[j]; j--) if (j==2) { if (i != j) y = Fl_pow(y, i-j+1, p); return (p1 * y) % p; } r = (i==j)? y: Fl_pow(y, i-j+1, p); p1 = ((p1*r) + x[j]) % p; } } else { for (i--; i>=2; i=j-1) { for (j=i; !x[j]; j--) if (j==2) { if (i != j) y = Fl_pow(y, i-j+1, p); return Fl_mul(p1, y, p); } r = (i==j)? y: Fl_pow(y, i-j+1, p); p1 = Fl_add((ulong)x[j], Fl_mul(p1,r,p), p); } } return p1; } static GEN _Flx_mul(void *p, GEN a, GEN b) { return Flx_mul(a,b, *(ulong*)p); } /* compute prod (x - a[i]) */ GEN Flv_roots_to_pol(GEN a, ulong p, long vs) { long i,k,lx = lg(a); GEN p1,p2; if (lx == 1) return Fl_to_Flx(1,vs); p1 = cgetg(lx, t_VEC); for (k=1,i=1; i= p) p2[3] -= p; if (p2[3]) p2[3] = p - p2[3]; /* - (a[i] + a[i+1]) mod p */ p2[4] = 1; } if (i < lx) { p2 = cgetg(4,t_VECSMALL); gel(p1,k++) = p2; p2[1] = vs; p2[2] = a[i]?p - a[i]:0; p2[3] = 1; } setlg(p1, k); return divide_conquer_assoc(p1, _Flx_mul,(void *)&p); } GEN Flx_div_by_X_x(GEN a, ulong x, ulong p, ulong *rem) { long l = lg(a), i; GEN a0, z0; GEN z = cgetg(l-1,t_VECSMALL); z[1] = a[1]; a0 = a + l-1; z0 = z + l-2; *z0 = *a0--; if (u_OK_ULONG(p)) { for (i=l-3; i>1; i--) /* z[i] = (a[i+1] + x*z[i+1]) % p */ { ulong t = (*a0-- + x * *z0--) % p; *z0 = (long)t; } if (rem) *rem = (*a0 + x * *z0) % p; } else { for (i=l-3; i>1; i--) { ulong t = Fl_add((ulong)*a0--, Fl_mul(x, *z0--, p), p); *z0 = (long)t; } if (rem) *rem = Fl_add((ulong)*a0, Fl_mul(x, *z0, p), p); } return z; } /* u P(X) + v P(-X) */ GEN Flx_even_odd_comb(GEN P, ulong u, ulong v, ulong p) { long i, l = lg(P); GEN y = cgetg(l,t_VECSMALL); y[1]=P[1]; for (i=2; ip),D->mg, D->pol, D->p); } static GEN _mul_montgomery(void *data, GEN x, GEN y) { Flxq_muldata *D = (Flxq_muldata*)data; return Flx_rem_montgomery(Flx_mul(x,y,D->p),D->mg, D->pol, D->p); } static GEN _Flxq_sqr(void *data, GEN x) { Flxq_muldata *D = (Flxq_muldata*)data; return Flxq_sqr(x, D->pol, D->p); } static GEN _Flxq_mul(void *data, GEN x, GEN y) { Flxq_muldata *D = (Flxq_muldata*)data; return Flxq_mul(x,y, D->pol, D->p); } /* n-Power of x in Z/pZ[X]/(pol), as t_VECSMALL. */ GEN Flxq_pow(GEN x, GEN n, GEN pol, ulong p) { pari_sp av = avma; Flxq_muldata D; GEN y; if (!signe(n)) return Fl_to_Flx(1,varn(pol)); if (signe(n) < 0) x=Flxq_inv(x,pol,p); else x=Flx_rem(x, pol, p); if (is_pm1(n)) return x; D.pol = pol; D.p = p; /* not tuned*/ if (pol[2] && degpol(pol) >= Flx_POW_MONTGOMERY_LIMIT) { /* We do not handle polynomials multiple of x yet */ D.mg = Flx_invmontgomery(pol,p); y = leftright_pow(x, n, (void*)&D, &_sqr_montgomery, &_mul_montgomery); } else y = leftright_pow(x, n, (void*)&D, &_Flxq_sqr, &_Flxq_mul); return gerepileuptoleaf(av, y); } /* Inverse of x in Z/pZ[X]/(pol) or NULL if inverse doesn't exist * return lift(1 / (x mod (p,pol))) * not stack clean. * */ GEN Flxq_invsafe(GEN x, GEN T, ulong p) { GEN U, V; GEN z = Flx_extgcd(x, T, p, &U, &V); ulong iz; if (degpol(z)) return NULL; iz = Fl_inv ((ulong)z[2], p); return Flx_Fl_mul(U, iz, p); } GEN Flxq_inv(GEN x,GEN T,ulong p) { pari_sp av=avma; GEN U = Flxq_invsafe(x, T, p); if (!U) pari_err(talker,"non invertible polynomial in Flxq_inv"); return gerepileuptoleaf(av, U); } /* generates the list of powers of x of degree 0,1,2,...,l*/ GEN Flxq_powers(GEN x, long l, GEN T, ulong p) { GEN V = cgetg(l+2,t_VEC); long i, v = T[1]; gel(V,1) = Fl_to_Flx(1, v); if (l==0) return V; gel(V,2) = vecsmall_copy(x); if (l==1) return V; gel(V,3) = Flxq_sqr(x,T,p); if ((degpol(x)<<1) < degpol(T)) { for(i = 4; i < l+2; i++) gel(V,i) = Flxq_mul(gel(V,i-1),x,T,p); } else { for(i = 4; i < l+2; i++) { gel(V,i) = (i&1)? Flxq_sqr(gel(V, (i+1)>>1),T,p) : Flxq_mul(gel(V, i-1),x,T,p); } } return V; } GEN FlxV_Flc_mul(GEN V, GEN W, ulong p) { pari_sp ltop=avma; long i; GEN z = Flx_Fl_mul(gel(V,1),W[1],p); for(i=2;i1; i--) if (lgpol(x[i])) break; stackdummy((pari_sp)(x + lg(x)), (pari_sp)(x + i+1)); setlg(x, i+1); setsigne(x, i!=1); return x; } /* FlxX are t_POL with Flx coefficients. * Normally the variable ordering should be respected.*/ GEN FlxX_to_ZXX(GEN B) { long lb=lg(B); long i; GEN b=cgetg(lb,t_POL); for (i=2; i P(Y,X), n-1 is the degree in Y */ GEN FlxX_swap(GEN x, long n, long ws) { long j, lx = lg(x), ly = n+3; GEN y = cgetg(ly, t_POL); y[1] = x[1]; for (j=2; jlx) swapspec(x,y, lx,ly); lz = lx; z = cgetg(lz, t_POL); z[1]=x[1]; for (i=2; i= ly) { z[1] = x[1]; for (i=2; i=dy; i--) { av=avma; p1=gel(x,i); for (j=i-dy+1; j<=i && j<=dz; j++) p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p),p); if (lead) p1 = Flx_mul(p1, lead,p); tetpil=avma; gel(z,i-dy) = gerepile(av,tetpil,Flx_rem(p1,T,p)); } if (!pr) { if (lead) gunclone(lead); return z-2; } rem = (GEN)avma; av = (pari_sp)new_chunk(dx+3); for (sx=0; ; i--) { p1 = gel(x,i); for (j=0; j<=i && j<=dz; j++) p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p),p); tetpil=avma; p1 = Flx_rem(p1, T, p); if (lgpol(p1)) { sx = 1; break; } if (!i) break; avma=av; } if (pr == ONLY_DIVIDES) { if (lead) gunclone(lead); if (sx) { avma=av0; return NULL; } avma = (pari_sp)rem; return z-2; } lr=i+3; rem -= lr; rem[0] = evaltyp(t_POL) | evallg(lr); rem[1] = z[-1]; p1 = gerepile((pari_sp)rem,tetpil,p1); rem += 2; gel(rem,i) = p1; for (i--; i>=0; i--) { av=avma; p1 = gel(x,i); for (j=0; j<=i && j<=dz; j++) p1 = Flx_sub(p1, Flx_mul(gel(z,j),gel(y,i-j),p), p); tetpil=avma; gel(rem,i) = gerepile(av,tetpil, Flx_rem(p1, T, p)); } rem -= 2; if (lead) gunclone(lead); if (!sx) (void)FlxX_renormalize(rem, lr); if (pr == ONLY_REM) return gerepileupto(av0,rem); *pr = rem; return z-2; } static GEN FlxqX_invmontgomery_basecase(GEN T, GEN Q, ulong p) { long i, l=lg(T)-1, k; long sv=Q[1]; GEN r=cgetg(l,t_POL); r[1]=T[1]; gel(r,2) = zero_Flx(sv); gel(r,3) = Fl_to_Flx(1,sv); for (i=4;i= 0); if (!signe(P)) break; if (low_stack(st_lim, stack_lim(btop, 1))) { if (DEBUGMEM>1) pari_warn(warnmem,"FlxqX_safegcd"); gerepileall(btop, 2, &P,&Q); } swap(P, Q); dg = -dg; } Q = FlxqX_Flxq_mul(Q, U, T, p); /* normalize GCD */ return gerepileupto(ltop, Q); } /*******************************************************************/ /* */ /* (Fl[X]/T(X))[Y] / S(Y) */ /* */ /*******************************************************************/ /*Preliminary implementation to speed up FpX_ffisom*/ typedef struct { GEN S, T, mg; ulong p; } FlxYqQ_muldata; /* reduce x in Fl[X, Y] in the algebra Fl[X, Y]/ (P(X),Q(Y)) */ static GEN FlxYqQ_redswap(GEN x, GEN S, GEN mg, GEN T, ulong p) { pari_sp ltop=avma; long n=degpol(S); long m=degpol(T); long w = S[1]; GEN V = FlxX_swap(x,n,w); V = FlxqX_red(V,T,p); V = FlxX_swap(V,m,w); return gerepilecopy(ltop,V); } static GEN FlxYqQ_sqr(void *data, GEN x) { FlxYqQ_muldata *D = (FlxYqQ_muldata*)data; return FlxYqQ_redswap(FlxqX_sqr(x, D->S, D->p),D->S,D->mg,D->T,D->p); } static GEN FlxYqQ_mul(void *data, GEN x, GEN y) { FlxYqQ_muldata *D = (FlxYqQ_muldata*)data; return FlxYqQ_redswap(FlxqX_mul(x,y, D->S, D->p),D->S,D->mg,D->T,D->p); } /* x in Z[X,Y], S in Z[X] over Fq = Z[Y]/(p,T); compute lift(x^n mod (S,T,p)) */ GEN FlxYqQ_pow(GEN x, GEN n, GEN S, GEN T, ulong p) { pari_sp av = avma; FlxYqQ_muldata D; GEN y; D.S = S; D.T = T; D.p = p; y = leftright_pow(x, n, (void*)&D, &FlxYqQ_sqr, &FlxYqQ_mul); return gerepileupto(av, y); } typedef struct { GEN T, S; GEN mg; ulong p; } kronecker_muldata; static GEN FlxqXQ_red(void *data, GEN x) { kronecker_muldata *D = (kronecker_muldata*)data; GEN t = FlxqX_from_Kronecker(x, D->T,D->p); t = FlxqX_divrem(t, D->S,D->T,D->p, ONLY_REM); return FlxX_to_Kronecker(t,D->T); } static GEN FlxqXQ_mul(void *data, GEN x, GEN y) { return FlxqXQ_red(data, Flx_mul(x,y,((kronecker_muldata*) data)->p)); } static GEN FlxqXQ_sqr(void *data, GEN x) { return FlxqXQ_red(data, Flx_sqr(x,((kronecker_muldata*) data)->p)); } #if 0 static GEN FlxqXQ_red_montgomery(void *data, GEN x) { kronecker_muldata *D = (kronecker_muldata*)data; GEN t = FlxqX_from_Kronecker(x, D->T,D->p); t = FlxqX_rem_montgomery(t, D->S,D->mg,D->T,D->p); return FlxX_to_Kronecker(t,D->T); } static GEN FlxqXQ_mul_montgomery(void *data, GEN x, GEN y) { return FlxqXQ_red_montgomery(data, Flx_mul(x,y,((kronecker_muldata*) data)->p)); } static GEN FlxqXQ_sqr_montgomery(void *data, GEN x) { return FlxqXQ_red_montgomery(data, Flx_sqr(x,((kronecker_muldata*) data)->p)); } #endif long FlxqXQ_POW_MONTGOMERY_LIMIT = 0; /* x over Fq, return lift(x^n) mod S */ GEN FlxqXQ_pow(GEN x, GEN n, GEN S, GEN T, ulong p) { pari_sp av0 = avma; GEN y; kronecker_muldata D; D.S = S; D.T = T; D.p = p; #if 0 if (lgpol(S[2]) && degpol(S) >= FlxqXQ_POW_MONTGOMERY_LIMIT) { /* We do not handle polynomials multiple of x yet */ D.mg = FlxqX_invmontgomery(S,T,p); y = leftright_pow(FlxX_to_Kronecker(x,T), n, (void*)&D, &FlxqXQ_sqr_montgomery, &FlxqXQ_mul_montgomery); y = leftright_pow(FlxX_to_Kronecker(x,T), n, (void*)&D, &FlxqXQ_sqr, &FlxqXQ_mul); } else #endif y = leftright_pow(FlxX_to_Kronecker(x,T), n, (void*)&D, &FlxqXQ_sqr, &FlxqXQ_mul); y = FlxqX_from_Kronecker(y, T,p); return gerepileupto(av0, y); } struct _FlxqX {ulong p; GEN T;}; static GEN _FlxqX_mul(void *data,GEN a,GEN b) { struct _FlxqX *d=(struct _FlxqX*)data; return FlxqX_mul(a,b,d->T,d->p); } GEN FlxqXV_prod(GEN V, GEN T, ulong p) { struct _FlxqX d; d.p=p; d.T=T; return divide_conquer_assoc(V, &_FlxqX_mul, (void*)&d); } GEN FlxqV_roots_to_pol(GEN V, GEN T, ulong p, long v) { pari_sp ltop = avma; long k; GEN W = cgetg(lg(V),t_VEC); for(k=1; k < lg(V); k++) gel(W,k) = deg1pol_i(Fl_to_Flx(1,T[1]),Flx_neg(gel(V,k),p),v); return gerepileupto(ltop, FlxqXV_prod(W, T, p)); }