/* $Id: rootpol.c,v 1.59 2002/09/07 01:28:53 karim Exp $ Copyright (C) 2000 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. */ /*******************************************************************/ /* */ /* ROOTS OF COMPLEX POLYNOMIALS */ /* (written by Xavier Gourdon) */ /* */ /*******************************************************************/ #include "pari.h" extern GEN polrecip_i(GEN x); #define pariINFINITY 100000 #define NEWTON_MAX 10 static long KARASQUARE_LIMIT, COOK_SQUARE_LIMIT, Lmax; /********************************************************************/ /** **/ /** ARITHMETIQUE RAPIDE **/ /** **/ /********************************************************************/ /* fast product of x,y which must be integer or complex of integer */ static GEN quickmulcc(GEN x, GEN y) { long tx=typ(x),ty=typ(y); GEN z; if (tx==t_INT) { if (ty==t_INT) return mulii(x,y); if (ty==t_COMPLEX) { z=cgetg(3,t_COMPLEX); z[1]=(long) mulii(x,(GEN) y[1]); z[2]=(long) mulii(x,(GEN) y[2]); return z; } } if (tx==t_COMPLEX) { if (ty==t_INT) { z=cgetg(3,t_COMPLEX); z[1]=(long) mulii((GEN)x[1],y); z[2]=(long) mulii((GEN)x[2],y); return z; } if (ty==t_COMPLEX) { gpmem_t av, tetpil; GEN p1,p2; z=cgetg(3,t_COMPLEX); av=avma; p1=mulii((GEN)x[1],(GEN)y[1]); p2=mulii((GEN)x[2],(GEN)y[2]); x=addii((GEN)x[1],(GEN)x[2]); y=addii((GEN)y[1],(GEN)y[2]); y=mulii(x,y); x=addii(p1,p2); tetpil=avma; z[1]=lsubii(p1,p2); z[2]=lsubii(y,x); gerepilemanyvec(av,tetpil,z+1,2); return z; } } err(talker,"bug in quickmulcc"); return NULL; /* not reached */ } static void set_karasquare_limit(long bitprec) { if (bitprec<600) { KARASQUARE_LIMIT=8; COOK_SQUARE_LIMIT=400; return; } if (bitprec<2000) { KARASQUARE_LIMIT=4; COOK_SQUARE_LIMIT=200; return; } if (bitprec<3000) { KARASQUARE_LIMIT=4; COOK_SQUARE_LIMIT=125; return; } if (bitprec<5000) { KARASQUARE_LIMIT=2; COOK_SQUARE_LIMIT=75; return; } KARASQUARE_LIMIT=1; COOK_SQUARE_LIMIT=50; } /* the pari library does not have specific procedure for the square of polynomials. This one is twice faster than gmul */ static GEN mysquare(GEN p) { GEN s,aux1,aux2; long i, j, n=degpol(p), nn; gpmem_t ltop, lbot; if (n==-1) return gcopy(p); nn=n<<1; s=cgetg(nn+3,t_POL); s[1] = evalsigne(1) | evalvarn(varn(p)) | evallgef(nn+3); for (i=0; i<=n; i++) { aux1=gzero; ltop=avma; for (j=0; j<(i+1)>>1; j++) { aux2=quickmulcc((GEN) p[j+2],(GEN)p[i-j+2]); aux1=gadd(aux1,aux2); } if (i%2==1) { lbot=avma; s[i+2]=lpile(ltop,lbot,gshift(aux1,1)); } else { aux1=gshift(aux1,1); aux2=quickmulcc((GEN)p[2+(i>>1)],(GEN)p[2+(i>>1)]); lbot=avma; s[i+2]=lpile(ltop,lbot,gadd(aux1,aux2)); } } for (i=n+1; i<=nn; i++) { aux1=gzero; ltop=avma; for (j=i-n; j<(i+1)>>1; j++) { aux2=quickmulcc((GEN)p[j+2],(GEN)p[i-j+2]); aux1=gadd(aux1,aux2); } if (i%2==1) { lbot=avma; s[i+2]=lpile(ltop,lbot,gshift(aux1,1)); } else { aux1=gshift(aux1,1); aux2=quickmulcc((GEN)p[2+(i>>1)],(GEN)p[2+(i>>1)]); lbot=avma; s[i+2]=lpile(ltop,lbot,gadd(aux1,aux2)); } } return s; } static GEN karasquare(GEN p) { GEN p1,s0,s1,s2,aux; long n=degpol(p),n0,n1,i,var,nn0; gpmem_t ltop; if (n<=KARASQUARE_LIMIT) return mysquare(p); ltop=avma; var=evalsigne(1)+evalvarn(varn(p)); n0=n>>1; n1=n-n0-1; setlgef(p,n0+3); /* hack to have the first half of p */ s0=karasquare(p); p1=cgetg(n1+3,t_POL); p1[1]=var+evallgef(n1+3); for (i=2; i<=n1+2; i++) p1[i]=p[1+i+n0]; s2=karasquare(p1); s1=karasquare(gadd(p,p1)); s1=gsub(s1,gadd(s0,s2)); nn0=n0<<1; aux=cgetg((n<<1)+3,t_POL); aux[1]=var+evallgef(2*n+3); var=lgef(s0); for (i=2; i>1; n1=(n-1)>>1; auxi=evalsigne(1)|evalvarn(varn(p)); p0=cgetg(n0+3,t_POL); p0[1]=auxi|evallgef(n0+3); p1=cgetg(n1+3,t_POL); p1[1]=auxi|evallgef(n1+3); for (i=0; i<=n0; i++) p0[i+2]=p[2+(i<<1)]; for (i=0; i<=n1; i++) p1[i+2]=p[3+(i<<1)]; s0=cook_square(p0); s1=cook_square(p1); ns1 = degpol(s1); ss1 = cgetg(ns1+4, t_POL); ss1[1] = auxi | evallgef(ns1+4); ss1[2]=zero; for (i=0; i<=ns1; i++) ss1[3+i]=lneg((GEN)s1[2+i]); /* now ss1 contains -x * s1 */ return gadd(s0,ss1); } /********************************************************************/ /** **/ /** FACTORISATION SQUAREFREE AVEC LE GCD MODULAIRE **/ /** **/ /********************************************************************/ /* return a n x 2 matrix: * col 1 contains the i's such that A_i non constant * col 2 the A_i's, s.t. pol = A_i1^i1.A_i2^i2...A_in^in. * if pol is constant return [;] */ GEN square_free_factorization(GEN pol) { long deg,i,j,m; GEN p1,x,t1,v1,t,v,A; if (typ(pol)!=t_POL) err(typeer,"square_free_factorization"); deg=degpol(pol); if (deg<1) return cgetg(1,t_MAT); p1 = content(pol); if (!gcmp1(p1)) pol = gdiv(pol,p1); x=cgetg(3,t_MAT); t1 = NULL; /* gcc -Wall */ if (deg > 1) { t1 = modulargcd(pol,derivpol(pol)); if (isscalar(t1)) deg = 1; } if (deg==1) { x[1]=lgetg(2,t_COL); p1=(GEN)x[1]; p1[1]=un; x[2]=lgetg(2,t_COL); p1=(GEN)x[2]; p1[1]=(long)pol; return x; } A=new_chunk(deg+1); v1=gdivexact(pol,t1); v=v1; i=0; while (lgef(v)>3) { v=modulargcd(t1,v1); i++; A[i]=(long)gdivexact(v1,v); t=gdivexact(t1,v); v1=v; t1=t; } m=1; x[1]=lgetg(deg+1,t_COL); x[2]=lgetg(deg+1,t_COL); for (j=1; j<=i; j++) if (isnonscalar(A[j])) { p1=(GEN)x[1]; p1[m] = lstoi(j); p1=(GEN)x[2]; p1[m] = A[j]; m++; } setlg(x[1],m); setlg(x[2],m); return x; } /********************************************************************/ /** **/ /** CALCUL DU MODULE DES RACINES **/ /** **/ /********************************************************************/ static double log2ir(GEN x) { double l; if (signe(x)==0) return (double) -pariINFINITY; if (typ(x)==t_INT) { if (lgefint(x)==3) return (double) log2( (double)(ulong) x[2]); l=(double)(ulong) x[2]+ (double)(ulong) x[3] / exp2((double) BITS_IN_LONG); return log2(l)+ (double) BITS_IN_LONG * (lgefint(x)-3.); } /* else x is real */ return 1.+ (double) expo(x)+log2( (double)(ulong) x[2]) - (double) BITS_IN_LONG; } double mylog2(GEN z) { double x,y; if (typ(z)!=t_COMPLEX) return log2ir(z); x = log2ir((GEN) z[1]); y = log2ir((GEN) z[2]); if (fabs(x-y)>10) return (x>y)? x: y; return x+0.5*log2( 1 + exp2(2*(y-x))); } /* find s such that A_h <= 2^s <= 2 A_i for one h and all i < n = deg(p), * with A_i := (binom(n,i) lc(p) / p_i) ^ 1/(n-i), and p = sum p_i X^i */ static long findpower(GEN p) { double x, logbinomial, mins = pariINFINITY; long n = degpol(p),i; logbinomial = mylog2((GEN)p[n+2]); /* log2(lc * binom(n,i)) */ for (i=n-1; i>=0; i--) { logbinomial += log2((double) (i+1) / (double) (n-i)); x = mylog2((GEN)p[i+2]); if (x != -pariINFINITY) { double s = (logbinomial - x) / (double) (n-i); if (s < mins) mins = s; } } i = (long)ceil(mins); if (i - mins > 1 - 1e-12) i--; return i; } /* returns the exponent for the procedure modulus, from the newton diagram */ static long newton_polygon(GEN p, long k) { double *logcoef,pente; long n=degpol(p),i,j,h,l,*vertex,pentelong; logcoef = (double*) gpmalloc((n+1)*sizeof(double)); vertex = (long*) gpmalloc((n+1)*sizeof(long)); /* vertex[i]=1 if i a vertex of convex hull, 0 otherwise */ for (i=0; i<=n; i++) { logcoef[i] = mylog2((GEN)p[2+i]); vertex[i] = 0; } vertex[0]=1; for (i=0; i < n; i=h) { pente = logcoef[i+1]-logcoef[i]; h = i+1; for (j=i+1; j<=n; j++) if (pente < (logcoef[j]-logcoef[i])/(double)(j-i)) { h = j; pente = (logcoef[j]-logcoef[i])/(double)(j-i); } vertex[h] = 1; } h = k; while (!vertex[h]) h++; l = k-1; while (!vertex[l]) l--; pentelong = (long) floor((logcoef[h]-logcoef[l])/(double)(h-l) + 0.5); free(logcoef); free(vertex); return pentelong; } /* change z into z*2^e, where z is real or complex of real */ static void myshiftrc(GEN z, long e) { if (typ(z)==t_COMPLEX) { if (signe(z[1])!=0) setexpo(z[1], expo(z[1])+e); if (signe(z[2])!=0) setexpo(z[2], expo(z[2])+e); } else if (signe(z)!=0) setexpo(z,expo(z)+e); } /* return z*2^e, where z is integer or complex of integer (destroy z) */ static GEN myshiftic(GEN z, long e) { if (typ(z)==t_COMPLEX) { z[1]=signe(z[1])? lmpshift((GEN) z[1],e): zero; z[2]=lmpshift((GEN) z[2],e); return z; } return signe(z)? mpshift(z,e): gzero; } /* as realun with precision in bits, not in words */ static GEN myrealun(long bitprec) { if (bitprec < 0) bitprec = 0; return realun(bitprec/BITS_IN_LONG+3); } static GEN mygprecrc(GEN x, long bitprec, long e) { long tx=typ(x); GEN y; if (bitprec<0) bitprec=0; /* should rarely happen */ switch(tx) { case t_REAL: y=cgetr(bitprec/BITS_IN_LONG+3); affrr(x,y); if (!signe(x)) setexpo(y,-bitprec+e); break; case t_COMPLEX: y=cgetg(3,t_COMPLEX); y[1]=(long) mygprecrc((GEN)x[1],bitprec,e); y[2]=(long) mygprecrc((GEN)x[2],bitprec,e); break; default: y=gcopy(x); } return y; } /* gprec behaves badly with the zero for polynomials. The second parameter in mygprec is the precision in base 2 */ static GEN mygprec(GEN x, long bitprec) { long tx=typ(x),lx,i,e = gexpo(x); GEN y; switch(tx) { case t_POL: lx=lgef(x); y=cgetg(lx,tx); y[1]=x[1]; for (i=2; i=0; i--) { r[i+2] = lmul(aux,(GEN)q[i+2]); aux = mulrr(aux,gR); } return r; } /* change q in 2^(n*e) p(x*2^(-e)), n=deg(q) [ ~as above with R = 2^-e ]*/ static void homothetie2n(GEN p, long e) { if (e) { long i,n=lgef(p)-1; for (i=2; i<=n; i++) myshiftrc((GEN) p[i],(n-i)*e); } } /* return 2^f * 2^(n*e) p(x*2^(-e)), n=deg(q) */ static void homothetie_gauss(GEN p, long e,long f) { if (e||f) { long i, n=lgef(p)-1; for (i=2; i<=n; i++) p[i]=(long) myshiftic((GEN) p[i],f+(n-i)*e); } } /* provides usually a good lower bound on the largest modulus of the roots, puts in k an upper bound of the number of roots near the largest root at a distance eps */ static double lower_bound(GEN p, long *k, double eps) { long n=degpol(p), i, j; gpmem_t ltop=avma; GEN a,s,S,icd; double r,*rho; if (n<4) { *k=n; return 0.; } S = cgetg(5,t_VEC); a = cgetg(5,t_VEC); rho=(double *) gpmalloc(4*sizeof(double)); icd = gdiv(realun(DEFAULTPREC), (GEN) p[n+2]); for (i=1; i<=4; i++) a[i] = lmul(icd,(GEN)p[n+2-i]); for (i=1; i<=4; i++) { s = gmulsg(i,(GEN)a[i]); for (j=1; j0. && eps<1.2) *k = (long) floor((n*rho[0]/r+n)/(1+exp(-eps)*cos(eps))); else *k = n; free(rho); avma = ltop; return r; } /* returns the maximum of the modulus of p with a relative error tau */ GEN max_modulus(GEN p, double tau) { GEN r,q,aux,gunr; gpmem_t av, ltop = avma; long i,k,n=degpol(p),nn,bitprec,M,e; double rho,eps, tau2 = (tau > 3.0)? 0.5: tau/6.; r = cgeti(BIGDEFAULTPREC); av = avma; eps = - 1/log(1.5*tau2); /* > 0 */ bitprec = (long) ((double) n*log2(1./tau2)+3*log2((double) n))+1; gunr = myrealun(bitprec+2*n); aux = gdiv(gunr,(GEN) p[2+n]); q = gmul(aux,p); q[2+n] = (long)gunr; e = findpower(q); homothetie2n(q,e); affsi(e, r); q = mygprec(q,bitprec+(n<<1)); pol_to_gaussint(q,bitprec); M = (long) (log2( log(4.*n) / (2*tau2) )) + 2; nn = n; for (i=0,e=0;;) { /* nn = deg(q) */ rho = lower_bound(q,&k,eps); if (rho > exp2(-(double) e)) e = (long) -floor(log2(rho)); affii(shifti(addis(r,e), 1), r); if (++i == M) break; bitprec = (long) ((double) k* log2(1./tau2) + (double) (nn-k)*log2(1./eps) + 3*log2((double) nn)) + 1; homothetie_gauss(q,e,bitprec-(long)floor(mylog2((GEN) q[2+nn])+0.5)); nn -= polvaluation(q, &q); set_karasquare_limit(gexpo(q)); q = gerepileupto(av, graeffe(q)); tau2 *= 1.5; if (tau2 > 0.9) tau2 = 0.5; eps = -1/log(tau2); /* > 0 */ e = findpower(q); } if (!signe(r)) { avma = ltop; return realun(DEFAULTPREC); } r = itor(r, DEFAULTPREC); setexpo(r, expo(r) - M); rho = rtodbl(r); /* rho = sum e_i 2^-i */ avma = ltop; return mpexp(dbltor(-rho * LOG2)); /* 2^-r */ } /* return the k-th modulus (in ascending order) of p, rel. error tau*/ static GEN modulus(GEN p, long k, double tau) { GEN q,gunr; long i, kk=k, imax, n=degpol(p), nn, bitprec, decprec, e; gpmem_t av, ltop=avma; double tau2,r; tau2 = tau/6; bitprec= (long) ((double) n*(2.+log2(3.*(double) n)+log2(1./tau2))); decprec=(long) ((double) bitprec * L2SL10)+1; gunr=myrealun(bitprec); av = avma; q = gprec(p,decprec); q = gmul(gunr,q); e = newton_polygon(q,k); homothetie2n(q,e); r = (double) e; imax=(long) ((log2(3./tau)+log2(log(4.*(double) n)) ))+1; for (i=1; i 1.) tau2 = 1.; bitprec= 1+(long) ((double) nn*(2.+log2(3.*(double) nn)+log2(1./tau2))); } avma=ltop; return mpexp(dbltor(-r * LOG2)); } /* return the k-th modulus r_k of p, rel. error tau, knowing that rmin < r_k < rmax. This helps because the information enable us to use less precision... quicker ! */ static GEN pre_modulus(GEN p, long k, double tau, GEN rmin, GEN rmax) { GEN R, q, aux; long n=degpol(p), i, imax, imax2, bitprec; gpmem_t ltop=avma, av; double tau2, aux2; tau2=tau/6.; aux = mulrr(mpsqrt(divrr(rmax,rmin)), dbltor(exp(4*tau2))); imax = (long) log2(log((double)n)/ rtodbl(mplog(aux))); if (imax<=0) return modulus(p,k,tau); R = mpsqrt(mulrr(rmin,rmax)); av = avma; bitprec = (long) ((double) n*(2. + log2ir(aux) - log2(tau2))); q = homothetie(p,R,bitprec); imax2 = (long) ((log2(3./tau)+log2(log(4.*(double) n)) ))+1; if (imax>imax2) imax=imax2; for (i=0; ilogmax) { logmax=aux; k=i; } } avma=ltop; return delta_k+k; } /********************************************************************/ /** **/ /** CALCUL D'UN FACTEUR PAR INTEGRATION SUR LE CERCLE **/ /** **/ /********************************************************************/ static GEN gmulbyi(GEN z) { GEN aux = cgetg(3,t_COMPLEX); if (typ(z)!=t_COMPLEX) { aux[1]=zero; aux[2]=(long) z; } else { aux[1]=lneg((GEN)z[2]); aux[2]=z[1]; } return aux; } static void fft(GEN Omega, GEN p, GEN f, long Step, long l) { gpmem_t ltop; long i,l1,l2,l3,rap=Lmax/l,rapi,Step4; GEN f1,f2,f3,f02,f13,g02,g13,ff; if (l==2) { f[0]=ladd((GEN)p[0],(GEN)p[Step]); f[1]=lsub((GEN)p[0],(GEN)p[Step]); return; } if (l==4) { f1=gadd((GEN)p[0],(GEN)p[(Step<<1)]); f3=gadd((GEN)p[Step],(GEN)p[3*Step]); f[0]=ladd(f1,f3); f[2]=lsub(f1,f3); f2=gsub((GEN)p[0],(GEN)p[(Step<<1)]); f02=gsub((GEN)p[Step],(GEN)p[3*Step]); f02=gmulbyi(f02); f[1]=ladd(f2,f02); f[3]=lsub(f2,f02); return; } l1=(l>>2); l2=(l1<<1); l3=l1+l2; Step4=(Step<<2); ltop=avma; fft(Omega,p,f,Step4,l1); fft(Omega,p+Step,f+l1,Step4,l1); fft(Omega,p+(Step<<1),f+l2,Step4,l1); fft(Omega,p+3*Step,f+l3,Step4,l1); ff=cgetg(l+1,t_VEC); for (i=0; i>1),N4=(N>>2),N8=(N>>3); RU = (GEN*)cgetg(N+1,t_VEC); RU++; prim = RUgen(N, bitprec); RU[0] = myrealun(bitprec); for (i=1; i<=N8; i++) RU[i] = gmul(prim, RU[i-1]); for (i=1; i>1; RU[0] = gun; RU[1] = z; for (i=2; in); } static void parameters(GEN p, double *mu, double *gamma, int polreal, double param, double param2) { GEN q,pc,Omega,coef,RU,prim,aux,aux0,ggamma,gx,mygpi; long n=degpol(p), bitprec, NN, K, i, j; gpmem_t ltop2, ltop=avma, limite=stack_lim(ltop, 1); double lx; bitprec=gexpo(p)+(long)param2+8; NN=(long) (param*3.14)+1; if (NN0 && i1) err(warnmem,"parameters"); gptr[0]=&ggamma; gptr[1]=&RU; gerepilemany(ltop2,gptr,2); } } ggamma=gdivgs(ggamma,NN); *gamma=gtodouble(glog(ggamma,DEFAULTPREC))/log(2.); avma=ltop; } /* NN is a multiple of Lmax */ static void dft(GEN p, long k, long NN, long bitprec, GEN F, GEN H, long polreal) { GEN Omega,q,qd,pc,pdc,alpha,beta,gamma,RU,aux,U,W,mygpi,prim,prim2,*gptr[3]; long n=degpol(p),i,j,K; gpmem_t ltop; mygpi=mppi(bitprec/BITS_IN_LONG+3); aux = gdivgs(mygpi,NN/2); /* 2 Pi/NN */ prim = exp_Ir(aux); aux = gmulbyi(aux); prim2 = myrealun(bitprec); RU=cgetg(n+2,t_VEC); RU++; Omega=initRU(Lmax,bitprec); K=NN/Lmax; q=mygprec(p,bitprec); qd=derivpol(q); pc=cgetg(Lmax+1,t_VEC); pc++; for (i=n+1; i0 && i-bitprec && i1) err(warnmem,"refine_H"); gerepilemany(ltop,gptr,2); } bitprec1=-error+shiftbitprec; aux=gmul(mygprec(H,bitprec1),mygprec(D,bitprec1)); aux=mygprec(aux,bitprec1); aux=gres(aux,mygprec(F,bitprec1)); bitprec1=-error*2+shiftbitprec; if (bitprec1>bitprec2) bitprec1=bitprec2; H=gadd(mygprec(H,bitprec1),aux); D=gsub(gun,gres(gmul(H,G),F)); error=gexpo(D); if (error<-bitprec1) error=-bitprec1; } if (error>-bitprec/2) return NULL; /* FAIL */ return gerepilecopy(ltop,H); } /* return 0 if fails, 1 else */ static long refine_F(GEN p, GEN *F, GEN *G, GEN H, long bitprec, double gamma) { GEN pp,FF,GG,r,HH,f0; long error, i, bitprec1=0, bitprec2, shiftbitprec; long shiftbitprec2, n=degpol(p), enh, normF, normG; gpmem_t ltop=avma, limite=stack_lim(ltop, 1); FF=*F; HH=H; GG=poldivres(p,*F,&r); normF=gexpo(FF); normG=gexpo(GG); enh=gexpo(H); if (enh<0) enh=0; shiftbitprec=normF+2*normG+enh+(long) (4.*log2((double)n)+gamma) +1; shiftbitprec2=enh+2*(normF+normG)+(long) (2.*gamma+5.*log2((double)n))+1; bitprec2=bitprec+shiftbitprec; error=gexpo(r); if (error<-bitprec) error=1-bitprec; for (i=0; (error>-bitprec && i=2)) { shiftbitprec+=n; shiftbitprec2+=n; bitprec2+=n; } if (low_stack(limite, stack_lim(ltop,1))) { GEN *gptr[4]; gptr[0]=&FF; gptr[1]=&GG; gptr[2]=&r; gptr[3]=&HH; if(DEBUGMEM>1) err(warnmem,"refine_F"); gerepilemany(ltop,gptr,4); } bitprec1=-error+shiftbitprec2; HH=refine_H(mygprec(FF,bitprec1),mygprec(GG,bitprec1), mygprec(HH,bitprec1),1-error,shiftbitprec2); if (!HH) return 0; /* procedure failed */ bitprec1=-error+shiftbitprec; r=gmul(mygprec(HH,bitprec1),mygprec(r,bitprec1)); r=mygprec(r,bitprec1); f0=gres(r,mygprec(FF,bitprec1)); bitprec1=-2*error+shiftbitprec; if (bitprec1>bitprec2) bitprec1=bitprec2; FF=gadd(mygprec(FF,bitprec1),f0); bitprec1=-3*error+shiftbitprec; if (bitprec1>bitprec2) bitprec1=bitprec2; pp=mygprec(p,bitprec1); GG=poldivres(pp,mygprec(FF,bitprec1),&r); error=gexpo(r); if (error<-bitprec1) error=-bitprec1; } if (error>-bitprec) return 0; /* FAIL */ *F=FF; *G=GG; return 1; } /* returns F and G from the unit circle U such that |p-FG|<2^(-bitprec) |cd|, where cd is the leading coefficient of p */ static void split_fromU(GEN p, long k, double delta, long bitprec, GEN *F, GEN *G, double param, double param2) { GEN pp,FF,GG,H; long n=degpol(p),NN,bitprec2; int polreal=isreal(p); gpmem_t ltop; double mu,gamma; pp=gdiv(p,(GEN)p[2+n]); Lmax=4; while (Lmax<=n) Lmax=(Lmax<<1); parameters(pp,&mu,&gamma,polreal,param,param2); H =cgetg(k+2,t_POL); H[1] =evalsigne(1) | evalvarn(varn(p)) | evallgef(k+2); FF=cgetg(k+3,t_POL); FF[1]=evalsigne(1) | evalvarn(varn(p)) | evallgef(k+3); FF[k+2]=un; NN=(long) (0.5/delta); NN+=(NN%2); if (NN<2) NN=2; NN=NN*Lmax; ltop=avma; for(;;) { bitprec2=(long) (((double) NN*delta-mu)/log(2.))+gexpo(pp)+8; dft(pp,k,NN,bitprec2,FF,H,polreal); if (refine_F(pp,&FF,&GG,H,bitprec,gamma)) break; NN <<= 1; avma=ltop; } *G=gmul(GG,(GEN)p[2+n]); *F=FF; } static void optimize_split(GEN p, long k, double delta, long bitprec, GEN *F, GEN *G, double param, double param2) { long n=degpol(p); GEN FF,GG; if (k<=n/2) split_fromU(p,k,delta,bitprec,F,G,param,param2); else { /* start from the reciprocal of p */ split_fromU(polrecip_i(p),n-k,delta,bitprec,&FF,&GG,param,param2); *F=polrecip(GG); *G=polrecip(FF); } } /********************************************************************/ /** **/ /** RECHERCHE DU CERCLE DE SEPARATION **/ /** **/ /********************************************************************/ /* return p(2^e*x) *2^(-n*e) */ static void scalepol2n(GEN p, long e) { long i,n=lgef(p)-1; for (i=2; i<=n; i++) p[i]=lmul2n((GEN)p[i],(i-n)*e); } /* returns p(x/R)*R^n */ static GEN scalepol(GEN p, GEN R, long bitprec) { GEN q,aux,gR; long i; aux = gR = mygprec(R,bitprec); q = mygprec(p,bitprec); for (i=lgef(p)-2; i>=2; i--) { q[i]=lmul(aux,(GEN)q[i]); aux = gmul(aux,gR); } return q; } extern GEN addshiftpol(GEN x, GEN y, long d); /* returns q(x) = p(x+b) */ static GEN shiftpol(GEN p, GEN b) { long i; gpmem_t av = avma, limit = stack_lim(av, 1); GEN q = gzero; if (gcmp0(b)) return p; for (i=lgef(p)-1; i>=2; i--) { if (!signe(q)) { q = scalarpol((GEN)p[i], varn(p)); continue; } q = addshiftpol(q, gmul(b,q), 1); /* q = q*(x + b) */ q[2] = ladd((GEN)q[2], (GEN)p[i]); /* q = q + p[i] */ if (low_stack(limit, stack_lim(av,1))) { if(DEBUGMEM>1) err(warnmem,"rootpol.c:shiftpol()"); q = gerepilecopy(av, q); } } return gerepilecopy(av, q); } /* return (conj(a)X-1)^n * p[ (X-a) / (conj(a)X-1) ] */ static GEN conformal_pol(GEN p, GEN a, long bitprec) { GEN r,pui,num,aux, unr = myrealun(bitprec); long n=degpol(p), i; gpmem_t av, limit; aux = pui = cgetg(4,t_POL); pui[1] = evalsigne(1) | evalvarn(varn(p)) | evallgef(4); pui[2] = (long)negr(unr); pui[3] = lconj(a); /* X conj(a) - 1 */ num = cgetg(4,t_POL); num[1] = pui[1]; num[2] = lneg(a); num[3] = (long)unr; /* X - a */ r = (GEN)p[2+n]; av = avma; limit = stack_lim(av,2); for (i=n-1; ; i--) { r = gadd(gmul(r,num), gmul(aux,(GEN) p[2+i])); if (i == 0) return r; aux = gmul(pui,aux); if (low_stack(limit, stack_lim(av,2))) { GEN *gptr[2]; gptr[0] = &r; gptr[1] = &aux; if(DEBUGMEM>1) err(warnmem,"rootpol.c:conformal_pol()"); gerepilemany(av, gptr, 2); } } } static GEN compute_radius(GEN* radii, GEN p, long k, double aux, double *delta) { long i, n = degpol(p); GEN rmin,rmax,p1; if (k>1) { i=k-1; while (i>0 && !signe(radii[i])) i--; rmin = pre_modulus(p,k,aux, radii[i], radii[k]); } else /* k=1 */ rmin = min_modulus(p,aux); affrr(rmin, radii[k]); if (k+1=1; i--) { if (!signe(radii[i]) || cmprr(radii[i], p1) > 0) affrr(p1, radii[i]); else p1 = radii[i]; } p1 = radii[k+1]; for (i=k+1; i<=n; i++) { if (!signe(radii[i]) || cmprr(radii[i], p1) < 0) affrr(p1, radii[i]); else p1 = radii[i]; } *delta = rtodbl(gmul2n(mplog(divrr(rmax,rmin)), -1)); if (*delta > 1.) *delta = 1.; return mpsqrt(mulrr(rmin,rmax)); } static GEN update_radius(GEN *radii, GEN rho, double *par, double *par2) { GEN p1, invrho = ginv(rho); long i, n = lg(radii); double t, param = 0., param2 = 0.; for (i=1; i 1.) param2 += log2(t); } *par = param; *par2 = param2; return invrho; } /* apply the conformal mapping then split from U */ static void conformal_mapping(GEN *radii, GEN ctr, GEN p, long k, long bitprec, double aux, GEN *F,GEN *G) { long bitprec2, n=degpol(p), decprec, i; gpmem_t ltop = avma, av; GEN q,FF,GG,a,R, *gptr[2]; GEN rho,invrho; double delta,param,param2; bitprec2=bitprec+(long) (n*(2.*log2(2.732)+log2(1.5)))+1; a=gsqrt(stoi(3), 2*MEDDEFAULTPREC - 2); a=gmul(mygprec(a,bitprec2),mygprec(ctr,bitprec2)); a=gdivgs(a,-6); /* a = -ctr/2sqrt(3) */ av = avma; q = mygprec(p,bitprec2); q = conformal_pol(q,a,bitprec2); for (i=1; i<=n; i++) if (signe(radii[i])) /* updating array radii */ { gpmem_t a = avma; GEN p1 = gsqr(radii[i]); /* 2(r^2 - 1) / (r^2 - 3(r-1)) */ p1 = divrr(gmul2n((subrs(p1,1)),1), subrr(p1, mulsr(3,subrs(radii[i],1)))); affrr(mpsqrt(addsr(1,p1)), radii[i]); avma = a; } rho = compute_radius(radii, q,k,aux/10., &delta); invrho = update_radius(radii, rho, ¶m, ¶m2); bitprec2 += (long) (((double)n) * fabs(log2ir(rho)) + 1.); R = mygprec(invrho,bitprec2); q = scalepol(q,R,bitprec2); gptr[0] = &q; gptr[1] = &R; gerepilemany(av,gptr,2); optimize_split(q,k,delta,bitprec2,&FF,&GG,param,param2); bitprec2 += n; R = ginv(R); FF = scalepol(FF,R,bitprec2); GG = scalepol(GG,R,bitprec2); a = mygprec(a,bitprec2); FF = conformal_pol(FF,a,bitprec2); GG = conformal_pol(GG,a,bitprec2); a = ginv(gsub(gun, gnorm(a))); a = glog(a,(long) (bitprec2 * L2SL10)+1); decprec = (long) ((bitprec+n) * L2SL10)+1; FF = gmul(FF,gexp(gmulgs(a,k),decprec)); GG = gmul(GG,gexp(gmulgs(a,n-k),decprec)); *F = mygprec(FF,bitprec+n); *G = mygprec(GG,bitprec+n); gptr[0]=F; gptr[1]=G; gerepilemany(ltop,gptr,2); } static GEN myrealzero(void) { GEN x = cgetr(3); x[1] = evalexpo(-bit_accuracy(3)); return x; } /* split p, this time with no scaling. returns in F and G two polynomials such that |p-FG|< 2^(-bitprec)|p| */ static void split_2(GEN p, long bitprec, GEN ctr, double thickness, GEN *F, GEN *G) { GEN rmin,rmax,rho,invrho; double kappa,aux,delta,param,param2; long n=degpol(p),i,j,k,bitprec2; GEN q,FF,GG,R; GEN *radii = (GEN*) cgetg(n+1, t_VEC); for (i=2; ii+1) { if (i+j==n+1) rho = mpsqrt(mulrr(rmin,rmax)); else { kappa = 1. - log(1.+(double)min(i,n-j)) / log(1.+(double)min(j,n-i)); if (i+j n)) { rmax=rho; j=k+1; affrr(mulrr(rho, dbltor(exp(-aux))), radii[j]); } else { rmin=rho; i=k; affrr(mulrr(rho, dbltor(exp(aux))), radii[i]); } } aux = rtodbl(mplog(divrr(rmax, rmin))); if (ctr) { rho = mpsqrt(mulrr(rmax,rmin)); invrho = ginv(rho); for (i=1; i<=n; i++) if (signe(radii[i])) affrr(mulrr(radii[i],invrho), radii[i]); bitprec2 = bitprec + (long) ((double)n * fabs(log2ir(rho)) + 1.); R = mygprec(invrho,bitprec2); q = scalepol(p,R,bitprec2); conformal_mapping(radii, ctr, q, k, bitprec2, aux, &FF, &GG); } else { rho = compute_radius(radii, p, k, aux/10., &delta); invrho = update_radius(radii, rho, ¶m, ¶m2); bitprec2 = bitprec + (long) ((double)n * fabs(log2ir(rho)) + 1.); R = mygprec(invrho,bitprec2); q = scalepol(p,R,bitprec2); optimize_split(q,k,delta,bitprec2,&FF,&GG,param,param2); } bitprec += n; bitprec2 += n; R = ginv(mygprec(R,bitprec2)); *F = mygprec(scalepol(FF,R,bitprec2), bitprec); *G = mygprec(scalepol(GG,R,bitprec2), bitprec); } /* procedure corresponding to steps 5,6,.. page 44 in the RR n. 1852 */ /* put in F and G two polynomial such that |p-FG|<2^(-bitprec)|p| where the maximum modulus of the roots of p is <=1 and the sum of roots is zero */ static void split_1(GEN p, long bitprec, GEN *F, GEN *G) { long bitprec2,i,imax,n=degpol(p), polreal = isreal(p), ep = gexpo(p); GEN rmax,rmin,thickness,quo; GEN ctr,q,qq,FF,GG,v,gr,r, newq = NULL; /* gcc -Wall */ r = max_modulus(p,0.01); bitprec2 = bitprec+n; gr = mygprec(ginv(r),bitprec2); q = scalepol(p,gr,bitprec2); bitprec2 = bitprec + gexpo(q) - ep + (long)((double)n*2.*log2(3.)+1); v = cgetg(5,t_VEC); v[1] = lmul2n(myrealun(bitprec2), 1); v[2] = lneg((GEN)v[1]); v[3] = lmul((GEN)v[1],gi); v[4] = lneg((GEN)v[3]); q = mygprec(q,bitprec2); thickness = realun(3); ctr = NULL; imax = polreal? 3: 4; for (i=1; i<=imax; i++) { qq = shiftpol(q, (GEN)v[i]); rmin = min_modulus(qq,0.05); if (cmpsr(3, mulrr(rmin, thickness)) > 0) { rmax = max_modulus(qq,0.05); quo = divrr(rmax,rmin); if (cmprr(quo, thickness) > 0) { thickness=quo; newq=qq; ctr=(GEN)v[i]; } } if (expo(thickness) > 0) break; /* thickness > 2 */ if (polreal && i==2 && rtodbl(thickness) > 1.5) break; } bitprec2 = bitprec + gexpo(newq) - ep + (long)((double)n*log2(3.)+1); split_2(newq,bitprec2,ctr, rtodbl(mplog(thickness)),&FF,&GG); r = gneg(mygprec(ctr,bitprec2)); FF = shiftpol(FF,r); GG = shiftpol(GG,r); gr = ginv(gr); bitprec2 = bitprec - ep + gexpo(FF)+gexpo(GG); *F = scalepol(FF,gr,bitprec2); *G = scalepol(GG,gr,bitprec2); } /* put in F and G two polynomials such that |P-FG|<2^(-bitprec)|P|, where the maximum modulus of the roots of p is < 0.5 */ static int split_0_2(GEN p, long bitprec, GEN *F, GEN *G) { GEN q,b,FF,GG; long n=degpol(p),k,bitprec2,i, eq; double aux = mylog2((GEN)p[n+1]) - mylog2((GEN)p[n+2]); /* beware double overflow */ if (aux >= 0 && (aux > 1e4 || exp2(aux) > 2.5*n)) return 0; aux = (aux < -300)? 0.: (double) n*log2(1 + exp2(aux)/(double)n); bitprec2=bitprec+1+(long) (log2((double)n)+aux); q=mygprec(p,bitprec2); b=gdivgs(gdiv((GEN)q[n+1],(GEN)q[n+2]),-n); q = shiftpol(q,b); k=0; eq=gexpo(q); while (k <= n/2 && (gexpo((GEN)q[k+2]) < -(bitprec2+2*(n-k)+eq) || gcmp0((GEN)q[k+2]))) k++; if (k>0) { if (k>n/2) k=n/2; bitprec2+=(k<<1); FF=cgetg(k+3,t_POL); FF[1]=evalsigne(1)|evalvarn(varn(p))|evallgef(k+3); for (i=0; i0) { if (k>n/2) k=n/2; FF=cgetg(k+3,t_POL); FF[1]=evalsigne(1) | evalvarn(varn(p)) | evallgef(k+3); for (i=0; ie_max) e_max=e; roots_pol[i] = (long)mygprec_absolute((GEN)roots_pol[i],-e); } return e_max; } /********************************************************************/ /** **/ /** MAIN **/ /** **/ /********************************************************************/ static GEN append_clone(GEN r, GEN a) { a = gclone(a); appendL(r, a); return a; } /* put roots in placeholder roots_pol so that |P-L_1...L_n|<2^(-bitprec)|P| * and returns prod (x-roots_pol[i]) for i=1..degree(p) */ static GEN split_complete(GEN p, long bitprec, GEN roots_pol) { long n=degpol(p), decprec; gpmem_t ltop; GEN p1,F,G,a,b,m1,m2,m; if (n==1) { a=gneg_i(gdiv((GEN)p[2],(GEN)p[3])); (void)append_clone(roots_pol,a); return p; } ltop = avma; if (n==2) { F=gsub(gsqr((GEN)p[3]),gmul2n(gmul((GEN)p[2],(GEN)p[4]),2)); decprec=(long) ((double) bitprec * L2SL10)+1; F=gsqrt(F,decprec); p1 = gmul2n((GEN)p[4],1); a = gneg_i(gdiv(gadd(F,(GEN)p[3]), p1)); b = gdiv(gsub(F,(GEN)p[3]), p1); a = append_clone(roots_pol,a); b = append_clone(roots_pol,b); avma = ltop; m=gmul(gsub(polx[varn(p)],mygprec(a,3*bitprec)), gsub(polx[varn(p)],mygprec(b,3*bitprec))); return gmul(m,(GEN)p[4]); } split_0(p,bitprec,&F,&G); m1 = split_complete(F,bitprec,roots_pol); m2 = split_complete(G,bitprec,roots_pol); return gerepileupto(ltop, gmul(m1,m2)); } /* compute a bound on the maximum modulus of roots of p */ GEN cauchy_bound(GEN p) { long i, n = degpol(p), prec = DEFAULTPREC; GEN lc, y, q = gmul(p, realun(prec)), x = gzero; if (n <= 0) err(constpoler,"cauchy_bound"); lc = gabs((GEN)q[n+2],prec); /* leading coefficient */ lc = ginv(lc); for (i=0; i 0) x = y; } return gexp(x, prec); } static GEN mygprecrc_special(GEN x, long bitprec, long e) { long tx=typ(x),lx,ex; GEN y; if (bitprec<=0) bitprec=0; /* should not happen */ switch(tx) { case t_REAL: lx=bitprec/BITS_IN_LONG+3; if (lxex) setexpo(y,ex); break; case t_COMPLEX: y=cgetg(3,t_COMPLEX); y[1]=(long) mygprecrc_special((GEN)x[1],bitprec,e); y[2]=(long) mygprecrc_special((GEN)x[2],bitprec,e); break; default: y=gcopy(x); } return y; } /* like mygprec but keep at least the same precision as before */ static GEN mygprec_special(GEN x, long bitprec) { long tx=typ(x),lx,i,e; GEN y; switch(tx) { case t_POL: lx=lgef(x); y=cgetg(lx,tx); y[1]=x[1]; e=gexpo(x); for (i=2; i 1) m = gmul(m,lc); e = gexpo(gsub(q, m)) - gexpo(lc) + (long)log2((double)n) + 1; if (e<-2*bitprec2) e=-2*bitprec2; /* to avoid e=-pariINFINITY */ if (e < 0) { e = bitprec + a_posteriori_errors(p,roots_pol,e); if (e < 0) return roots_pol; } if (DEBUGLEVEL > 7) fprintferr("all_roots: restarting, i = %ld, e = %ld\n", i,e); } } /* true if x is an exact scalar, that is integer or rational */ static int isexactscalar(GEN x) { long tx=typ(x); return (tx==t_INT || is_frac_t(tx)); } static int isexactpol(GEN p) { long i,n=degpol(p); for (i=0; i<=n; i++) if (isexactscalar((GEN)p[i+2])==0) return 0; return 1; } static long isvalidcoeff(GEN x) { long tx=typ(x); switch(tx) { case t_INT: case t_REAL: case t_FRAC: case t_FRACN: return 1; case t_COMPLEX: if (isvalidcoeff((GEN)x[1]) && isvalidcoeff((GEN)x[2])) return 1; } return 0; } static long isvalidpol(GEN p) { long i,n = lgef(p); for (i=2; i