Permalink
Cannot retrieve contributors at this time
1749 lines (1658 sloc)
50.3 KB
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <math.h> | |
| #include <time.h> | |
| #include <gmp.h> | |
| typedef unsigned char uchar; | |
| typedef unsigned long long ull; | |
| typedef long long ll; | |
| typedef unsigned int uint; | |
| void error(char *s) { | |
| puts(s); | |
| exit(1); | |
| } | |
| /* base 2 logarithm */ | |
| double log2(double a) { | |
| const double z=1.44269504088896340736; /* 1/log(2) */ | |
| return log(a)*z; | |
| } | |
| #define BIGDEG 10 | |
| #define MAXDEG 10 | |
| /* input parameters */ | |
| mpz_t opt_n; /* number to factorize */ | |
| ull opt_Ba; /* bound for algebraic factor base */ | |
| ull opt_Br; /* bound for rational factor base */ | |
| int opt_Bq; /* number of quadratic characters */ | |
| int opt_deg; /* degree of polynomial (must be odd, >=3) */ | |
| mpz_t opt_m; /* m value for base-m algorithm */ | |
| ull opt_sievew; /* width of line sieve */ | |
| int opt_thr; /* threshold for accepting a number in the sieve */ | |
| int opt_skip; /* skip this amount of smallest primes in the sieve */ | |
| int opt_extra; /* number of extra relations wanted for linear algebra */ | |
| int opt_signb; /* -1: a-bm, 1: a+bm */ | |
| void getnextline(char *s) { | |
| int l; | |
| loop: | |
| if(!fgets(s,1048570,stdin)) { s[0]=0; return; } | |
| if(s[0]=='\n' || s[0]=='\r') goto loop; | |
| if(s[0]==';' || s[0]=='%' || s[0]=='#') goto loop; | |
| l=strlen(s); | |
| while(l && (s[l]=='\n' || s[l]=='\r')) s[l--]=0; | |
| } | |
| gmp_randstate_t gmpseed; | |
| /* get a d-digit random number */ | |
| void getmpzrandom(mpz_t r,int d) { | |
| static char t[1024]; | |
| mpz_t a,b; | |
| int i; | |
| if(d>1022) error("too many digits"); | |
| mpz_init(a); mpz_init(b); | |
| t[i]='1'; | |
| for(i=1;i<=d;i++) t[i]='0'; | |
| t[i]=0; | |
| mpz_set_str(a,t,10); | |
| t[i-1]=0; | |
| mpz_set_str(b,t,10); | |
| do mpz_urandomm(r,gmpseed,a); while(mpz_cmp(r,b)<0); | |
| mpz_clear(b); mpz_clear(a); | |
| } | |
| /* return a mod p where a is mpz and p is int */ | |
| int mpz_mod_int(mpz_t a,int p) { | |
| mpz_t b; | |
| int r; | |
| mpz_init(b); | |
| r=mpz_mod_ui(b,a,p); | |
| mpz_clear(b); | |
| return r; | |
| } | |
| void readoptions() { | |
| static char s[1048576],t[4096]; | |
| int z,i; | |
| /* read n */ | |
| mpz_init(opt_n); | |
| mpz_init(opt_m); | |
| getnextline(s); | |
| sscanf(s,"%4090s",t); | |
| if(t[0]=='c') { | |
| /* generate a z-digit composite number without small prime factors */ | |
| z=strtol(t+1,NULL,10); | |
| do { | |
| getmpzrandom(opt_n,z); | |
| if(!mpz_mod_int(opt_n,2)) continue; | |
| if(mpz_probab_prime_p(opt_n,25)) continue; | |
| if(z>10) for(i=3;i<20000;i+=2) if(!mpz_mod_int(opt_n,i)) continue; | |
| } while(0); | |
| } else if(t[0]=='r') { | |
| /* generate RSA number: a z-digit number that is the product | |
| of two similarly sized primes */ | |
| z=strtol(t+1,NULL,10); | |
| /* TODO, pick two primes of z/2 digits and multiply */ | |
| error("not implemented yet"); | |
| } else { | |
| /* take literal number */ | |
| mpz_set_str(opt_n,t,10); | |
| } | |
| getnextline(s); sscanf(s,"%I64d",&opt_Ba); | |
| getnextline(s); sscanf(s,"%I64d",&opt_Br); | |
| getnextline(s); sscanf(s,"%d",&opt_Bq); | |
| getnextline(s); sscanf(s,"%d",&opt_deg); | |
| getnextline(s); mpz_set_str(opt_m,s,10); | |
| getnextline(s); sscanf(s,"%I64d",&opt_sievew); | |
| getnextline(s); sscanf(s,"%d",&opt_thr); | |
| /* TODO support percentage for skip (that is, skip x percent of the | |
| primes) */ | |
| getnextline(s); sscanf(s,"%d",&opt_skip); | |
| getnextline(s); sscanf(s,"%d",&opt_extra); | |
| if(opt_deg>MAXDEG) error("too high degree"); | |
| if(!(opt_deg&1)) error("degree must be odd"); | |
| getnextline(s); sscanf(s,"%d",&opt_signb); | |
| if(opt_signb!=1 && opt_signb!=-1) error("wrong sign"); | |
| } | |
| /* all polynomials have the following format: | |
| coefficients in f[i], f[0]=a_0, f[1]=a_1, ..., f[i]=a_i | |
| size of f[] is MAXDEG+1 */ | |
| /* auxilliary routines */ | |
| /* need these since gmp doesn't support long long */ | |
| ull mpz_get_ull(mpz_t a) { | |
| static char s[1048576]; | |
| ull ret; | |
| mpz_get_str(s,10,a); | |
| sscanf(s,"%I64d",&ret); | |
| return ret; | |
| } | |
| void mpz_set_ull(mpz_t b,ull a) { | |
| mpz_import(b, 1, 1, sizeof(a), 0, 0, &a); | |
| } | |
| /* return a mod p where a is mpz */ | |
| ull mpz_mod_ull(mpz_t a,ull p) { | |
| ull r; | |
| mpz_t b; | |
| mpz_init(b); | |
| mpz_set_ull(b,p); | |
| mpz_mod(b,a,b); | |
| r=mpz_get_ull(b); | |
| mpz_clear(b); | |
| return r; | |
| } | |
| ull gcd(ull a,ull b) { | |
| return b?gcd(b,a%b):a; | |
| } | |
| /* nfs init stage: create polynomials, determine bounds, create factor base */ | |
| /* includes many subroutines for polynomials */ | |
| /* calculate asymptotically optimal d (degree of polynomial) | |
| warning, doesn't work for n with more than 307 digits or so | |
| (n must fit in double) */ | |
| int findhighestdegree(mpz_t n) { | |
| double N=mpz_get_d(n); | |
| return pow(3*log(N)/log(log(N)),1./3); | |
| } | |
| /* calculate upper bound for factor base | |
| warning, doesn't work for n with more than 307 digits or so | |
| (n must fit in double) */ | |
| ull findB(mpz_t n) { | |
| double N=mpz_get_d(n),z=1./3; | |
| return exp(pow(8./9,z)*pow(log(N),z)*pow(log(log(N)),z+z)); | |
| } | |
| /* calculate number of quadratic characters to obtain | |
| warning, doesn't work for n with more than 307 digits or so | |
| (n must fit in double) */ | |
| ull findK(mpz_t n) { | |
| double N=mpz_get_d(n); | |
| return 3*log(N)/log(2.728182818); | |
| } | |
| /* given n, m and d, return polynomial of degree d which is the | |
| base-m expansion of n. return 0 if something went wrong (degree doesn't | |
| match expansion, polynomial isn't monic etc) */ | |
| /* assume that *f is allocated with (d+1) uninitialized elements. | |
| f[0]=a_0, f[1]=a_1, ..., f[d]=1, | |
| polynomial is f(x)=a_d x^d + ... + a_1 x + a_0 */ | |
| int getpolynomial(mpz_t n,mpz_t m,int d,mpz_t *f) { | |
| mpz_t N; | |
| int i,r=1; | |
| mpz_init_set(N,n); | |
| for(i=0;i<=d;i++) { | |
| mpz_init(f[i]); | |
| mpz_fdiv_qr(N,f[i],N,m); | |
| } | |
| /* error if base-m expansion of n requires degree!=d or f isn't monic */ | |
| if(mpz_cmp_si(N,0) || mpz_cmp_si(f[d],1)) r=0; | |
| mpz_clear(N); | |
| return r; | |
| } | |
| void printmpzpoly(mpz_t *f,int d) { | |
| for(;d>1;d--) gmp_printf("%Zd x^%d + ",f[d],d); | |
| gmp_printf("%Zd x + %Zd\n",f[1],f[0]); | |
| } | |
| void printullpoly(ull *f,int d) { | |
| printf("(%d) ",d); | |
| for(;d>-1;d--) printf("%I64u ",f[d]); | |
| } | |
| /* calculate the norm of a-b*alpha without using division. | |
| needs the minimal polynomial f (must be monic!) and its | |
| degree d (f[] has d+1 elements, where f[0]=a_0, f[i]=a_i and f[d]=1). | |
| put the answer in r */ | |
| void calcnorm(mpz_t r,mpz_t a,mpz_t b,mpz_t *f,int d) { | |
| static mpz_t x[MAXDEG+1]; | |
| mpz_t y,temp; | |
| int i; | |
| mpz_init(temp); | |
| for(i=0;i<=d;i++) mpz_init(x[i]); | |
| mpz_set_si(x[0],1); | |
| mpz_set(x[1],a); | |
| mpz_init_set_si(y,1); | |
| mpz_set_si(r,0); | |
| for(i=2;i<=d;i++) mpz_mul(x[i],x[i-1],a); | |
| for(i=d;i>=0;i--) { | |
| mpz_mul(temp,y,x[i]); | |
| mpz_addmul(r,temp,f[i]); | |
| mpz_mul(y,y,b); | |
| } | |
| mpz_clear(temp); | |
| mpz_clear(y); | |
| for(i=0;i<=d;i++) mpz_clear(x[i]); | |
| } | |
| /* factor base routines */ | |
| uchar *sieve; | |
| #define SETBIT(p) sieve[(p)>>3]|=1<<((p)&7); | |
| #define CLEARBIT(p) sieve[(p)>>3]&=~(1<<((p)&7)); | |
| #define CHECKBIT(p) (sieve[(p)>>3]&(1<<((p)&7))) | |
| /* allocate and generate bit-packed sieve up to (not including) N */ | |
| void createsieve(ull N) { | |
| ull i,j; | |
| sieve=malloc((N+7)>>3); | |
| memset(sieve,0xaa,(N+7)>>3); | |
| sieve[0]=172; | |
| for(i=2;i*i<N;i++) if(CHECKBIT(i)) for(j=i*i;j<N;j+=i) CLEARBIT(j); | |
| } | |
| /* algebraic factor base */ | |
| ull *p1,*r1; | |
| ull bn1; | |
| /* rational factor base */ | |
| ull *p2; | |
| ull bn2; | |
| /* quadratic characters */ | |
| ull *p3,*r3; | |
| ull bn3; | |
| /* evaluate f(x), assume f monic */ | |
| void evalpoly(mpz_t *f,int deg,mpz_t x,mpz_t ret) { | |
| int i; | |
| mpz_set(ret,f[deg]); | |
| for(i=deg-1;i>=0;i--) { | |
| mpz_mul(ret,ret,x); | |
| mpz_add(ret,ret,f[i]); | |
| } | |
| } | |
| /* warning, requires 64-bit compiler, i think */ | |
| typedef __uint128_t ulll; | |
| ull ullmulmod2(ull a,ull b,ull mod) { return (ulll)a*b%mod; } | |
| /* evaluate f(x)%p, assume f monic. requires p<2^63 */ | |
| ull evalpolymod(ull *f,int df,ull x,ull p) { | |
| ull r=f[df]; | |
| int i; | |
| for(i=df-1;i>=0;i--) r=(ullmulmod2(r,x,p)+f[i])%p; | |
| return r; | |
| } | |
| /* start of routine that finds all roots (aka linear factors) of a polynomial | |
| modulo a prime */ | |
| /* begins with various routines for doing polynomial arithmetic over Z_p */ | |
| /* in general, all routines that do stuff modulo m should be fed numbers in | |
| 0, 1, ..., m-1 */ | |
| /* calculate inverse of a mod m (m can be composite, but 0 will be | |
| returned if an inverse doesn't exist). warning, don't use if m>=2^63 */ | |
| ll inverse(ll a,ll m) { | |
| ll b=m,x=0,y=1,t,q,lastx=1,lasty=0; | |
| while(b) { | |
| q=a/b; | |
| t=a,a=b,b=t%b; | |
| t=x,x=lastx-q*x,lastx=t; | |
| t=y,y=lasty-q*y,lasty=t; | |
| } | |
| return a==1?(lastx%m+m)%m:0; | |
| } | |
| /* modular square root! */ | |
| /* calculate the jacobi symbol, returns 0, 1 or -1 */ | |
| /* 1: a is quadratic residue mod m, -1: a is not, 0: a mod m=0 */ | |
| /* based on algorithm 2.3.5 in "prime numbers" (crandall, pomerance) */ | |
| /* WARNING, m must be an odd positive number */ | |
| int jacobi(ll a,ll m) { | |
| int t=1; | |
| ll z; | |
| a%=m; | |
| while(a) { | |
| while(!(a&1)) { | |
| a>>=1; | |
| if((m&7)==3 || (m&7)==5) t=-t; | |
| } | |
| z=a,a=m,m=z; | |
| if((a&3)==3 && (m&3)==3) t=-t; | |
| a%=m; | |
| } | |
| if(m==1) return t; | |
| return 0; | |
| } | |
| ull ullpowmod(ull n,ull k,ull mod) { | |
| int i,j; | |
| ull v=n,ans=1; | |
| if(!k) return 1; | |
| /* find topmost set bit */ | |
| for(i=63;!(k&(1ULL<<i));i--); | |
| for(j=0;j<=i;j++) { | |
| if(k&(1ULL<<j)) ans=ullmulmod2(ans,v,mod); | |
| v=ullmulmod2(v,v,mod); | |
| } | |
| return ans; | |
| } | |
| /* calculate legendre symbol, returns 0, 1 or -1 */ | |
| /* 1: a is quadratic residue mod p, -1: a is not, 0: a mod p=0 */ | |
| /* WARNING, p must be an odd prime */ | |
| int legendre(ll a,ll p) { | |
| a%=p; | |
| if(a<0) a+=p; | |
| int z=ullpowmod(a,(p-1)>>1,p); | |
| return z==p-1?-1:z; | |
| } | |
| ull rand64() { | |
| return (rand()&32767) + | |
| ((rand()&32767)<<15) + | |
| ((rand()&32767ULL)<<30) + | |
| ((rand()&32767ULL)<<45) + | |
| ((rand()&15ULL)<<60); | |
| } | |
| /* find square root of a modulo p (p prime) using tonelli-shanks */ | |
| /* runtime O(ln^4 p) */ | |
| /* mod 3,5,7: algorithm 2.3.8 from "prime numbers" (crandall, pomerance) */ | |
| /* mod 1: from http://www.mast.queensu.ca/~math418/m418oh/m418oh11.pdf */ | |
| ull sqrtmod(ull a,ull p) { | |
| int p8,alpha,i; | |
| ull x,c,s,n,b,J,r2a,r; | |
| if(p==2) return a&1; | |
| a%=p; | |
| if(legendre(a,p)!=1) return 0; /* no square root */ | |
| p8=p&7; | |
| if(p8==3 || p8==5 || p8==7) { | |
| if((p8&3)==3) return ullpowmod(a,(p+1)/4,p); | |
| x=ullpowmod(a,(p+3)/8,p); | |
| c=ullmulmod2(x,x,p); | |
| return c==a?x:ullmulmod2(x,ullpowmod(2,(p-1)/4,p),p); | |
| } | |
| alpha=0; | |
| s=p-1; | |
| while(!(s&1)) s>>=1,alpha++; | |
| r=ullpowmod(a,(s+1)/2,p); | |
| r2a=ullmulmod2(r,ullpowmod(a,(s+1)/2-1,p),p); | |
| do n=rand64()%(p-2)+2; while(legendre(n,p)!=-1); | |
| b=ullpowmod(n,s,p); | |
| J=0; | |
| for(i=0;i<alpha-1;i++) { | |
| c=ullpowmod(b,2*J,p); | |
| c=ullmulmod2(r2a,c,p); | |
| c=ullpowmod(c,1ULL<<(alpha-i-2),p); | |
| if(c==p-1) J+=1ULL<<i; | |
| } | |
| return ullmulmod2(r,ullpowmod(b,J,p),p); | |
| } | |
| /* set b(x)=a(x) */ | |
| void polyset(ull *a,int da,ull *b,int *db) { | |
| int i; | |
| for(*db=da,i=0;i<=*db;i++) b[i]=a[i]; | |
| } | |
| /* set c(x)=a(x)+b(x) */ | |
| void polyaddmod(ull *a,int da,ull *b,int db,ull *c,int *dc,ull p) { | |
| static ull r[MAXDEG+1]; | |
| int i,dr; | |
| dr=da>db?da:db; | |
| for(i=da+1;i<=dr;i++) r[i]=0; | |
| for(i=0;i<=da;i++) r[i]=a[i]; | |
| for(i=0;i<=db;i++) { | |
| r[i]+=b[i]; | |
| if(r[i]>=p) r[i]-=p; | |
| } | |
| while(dr>-1 && !r[dr]) dr--; | |
| *dc=dr; | |
| for(i=0;i<=dr;i++) c[i]=r[i]; | |
| } | |
| /* negates a (modifies a) */ | |
| void polynegmod(ull *a,int da,ull p) { | |
| for(;da>-1;da--) a[da]=((ll)p-(ll)a[da])%p; | |
| } | |
| /* given polynomials a(x) and b(x), calculate quotient and | |
| remainder of a(x)/b(x) (mod p) | |
| dega, degb are the degrees of a and b, respectively. assume that *c, *d | |
| has enough pre-allocated memory to hold the results. don't assume that | |
| any of a,b,c,d are non-overlapping memory areas. | |
| if c is non-NULL, return quotient. | |
| if d is non-NULL, return remainder. remainder==0 has degree -1. | |
| */ | |
| void polydivmod(ull *a,int dega,ull *b,int degb,ull *c,int *degc,ull *d,int *degd,ull p) { | |
| static ull u[MAXDEG+1],q[MAXDEG+1]; | |
| ull inv=inverse(b[degb],p); | |
| int k,j; | |
| for(k=0;k<=dega;k++) u[k]=a[k]; | |
| for(k=dega-degb;k>-1;k--) { | |
| q[k]=ullmulmod2(u[degb+k],inv,p); | |
| for(j=degb+k-1;j>=k;j--) { | |
| u[j]=u[j]-ullmulmod2(q[k],b[j-k],p); | |
| if(u[j]>=p) u[j]+=p; | |
| } | |
| } | |
| if(c) for(*degc=dega-degb,k=*degc;k>-1;k--) c[k]=q[k]; | |
| if(d) for(*degd=-1,k=0;k<degb && k<=dega;k++) if((d[k]=u[k])) *degd=k; | |
| } | |
| /* make polynomial monic, destroy input polynomial */ | |
| void polymonic(ull *a,int da,ull p) { | |
| ull z; | |
| int i; | |
| if(da<0 || a[da]==1) return; | |
| z=inverse(a[da],p); | |
| for(i=0;i<da;i++) a[i]=ullmulmod2(a[i],z,p); | |
| a[da]=1; | |
| } | |
| /* return a(x)*b(x) over Z_p */ | |
| void polymulmod(ull *a,int dega,ull *b,int degb,ull *c,int *degc,ull p) { | |
| static ull r[2*MAXDEG+1]; | |
| int i,j; | |
| *degc=dega+degb; | |
| for(i=0;i<=*degc;i++) r[i]=0; | |
| for(i=0;i<=dega;i++) { | |
| for(j=0;j<=degb;j++) { | |
| r[i+j]=r[i+j]+ullmulmod2(a[i],b[j],p); | |
| if(r[i+j]>=p) r[i+j]-=p; | |
| } | |
| } | |
| for(i=0;i<=*degc;i++) c[i]=r[i]; | |
| while(*degc>-1 && !c[*degc]) (*degc)--; | |
| } | |
| /* reduce a(x) mod v(x) over Z_p */ | |
| /* runtime: O(degree^2) */ | |
| void polyreduce(ull *a,int da,ull *v,int dv,ull *c,int *dc,ull p) { | |
| static ull w[2*MAXDEG+1]; | |
| ull t; | |
| int i,j,z; | |
| for(i=0;i<=da;i++) w[i]=a[i]; | |
| for(i=da+1;i<=dv;i++) w[i]=0; | |
| /* for each i=da, da-1, ..., dv, subtract a(i)*v(x)*x^(i-dv) */ | |
| for(i=da;i>=dv;i--) for(j=0;j<=dv;j++) { | |
| z=i-dv; t=w[i]; | |
| w[z+j]=(w[z+j]+p-ullmulmod2(t,v[j],p))%p; | |
| } | |
| /* tighten dc */ | |
| for(*dc=-1,i=0;i<dv;i++) if((c[i]=w[i])) *dc=i; | |
| } | |
| /* given f, return g=f' (mod p) */ | |
| void polyderivemod(ull *f,int df,ull *g,int *dg,ull p) { | |
| int i; | |
| *dg=df-1; | |
| for(i=1;i<=df;i++) g[i-1]=ullmulmod2(f[i],i,p); | |
| while(*dg>-1 && !g[*dg]) (*dg)--; | |
| } | |
| /* return a(x)*b(x) mod v(x) over Z_p */ | |
| /* this can probably also be used to multiply two elements | |
| in the quotient ring Z_p/<v(x)> */ | |
| /* WARNING, not efficient. integrate mulmod and reduce more tightly */ | |
| void polymulmodmod(ull *a,int da,ull *b,int db,ull *v,int dv,ull *c,int *dc,ull p) { | |
| static ull d[2*MAXDEG+1]; | |
| int dd; | |
| polymulmod(a,da,b,db,d,&dd,p); | |
| polyreduce(d,dd,v,dv,c,dc,p); | |
| } | |
| /* return a(x)^n mod v(x) over Z_p, put result in c */ | |
| /* warning, not very efficient, really, but care about that later */ | |
| void polypowmodmod(ull *a,int da,ull n,ull *v,int dv,ull *c,int *dc,ull p) { | |
| ull z[MAXDEG+1],y[MAXDEG+1]={1}; | |
| int dz,dy=0,i; | |
| polyset(a,da,z,&dz); | |
| while(n) { | |
| if(n&1) { | |
| n>>=1; | |
| polymulmodmod(y,dy,z,dz,v,dv,y,&dy,p); | |
| if(!n) break; | |
| } else n>>=1; | |
| polymulmodmod(z,dz,z,dz,v,dv,z,&dz,p); | |
| } | |
| for(*dc=dy,i=0;i<=*dc;i++) c[i]=y[i]; | |
| } | |
| /* return a(x)^n mod v(x) over Z_p, put result in c, exponent is mpz */ | |
| /* warning, not very efficient, really, but care about that later */ | |
| void polypowmodmodmpz(ull *a,int da,mpz_t N,ull *v,int dv,ull *c,int *dc,ull p) { | |
| ull z[MAXDEG+1],y[MAXDEG+1]={1}; | |
| int dz=da,dy=0,i; | |
| mpz_t t,n; | |
| mpz_init(t); | |
| mpz_init_set(n,N); | |
| for(i=0;i<=dz;i++) z[i]=a[i]; | |
| while(mpz_cmp_si(n,0)>0) { | |
| if(mpz_mod_ui(t,n,2)) { | |
| mpz_fdiv_q_2exp(n,n,1); | |
| polymulmodmod(y,dy,z,dz,v,dv,y,&dy,p); | |
| if(!mpz_cmp_si(n,0)) break; | |
| } else mpz_fdiv_q_2exp(n,n,1); | |
| polymulmodmod(z,dz,z,dz,v,dv,z,&dz,p); | |
| } | |
| for(*dc=dy,i=0;i<=*dc;i++) c[i]=y[i]; | |
| mpz_clear(n); | |
| mpz_clear(t); | |
| } | |
| /* return a(x)^n over Z_p */ | |
| void polypowmod(ull *a,int da,ull n,ull *c,int *dc,ull p) { | |
| ull z[MAXDEG+1],y[MAXDEG+1]={1}; | |
| int dz=da,dy=0,i; | |
| for(i=0;i<=dz;i++) z[i]=a[i]; | |
| while(n) { | |
| if(n&1) { | |
| n>>=1; | |
| polymulmod(y,dy,z,dz,y,&dy,p); | |
| if(!n) break; | |
| } else n>>=1; | |
| polymulmod(z,dz,z,dz,z,&dz,p); | |
| } | |
| for(*dc=dy,i=0;i<=*dc;i++) c[i]=y[i]; | |
| } | |
| /* given polynomials a(x), b(x), calculate g(x)=gcd(a(x),b(x)) mod p. */ | |
| void polygcdmod(ull *a,int da,ull *b,int db,ull *g,int *dg,ull p) { | |
| static ull c[MAXDEG+1],d[MAXDEG+1],e[MAXDEG+1]; | |
| int dc,dd,de,i; | |
| polyset(a,da,c,&dc); | |
| polyset(b,db,d,&dd); | |
| /* sanity check: a==0 */ | |
| if(da<0) { | |
| for(*dg=dd,i=0;i<=*dg;i++) g[i]=d[i]; | |
| goto end; | |
| } | |
| while(dd>-1) { | |
| polydivmod(c,dc,d,dd,NULL,NULL,e,&de,p); | |
| polyset(d,dd,c,&dc); | |
| polyset(e,de,d,&dd); | |
| } | |
| polyset(c,dc,g,dg); | |
| end: | |
| /* make output monic */ | |
| polymonic(g,*dg,p); | |
| } | |
| /* calculate inverse of a mod m (m can be composite, but 0 will be | |
| returned if an inverse doesn't exist). warning, don't use if m>=2^63 */ | |
| ll inversemal(ll a,ll m) { | |
| ll b=m,x=0,y=1,t,q,lastx=1,lasty=0; | |
| while(b) { | |
| q=a/b; | |
| t=a,a=b,b=t%b; | |
| t=x,x=lastx-q*x,lastx=t; | |
| t=y,y=lasty-q*y,lasty=t; | |
| } | |
| return a==1?(lastx%m+m)%m:0; | |
| } | |
| /* find the inverse g(x)=a^1(x) of a(x) mod f(x) mod p using | |
| the extended euclid algorithm */ | |
| /* f(x) is assumed to be monic. if an inverse doesn't exist, return g=0 */ | |
| void polyinversemodmod(ull *in,int din,ull *f,int df,ull *g,int *dg,ull p) { | |
| ull b[MAXDEG+1],x[MAXDEG+1],y[MAXDEG+1],lastx[MAXDEG+1],lasty[MAXDEG+1]; | |
| ull t[MAXDEG+1],q[MAXDEG+1],a[MAXDEG+1],z[MAXDEG+1],v; | |
| int db,dx,dy,lastdx,lastdy,dt,dq,da,dz,i; | |
| if(din<0) { *dg=-1; return; } | |
| polyset(f,df,b,&db); | |
| polyset(in,din,a,&da); | |
| dx=-1; y[0]=1; dy=0; | |
| lastx[0]=1; lastdx=0; lastdy=-1; | |
| while(db>-1) { | |
| /* set a=b, b=a%b, q=a/b */ | |
| polyset(a,da,t,&dt); | |
| polyset(b,db,a,&da); | |
| polydivmod(t,dt,a,da,q,&dq,b,&db,p); | |
| /* set x=lastx-q*x, lastx=x */ | |
| polyset(x,dx,t,&dt); | |
| polymulmod(q,dq,x,dx,z,&dz,p); | |
| polyset(lastx,lastdx,x,&dx); | |
| polynegmod(z,dz,p); | |
| polyaddmod(x,dx,z,dz,x,&dx,p); | |
| polyset(t,dt,lastx,&lastdx); | |
| /* set y=lasty-q*y, lasty=y */ | |
| polyset(y,dy,t,&dt); | |
| polymulmod(q,dq,y,dy,z,&dz,p); | |
| polyset(lasty,lastdy,y,&dy); | |
| polynegmod(z,dz,p); | |
| polyaddmod(y,dy,z,dz,y,&dy,p); | |
| polyset(t,dt,lasty,&lastdy); | |
| } | |
| /* now a is gcd(a,f). if !=1 return failure */ | |
| if(da>0) { *dg=-1; return; } | |
| /* lastx is inverse, multiply with inverse of a[0] */ | |
| if(a[0]!=1) { | |
| v=inverse(a[0],p); | |
| for(i=0;i<=lastdx;i++) lastx[i]=ullmulmod2(lastx[i],v,p); | |
| } | |
| for(*dg=lastdx,i=0;i<=lastdx;i++) g[i]=lastx[i]; | |
| } | |
| /* return 1 if u(x) is squarefree. u is squarefree iff gcd(u,u')==1. | |
| u(x) must be monic. unpredictable results if deg u <= p */ | |
| int ispolymodsquarefree(ull *u,int du,ull p) { | |
| static ull ud[MAXDEG+1],g[MAXDEG+1]; | |
| int dud,dg,i; | |
| for(dud=du-1,i=0;i<du;i++) ud[i]=ullmulmod2(u[i+1],i+1,p); | |
| while(dud>-1 && !ud[dud]) dud--; | |
| polygcdmod(u,du,ud,dud,g,&dg,p); | |
| return dg==0; | |
| } | |
| /* find all roots by naive method (evaluate in(x) for all x), inefficient */ | |
| void polylinmodnaive(ull *in,int dv,ull p,ull *f,int *fn) { | |
| ll x; | |
| for(*fn=x=0;x<p;x++) if(!evalpolymod(in,dv,x,p)) f[(*fn)++]=x; | |
| } | |
| /* find roots of u(x) mod p, p must be an odd prime larger than | |
| the degree of u(x) */ | |
| /* based on algorithm 1.6.1 in cohen */ | |
| void polyfindrootmod(ull *z,int dz,ull p,ull *f,int *fn) { | |
| /* cast out gcd(f',f) */ | |
| static ull g[MAXDEG+1],ud[MAXDEG+1],u[MAXDEG+1],m1[MAXDEG+1]; | |
| static ull q[MAXDEG+1][MAXDEG+1]; | |
| ull d,e; | |
| int du,dg,dud,qn=1,done,i,dm1; | |
| static int dq[MAXDEG+1]; | |
| *fn=0; | |
| polyset(z,dz,u,&du); | |
| /* force u monic */ | |
| polymonic(u,du,p); | |
| polyderivemod(u,du,ud,&dud,p); | |
| polygcdmod(u,du,ud,dud,g,&dg,p); | |
| /* force gcd monic */ | |
| polymonic(g,dg,p); | |
| /* divide out squares */ | |
| polydivmod(u,du,g,dg,u,&du,NULL,NULL,p); | |
| /* cast out 0-factor */ | |
| if(!u[0]) { | |
| g[0]=0; g[1]=1; dg=1; | |
| polydivmod(u,du,g,dg,u,&du,NULL,NULL,p); | |
| f[(*fn)++]=0; | |
| } | |
| /* m1(x)=-1 (p-1) */ | |
| m1[0]=p-1; dm1=0; | |
| /* take gcd(x^(p-1)-1, u(x)) and isolate roots */ | |
| /* first take d=x^(p-1) mod u, then take gcd(d-1,u) */ | |
| g[0]=0; g[1]=1; dg=1; | |
| polypowmodmod(g,dg,p-1,u,du,g,&dg,p); | |
| polyaddmod(g,dg,m1,dm1,g,&dg,p); | |
| polygcdmod(g,dg,u,du,q[0],&dq[0],p); | |
| do { | |
| done=1; | |
| /* if deg>2, try to split polynomial. benchmarking shows it's faster | |
| to split down to deg 2 rather than deg 1. */ | |
| for(i=0;i<qn;i++) if(dq[i]>2) { | |
| do { | |
| g[0]=rand64()%p; g[1]=1; dg=1; | |
| polypowmodmod(g,dg,p>>1,q[i],dq[i],g,&dg,p); | |
| polyaddmod(g,dg,m1,dm1,g,&dg,p); | |
| polygcdmod(g,dg,q[i],dq[i],g,&dg,p); | |
| } while(!dg || dg==dq[i]); | |
| polydivmod(q[i],dq[i],g,dg,q[i],&dq[i],NULL,NULL,p); | |
| polyset(g,dg,q[qn],&dq[qn]); | |
| qn++; | |
| done=0; | |
| } | |
| } while(!done); | |
| /* go through each item in the list, and output roots */ | |
| for(i=0;i<qn;i++) { | |
| if(dq[i]==1) { | |
| if(q[i][1]==1) f[(*fn)++]=(p-q[i][0])%p; | |
| else f[(*fn)++]=ullmulmod2((p-q[i][0])%p,inverse(q[i][1],p),p); | |
| } else if(dq[i]==2) { | |
| d=ullmulmod2(q[i][1],q[i][1],p); | |
| e=ullmulmod2(q[i][0],q[i][2],p); | |
| e=sqrtmod((d+p-ullmulmod2(e,4,p))%p,p); | |
| d=ullmulmod2(inverse(2,p),q[i][2],p); | |
| f[(*fn)++]=ullmulmod2((p+e-q[i][1])%p,d,p); | |
| f[(*fn)++]=ullmulmod2((p+p-e-q[i][1])%p,d,p); | |
| } | |
| } | |
| } | |
| /* entry point for new routine */ | |
| void findideals2(ull *u,int du,ull p,ull *f,int *fn) { | |
| /* naive algorithm for small enough p: evaluate f(r) for all 0<=r<p */ | |
| if(p<200 || p<=du) return polylinmodnaive(u,du,p,f,fn); | |
| polyfindrootmod(u,du,p,f,fn); | |
| } | |
| /* determinant using stupid and slow O(n!) algorith, but n will never | |
| be huge (say, never larger than 6 and in practice it will always be 3). | |
| generate permutations using fancy loop-free algorithm by knuth | |
| where successively generated permutations have alternating parity | |
| [an easy O(n^3) algorithm: gauss-jordan and return product of diagonal | |
| times the numbers we divided the rows with] */ | |
| ull calcdet(ull A[MAXDEG+1][MAXDEG+1],int n,ull p) { | |
| ull res=0,r; | |
| int o[100],c[100],j,s,q,a[100],sign=1; | |
| char t; | |
| for(j=0;j<n;j++) c[j]=0,o[j]=1,a[j]=j; | |
| p2: | |
| /* visit permutation */ | |
| r=sign?1:p-1; | |
| for(j=0;j<n;j++) r=ullmulmod2(r,A[j][a[j]],p); | |
| res+=r; | |
| if(res>=p) res-=p; | |
| sign^=1; | |
| /* end visit */ | |
| j=n; s=0; | |
| p4: | |
| q=c[j-1]+o[j-1]; | |
| if(q<0) goto p7; | |
| if(q==j) goto p6; | |
| t=a[j-c[j-1]+s-1]; a[j-c[j-1]+s-1]=a[j-q+s-1]; a[j-q+s-1]=t; | |
| c[j-1]=q; | |
| goto p2; | |
| p6: | |
| if(j==1) return res; | |
| s++; | |
| p7: | |
| o[j-1]=-o[j-1]; j--; | |
| goto p4; | |
| } | |
| /* calculate norm mod p of general element a(x) in field with minimal | |
| polynomial f(x). uses determinant method */ | |
| /* tested against calcnorm() with tens of millions of numbers of the form | |
| a+b*alpha with degrees 3-6, with a,b huge modulo a huge prime */ | |
| ull calcnormmod(ull *a,int da,ull *f,int df,ull p) { | |
| static ull A[MAXDEG+1][MAXDEG+1]; | |
| ull b[MAXDEG+1]={0,1},c[MAXDEG+1]; | |
| int i,j,db=1,dc; | |
| polyset(a,da,c,&dc); | |
| for(i=0;i<=dc;i++) A[i][0]=a[i]; | |
| for(;i<df;i++) A[i][0]=0; | |
| for(j=1;j<df;j++) { | |
| polymulmodmod(c,dc,b,db,f,df,c,&dc,p); | |
| for(i=0;i<=dc;i++) A[i][j]=c[i]; | |
| for(;i<df;i++) A[i][j]=0; | |
| } | |
| return calcdet(A,df,p); | |
| } | |
| /* B1 and B2 are upper bound for primes (algebraic and rational) | |
| f is polynomial, deg is degree | |
| p1,r1 is algebraic factor base, bn1 is number of primes | |
| p2 is rational factor base, bn2 is number of primes */ | |
| void createfactorbases(ull B1,ull B2,ull Bk,mpz_t *f,int deg,ull **_p1,ull **_r1,ull *bn1,ull **_p2,ull *bn2,ull **_p3,ull **_r3,ull *bn3) { | |
| static ull b[MAXDEG+1]; | |
| static ull root[MAXDEG+1]; | |
| ull B=B1>B2?B1:B2,i,j,q; | |
| ull *p1,*r1,*p2,*p3,*r3; | |
| int fn; | |
| int db,k; | |
| char *sieve=malloc(B+1); | |
| memset(sieve,1,B+1); | |
| for(i=2;i*i<=B;i++) if(sieve[i]) for(j=i*i;j<=B;j+=i) sieve[j]=0; | |
| /* generate rational factor base */ | |
| for(*bn2=0,i=2;i<=B2;i++) if(sieve[i]) (*bn2)++; | |
| if(!(p2=malloc(*bn2*sizeof(ull)))) error("couldn't allocate rational factor base"); | |
| for(*bn2=0,i=2;i<=B2;i++) if(sieve[i]) p2[(*bn2)++]=i; | |
| /* generate algebraic factor base */ | |
| for(*bn1=0,i=2;i<=B1;i++) if(sieve[i]) { | |
| /* find all eligible r: r such that f(r)=0 (mod p) using factorization */ | |
| db=deg; | |
| for(k=0;k<=db;k++) b[k]=mpz_mod_ull(f[k],i); | |
| findideals2(b,db,i,root,&fn); | |
| *bn1+=fn; | |
| } | |
| if(!(p1=malloc(*bn1*sizeof(ull)))) error("couldn't allocate algebraic factor base"); | |
| if(!(r1=malloc(*bn1*sizeof(ull)))) error("couldn't allocate algebraic factor base"); | |
| for(*bn1=0,i=2;i<=B1;i++) if(sieve[i]) { | |
| /* find all roots again. we happily waste some computing resources since | |
| the sieve stage will dominate the runtime anyway */ | |
| /* slow method again TODO replace with factorization */ | |
| db=deg; | |
| for(k=0;k<=db;k++) b[k]=mpz_mod_ull(f[k],i); | |
| findideals2(b,db,i,root,&fn); | |
| for(j=0;j<fn;j++) p1[*bn1]=i,r1[(*bn1)++]=root[j]; | |
| } | |
| /* generate quadratic characters */ | |
| *bn3=Bk; | |
| if(!(p3=malloc(*bn3*sizeof(ull)))) error("couldn't allocate quadratic characters"); | |
| if(!(r3=malloc(*bn3*sizeof(ull)))) error("couldn't allocate quadratic characters"); | |
| for(i=0,q=B1+1;i<*bn3;q++) { | |
| /* check if q is prime */ | |
| for(j=0;j<*bn2 && p2[j]*p2[j]<=q;j++) if(q%p2[j]==0) goto noprime; | |
| db=deg; | |
| for(k=0;k<=db;k++) b[k]=mpz_mod_ull(f[k],q); | |
| findideals2(b,db,q,root,&fn); | |
| if(!fn) continue; | |
| /* find value from root such that f'(value)!=0 mod q */ | |
| polyderivemod(b,db,b,&db,q); | |
| for(k=0;k<fn;k++) if(evalpolymod(b,db,root[k],q)) { | |
| p3[i]=q; | |
| r3[i]=root[k]; | |
| i++; | |
| break; | |
| } | |
| noprime:; | |
| } | |
| // TEMP output factor bases | |
| // for(i=0;i<*bn1;i++) printf("(%I64d %I64d)\n",p1[i],r1[i]);printf("\n"); | |
| // for(i=0;i<*bn2;i++) printf("%I64d ",p2[i]);printf("\n"); | |
| // for(i=0;i<*bn3;i++) printf("(%I64d %I64d)\n",p3[i],r3[i]);printf("\n"); | |
| free(sieve); | |
| *_p1=p1; *_r1=r1; *_p2=p2; *_p3=p3; *_r3=r3; | |
| } | |
| /* return index of v in p, or -1 if it doesn't exist */ | |
| ull bs(ull *p,ull bn,ull v) { | |
| ull lo=0,hi=bn,mid; | |
| while(lo<hi) { | |
| mid=(lo+hi)>>1; | |
| if(v>p[mid]) lo=mid+1; | |
| else hi=mid; | |
| } | |
| return lo<bn && p[lo]==v?lo:-1; | |
| } | |
| /* matrix (global) */ | |
| uint **M; | |
| int notsmooth,missed,smooth; | |
| /* gaussian elimination mod 2 on bitmasks, A is n*m, b is n*o */ | |
| /* a is a malloced array of pointers, each a[i] is of size | |
| sizeof(uint)*(m+o+31)/32 */ | |
| /* return 0: no solutions, 1: one solution, 2: free variables */ | |
| #define ISSET(a,row,col) (a[(row)][(col)>>5]&(1U<<((col)&31))) | |
| #define MSETBIT(a,row,col) a[(row)][(col)>>5]|=(1U<<((col)&31)) | |
| #define MTOGGLEBIT(a,row,col) a[(row)][(col)>>5]^=(1U<<((col)&31)) | |
| int bitgauss32(uint **a,int n,int m,int o) { | |
| int i,j,k,z=m+o,c=0,fri=0,bz=(z+31)>>5; | |
| uint t; | |
| /* process each column */ | |
| for(i=0;i<m;i++) { | |
| /* TODO check words instead of bits */ | |
| for(j=c;j<n;j++) if(ISSET(a,j,i)) break; | |
| if(j==n) { fri=1; continue; } | |
| /* swap? */ | |
| if(j>c) for(k=0;k<bz;k++) { | |
| t=a[j][k],a[j][k]=a[c][k],a[c][k]=t; | |
| } | |
| /* subtract multiples of this row */ | |
| for(j=0;j<n;j++) if(j!=c && ISSET(a,j,i)) { | |
| for(k=0;k<bz;k++) a[j][k]^=a[c][k]; | |
| } | |
| c++; | |
| } | |
| /* detect no solution: rows with 0=b */ | |
| for(i=0;i<n;i++) { | |
| /* TODO make bit-efficient solution later */ | |
| for(j=0;j<m;j++) if(ISSET(a,i,j)) goto ok; | |
| for(;j<z;j++) if(ISSET(a,i,j)) return 0; | |
| ok:; | |
| } | |
| return 1+fri; | |
| } | |
| /* find all free variables: variable i is free if there is no row having its first | |
| 1-element in column i */ | |
| int findfreevars(uint **a,int rows,int cols,uchar *freevar) { | |
| int i,j,r=cols; | |
| memset(freevar,1,cols); | |
| for(i=0;i<rows;i++) { | |
| for(j=0;j<cols;j++) if(ISSET(a,i,j)) { | |
| freevar[j]=0; | |
| r--; | |
| break; | |
| } | |
| } | |
| return r; | |
| } | |
| /* find exponents of square. id is the index of the free variable we want to | |
| use | |
| rows: factor base | |
| cols: relations */ | |
| void getsquare(uint **a,int rows,int cols,uchar *freevar,int id,uchar *v) { | |
| int i,j,k; | |
| memset(v,0,cols); | |
| /* set id-th free variable */ | |
| for(j=i=0;i<cols;i++) if(freevar[i]) { | |
| if(id==j) { v[i]=1; break; } | |
| j++; | |
| } | |
| /* get solution vector by back substitution! set the first 1-element to the | |
| xor of the others. */ | |
| for(i=rows-1;i>=0;i--) { | |
| for(j=0;j<cols;j++) if(ISSET(a,i,j)) goto ok; | |
| continue; | |
| ok: | |
| for(k=j++;j<cols;j++) if(ISSET(a,i,j) && v[j]) v[k]^=1; | |
| } | |
| } | |
| /* store rational factors for pairs (a,b) */ | |
| ull **faclist; | |
| int *facn; | |
| /* store algebraic factors for pairs (a,b) */ | |
| ull **alglist; | |
| int *algn; | |
| /* get rational square root! */ | |
| void getratroot(mpz_t n,uchar *v,int cols,mpz_t *f,int df,mpz_t m,mpz_t root,int *aval,int *bval) { | |
| mpz_t t; | |
| static mpz_t fd[MAXDEG+1]; | |
| static int *ev; | |
| int dfd; | |
| mpz_init(t); | |
| mpz_set_si(root,1); | |
| int i,j; | |
| ev=calloc(bn2,sizeof(int)); | |
| if(!ev) error("out of memory"); | |
| for(i=0;i<cols;i++) if(v[i]) for(j=0;j<facn[i];j++) ev[faclist[i][j]]++; | |
| /* sanity */ | |
| for(i=0;i<bn2;i++) if(ev[i]&1) error("odd exponent in rat"); | |
| for(i=0;i<bn2;i++) if(ev[i]) { | |
| mpz_set_ull(t,p2[i]); | |
| for(j=0;j+j<ev[i];j++) mpz_mul(root,root,t); | |
| mpz_mod(root,root,n); | |
| } | |
| /* multiply value with f'(m)^2 */ | |
| dfd=df-1; | |
| for(i=0;i<=dfd;i++) { | |
| mpz_init_set(fd[i],f[i+1]); | |
| mpz_mul_ui(fd[i],fd[i],i+1); | |
| } | |
| evalpoly(fd,dfd,m,t); | |
| mpz_mod(t,t,n); /* t = f'(m) mod n */ | |
| mpz_mul(root,root,t); /* multiply in f'(m) */ | |
| mpz_mod(root,root,n); /* and reduce mod n */ | |
| mpz_mul(t,root,root); | |
| mpz_mod(t,t,n); | |
| gmp_printf("rational root: %Zd, square %Zd\n",root,t); | |
| for(i=0;i<=dfd;i++) mpz_clear(fd[i]); | |
| free(ev); | |
| mpz_clear(t); | |
| } | |
| /* start of routines for algebraic square root */ | |
| /* return 1 if f(x) is irreducible mod p */ | |
| int polyirredmod(mpz_t *in,int df,ull p) { | |
| /* check if gcd(x^(p^d)-x,f) is a non-constant | |
| polynomial for 1<=d<=df/2 */ | |
| static ull g[MAXDEG+1],h[MAXDEG+1],f[MAXDEG+1]; | |
| int dg,i,j,dh; | |
| for(i=0;i<=df;i++) f[i]=mpz_mod_ull(in[i],p); | |
| for(i=1;i+i<=df;i++) { | |
| /* form x^p^i - x */ | |
| /* use that x^p^i = ((x^p)^p) ... ^p (i times) */ | |
| g[0]=0; g[1]=1; dg=1; | |
| for(j=0;j<i;j++) polypowmodmod(g,dg,p,f,df,g,&dg,p); | |
| h[0]=0; h[1]=p-1; dh=1; | |
| polyaddmod(g,dg,h,dh,g,&dg,p); | |
| polygcdmod(g,dg,f,df,g,&dg,p); | |
| if(dg>0) return 0; | |
| } | |
| return 1; | |
| } | |
| /* calculate the legendre symbol of the element a (in polynomial format) | |
| in the field F_p^df: | |
| 1 if element is a quadratic residue, -1 if not. | |
| p must be an odd prime! */ | |
| int polylegendre(ull *a,int da,ull *f,int df,ull p) { | |
| ull b[MAXDEG+1]; | |
| mpz_t n,P; | |
| int db,i; | |
| for(i=0;i<=da;i++) if(a[i]) goto notzero; | |
| return 0; | |
| notzero: | |
| mpz_init(n); | |
| mpz_init(P); | |
| mpz_set_ull(P,p); | |
| mpz_pow_ui(n,P,df); | |
| mpz_sub_ui(n,n,1); | |
| mpz_divexact_ui(n,n,2); | |
| polypowmodmodmpz(a,da,n,f,df,b,&db,p); | |
| mpz_clear(n); | |
| mpz_clear(P); | |
| if(b[0]==p-1) return -1; | |
| if(b[0]==1) return 1; | |
| error("error in polylegendre, res not 1 or -1"); | |
| return 0; | |
| } | |
| int findexpdiv2(mpz_t P,int df) { | |
| mpz_t s; | |
| int r=0; | |
| mpz_init(s); | |
| mpz_pow_ui(s,P,df); | |
| mpz_sub_ui(s,s,1); | |
| while(!mpz_tstbit(s,0)) { | |
| r++; | |
| mpz_fdiv_q_2exp(s,s,1); | |
| } | |
| mpz_clear(s); | |
| return r; | |
| } | |
| /* given a, find b such that b^2=a in the field F_{p^df} given by the | |
| minimal polynomial f with degree df */ | |
| /* based on description in briggs */ | |
| /* algorithm is pretty much tonelli-shanks, adapted to F_{p^df} */ | |
| /* warning, i took a dubious short cut when implementing. p^df-1 should | |
| not have a divisor 2^s for a large s. this was circumvented by avoiding | |
| finite fields with this property */ | |
| void polysqrtmod(ull *a,int da,ull *f,int df,ull *b,int *db,ull p) { | |
| mpz_t s,z; | |
| ull j,c[MAXDEG+1],d[MAXDEG+1],e[MAXDEG+1]; | |
| int r=0,dc,i,dd,t,de; | |
| /* does the square root exist? */ | |
| if(1!=polylegendre(a,da,f,df,p)) { *db=-1; printf("not a square\n"); return; } | |
| mpz_init(s); | |
| mpz_init(z); | |
| /* write p^df-1 as 2^r * s for s odd */ | |
| mpz_set_ull(s,p); | |
| mpz_pow_ui(s,s,df); | |
| mpz_sub_ui(s,s,1); | |
| while(!mpz_tstbit(s,0)) { | |
| r++; | |
| mpz_fdiv_q_2exp(s,s,1); | |
| } | |
| if(r>10) error("error, unsuitable r"); | |
| /* find an element in F_{p^df} which is a non-residue */ | |
| for(j=1;;j++) { | |
| for(dc=df-1,i=0;i<=dc;i++) c[i]=j; | |
| // for(dc=1,i=0;i<=dc;i++) c[i]=j; | |
| if(-1==polylegendre(c,dc,f,df,p)) break; | |
| } | |
| /* d=a^s */ | |
| polypowmodmodmpz(a,da,s,f,df,d,&dd,p); | |
| /* find t such that c^2st = d. guaranteed to be <2^r */ | |
| for(t=0;t<(1<<r);t++) { | |
| mpz_mul_ui(z,s,2*t); | |
| polypowmodmodmpz(c,dc,z,f,df,e,&de,p); | |
| /* c^2st == d? */ | |
| if(de==dd) { | |
| for(i=0;i<=de;i++) if(e[i]!=d[i]) goto noteq; | |
| goto eq; | |
| } | |
| noteq:; | |
| } | |
| error("didn't find t in sqrt"); | |
| eq:; | |
| mpz_mul_ui(z,s,t); | |
| polypowmodmodmpz(c,dc,z,f,df,e,&de,p); | |
| /* calculate the inverse of e */ | |
| polyinversemodmod(e,de,f,df,e,&de,p); | |
| /* the root is a^(s+1)/2 * e^-1 */ | |
| mpz_add_ui(s,s,1); | |
| mpz_fdiv_q_2exp(s,s,1); | |
| polypowmodmodmpz(a,da,s,f,df,c,&dc,p); | |
| polymulmodmod(c,dc,e,de,f,df,b,db,p); | |
| mpz_clear(z); | |
| mpz_clear(s); | |
| } | |
| /* here follows some subroutines for polynomial arithmetic over Z */ | |
| /* multiply two polynomials, c(x)=a(x)*b(x) */ | |
| void polymulmpz(mpz_t *a,int da,mpz_t *b,int db,mpz_t *c,int *dc) { | |
| static mpz_t r[2*BIGDEG+2]; | |
| int i,j; | |
| for(i=0;i<=da+db;i++) mpz_init_set_ui(r[i],0); | |
| for(i=0;i<=da;i++) for(j=0;j<=db;j++) mpz_addmul(r[i+j],a[i],b[j]); | |
| for(*dc=da+db,i=0;i<=*dc;i++) mpz_set(c[i],r[i]); | |
| for(i=0;i<=da+db;i++) mpz_clear(r[i]); | |
| } | |
| /* reduce a(x) mod f(x), return result in b(x) */ | |
| void polyreducempz(mpz_t *a,int da,mpz_t *f,int df,mpz_t *b,int *db) { | |
| mpz_t w[2*BIGDEG+2],t; | |
| int i,j,z; | |
| mpz_init(t); | |
| for(i=0;i<=da;i++) mpz_init_set(w[i],a[i]); | |
| for(;i<=df;i++) mpz_init_set_ui(w[i],0); | |
| /* for each i=da, da-1, ..., dv, subtract a(i)*v(x)*x^(i-dv) */ | |
| for(i=da;i>=df;i--) for(j=0;j<=df;j++) { | |
| z=i-df; | |
| mpz_set(t,w[i]); | |
| mpz_submul(w[z+j],t,f[j]); | |
| } | |
| for(i=0;i<df;i++) mpz_set(b[i],w[i]); | |
| *db=df-1; | |
| /* tighten db */ | |
| while(*db>-1 && !mpz_cmp_si(b[*db],0)) (*db)--; | |
| for(i=0;i<=da;i++) mpz_clear(w[i]); | |
| for(;i<=df;i++) mpz_clear(w[i]); | |
| mpz_clear(t); | |
| } | |
| /* given f, return g=f' */ | |
| void polyderivempz(mpz_t *f,int df,mpz_t *g,int *dg) { | |
| int i; | |
| *dg=df-1; | |
| for(i=1;i<=df;i++) mpz_mul_si(g[i-1],f[i],i); | |
| while(*dg>-1 && !mpz_cmp_si(g[*dg],0)) (*dg)--; | |
| } | |
| /* calculate the algebraic number and display it */ | |
| void printalgnum(mpz_t n,uchar *v,int cols,mpz_t *f,int df,mpz_t m,int *aval,int *bval) { | |
| mpz_t a[2*BIGDEG+2],b[BIGDEG+1]; | |
| int da,db,i; | |
| for(i=0;i<2*BIGDEG+2;i++) mpz_init_set_ui(a[i],i==0); | |
| for(i=0;i<BIGDEG+1;i++) mpz_init(b[i]); | |
| da=0; | |
| /* multiply with f'(alpha)^2 */ | |
| polyderivempz(f,df,b,&db); | |
| polymulmpz(a,da,b,db,a,&da); | |
| polyreducempz(a,da,f,df,a,&da); | |
| polymulmpz(a,da,b,db,a,&da); | |
| polyreducempz(a,da,f,df,a,&da); | |
| for(i=0;i<cols;i++) if(v[i]) { | |
| mpz_set_si(b[0],aval[i]); | |
| mpz_set_si(b[1],-bval[i]); | |
| db=1; | |
| polymulmpz(a,da,b,db,a,&da); | |
| polyreducempz(a,da,f,df,a,&da); | |
| } | |
| printf("algebraic square:\n"); | |
| printmpzpoly(a,da); | |
| for(i=0;i<BIGDEG+1;i++) mpz_clear(b[i]); | |
| for(i=0;i<2*BIGDEG+2;i++) mpz_clear(a[i]); | |
| } | |
| /* get algebraic square root! v is the subset of (a,b) pairs */ | |
| /* use couveignes' algorithm */ | |
| int getalgroot(mpz_t n,uchar *v,int cols,mpz_t *in,int df,mpz_t m,mpz_t root,int *aval,int *bval) { | |
| double logest=0,b; | |
| mpz_t P,M,temp,ans; | |
| ull *q,pp,*ai,f[MAXDEG+1],fd[MAXDEG+1],g[MAXDEG+1],h[MAXDEG+1]; | |
| ull n1,n2,xi; | |
| const ull MAX=(1ULL<<61)-1; /* start here to check for primes */ | |
| int i,s,maxu,qn,dfd,j,dg,dh,k,ret=0; | |
| double zp; | |
| static int *ev; | |
| /* populate exponent vector */ | |
| ev=calloc(bn1,sizeof(int)); | |
| if(!ev) error("out of memory"); | |
| for(i=0;i<cols;i++) if(v[i]) for(j=0;j<algn[i];j++) { | |
| if(alglist[i][j]<0 || alglist[i][j]>=bn1) error("error"); | |
| ev[alglist[i][j]]++; | |
| } | |
| mpz_init(P); | |
| mpz_init_set_si(M,1); | |
| mpz_init(temp); | |
| mpz_init(ans); | |
| mpz_set_ui(ans,0); | |
| /* rough estimate: | |
| d^(d+5)/2 * n * (2*u*sqrt(d)*m)^(s/2) | |
| calculate log2 of this since it's huge */ | |
| /* if this turns out to be bad, check the paper of couveignes for a | |
| tighter bound using complex roots and direct evaluation of stuff */ | |
| logest=log2(df)*(df+5)*.5; | |
| logest+=mpz_sizeinbase(n,2); | |
| /* get u and s */ | |
| maxu=0; | |
| for(i=0;i<cols;i++) { | |
| if(maxu<-aval[i]) maxu=-aval[i]; | |
| if(maxu<aval[i]) maxu=aval[i]; | |
| if(maxu<-bval[i]) maxu=-bval[i]; | |
| if(maxu<bval[i]) maxu=bval[i]; | |
| } | |
| for(s=i=0;i<cols;i++) s+=v[i]; | |
| b=2*maxu*sqrt(df)*mpz_get_d(m); | |
| logest+=s*.5*log2(b); | |
| printf("estimate: %f bits\n",logest); | |
| /* find multiple q such that their product has >= logest digits */ | |
| qn=(int)(1+logest/log2(MAX)); | |
| q=malloc(qn*sizeof(ull)); | |
| if(!q) error("out of memory in algroot"); | |
| ai=malloc(qn*sizeof(ull)); | |
| if(!ai) error("out of memory in algroot"); | |
| /* don't be super duper tight and take primes just below 2^63. | |
| it seems there are overflow issues in some of the subroutines, | |
| the suspects are polyderivemod and polymulmodmod (and their callees) */ | |
| for(pp=MAX,i=0;i<qn;pp+=2) { | |
| mpz_set_ull(P,pp); | |
| /* P must be prime and f(x) mod P must be irreducible */ | |
| if(!mpz_probab_prime_p(P,30)) continue; | |
| if(!polyirredmod(in,df,pp)) continue; | |
| /* we also want to avoid P such that 2^r for large r divides P^df-1 */ | |
| if(findexpdiv2(P,df)>5) continue; | |
| q[i++]=pp; | |
| mpz_mul(M,M,P); | |
| } | |
| /* for each i, compute a_i */ | |
| for(zp=i=0;i<qn;i++) { | |
| mpz_set_ull(P,q[i]); | |
| mpz_fdiv_q(temp,M,P); | |
| pp=mpz_mod_ull(temp,q[i]); | |
| ai[i]=inverse(pp,q[i]); | |
| } | |
| /* for each q_i, calculate f'^2 * prod(a-bx) mod f, mod q_i | |
| and calculate its square root in Z_p/<f> */ | |
| dfd=df-1; | |
| for(i=0;i<qn;i++) { | |
| // printf("prime %I64d\n",q[i]); | |
| for(j=0;j<=df;j++) f[j]=mpz_mod_ull(in[j],q[i]); | |
| polyderivemod(f,df,fd,&dfd,q[i]); | |
| /* form f'^2 * prod_{(a,b)} (a-b*alpha) mod q[i] */ | |
| polymulmodmod(fd,dfd,fd,dfd,f,df,g,&dg,q[i]); | |
| for(j=0;j<cols;j++) if(v[j]) { | |
| h[0]=(aval[j]%(ll)q[i]+(ll)q[i])%(ll)q[i]; | |
| h[1]=((-(ll)bval[j])%(ll)q[i]+(ll)q[i])%(ll)q[i]; | |
| dh=1; | |
| polymulmodmod(g,dg,h,dh,f,df,g,&dg,q[i]); | |
| } | |
| // printf("%d: take sqrt of ",i);printullpoly(g,dg);printf("mod %I64d\n",q[i]); | |
| // printf("norm before root: %I64d\n",calcnormmod(g,dg,f,df,q[i])); | |
| /* take square root of g */ | |
| polysqrtmod(g,dg,f,df,g,&dg,q[i]); | |
| /* sanity, not a square */ | |
| if(dg<0) { | |
| printf("failed in %d of %d\n",i+1,qn); | |
| puts("error!"); | |
| printf("p %I64d, f(x) mod p = ",q[i]); | |
| printullpoly(f,df);printf("\n"); | |
| printf("g(x) mod p is not square: "); | |
| printullpoly(g,dg);printf("\n"); | |
| goto quit; | |
| } | |
| /* norm of root (in g,dg) */ | |
| n1=calcnormmod(g,dg,f,df,q[i]); | |
| /* norm of f'(alpha) */ | |
| n2=calcnormmod(fd,dfd,f,df,q[i]); | |
| /* norm of square root of all prime ideals */ | |
| for(j=0;j<bn1;j++) if(ev[j]) { | |
| /* norm of prime factor represented by the pair (p,r) is p */ | |
| for(k=0;k+k<ev[j];k++) n2=ullmulmod2(n2,p1[j],q[i]); | |
| } | |
| /* if the norms are different, negate the root */ | |
| // printf("before neg %I64d %I64d, n^2=%I64d\n",n1,n2,ullmulmod2(n2,n2,q[i])); | |
| if(n1!=n2) for(j=0;j<=dg;j++) g[j]=(q[i]-g[j])%q[i]; | |
| // printf("sqrt is ");printullpoly(g,dg);printf("\n"); | |
| n1=calcnormmod(g,dg,f,df,q[i]); | |
| // printf("after neg %I64d %I64d\n",n1,n2); | |
| if(n1!=n2) { printf("error %d/%d, norms are not equal!\n",i+1,qn); goto quit; } | |
| /* calculate a_i*x_i*P_i mod n and add it to result */ | |
| mpz_set_ull(P,q[i]); | |
| mpz_fdiv_q(temp,M,P); | |
| mpz_set_ull(P,ai[i]); | |
| mpz_mul(temp,temp,P); | |
| xi=evalpolymod(g,dg,mpz_mod_ull(m,q[i]),q[i]); | |
| mpz_set_ull(P,xi); | |
| mpz_mul(temp,temp,P); | |
| mpz_add(ans,ans,temp); | |
| mpz_fdiv_r(ans,ans,M); | |
| } | |
| ret=1; | |
| mpz_set(root,ans); | |
| mpz_fdiv_r(root,root,n); | |
| mpz_mul(temp,root,root); | |
| mpz_fdiv_r(temp,temp,n); | |
| gmp_printf("root %Zd root^2 %Zd\n",root,temp); | |
| quit: | |
| free(q); | |
| mpz_clear(ans); | |
| mpz_clear(temp); | |
| mpz_clear(M); | |
| mpz_clear(P); | |
| free(ev); | |
| return ret; | |
| } | |
| /* use trial division to check that a-bm (rational) and a-b*alpha (algebraic) | |
| are smooth with regard to our factor base. return 1 if smooth and also | |
| return the indexes of the factors in *f1,*f2,*f3. also set f0 to 1 if | |
| a-bm is negative. f3 will contain list of indexes where legendre | |
| symbol=-1. */ | |
| int trialsmooth(mpz_t a,mpz_t b,mpz_t *f,int deg,mpz_t m,int *f0,ull *f1,int *fn1,ull *f2,int *fn2,ull *f3,int *fn3) { | |
| mpz_t rat,alg,t,u,div; | |
| ull i,j,r,A,B; | |
| int ret=0; | |
| mpz_init(t); | |
| mpz_init(u); | |
| mpz_set(t,a); | |
| mpz_set(u,b); | |
| mpz_abs(t,t); | |
| mpz_abs(u,u); | |
| mpz_gcd(t,t,u); | |
| if(mpz_cmp_si(t,1)) goto cleanupgcd; | |
| mpz_init(div); | |
| /* rat = a-bm */ | |
| mpz_init(rat); | |
| mpz_mul(rat,b,m); | |
| mpz_sub(rat,a,rat); | |
| // gmp_printf("\nrat %Zd: ",rat); | |
| /* check for negative a-bm */ | |
| if(mpz_cmp_si(rat,0)<0) *f0=1,mpz_abs(rat,rat); | |
| else *f0=0; | |
| *fn2=0; | |
| /* trial division on a-bm */ | |
| for(i=0;i<bn2;i++) { | |
| /* break if p2[i]^2 > rat */ | |
| mpz_set_ull(div,p2[i]); | |
| mpz_mul(t,div,div); | |
| if(mpz_cmp(t,rat)>0) break; | |
| /* factor out div from rat and keep count */ | |
| mpz_fdiv_qr(t,u,rat,div); | |
| if(mpz_cmp_si(u,0)) continue; | |
| mpz_set(rat,t); | |
| f2[(*fn2)++]=i; | |
| // printf("%I64d ",p2[i]); | |
| while(1) { | |
| mpz_fdiv_qr(t,u,rat,div); | |
| if(mpz_cmp_si(u,0)) break; | |
| mpz_set(rat,t); | |
| f2[(*fn2)++]=i; | |
| // printf("%I64d ",p2[i]); | |
| } | |
| } | |
| /* if remainder of rat > largest prime in factor base, number isn't smooth */ | |
| mpz_set_ull(div,p2[bn2-1]); | |
| if(mpz_cmp(div,rat)<0) goto cleanuprat; | |
| if(mpz_cmp_si(rat,1)>0) { | |
| /* add remainder to primes */ | |
| f2[(*fn2)++]=bs(p2,bn2,mpz_get_ull(rat)); | |
| if(mpz_get_ull(rat)!=p2[bs(p2,bn2,mpz_get_ull(rat))]) error("sanity test failed, rational remainder is not equal to prime found"); | |
| // printf("%I64d ",mpz_get_ull(rat)); | |
| } | |
| /* alg = norm(a-b*alpha) */ | |
| mpz_init(alg); | |
| calcnorm(alg,a,b,f,deg); | |
| mpz_abs(alg,alg); | |
| // gmp_printf("\nalg %Zd: \n",alg); | |
| *fn1=0; | |
| /* trial division on norm(a-b*alpha) */ | |
| for(i=0;i<bn1;i++) { | |
| /* break if p1[i]^2 > alg */ | |
| mpz_set_ull(div,p1[i]); | |
| mpz_mul(t,div,div); | |
| if(mpz_cmp(t,alg)>0) break; | |
| /* check if p1[i] divides alg */ | |
| mpz_fdiv_r(t,alg,div); | |
| if(mpz_cmp_si(t,0)) continue; | |
| /* if a-br=0 mod p this is the prime we want */ | |
| mpz_set_ull(t,r1[i]); | |
| mpz_mul(t,b,t); | |
| mpz_sub(t,a,t); | |
| mpz_fdiv_r(t,t,div); | |
| if(mpz_cmp_si(t,0)) continue; | |
| mpz_fdiv_q(alg,alg,div); | |
| // gmp_printf("[%I64d %I64d] ",p1[i],r1[i]); | |
| /* factor out div from alg and keep count */ | |
| f1[(*fn1)++]=i; | |
| while(1) { | |
| mpz_fdiv_qr(t,u,alg,div); | |
| if(mpz_cmp_si(u,0)) break; | |
| mpz_set(alg,t); | |
| // gmp_printf("[%I64d %I64d] ",p1[i],r1[i]); | |
| f1[(*fn1)++]=i; | |
| } | |
| } | |
| /* check if alg>largest prime in factor base */ | |
| mpz_set_ull(div,p1[bn1-1]); | |
| // gmp_printf("rest %Zd vs %Zd\n",alg,div); | |
| if(mpz_cmp(div,alg)<0) goto cleanupalg; | |
| if(mpz_cmp_si(alg,1)>0) { | |
| /* add reminder to primes */ | |
| /* find index of first eligible pair (p,r) */ | |
| i=bs(p1,bn1,mpz_get_ull(alg)); | |
| if(i==-1) { | |
| gmp_printf("a = %Zd, b = %Zd\n",a,b); | |
| printf("tried to find %I64d, not in factor base\n",mpz_get_ull(alg)); | |
| r=mpz_get_ull(alg); | |
| for(i=0;i<bn1;i++) if(p1[i]>r-1000 && p1[i]<r+1000) printf("[%I64d %I64d] ",p1[i],r1[i]); | |
| error("\n"); | |
| } | |
| /* find r such that a-br=0 (mod p) which is a*inverse(b) mod p */ | |
| mpz_fdiv_r(u,a,alg); | |
| A=mpz_get_ull(u); | |
| mpz_fdiv_r(u,b,alg); | |
| B=mpz_get_ull(u); | |
| r=ullmulmod2(inverse(B,p1[i]),A,p1[i]); | |
| for(j=i;j<bn1;j++) { | |
| if(p1[j]!=p1[i]) break; | |
| if(r1[j]==r) goto ok; | |
| } | |
| error("(p,r) not found, shouldn't happen!"); | |
| ok: | |
| f1[(*fn1)++]=j; | |
| // printf("[%I64d %I64d]\n",p1[i],r1[i]); | |
| } | |
| /* we won, (a,b) is smooth. now get the quadratic characters */ | |
| *fn3=0; | |
| for(i=0;i<bn3;i++) { | |
| /* if legendre(a-br/p)==-1, then add this (p,r) */ | |
| mpz_set_ull(t,p3[i]); | |
| mpz_set_ull(u,r3[i]); | |
| mpz_mul(u,b,u); | |
| mpz_sub(u,a,u); | |
| if(mpz_legendre(u,t)<0) f3[(*fn3)++]=i; | |
| } | |
| ret=1; | |
| cleanupalg: | |
| mpz_clear(alg); | |
| cleanuprat: | |
| mpz_clear(rat); | |
| mpz_clear(div); | |
| cleanupgcd: | |
| mpz_clear(u); | |
| mpz_clear(t); | |
| return ret; | |
| } | |
| /* sieve from a1,b to a2,b, inclusive. restriction: a1 and a2 are int */ | |
| /* return 1 whenever enough relations are found */ | |
| int linesieve(int a1,int a2,int b,mpz_t n,mpz_t *f,int fn,mpz_t m,int extra,int *aval,int *bval) { | |
| int *sieve; | |
| mpz_t rat,norm,A,B,t,u; | |
| double invl2=1./log(2); | |
| ull j,z; | |
| int size=a2-a1+1,i,a,v,lgp,ret=0; | |
| int flog[MAXDEG+1]; | |
| int blog[MAXDEG+1]; | |
| double temp; | |
| if(!(sieve=malloc(size*sizeof(int)))) error("out of memory in line sieve"); | |
| mpz_init(rat); | |
| mpz_init(norm); | |
| mpz_init(t); | |
| mpz_init(u); | |
| mpz_init(A); | |
| mpz_init(B); | |
| /* initialize rat=a-bm */ | |
| mpz_set_si(B,b); | |
| mpz_mul(t,B,m); | |
| mpz_set_si(A,a1); | |
| mpz_sub(rat,A,t); | |
| mpz_set(t,rat); | |
| /* precalculate values for fast log_2(norm) */ | |
| for(i=0;i<=fn;i++) flog[i]=mpz_sizeinbase(f[i],2); | |
| for(temp=0,i=0;i<=fn;i++,temp+=log(temp)*invl2) blog[i]=(int)(0.5+temp); | |
| for(a=a1,i=0;i<size;i++) { | |
| calcnorm(norm,A,B,f,fn); | |
| /* store lg norm + lg rat in sieve */ | |
| v=mpz_sizeinbase(t,2)+mpz_sizeinbase(norm,2); | |
| /* fast version! approximate log2(norm) faster than calculating | |
| the full norm every time */ | |
| /* TODO */ | |
| /* v+=mpz_sizeinbase(t,2); */ | |
| sieve[i]=v; | |
| mpz_add_ui(t,t,1); | |
| mpz_add_ui(A,A,1); | |
| } | |
| /* process each rational prime */ | |
| for(j=opt_skip;j<bn2;j++) { | |
| lgp=.5+log(p2[j])*invl2; | |
| /* find starting point: first smallest i>=0 such that a+i-bm=0 mod p */ | |
| z=mpz_mod_ull(rat,p2[j]); | |
| /* subtract lg(prime) for each eligible element in sieve */ | |
| // gmp_printf("start for %d (%Zd, mod=%d): %d\n",(int)p2[j],rat,(int)z,(int)(z?p2[j]-z:0)); | |
| for(i=z?p2[j]-z:0;i<size;i+=p2[j]) sieve[i]-=lgp; | |
| } | |
| /* process each algebraic prime */ | |
| mpz_set_si(A,a1); | |
| for(j=opt_skip;j<bn1;j++) { | |
| lgp=.5+log(p1[j])*invl2; | |
| // printf("process %I64d, subtract %d\n",p1[j],lgp); | |
| /* find starting point: find smallest i>=0 such that a+i-br=0 mod p */ | |
| mpz_set_ull(t,r1[j]); | |
| mpz_mul(t,t,B); | |
| mpz_sub(t,A,t); | |
| z=mpz_mod_ull(t,p1[j]); | |
| for(i=z?p1[j]-z:0;i<size;i+=p1[j]) sieve[i]-=lgp; | |
| } | |
| /* find candidates for smooth numbers by taking the ones with small | |
| remaining log values. only taking 0-values is too strict, since | |
| sieve doesn't subtract powers of primes, and all logs are rounded | |
| to int */ | |
| for(i=0;i<size;i++) { | |
| /* WARNING, magic constants */ | |
| static ull f1[100000],f2[100000],f3[100000]; | |
| int fn1=0,fn2=0,fn3=0,f0; | |
| a=a1+i; | |
| if(a==0) continue; | |
| if(gcd(a>0?a:-a,b>0?b:-b)>1) continue; | |
| if(sieve[i]<=opt_thr) { | |
| mpz_add_ui(t,A,i); | |
| // printf("check %d %d\n",a1+i,b); | |
| if(trialsmooth(t,B,f,fn,m,&f0,f1,&fn1,f2,&fn2,f3,&fn3)) { | |
| /* insert in transposed matrix: | |
| column i is the ith relation we find | |
| row corresponds to -1, prime or quadratic character */ | |
| if(f0) MSETBIT(M,0,smooth); | |
| for(j=0;j<fn1;j++) MTOGGLEBIT(M,1+f1[j],smooth); | |
| for(j=0;j<fn2;j++) MTOGGLEBIT(M,1+bn1+f2[j],smooth); | |
| for(j=0;j<fn3;j++) MSETBIT(M,1+bn1+bn2+f3[j],smooth); | |
| /* store the rational divisors */ | |
| faclist[smooth]=malloc(fn2*sizeof(ull)); | |
| if(!faclist[smooth]) error("out of memory trialsmooth"); | |
| alglist[smooth]=malloc(fn1*sizeof(ull)); | |
| if(!alglist[smooth]) error("out of memory trialsmooth"); | |
| memcpy(faclist[smooth],f2,sizeof(ull)*fn2); | |
| facn[smooth]=fn2; | |
| memcpy(alglist[smooth],f1,sizeof(ull)*fn1); | |
| algn[smooth]=fn1; | |
| /* store the actual a,b pair */ | |
| aval[smooth]=a1+i; | |
| bval[smooth]=b; | |
| smooth++; | |
| if(smooth%100==0) { | |
| // printf("(%d, %d)\n",a1+i,b); | |
| printf("%d/%I64d found: (%d, %d) is smooth, log %d\n",smooth,extra+1+bn1+bn2+bn3,a1+i,b,sieve[i]); | |
| } | |
| if(smooth==extra+1+bn1+bn2+bn3) { | |
| puts("==> enough relations gathered!"); | |
| ret=1; | |
| goto end; | |
| } | |
| } else notsmooth++; | |
| } else { | |
| /* remove the continue if you want to benchmark | |
| smooth numbers not found by the sieving */ | |
| continue; | |
| mpz_add_ui(t,A,i); | |
| if(trialsmooth(t,B,f,fn,m,&f0,f1,&fn1,f2,&fn2,f3,&fn3)) { | |
| printf("%d - %d*alpha is smooth, log %d MISSED\n",a1+i,b,sieve[i]); | |
| missed++; | |
| } | |
| } | |
| } | |
| end: | |
| mpz_clear(B); | |
| mpz_clear(A); | |
| mpz_clear(u); | |
| mpz_clear(t); | |
| mpz_clear(norm); | |
| mpz_clear(rat); | |
| free(sieve); | |
| return ret; | |
| } | |
| void testsieve(mpz_t n,mpz_t *f,int fn,mpz_t m,int extra,int *aval,int *bval) { | |
| int B; | |
| /* factor lists */ | |
| puts("start sieve"); | |
| notsmooth=missed=smooth=0; | |
| faclist=malloc((1+bn1+bn2+bn3+extra)*sizeof(ull*)); | |
| if(!faclist) error("out of memory"); | |
| facn=malloc((1+bn1+bn2+bn3+extra)*sizeof(int)); | |
| if(!facn) error("out of memory"); | |
| alglist=malloc((1+bn1+bn2+bn3+extra)*sizeof(ull*)); | |
| if(!alglist) error("out of memory"); | |
| algn=malloc((1+bn1+bn2+bn3+extra)*sizeof(int)); | |
| if(!algn) error("out of memory"); | |
| for(B=1;;B++) if(linesieve(-opt_sievew,opt_sievew,-1*opt_signb*B,n,f,fn,m,extra,aval,bval)) break; | |
| printf("smooth numbers found: %d\n",smooth); | |
| printf("nonsmooth numbers trial-divided: %d\n",notsmooth); | |
| printf("smooth numbers missed: %d\n",missed); | |
| puts("end sievetest"); | |
| } | |
| void takegcd(mpz_t ans,mpz_t alg,mpz_t rat,mpz_t n) { | |
| mpz_t sub; | |
| mpz_init(sub); | |
| mpz_sub(sub,alg,rat); | |
| mpz_gcd(ans,n,sub); | |
| mpz_clear(sub); | |
| } | |
| /* takes a number n and returns a factor p, if found | |
| return values: | |
| 1: factor found | |
| 0: factor not found | |
| -1: n is even | |
| -2: n is a perfect power | |
| -3: n is probably prime | |
| -4: mysterious error */ | |
| int donfs(mpz_t n) { | |
| mpz_t m,f[MAXDEG+1],r,temp; | |
| mpz_t ratrot,algrot; | |
| ull Br=opt_Br,Ba=opt_Ba,rows,k; | |
| int *aval,*bval; | |
| int deg=opt_deg; | |
| int err,retval=0,i,Bk=opt_Bq,j; | |
| int extra=opt_extra,zero; | |
| uchar *v; | |
| uchar *freevar; | |
| mpz_init(r); mpz_init(m); mpz_init(temp); | |
| mpz_init(ratrot); mpz_init(algrot); | |
| for(i=0;i<=MAXDEG;i++) mpz_init(f[i]); | |
| /* check prerequisites: n cannot be even, prime or perfect power */ | |
| /* (if n is a perfect power, try running nfs again on the root */ | |
| mpz_fdiv_r_ui(r,n,2); | |
| if(!mpz_cmp_si(r,0)) { retval=-1; goto end; } | |
| if(mpz_perfect_power_p(n)) { retval=-2; goto end; } | |
| /* we want to be really, REALLY sure that n is composite */ | |
| if(mpz_probab_prime_p(n,100)) { retval=-3; goto end; } | |
| mpz_set(m,opt_m); | |
| if(!mpz_cmp_si(m,0)) mpz_root(m,n,deg); /* deg-th root of n, get our base m */ | |
| gmp_printf("m = %Zd\n",m); | |
| err=getpolynomial(n,m,deg,f); | |
| if(!err) error("polynomial isn't monic or is otherwise wrong"); | |
| printmpzpoly(f,deg); | |
| /* for now, only try to find linear factors when | |
| the a_0 coefficient is small enough */ | |
| /* TODO replace with better way to find all linear factors. | |
| fully factorize f[0] (possibly by pollard rho or even qs) and | |
| generate all divisors by generating all exponent tuples. in this way, | |
| the program will have full degree 3 support */ | |
| /* TODO move this to a function */ | |
| if(mpz_cmp_si(f[0],2000000000)<0) { | |
| if(!mpz_cmp_si(f[0],0)) { | |
| printmpzpoly(f,deg); | |
| gmp_printf("f(x) factored, found factor %Zd\n",m); | |
| retval=1; | |
| goto end; | |
| } | |
| j=mpz_get_si(f[0]); | |
| for(i=1;i*i<=j;i++) if(j%i==0) { | |
| if(i>1) { | |
| mpz_set_si(r,-i); | |
| evalpoly(f,deg,r,temp); | |
| if(!mpz_cmp_si(temp,0)) { | |
| printmpzpoly(f,deg); | |
| gmp_printf("f(x) factored, found factor %d\n",i); | |
| retval=1; | |
| goto end; | |
| } | |
| } | |
| mpz_set_si(r,-j/i); | |
| evalpoly(f,deg,r,temp); | |
| if(!mpz_cmp_si(temp,0)) { | |
| printmpzpoly(f,deg); | |
| gmp_printf("f(x) factored, found factor %d\n",i); | |
| retval=1; | |
| goto end; | |
| } | |
| } | |
| } | |
| /* TODO try to factorize polynomial properly and terminate early */ | |
| /* factor base */ | |
| if(!Ba) Ba=findB(n)*1; | |
| if(!Br) Br=findB(n)*1; | |
| if(!Bk) Bk=findK(n)*0.25; /* number of quadratic characters */ | |
| puts("factor base info:"); | |
| printf(" bound %I64d\n",Ba); | |
| createfactorbases(Ba,Br,Bk,f,deg,&p1,&r1,&bn1,&p2,&bn2,&p3,&r3,&bn3); | |
| printf(" %I64d rational primes\n",bn2); | |
| printf(" %I64d algebraic primes\n",bn1); | |
| printf(" %d quadratic characters\n",Bk); | |
| printf(" total size %I64d\n",bn1+bn2+Bk); | |
| if(!extra) extra=3+(bn1+bn2+bn3+1)/1000; | |
| puts("continue with factorization, altsaa"); | |
| /* allocate memory for matrix, uncompressed */ | |
| rows=1+bn1+bn2+bn3; | |
| M=malloc(sizeof(uint *)*rows); | |
| for(i=0;i<rows;i++) { | |
| M[i]=calloc(((rows+31+extra)/32),sizeof(uint)); | |
| if(!M[i]) error("out of memory while allocating matrix"); | |
| } | |
| aval=malloc(sizeof(int)*(rows+extra)); | |
| if(!aval) error("out of memory"); | |
| bval=malloc(sizeof(int)*(rows+extra)); | |
| if(!bval) error("out of memory"); | |
| testsieve(n,f,deg,m,extra,aval,bval); | |
| puts("start gauss"); | |
| // printf("before solve\n");for(i=0;i<rows;i++) { for(j=0;j<rows+extra;j++) printf("%d",ISSET(M,i,j)?1:0); printf("\n");} | |
| bitgauss32(M,rows,rows+extra,0); | |
| // printf("after solve\n");for(i=0;i<rows;i++) { for(j=0;j<rows+extra;j++) printf("%d",ISSET(M,i,j)?1:0); printf("\n");} | |
| v=malloc(rows+extra); | |
| if(!v) error("out of memory"); | |
| freevar=malloc(rows+extra); | |
| if(!freevar) error("out of memory"); | |
| zero=findfreevars(M,rows,rows+extra,freevar); | |
| printf("gauss done, %d free variables found\n",zero); | |
| for(k=0;k<zero;k++) { | |
| puts("-------------------------------------"); | |
| getsquare(M,rows,rows+extra,freevar,k,v); | |
| // printalgnum(n,v,rows+extra,f,deg,m,aval,bval); | |
| if(!getalgroot(n,v,rows+extra,f,deg,m,algrot,aval,bval)) continue; | |
| // printf("takevector:\n");for(i=0;i<rows+extra;i++) printf("%d",v[i]);printf("\n"); | |
| getratroot(n,v,rows+extra,f,deg,m,ratrot,aval,bval); | |
| gmp_printf("algroot %Zd ratroot %Zd\n",algrot,ratrot); | |
| takegcd(temp,algrot,ratrot,n); | |
| /* trivial result, try next linear combination */ | |
| if(!mpz_cmp_si(temp,1) || !mpz_cmp(temp,n)) continue; | |
| gmp_printf("found factor %Zd after %d tries\n",temp,k+1); | |
| retval=1; | |
| break; | |
| } | |
| if(!retval) puts("no factor found"); | |
| free(v); | |
| end: | |
| for(i=0;i<=MAXDEG;i++) mpz_clear(f[i]); | |
| mpz_clear(ratrot); mpz_clear(algrot); | |
| mpz_clear(m); mpz_clear(r); mpz_clear(temp); | |
| return retval; | |
| } | |
| int main() { | |
| gmp_randinit_mt(gmpseed); | |
| gmp_randseed_ui(gmpseed,time(0)); | |
| readoptions(); | |
| gmp_printf("try to factor %Zd\n",opt_n); | |
| printf("return %d\n",donfs(opt_n)); | |
| return 0; | |
| } |