/* $Id: aprcl.c,v 1.23 2002/07/15 13:26:20 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. */ /*******************************************************************/ /* */ /* THE APRCL PRIMALITY TEST */ /* */ /*******************************************************************/ #include "pari.h" static ulong kglob,ctglob,NBITSN,sgtaut,sgt6,sgtjac; static int isinstep5,ishack; static GEN *tabaall,*tabtall,*tabcyc,tabE,tabTH,tabeta,*tabmatvite,*tabmatinvvite,globfa,tabfaq,tabj,sgt,ctsgt,tabavite,tabpkvite,errfill; #define pkfalse (ishack ? 1 : pk) #define dotime (DEBUGLEVEL>=3) typedef struct red_t { long n; GEN C; /* polcyclo(n) */ GEN N; /* prime we are certifying */ GEN (*red)(GEN x, struct red_t *); } red_t; /* powering t_POLMOD mod N, flexible window */ static GEN makepoldeg1(GEN c, GEN d) { GEN res; if (signe(c)) { res = cgetg(4,t_POL); res[1] = evalsigne(1)|evallgef(4); res[2] = (long)d; res[3] = (long)c; } else if (signe(d)) { res = cgetg(3,t_POL); res[1] = evalsigne(1)|evallgef(3); res[2] = (long)d; } else { res = cgetg(2,t_POL); res[1] = evalsigne(0)|evallgef(2); } return res; } /* T mod polcyclo(p), assume deg(T) < 2p */ static GEN red_mod_cyclo(GEN T, long p) { long i, d; GEN y, *z; d = degpol(T) - p; /* < p */ if (d <= -2) return T; /* reduce mod (x^p - 1) */ y = dummycopy(T); z = (GEN*)(y+2); for (i = 0; i<=d; i++) z[i] = addii(z[i], z[i+p]); /* reduce mod x^(p-1) + ... + 1 */ d = p-1; if (signe(z[d])) for (i=0; i 0. Return x mod polcyclo(2^n) = (x^(2^(n-1)) + 1). DESTROY x */ static GEN u_red_mod_cyclo2n(GEN x, int n) { long i, pow2 = 1<<(n-1); GEN z; for (i = lg(x)-1; i>pow2; i--) x[i-pow2] -= x[i]; for (; i>0; i--) if (x[i]) break; i += 2; z = cgetg(i, t_POL); z[1] = evalsigne(1)|evallgef(i); for (i--; i>=2; i--) z[i] = lstoi(x[i-1]); return z; } /* x t_POL, n > 0. Return x mod polcyclo(2^n) = (x^(2^(n-1)) + 1). DESTROY x */ static GEN red_mod_cyclo2n(GEN x, int n) { long i, pow2 = 1<<(n-1); for (i = lgef(x)-1; i>pow2+1; i--) if (signe(x[i])) x[i-pow2] = lsubii((GEN)x[i-pow2], (GEN)x[i]); for (; i>1; i--) if (signe(x[i])) break; i += 1; setlgef(x,i); return x; } /* x a non-zero VECSMALL */ static GEN smallpolrev(GEN x) { long i,j, lx = lg(x); GEN y; while (lx-- && x[lx]==0) /* empty */; i = lx+2; y = cgetg(i,t_POL); y[1] = evallgef(i) | evalsigne(1); for (j=2; jC = polcyclo(p) */ static GEN _red2(GEN x, red_t *R) { return FpX_red(red_mod_cyclo(x, R->n), R->N); } static GEN _red(GEN x, red_t *R) { return FpX_red(gres(x, R->C), R->N); } static GEN _redsimple(GEN x, red_t *R) { return modii(x, R->N); } static GEN sqrmod(GEN x, red_t *R) { return R->red(gsqr(x), R); } static GEN sqrconst(GEN pol, red_t *R) { GEN p1 = cgetg(3,t_POL); p1[2] = (long)modii(sqri((GEN)pol[2]), R->N); p1[1] = pol[1]; return p1; } /* pol^2 mod (x^2+x+1, N) */ static GEN sqrmod3(GEN pol, red_t *R) { GEN a,b,bma,A,B; long lv=lgef(pol); if (lv==2) return pol; if (lv==3) return sqrconst(pol, R); a = (GEN)pol[3]; b = (GEN)pol[2]; bma = subii(b,a); A = modii(mulii(a,addii(b,bma)), R->N); B = modii(mulii(bma,addii(a,b)), R->N); return makepoldeg1(A,B); } /* pol^2 mod (x^2+1,N) */ static GEN sqrmod4(GEN pol, red_t *R) { GEN a,b,A,B; long lv=lgef(pol); if (lv==2) return pol; if (lv==3) return sqrconst(pol, R); a = (GEN)pol[3]; b = (GEN)pol[2]; A = modii(mulii(a, shifti(b,1)), R->N); B = modii(mulii(subii(b,a),addii(b,a)), R->N); return makepoldeg1(A,B); } /* pol^2 mod (polcyclo(5),N) */ static GEN sqrmod5(GEN pol, red_t *R) { GEN c2,b,c,d,A,B,C,D; long lv=lgef(pol); if (lv==2) return pol; if (lv==3) return sqrconst(pol, R); b = (GEN)pol[4]; c = (GEN)pol[3]; c2 = shifti(c,1); d = (GEN)pol[2]; if (lv==4) { A = mulii(b, subii(c2,b)); B = addii(sqri(c), mulii(b, subii(shifti(d,1),b))); C = subii(mulii(c2,d), sqri(b)); D = mulii(subii(d,b), addii(d,b)); } else { GEN a = (GEN)pol[5], a2 = shifti(a,1); /* 2a(d - c) + b(2c - b) */ A = addii(mulii(a2, subii(d,c)), mulii(b, subii(c2,b))); /* c(c - 2a) + b(2d - b) */ B = addii(mulii(c, subii(c,a2)), mulii(b, subii(shifti(d,1),b))); /* (a-b)(a+b) + 2c(d - a) */ C = addii(mulii(subii(a,b),addii(a,b)), mulii(c2,subii(d,a))); /* 2a(b - c) + (d-b)(d+b) */ D = addii(mulii(a2, subii(b,c)), mulii(subii(d,b), addii(d,b))); } A = modii(A, R->N); B = modii(B, R->N); C = modii(C, R->N); D = modii(D, R->N); return coefs_to_pol(4,A,B,C,D); } static GEN _mul(GEN x, GEN y, red_t *R) { return R->red(gmul(x,y), R); } static GEN _powpolmod(int pk, GEN jac, red_t *R, GEN (*_sqr)(GEN, red_t *)) { const GEN taba = tabaall[pkfalse]; const GEN tabt = tabtall[pkfalse]; const int efin = lg(taba)-1; GEN res,pol2, *vz; int lv,tf,f,i; gpmem_t av; lv = 1 << (kglob-1); vz = (GEN*)cgetg(lv+1,t_VEC); res = lift(jac); pol2 = _sqr(res, R); vz[1] = res; for (i=2; i<=lv; i++) vz[i] = _mul(vz[i-1],pol2, R); av = avma; for (f=efin; f>=1; f--) { res = f==efin ? vz[taba[f]] : _mul(vz[taba[f]],res, R); tf = tabt[f]; for (i=1; i<=tf; i++) res = _sqr(res, R); if ((f&7) == 0) res = gerepilecopy(av, res); } return res; } static GEN _powpolmodsimple(red_t *R, int pk, GEN jac) { GEN vres,w,wpow,wnew,ma; long lvres; int ph,j; vres = gtovec(lift(jac)); settyp(vres,t_COL); lvres = lg(vres); ma = tabmatvite[pkfalse]; ph = lg(ma); w = cgetg(lvres,t_MAT); for (j=1; jred = &_redsimple; wpow = cgetg(ph,t_COL); for (j=1; jred = &_red; _sqr = &sqrmod3; } else if (pk == 4) { R->red = &_red; _sqr = &sqrmod4; } else if (pk == 5) { R->red = &_red; _sqr = &sqrmod5; } else if (k == 1 && pk >= 7) { R->n = pk; R->red = &_red2; _sqr = &sqrmod; } else { R->red = &_red; _sqr = &sqrmod; } return _powpolmod(pk, jac, R, _sqr); } static GEN e(ulong t) { GEN fa,pr,ex,s; ulong nbd,m,k,d; int lfa,i,j; fa = decomp(stoi(t)); pr = (GEN)fa[1]; settyp(pr, t_VECSMALL); ex = (GEN)fa[2]; settyp(ex, t_VECSMALL); lfa = lg(pr); nbd = 1; for (i=1; i return t. For e(t) < 10^529, N < 10^1058 */ if (Bint < 540) return 6; if (Bint < 963) return 12; if (Bint < 1023) return 24; if (Bint < 1330) return 48; if (Bint < 1628) return 36; if (Bint < 1967) return 60; if (Bint < 2349) return 120; if (Bint < 3083) return 180; if (Bint < 3132) return 240; if (Bint < 3270) return 504; if (Bint < 3838) return 360; if (Bint < 4115) return 420; if (Bint < 4621) return 720; if (Bint < 4987) return 840; if (Bint < 5079) return 1440; if (Bint < 6212) return 1260; if (Bint < 6686) return 1680; if (Bint < 8137) return 2520; if (Bint < 8415) return 3360; if (Bint < 10437) return 5040; if (Bint < 11643) return 13860; if (Bint < 12826) return 10080; if (Bint < 11643) return 13860; if (Bint < 12826) return 10080; if (Bint < 13369) return 16380; if (Bint < 13540) return 21840; if (Bint < 15060) return 18480; if (Bint < 15934) return 27720; if (Bint < 17695) return 32760; if (Bint < 18816) return 36960; if (Bint < 21338) return 55440; if (Bint < 23179) return 65520; if (Bint < 23484) return 98280; if (Bint < 27465) return 110880; if (Bint < 30380) return 131040; if (Bint < 31369) return 166320; if (Bint < 33866) return 196560; if (Bint < 34530) return 262080; if (Bint < 36195) return 277200; if (Bint < 37095) return 360360; if (Bint < 38179) return 480480; if (Bint < 41396) return 332640; if (Bint < 43301) return 554400; if (Bint < 47483) return 720720; if (Bint < 47742) return 665280; if (Bint < 50202) return 831600; if (Bint < 52502) return 1113840; if (Bint < 60245) return 1441440; if (Bint < 63112) return 1663200; if (Bint < 65395) return 2227680; if (Bint < 69895) return 2162160; if (Bint < 71567) return 2827440; if (Bint < 75708) return 3326400; if (Bint < 79377) return 3603600; if (Bint < 82703) return 6126120; if (Bint < 91180) return 4324320; if (Bint < 93978) return 6683040; if (Bint < 98840) return 7207200; if (Bint < 99282) return 11138400; if (Bint < 105811) return 8648640; B = racine(N); for (t = 8648640+840;; t+=840) { gpmem_t av = avma; if (cmpii(e(t),B) > 0) break; avma = av; } avma = av0; return t; } extern ulong u_gener(ulong p); /* tabdl[i] = discrete log of i+1 in (Z/q)^*, q odd prime */ static GEN computetabdl(ulong q) { GEN v = cgetg(q-1,t_VECSMALL), w = v-1; /* w[i] = dl(i) */ ulong g,qm3s2,qm1s2,a,i; g = u_gener(q); qm3s2 = (q-3)>>1; qm1s2 = qm3s2+1; w[q-1] = qm1s2; a = 1; for (i=1; i<=qm3s2; i++) { a = mulssmod(g,a,q); w[a] = i; w[q-a] = i+qm1s2; } return v; } static void compute_fg(ulong q, int half, GEN *tabf, GEN *tabg) { const long qm3s2 = (q-3)>>1; const long l = half? qm3s2: q-2; ulong x; *tabf = computetabdl(q); *tabg = cgetg(l+1,t_VECSMALL); for (x=1; x<=l; x++) (*tabg)[x] = (*tabf)[x] + (*tabf)[q-x-1]; } /* p odd prime */ static GEN get_jac(ulong q, ulong p, int k, GEN tabf, GEN tabg) { GEN ze, vpk; long i, qm3s2; ulong x, pk; pk = u_pow(p,k); vpk = cgetg(pk+1,t_VECSMALL); for (i=1; i<=pk; i++) vpk[i] = 0; ze = tabcyc[pkfalse]; qm3s2 = (q-3)>>1; for (x=1; x<=qm3s2; x++) vpk[ tabg[x]%pk + 1 ] += 2; vpk[ (2*tabf[qm3s2+1])%pk + 1 ]++; return u_red(vpk,ze); } /* p = 2 */ static GEN get_jac2(GEN N, ulong q, int k, GEN *j2q, GEN *j3q) { GEN jpq, vpk, tabf, tabg; long i, qm3s2; ulong x, pk; if (k == 1) return NULL; compute_fg(q,0, &tabf,&tabg); pk = u_pow(2,k); vpk = cgetg(pk+1,t_VECSMALL); for (i=1; i<=pk; i++) vpk[i] = 0; qm3s2 = (q-3)>>1; for (x=1; x<=qm3s2; x++) vpk[ tabg[x]%pk + 1 ] += 2; vpk[ (2*tabf[qm3s2+1])%pk + 1 ]++; jpq = u_red_mod_cyclo2n(vpk, k); if (k == 2) return jpq; if (mod8(N) >= 5) { GEN v8 = cgetg(9,t_VECSMALL); for (x=1; x<=8; x++) v8[x] = 0; for (x=1; x<=q-2; x++) v8[ ((2*tabf[x]+tabg[x])&7) + 1 ]++; *j2q = polinflate(gsqr(u_red_mod_cyclo2n(v8,3)), pk>>3); *j2q = red_mod_cyclo2n(*j2q, k); } for (i=1; i<=pk; i++) vpk[i] = 0; for (x=1; x<=q-2; x++) vpk[ (tabf[x]+tabg[x])%pk + 1 ]++; *j3q = gmul(jpq, u_red_mod_cyclo2n(vpk,k)); *j3q = red_mod_cyclo2n(*j3q, k); return jpq; } static void calcjac(GEN et) { gpmem_t av; ulong q, l; int lfaq,p,k,i,j; GEN J,tabf,tabg,faq,faqpr,faqex; globfa = (GEN)decomp(shifti(et,-vali(et)))[1]; l = lg(globfa); tabfaq= cgetg(l,t_VEC); tabj = cgetg(l,t_VEC); for (i=1; i2) { for(;;u++) { a = powgi(gmodulcp(stoi(u),N),N1); b = gpowgs(a,ph); if (!gcmp1(b)) break; } } else { while(krosg(u,N)!=-1) u++; a = powgi(gmodulcp(stoi(u),N),N1); b = gpowgs(a,ph); } if (!isinstep5) tabavite[p] = (long)a; /* Checking b^p = 1 mod N is done economically in filltabs */ b = mppgcd(addis(lift(b),-1),N); if (!gcmp1(b)) {errfill = b; return NULL;} return gpowgs(a,(p*ph)/pk); } } static void filltabs(GEN N, int p, int k, ulong ltab) { const ulong mask = (1<= lg((GEN)tabcyc) || !signe(tabcyc[pk])); pk2 = pkfalse; tabcyc[pk2] = cyclo(pk,0); p1 = cgetg(pk+1,t_VEC); for (i=1; i<=pk; i++) p1[i] = (long)FpX_res(gpowgs(polx[0],i-1), tabcyc[pk2], N); tabeta[pk2] = (long)p1; if (p > 2) { LE = pk - pk/p + 1; E = cgetg(LE,t_VECSMALL); for (i=1,j=0; i= 3) { LE = (pk>>2) + 1; E = cgetg(LE,t_VECSMALL); for (i=1,j=0; i2 || k>=3) { tabE[pk2] = (long)E; p1 = cgetg(LE,t_VEC); for (i=1; i 2 && smodis(N,pk) == 1) { int ph,jj; GEN vpa,p1,p2,p3; if (DEBUGLEVEL&& !isinstep5) fprintferr("%ld ",pk); a = finda(N,pk,p); if (!a) return; ph = pk - pk/p; vpa = cgetg(ph+1,t_COL); vpa[1] = (long)a; if (k>1) a2 = gsqr(a); jj = 1; for (i=2; i>1: 0; m = shifti(m, -kglob); } avma = av; if (e > ltab) err(bugparier,"filltabs"); setlg(taba, e); tabaall[pk2] = taba; setlg(tabt, e); tabtall[pk2] = tabt; } static GEN calcglobs(GEN N, ulong t) { GEN fat,fapr,faex; int lfa,pk,p,ex,i,k; ulong ltab, ltaball; NBITSN = bit_accuracy(lgefint(N)) - 1; while (bittest(N,NBITSN)==0) NBITSN--; NBITSN++; kglob=3; while (((kglob+1)*(kglob+2) << (kglob-1)) < NBITSN) kglob++; ltab = (NBITSN/kglob) + 2; fat = decomp(stoi(t)); fapr = (GEN)fat[1]; faex = (GEN)fat[2]; lfa = lg(fapr); ltaball = 1; for (i=1; i ltaball) ltaball = pk; } inittabs(ltaball+1); if (DEBUGLEVEL) fprintferr("Fast pk-values: "); for (i=1; i ze^a */ static GEN aut(int pk, GEN z,int a) { GEN v = cgetg(pk+1,t_VEC); int i; for (i=1; i<=pk; i++) v[i] = (long)polcoeff0(z, (a*(i-1))%pk, 0); return gmodulcp(gtopolyrev(v,0), tabcyc[pkfalse]); } /* calculer z^v pour v dans Z[G] represente par des couples [sig_x^{-1},y] */ static GEN autvec(int pk, GEN z, GEN v) { int i,lv = lg(v); GEN s = gun; for (i=1; i=3 */ static int step4b(GEN N, ulong q, int k) { const int pk = u_pow(2,k); int ind,pk2; GEN AL,s1,s2,s3, j2q,j3q; red_t R; if (dotime) (void)timer2(); (void)get_jac2(N,q,k, &j2q,&j3q); if (dotime) sgtjac+=timer2(); pk2 = pkfalse; R.N = N; R.C = tabcyc[pk2]; AL = get_AL(N, pk); s1 = autvec(pk,j3q,(GEN)tabTH[pk2]); if (dotime) sgtaut+=timer2(); s2 = powpolmod(&R, k,pk,s1); if (dotime) {sgt[pk2]+=timer2();ctsgt[pk2]++;} s3 = autvec(pk,j3q,AL); if (dotime) sgtaut+=timer2(); s3 = _red(gmul(lift(s3),s2), &R); if (mod8(N) >= 5) s3 = _red(gmul(j2q, s3), &R); if (dotime) sgt[pk2]+=timer2(); ind = look_eta(pk, s3); if (ind == pk) return -1; if ((ind&1)==0) return 0; else { s3 = powmodulo(stoi(q), shifti(N,-1), N); if (dotime) {sgt[pk2]+=timer2();ctsgt[pk2]++;} return is_m1(s3, N); } } /* p=2, k=2 */ static int step4c(GEN N, ulong q) { const int pk = 4; int ind,pk2; GEN s0,s1,s3, jpq = get_jac2(N,q,2, NULL,NULL); red_t R; pk2 = pkfalse; R.N = N; R.C = tabcyc[pk2]; if (dotime) (void)timer2(); s0 = sqrmod4(jpq, &R); s1 = gmulsg(q,s0); s3 = powpolmod(&R, 2,pk,s1); if (mod4(N) == 3) s3 = _red(gmul(s0,s3), &R); if (dotime) {sgt[pk2]+=timer2();ctsgt[pk2]++;} ind = look_eta(pk, s3); if (ind == pk) return -1; if ((ind&1)==0) return 0; else { s3 = powmodulo(stoi(q), shifti(N,-1), N); if (dotime) sgt[pk2]+=timer2(); return is_m1(s3,N); } } /* p=2, k=1 */ static int step4d(GEN N, ulong q) { GEN s1; if (dotime) (void)timer2(); s1 = powmodulo(negi(stoi(q)), shifti(N,-1), N); if (dotime) {sgt[2]+=timer2();ctsgt[2]++;} if (gcmp1(s1)) return 0; if (is_m1(s1,N)) return (mod4(N) == 1); return -1; } /* return 1 [OK so far],0 [APRCL fails] or < 0 [not a prime] */ static int step5(GEN N, int p, GEN et) { ulong ct = 0, q; gpmem_t av; int k, fl = -1; byteptr d = diffptr+2; const ulong ltab = (NBITSN/kglob)+2; av = avma; for (q = 3; *d; ) { if (q%p != 1 || smodis(et,q) == 0) goto repeat; if (smodis(N,q) == 0) return -1; k = u_val(q-1, p); filltabs(N,p,k,ltab); if (p>=3) fl = step4a(N,q,p,k, NULL); else if (k >= 3) fl = step4b(N,q,k); else if (k == 2) fl = step4c(N,q); else fl = step4d(N,q); if (ishack) tabmatvite[1] = gzero; if (fl == -1) return (int)(-q); if (fl == 1) {ctglob = max(ctglob,ct); return 1;} ct++; avma = av; repeat: NEXT_PRIME_VIADIFF(q,d); } return 0; } static GEN step6(GEN N, ulong t, GEN et) { GEN N1,r,p1; ulong i; gpmem_t av; if (dotime) (void)timer2(); N1 = resii(N, et); r = gun; av = avma; for (i=1; i= 3) fl = step4a(N,q,p,k, gmael(tabj,i,j)); else if (k >= 3) fl = step4b(N,q,k); else if (k == 2) fl = step4c(N,q); else fl = step4d(N,q); if (fl == -1) return _res(q,p); if (fl == 1) flaglp[p] = 1; } } if (dotime) fprintferr("\nNormal test done; testing conditions lp\n"); isinstep5 = 1; for (i=1; i