474 lines
12 KiB
C++
474 lines
12 KiB
C++
/*
|
|
* Program to factor big numbers using Pomerance-Silverman-Montgomery
|
|
* multiple polynomial quadratic sieve.
|
|
* See "The Multiple Polynomial Quadratic Sieve", R.D. Silverman,
|
|
* Math. Comp. Vol. 48, 177, Jan. 1987, pp329-339
|
|
*
|
|
* Requires: big.cpp
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <cmath>
|
|
#include "big.h"
|
|
|
|
using namespace std;
|
|
|
|
#define SSIZE 1000000 /* Maximum sieve size */
|
|
|
|
Miracl precision=(30); /* number of bytes per big */
|
|
|
|
static int pk[]={0,1,2,3,5,6,7,10,11,13,14,15,17,0};
|
|
static Big NN,DD,DG,TT,RR,VV,PP,IG,AA,BB;
|
|
static Big *x,*y,*z,*w;
|
|
static int *epr,*r1,*r2,*rp,*pr;
|
|
static int *b,*e,*hash;
|
|
static unsigned int **EE,**G;
|
|
static unsigned char *logp,*sieve;
|
|
static int mm,mlf,jj,nbts,nlp,lp,hmod,hmod2;
|
|
static BOOL partial;
|
|
static miracl *mip;
|
|
|
|
int knuth(int mm,int *epr,Big& N,Big& D)
|
|
{ /* Input number to be factored N, find best multiplier k *
|
|
* set D=k.N */
|
|
Big T;
|
|
double fks,dp,top;
|
|
BOOL found;
|
|
int i,j,bk,nk,kk,rem,p;
|
|
|
|
top=(-10.0e0);
|
|
found=FALSE;
|
|
nk=0;
|
|
bk=0;
|
|
epr[0]=1;
|
|
epr[1]=2;
|
|
|
|
do
|
|
{ /* search for best Knuth-Schroepel multiplier */
|
|
kk=pk[++nk];
|
|
if (kk==0)
|
|
{ /* finished */
|
|
kk=pk[bk];
|
|
found=TRUE;
|
|
}
|
|
D=kk*N;
|
|
fks=log(2.0e0)/(2.0e0);
|
|
rem=D%8;
|
|
if (rem==1) fks*=(4.0e0);
|
|
if (rem==5) fks*=(2.0e0);
|
|
fks-=log((double)kk)/(2.0e0);
|
|
i=0;
|
|
j=1;
|
|
while (j<mm)
|
|
{ /* select small primes */
|
|
p=mip->PRIMES[++i];
|
|
rem=D%p;
|
|
if (spmd(rem,(p-1)/2,p)<=1) /* x = spmd(a,b,c) = a^b mod c */
|
|
{ /* use only if Jacobi symbol = 0 or 1 */
|
|
epr[++j]=p;
|
|
dp=(double)p;
|
|
if (kk%p==0) fks+=log(dp)/dp;
|
|
else fks+=2*log(dp)/(dp-1.0e0);
|
|
}
|
|
}
|
|
if (fks>top)
|
|
{ /* find biggest fks */
|
|
top=fks;
|
|
bk=nk;
|
|
}
|
|
} while (!found);
|
|
return kk;
|
|
}
|
|
|
|
int initv()
|
|
{ /* initialize */
|
|
Big T;
|
|
double dp;
|
|
int i,k,digits,pak,maxp;
|
|
|
|
nbts=8*sizeof(int);
|
|
|
|
cout << "input number to be factored N= \n";
|
|
cin >> NN;
|
|
if (prime(NN))
|
|
{
|
|
cout << "this number is prime!\n";
|
|
return (-1);
|
|
}
|
|
T=NN;
|
|
digits=1; /* digits in N */
|
|
while ((T/=10)>0) digits++;
|
|
|
|
if (digits<10) mm=digits;
|
|
else mm=25;
|
|
if (digits>20) mm=(digits*digits*digits*digits)/4096;
|
|
dp=(double)2*(mm+100); /* number of primes to generate */
|
|
|
|
maxp=(int)(dp*(log(dp*log(dp)))); /* Rossers upper bound */
|
|
gprime(maxp);
|
|
|
|
epr=(int *)mr_alloc(mm+1,sizeof(int));
|
|
|
|
k=knuth(mm,epr,NN,DD);
|
|
|
|
RR=sqrt(DD);
|
|
|
|
if (RR*RR==DD)
|
|
{
|
|
cout << k << "N is a perfect square!" << endl;
|
|
cout << "factors are" << endl;
|
|
if (prime(RR)) cout << "prime factor ";
|
|
else cout << "composite factor ";
|
|
cout << RR << endl;
|
|
NN=NN/RR;
|
|
if (prime(NN)) cout << "prime factor ";
|
|
else cout << "composite factor ";
|
|
cout << NN << endl;
|
|
return (-1);
|
|
}
|
|
cout << "using multiplier k= " << k;
|
|
cout << "\nand " << mm << " small primes as factor base\n";
|
|
gprime(0); /* reclaim PRIMES space */
|
|
|
|
mlf=2*mm;
|
|
|
|
r1=(int *)mr_alloc((mm+1),sizeof(int));
|
|
r2=(int *)mr_alloc((mm+1),sizeof(int));
|
|
rp=(int *)mr_alloc((mm+1),sizeof(int));
|
|
b=(int *)mr_alloc((mm+1),sizeof(int));
|
|
e=(int *)mr_alloc((mm+1),sizeof(int));
|
|
|
|
logp=(unsigned char *)mr_alloc(mm+1,1);
|
|
|
|
pr=(int *)mr_alloc((mlf+1),sizeof(int));
|
|
hash=(int *)mr_alloc(2*mlf+1,sizeof(int));
|
|
|
|
sieve=(unsigned char *)mr_alloc(SSIZE+1,1);
|
|
|
|
x=new Big[mm+1];
|
|
y=new Big[mm+1];
|
|
z=new Big[mlf+1];
|
|
w=new Big[mlf+1];
|
|
|
|
EE=(unsigned int **)mr_alloc(mm+1,sizeof(unsigned int *));
|
|
G=(unsigned int **) mr_alloc(mlf+1,sizeof(unsigned int *));
|
|
|
|
|
|
pak=1+mm/(8*sizeof(int));
|
|
|
|
for (i=0;i<=mm;i++)
|
|
{
|
|
x[i]=0;
|
|
y[i]=0;
|
|
b[i]=(-1);
|
|
EE[i]=(unsigned int *)mr_alloc(pak,sizeof(int));
|
|
}
|
|
for (i=0;i<=mlf;i++)
|
|
{
|
|
z[i]=0;
|
|
w[i]=0;
|
|
G[i]=(unsigned int *)mr_alloc(pak,sizeof(int));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
BOOL gotcha(Big& NN,Big& P)
|
|
{ /* use new factorisation */
|
|
Big XX,YY,T;
|
|
int r,j,i,k,n,rb,had,hp;
|
|
BOOL found;
|
|
found=TRUE;
|
|
if (partial)
|
|
{ /* check partial factorisation for usefulness */
|
|
had=lp%hmod;
|
|
forever
|
|
{ /* hash search for matching large prime */
|
|
hp=hash[had];
|
|
if (hp<0)
|
|
{ /* failed to find match */
|
|
found=FALSE;
|
|
break;
|
|
}
|
|
if (pr[hp]==lp) break; /* hash hit! */
|
|
had=(had+(hmod2-lp%hmod2))%hmod;
|
|
}
|
|
if (!found && nlp>=mlf) return FALSE;
|
|
}
|
|
XX=P;
|
|
YY=1;
|
|
for (k=1;k<=mm;k++)
|
|
{ /* build up square part in YY *
|
|
* reducing e[k] to 0s and 1s */
|
|
if (e[k]<2) continue;
|
|
r=e[k]/2;
|
|
e[k]%=2;
|
|
T=epr[k];
|
|
YY*=pow(T,r);
|
|
}
|
|
if (partial)
|
|
{ /* factored with large prime */
|
|
if (!found)
|
|
{ /* store new partial factorization */
|
|
hash[had]=nlp;
|
|
pr[nlp]=lp;
|
|
z[nlp]=XX;
|
|
w[nlp]=YY;
|
|
for (n=0,rb=0,j=0;j<=mm;j++)
|
|
{
|
|
G[nlp][n]|=((e[j]&1)<<rb);
|
|
if (++rb==nbts) n++,rb=0;
|
|
}
|
|
nlp++;
|
|
}
|
|
if (found)
|
|
{ /* match found so use as factorization */
|
|
cout << "\b\b\b\b\b*";
|
|
XX=(XX*z[hp])%NN;
|
|
YY=(YY*w[hp])%NN;
|
|
for (n=0,rb=0,j=0;j<=mm;j++)
|
|
{
|
|
e[j]+=((G[hp][n]>>rb)&1);
|
|
if (e[j]==2)
|
|
{
|
|
YY=(YY*epr[j])%NN;
|
|
e[j]=0;
|
|
}
|
|
if (++rb==nbts) n++,rb=0;
|
|
}
|
|
YY=(YY*lp)%NN;
|
|
}
|
|
}
|
|
else cout << "\b\b\b\b\b " << flush;
|
|
if (found)
|
|
{
|
|
for (k=mm;k>=0;k--)
|
|
{ /* use new factorization in search for solution */
|
|
if (e[k]%2==0) continue;
|
|
if (b[k]<0)
|
|
{ /* no solution this time */
|
|
found=FALSE;
|
|
break;
|
|
}
|
|
i=b[k];
|
|
XX=(XX*x[i])%NN; /* This is very inefficient - */
|
|
YY=(YY*y[i])%NN; /* There must be a better way! */
|
|
for (n=0,rb=0,j=0;j<=mm;j++)
|
|
{ /* Gaussian elimination */
|
|
e[j]+=((EE[i][n]>>rb)&1);
|
|
if (++rb==nbts) n++,rb=0;
|
|
}
|
|
}
|
|
for (j=0;j<=mm;j++)
|
|
{ /* update YY */
|
|
if (e[j]<2) continue;
|
|
T=epr[j];
|
|
YY=(YY*pow(T,e[j]/2,NN))%NN; /* x = pow(a,b,c) = a^b mod c */
|
|
}
|
|
if (!found)
|
|
{ /* store details in EE, x and y for later */
|
|
b[k]=jj;
|
|
x[jj]=XX;
|
|
y[jj]=YY;
|
|
for (n=0,rb=0,j=0;j<=mm;j++)
|
|
{
|
|
EE[jj][n]|=((e[j]&1)<<rb);
|
|
if (++rb==nbts) n++,rb=0;
|
|
}
|
|
jj++;
|
|
cout << setw(4) << jj;
|
|
}
|
|
}
|
|
if (found)
|
|
{ /* check for false alarm */
|
|
P=XX+YY;
|
|
cout << "\ntrying...\n";
|
|
if (XX==YY || P==NN) found=FALSE;
|
|
if (!found) cout << "working... " << setw(4) << jj << flush;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
BOOL factored(long lptr,Big& T)
|
|
{ /* factor quadratic residue T */
|
|
BOOL facted;
|
|
int i,j,r,st;
|
|
partial=FALSE;
|
|
facted=FALSE;
|
|
for (j=1;j<=mm;j++)
|
|
{ /* now attempt complete factorisation of T */
|
|
r=lptr%epr[j];
|
|
if (r<0) r+=epr[j];
|
|
if (r!=r1[j] && r!=r2[j]) continue;
|
|
while (T%epr[j]==0)
|
|
{ /* cast out epr[j] */
|
|
e[j]++;
|
|
T/=epr[j];
|
|
}
|
|
st=toint(T); /* st = T as an int; st=TOOBIG if too big */
|
|
if (st==1)
|
|
{
|
|
facted=TRUE;
|
|
break;
|
|
}
|
|
if ((T/epr[j])<=epr[j])
|
|
{ /* st is prime < epr[mm]^2 */
|
|
if (st==MR_TOOBIG || (st/epr[mm])>(1+mlf/50)) break;
|
|
if (st<=epr[mm])
|
|
for (i=j;i<=mm;i++)
|
|
if (st==epr[i])
|
|
{
|
|
e[i]++;
|
|
facted=TRUE;
|
|
break;
|
|
}
|
|
if (facted) break;
|
|
lp=st; /* factored with large prime */
|
|
partial=TRUE;
|
|
facted=TRUE;
|
|
break;
|
|
}
|
|
}
|
|
return facted;
|
|
}
|
|
|
|
void new_poly()
|
|
{ /* form the next polynomial */
|
|
int i,r,s,s1,s2;
|
|
|
|
r=mip->NTRY; /* MR_NTRY is global - number of trys at proving */
|
|
mip->NTRY=1; /* a probable prime */
|
|
do
|
|
{ /* looking for suitable prime DG = 3 mod 4 */
|
|
do DG+=4; while(!prime(DG));
|
|
TT=(DG-1)/2;
|
|
TT=pow(DD,TT,DG); /* check DD is quad residue */
|
|
} while (TT!=1);
|
|
mip->NTRY=r;
|
|
TT=(DG+1)/4;
|
|
BB=pow(DD,TT,DG);
|
|
TT=(DD-BB*BB)/DG;
|
|
AA=inverse(2*BB,DG);
|
|
AA=(AA*TT)%DG;
|
|
BB=AA*DG+BB; /* BB^2 = DD mod DG^2 */
|
|
AA=DG*DG;
|
|
IG=inverse(DG,DD); /* IG = 1/DG mod DD */
|
|
r1[0]=r2[0]=0;
|
|
for (i=1;i<=mm;i++)
|
|
{ /* find roots of quadratic mod each prime */
|
|
s=BB%epr[i];
|
|
r=AA%epr[i];
|
|
r=invers(r,epr[i]); /* r = 1/AA mod p */
|
|
s1=(epr[i]-s+rp[i]);
|
|
s2=(epr[i]-s+epr[i]-rp[i]);
|
|
r1[i]=smul(s1,r,epr[i]); /* s1 = s1*r mod epr[i] */
|
|
r2[i]=smul(s2,r,epr[i]);
|
|
}
|
|
}
|
|
|
|
|
|
int main()
|
|
{ /* factoring via quadratic sieve */
|
|
unsigned int i,j,a,*SV;
|
|
unsigned char logpi;
|
|
int k,S,r,s1,s2,NS,logm,ptr,threshold,epri;
|
|
long M,la,lptr;
|
|
mip=&precision;
|
|
if (initv()<0) return 0;
|
|
hmod=2*mlf+1; /* set up hash table */
|
|
TT=hmod;
|
|
while (!prime(TT)) TT-=2;
|
|
hmod=toint(TT);
|
|
hmod2=hmod-2;
|
|
for (k=0;k<hmod;k++) hash[k]=(-1);
|
|
|
|
M=50*(long)mm;
|
|
NS=M/SSIZE;
|
|
if (M%SSIZE!=0) NS++;
|
|
M=SSIZE*(long)NS;
|
|
logm=0;
|
|
la=M;
|
|
while ((la/=2)>0) logm++; /* logm = log(M) */
|
|
|
|
rp[0]=logp[0]=0;
|
|
for (k=1;k<=mm;k++)
|
|
{ /* find root mod each prime, and approx log of each prime */
|
|
r=DD%epr[k];
|
|
rp[k]=sqrmp(r,epr[k]); /* = square root of r mod epr[k] */
|
|
logp[k]=0;
|
|
r=epr[k];
|
|
while((r/=2)>0) logp[k]++;
|
|
}
|
|
r=DD%8; /* take special care of 2 */
|
|
if (r==5) logp[1]++;
|
|
if (r==1) logp[1]+=2;
|
|
threshold=logm-2*logp[mm];
|
|
|
|
jj=0;
|
|
nlp=0;
|
|
|
|
TT=RR;
|
|
while ((TT/=2)>0) threshold++; /* add in number of bits in RR */
|
|
DG=sqrt(DD*2);
|
|
DG=sqrt(DG/M);
|
|
if (DG%2==0) ++DG;
|
|
if (DG%4==1) DG+=2;
|
|
cout << "working... 0" << flush;
|
|
forever
|
|
{ /* try a new polynomial */
|
|
|
|
new_poly();
|
|
|
|
for (ptr=(-NS);ptr<NS;ptr++)
|
|
{ /* sieve over next period */
|
|
la=(long)ptr*SSIZE;
|
|
SV=(unsigned int *)sieve;
|
|
for (i=0;i<SSIZE/sizeof(int);i++) *SV++=0;
|
|
for (k=1;k<=mm;k++)
|
|
{ /* sieving with each prime */
|
|
epri=epr[k];
|
|
logpi=logp[k];
|
|
r=la%epri;
|
|
s1=(r1[k]-r)%epri;
|
|
if (s1<0) s1+=epri;
|
|
s2=(r2[k]-r)%epri;
|
|
if (s2<0) s2+=epri;
|
|
for (j=s1;j<SSIZE;j+=epri) sieve[j]+=logpi;
|
|
if (s1==s2) continue;
|
|
for (j=s2;j<SSIZE;j+=epri) sieve[j]+=logpi;
|
|
}
|
|
|
|
for (a=0;a<SSIZE;a++)
|
|
{ /* main loop - look for fully factored residues */
|
|
if (sieve[a]<threshold) continue;
|
|
lptr=la+a;
|
|
S=0;
|
|
TT=AA*lptr+BB;
|
|
PP=(TT*IG)%DD; /* PP = (AAx + BB)/G */
|
|
if (PP<0) PP+=DD;
|
|
VV=(PP*PP)%DD; /* VV = PP^2 mod kN */
|
|
if (TT<0) TT=(-TT);
|
|
if (TT<RR) S=1; /* check for -ve VV */
|
|
if (S==1) VV=DD-VV;
|
|
e[0]=S;
|
|
for (k=1;k<=mm;k++) e[k]=0;
|
|
if (!factored(lptr,VV)) continue;
|
|
if (gotcha(NN,PP))
|
|
{ /* factors found! */
|
|
PP=gcd(PP,NN);
|
|
cout << "factors are";
|
|
if (prime(PP)) cout << "\nprime factor " << PP;
|
|
else cout << "\ncomposite factor " << PP;
|
|
NN/=PP;
|
|
if (prime(NN)) cout << "\nprime factor " << NN;
|
|
else cout << "\ncomposite factor " << NN;
|
|
cout << endl;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|