#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "giants.h"
#ifdef _WIN32
#pragma warning( disable : 4127 4706 )
#endif
int error = 0;
int mulmode = AUTO_MUL;
int cur_prec = 0;
int cur_shift = 0;
static int cur_stack_size = 0;
static int cur_stack_elem = 0;
static int stack_glen = 0;
static giant *stack;
giant cur_den = NULL,
cur_recip = NULL;
int current_max_size = 0,
cur_run = 0;
double * sinCos=NULL;
int checkFFTerror = 0;
double maxFFTerror;
static giant u0=NULL, u1=NULL, v0=NULL, v1=NULL;
static double *z = NULL,
*z2 = NULL;
static giant popg(void);
static void pushg(int);
int gerr(void);
double gfloor(double);
int radixdiv(int, int, giant);
void columnwrite(FILE *, short *, char *, short, int);
void normal_addg(giant, giant);
void normal_subg(giant, giant);
void reverse_subg(giant, giant);
int binvaux(giant, giant);
int invaux(giant, giant);
int allzeros(int, int, giant);
void auxmulg(giant a, giant b);
void karatmulg(giant a, giant b);
void karatsquareg(giant b);
void grammarmulg(giant a, giant b);
void grammarsquareg(giant b);
int lpt(int, int *);
void addsignal(giant, double *, int);
void FFTsquareg(giant x);
void FFTmulg(giant y, giant x);
void scramble_real();
void fft_real_to_hermitian(double *z, int n);
void fftinv_hermitian_to_real(double *z, int n);
void mul_hermitian(double *a, double *b, int n);
void square_hermitian(double *b, int n);
void giant_to_double(giant x, int sizex, double *z, int L);
void gswap(giant *, giant *);
void onestep(giant, giant, gmatrix);
void mulvM(gmatrix, giant, giant);
void mulmM(gmatrix, gmatrix);
void writeM(gmatrix);
static void punch(giant, gmatrix);
static void dotproduct(giant, giant, giant, giant);
void fix(giant *, giant *);
void hgcd(int, giant, giant, gmatrix);
void shgcd(int, int, gmatrix);
double
gfloor(
double f
)
{
return floor(f);
}
void
init_sinCos(
int n
)
{
int j;
double e = TWOPI/n;
if (n<=cur_run)
return;
cur_run = n;
if (sinCos)
free(sinCos);
sinCos = (double *)malloc(sizeof(double)*(1+(n>>2)));
for (j=0;j<=(n>>2);j++)
{
sinCos[j] = sin(e*j);
}
}
double
s_sin(
int n
)
{
int seg = n/(cur_run>>2);
switch (seg)
{
case 0: return(sinCos[n]);
case 1: return(sinCos[(cur_run>>1)-n]);
case 2: return(-sinCos[n-(cur_run>>1)]);
case 3: return(-sinCos[cur_run-n]);
}
return 0;
}
double
s_cos(
int n
)
{
int quart = (cur_run>>2);
if (n < quart)
return(s_sin(n+quart));
return(-s_sin(n-quart));
}
int
gerr(void)
{
return(error);
}
giant
popg (
void
)
{
int i;
if (current_max_size <= 0) current_max_size = MAX_SHORTS;
if (cur_stack_size == 0) {
cur_stack_size = STACK_GROW;
stack = (giant *) malloc (cur_stack_size * sizeof(giant));
for(i = 0; i < STACK_GROW; i++)
stack[i] = NULL;
if (stack_glen == 0) stack_glen = current_max_size;
} else if (cur_stack_elem >= cur_stack_size) {
i = cur_stack_size;
cur_stack_size += STACK_GROW;
stack = (giant *) realloc (stack,cur_stack_size * sizeof(giant));
for (; i < cur_stack_size; i++)
stack[i] = NULL;
} else if (cur_stack_elem < cur_stack_size - 2*STACK_GROW) {
}
if (stack[cur_stack_elem] == NULL)
stack[cur_stack_elem] = malloc(stack_glen*sizeof(short)+sizeof(int));
stack[cur_stack_elem]->sign = 0;
return(stack[cur_stack_elem++]);
}
void
pushg (
int a
)
{
if (a < 0) return;
cur_stack_elem -= a;
if (cur_stack_elem < 0) cur_stack_elem = 0;
}
giant
newgiant(
int numshorts
)
{
int size;
giant thegiant;
if (numshorts > MAX_SHORTS) {
fprintf(stderr, "Requested giant too big.\n");
fflush(stderr);
}
if (numshorts<=0)
numshorts = MAX_SHORTS;
size = numshorts*sizeof(short)+sizeof(int);
thegiant = (giant)malloc(size);
thegiant->sign = 0;
if (newmin(2*numshorts,MAX_SHORTS) > current_max_size)
current_max_size = newmin(2*numshorts,MAX_SHORTS);
if (stack_glen == 0) stack_glen = current_max_size;
return(thegiant);
}
gmatrix
newgmatrix(
void
)
{
return((gmatrix) malloc (4*sizeof(giant)));
}
int
bitlen(
giant n
)
{
int b = 16, c = 1<<15, w;
if (isZero(n))
return(0);
w = n->n[abs(n->sign) - 1];
while ((w&c) == 0)
{
b--;
c >>= 1;
}
return (16*(abs(n->sign)-1) + b);
}
int
bitval(
giant n,
int pos
)
{
int i = abs(pos)>>4, c = 1 << (pos&15);
return ((n->n[i]) & c);
}
int
isone(
giant g
)
{
return((g->sign==1)&&(g->n[0]==1));
}
int isZero(
giant thegiant
)
{
register int count;
int length = abs(thegiant->sign);
register unsigned short * numpointer = thegiant->n;
if (length)
{
for(count = 0; count<length; ++count,++numpointer)
{
if (*numpointer != 0 )
return(FA);
}
}
return(TR);
}
void
gtog(
giant srcgiant,
giant destgiant
)
{
int numbytes = sizeof(int) + abs(srcgiant->sign)*sizeof(short);
memcpy((char *)destgiant,(char *)srcgiant,numbytes);
}
void
itog(
int i,
giant g
)
{
unsigned int j = abs(i);
if (i==0)
{
g->sign = 0;
g->n[0] = 0;
return;
}
g->n[0] = (unsigned short)(j & 0xFFFF);
j >>= 16;
if (j)
{
g->n[1] = (unsigned short)j;
g->sign = 2;
}
else
{
g->sign = 1;
}
if (i<0)
g->sign = -(g->sign);
}
signed int
gtoi(
giant x
)
{
register int size = abs(x->sign);
register int sign = (x->sign < 0) ? -1 : 1;
switch(size)
{
case 0:
break;
case 1:
return sign * x->n[0];
case 2:
return sign * (x->n[0]+((x->n[1])<<16));
default:
fprintf(stderr,"Giant too large for gtoi\n");
break;
}
return 0;
}
int
gsign(
giant g
)
{
if (isZero(g))
return(0);
if (g->sign >0)
return(1);
return(-1);
}
#if 0
int gcompg(a,b)
giant a,b;
{
int size = abs(a->sign);
if(isZero(a)) size = 0;
if (size == 0) {
if (isZero(b)) return(0); else return(-gsign(b));
}
if (b->sign == 0) return(gsign(a));
if (gsign(a)!=gsign(b)) return(gsign(a));
if (size>abs(b->sign)) return(gsign(a));
if (size<abs(b->sign)) return(-gsign(a));
do {
--size;
if (a->n[size] > b->n[size]) return(gsign(a));
if (a->n[size] < b->n[size]) return(-gsign(a));
} while(size>0);
return(0);
}
#else
int
gcompg(
giant a,
giant b
)
{
int sa = a->sign, j, sb = b->sign, va, vb, sgn;
if(sa > sb)
return(1);
if(sa < sb)
return(-1);
if(sa < 0)
{
sa = -sa;
sgn = -1;
}
else
{
sgn = 1;
}
for(j = sa-1; j >= 0; j--)
{
va = a->n[j];
vb = b->n[j];
if (va > vb)
return(sgn);
if (va < vb)
return(-sgn);
}
return(0);
}
#endif
void
setmulmode(
int mode
)
{
mulmode = mode;
}
int
radixdiv(
int base,
int divisor,
giant thegiant
)
{
int first = TR;
int finalsize = abs(thegiant->sign);
int j = finalsize-1;
unsigned short *digitpointer=&thegiant->n[j];
unsigned int num,rem=0;
if (divisor == 0)
{
error = DIVIDEBYZERO;
exit(error);
}
while (j>=0)
{
num=rem*base + *digitpointer;
*digitpointer = (unsigned short)(num/divisor);
if (first)
{
if (*digitpointer == 0)
--finalsize;
else
first = FA;
}
rem = num % divisor;
--digitpointer;
--j;
}
if ((divisor<0) ^ (thegiant->sign<0))
finalsize=-finalsize;
thegiant->sign=finalsize;
return(rem);
}
void
columnwrite(
FILE *filepointer,
short *column,
char *format,
short arg,
int newlines
)
{
char outstring[10];
short i;
sprintf(outstring,format,arg);
for (i=0; outstring[i]!=0; ++i)
{
if (newlines)
{
if (*column >= COLUMNWIDTH)
{
fputs("\\\n",filepointer);
*column = 0;
}
}
fputc(outstring[i],filepointer);
++*column;
}
}
void
gwrite(
giant thegiant,
FILE *filepointer,
int newlines
)
{
short column;
unsigned int i;
unsigned short *numpointer;
giant garbagegiant, basetengrand;
basetengrand = popg();
garbagegiant = popg();
if (isZero(thegiant))
{
fputs("0",filepointer);
}
else
{
numpointer = basetengrand->n;
gtog(thegiant,garbagegiant);
basetengrand->sign = 0;
do
{
*numpointer = (unsigned short)idivg(10000,garbagegiant);
++numpointer;
if (++basetengrand->sign >= current_max_size)
{
error = OVFLOW;
exit(error);
}
} while (!isZero(garbagegiant));
if (!error)
{
i = basetengrand->sign-1;
column = 0;
if (thegiant->sign<0 && basetengrand->n[i]!=0)
columnwrite(filepointer,&column,"-",0, newlines);
columnwrite(filepointer,&column,"%d",basetengrand->n[i],newlines);
for( ; i>0; )
{
--i;
columnwrite(filepointer,&column,"%04d",basetengrand->n[i],newlines);
}
}
}
pushg(2);
}
void
gwriteln(
giant theg,
FILE *filepointer
)
{
gwrite(theg, filepointer, 1);
fputc('\n',filepointer);
}
void
gread(
giant theg,
FILE *filepointer
)
{
char currentchar;
int isneg,size,backslash=FA,numdigits=0;
unsigned short *numpointer;
giant basetenthousand;
static char *inbuf = NULL;
basetenthousand = popg();
if (inbuf == NULL)
inbuf = (char*)malloc(MAX_DIGITS);
currentchar = (char)fgetc(filepointer);
if (currentchar=='-')
{
isneg=TR;
}
else
{
isneg=FA;
if (currentchar!='+')
ungetc(currentchar,filepointer);
}
do
{
currentchar = (char)fgetc(filepointer);
if ((currentchar>='0') && (currentchar<='9'))
{
inbuf[numdigits]=currentchar;
if(++numdigits==MAX_DIGITS)
break;
backslash=FA;
}
else
{
if (currentchar=='\\')
backslash=TR;
}
} while(((currentchar!=' ') && (currentchar!='\n') &&
(currentchar!='\t')) || (backslash) );
if (numdigits)
{
size = 0;
do
{
inbuf[numdigits] = 0;
numdigits-=4;
if (numdigits<0)
numdigits=0;
basetenthousand->n[size] = (unsigned short)strtol(&inbuf[numdigits],NULL,10);
++size;
} while (numdigits>0);
basetenthousand->sign = size;
theg->sign = 0;
numpointer = theg->n;
do
{
*numpointer = (unsigned short)
radixdiv(10000,1<<(8*sizeof(short)),basetenthousand);
++numpointer;
if (++theg->sign >= current_max_size)
{
error = OVFLOW;
exit(error);
}
} while (!isZero(basetenthousand));
if (isneg)
theg->sign = -theg->sign;
}
pushg(1);
}
void
negg(
giant g
)
{
g->sign = -g->sign;
}
void
absg(
giant g
)
{
if (g->sign <0)
g->sign=-g->sign;
}
void
iaddg(
int i,
giant g
)
{
int w,j=0,carry = 0, size = abs(g->sign);
giant tmp;
if (isZero(g))
{
itog(i,g);
}
else if(g->sign < 0) {
tmp = popg();
itog(i, tmp);
addg(tmp, g);
pushg(1);
return;
}
else
{
w = g->n[0]+i;
do
{
g->n[j] = (unsigned short) (w & 65535L);
carry = w >> 16;
w = g->n[++j]+carry;
} while ((carry!=0) && (j<size));
}
if (carry)
{
++g->sign;
g->n[size] = (unsigned short)carry;
}
}
void
normal_addg(
giant a,
giant b
)
{
int carry = 0;
int asize = a->sign, bsize = b->sign;
long k;
int j=0;
unsigned short *aptr = a->n, *bptr = b->n;
if (asize < bsize)
{
for (j=0; j<asize; j++)
{
k = *aptr++ + *bptr + carry;
carry = 0;
if (k >= 65536L)
{
k -= 65536L;
++carry;
}
*bptr++ = (unsigned short)k;
}
for (j=asize; j<bsize; j++)
{
k = *bptr + carry;
carry = 0;
if (k >= 65536L)
{
k -= 65536L;
++carry;
}
*bptr++ = (unsigned short)k;
}
}
else
{
for (j=0; j<bsize; j++)
{
k = *aptr++ + *bptr + carry;
carry = 0;
if (k >= 65536L)
{
k -= 65536L;
++carry;
}
*bptr++ = (unsigned short)k;
}
for (j=bsize; j<asize; j++)
{
k = *aptr++ + carry;
carry = 0;
if (k >= 65536L)
{
k -= 65536L;
++carry;
}
*bptr++ = (unsigned short)k;
}
}
if (carry)
{
*bptr = 1; ++j;
}
b->sign = j;
}
void
normal_subg(
giant a,
giant b
)
{
int j, size = b->sign;
unsigned int k;
if (a->sign == 0)
return;
k = 0;
for (j=0; j<a->sign; ++j)
{
k += 0xffff - a->n[j] + b->n[j];
b->n[j] = (unsigned short)(k & 0xffff);
k >>= 16;
}
for (j=a->sign; j<size; ++j)
{
k += 0xffff + b->n[j];
b->n[j] = (unsigned short)(k & 0xffff);
k >>= 16;
}
if (b->n[0] == 0xffff)
iaddg(1,b);
else
++b->n[0];
while ((size-- > 0) && (b->n[size]==0));
b->sign = (b->n[size]==0) ? 0 : size+1;
}
void
reverse_subg(
giant a,
giant b
)
{
int j, size = a->sign;
unsigned int k;
k = 0;
for (j=0; j<b->sign; ++j)
{
k += 0xffff - b->n[j] + a->n[j];
b->n[j] = (unsigned short)(k & 0xffff);
k >>= 16;
}
for (j=b->sign; j<size; ++j)
{
k += 0xffff + a->n[j];
b->n[j] = (unsigned short)(k & 0xffff);
k >>= 16;
}
b->sign = size;
if (b->n[0] == 0xffff)
iaddg(1,b);
else
++b->n[0];
while (!b->n[--size]);
b->sign = size+1;
}
void
addg(
giant a,
giant b
)
{
int asgn = a->sign, bsgn = b->sign;
if (asgn == 0)
return;
if (bsgn == 0)
{
gtog(a,b);
return;
}
if ((asgn < 0) == (bsgn < 0))
{
if (bsgn > 0)
{
normal_addg(a,b);
return;
}
absg(b);
if(a != b) absg(a);
normal_addg(a,b);
negg(b);
if(a != b) negg(a);
return;
}
if(bsgn > 0)
{
negg(a);
if (gcompg(b,a) >= 0)
{
normal_subg(a,b);
negg(a);
return;
}
reverse_subg(a,b);
negg(a);
negg(b);
return;
}
negg(b);
if(gcompg(b,a) < 0)
{
reverse_subg(a,b);
return;
}
normal_subg(a,b);
negg(b);
return;
}
void
subg(
giant a,
giant b
)
{
int asgn = a->sign, bsgn = b->sign;
if (asgn == 0)
return;
if (bsgn == 0)
{
gtog(a,b);
negg(b);
return;
}
if ((asgn < 0) != (bsgn < 0))
{
if (bsgn > 0)
{
negg(a);
normal_addg(a,b);
negg(a);
return;
}
negg(b);
normal_addg(a,b);
negg(b);
return;
}
if (bsgn > 0)
{
if (gcompg(b,a) >= 0)
{
normal_subg(a,b);
return;
}
reverse_subg(a,b);
negg(b);
return;
}
negg(a);
negg(b);
if (gcompg(b,a) >= 0)
{
normal_subg(a,b);
negg(a);
negg(b);
return;
}
reverse_subg(a,b);
negg(a);
return;
}
int
numtrailzeros(
giant g
)
{
register int numshorts = abs(g->sign), j, bcount=0;
register unsigned short gshort, c;
for (j=0;j<numshorts;j++)
{
gshort = g->n[j];
c = 1;
for (bcount=0;bcount<16; bcount++)
{
if (c & gshort)
break;
c <<= 1;
}
if (bcount<16)
break;
}
return(bcount + 16*j);
}
void
bdivg(
giant v,
giant u
)
{
int diff = bitlen(u) - bitlen(v);
giant scratch7;
if (diff<0)
{
itog(0,u);
return;
}
scratch7 = popg();
gtog(v, scratch7);
gshiftleft(diff,scratch7);
if (gcompg(u,scratch7) < 0)
diff--;
if (diff<0)
{
itog(0,u);
pushg(1);
return;
}
itog(1,u);
gshiftleft(diff,u);
pushg(1);
}
int
binvaux(
giant p,
giant x
)
{
giant scratch7, u0, u1, v0, v1;
if (isone(x))
return(1);
u0 = popg();
u1 = popg();
v0 = popg();
v1 = popg();
itog(1, v0);
gtog(x, v1);
itog(0,x);
gtog(p, u1);
scratch7 = popg();
while(!isZero(v1))
{
gtog(u1, u0);
bdivg(v1, u0);
gtog(x, scratch7);
gtog(v0, x);
mulg(u0, v0);
subg(v0,scratch7);
gtog(scratch7, v0);
gtog(u1, scratch7);
gtog(v1, u1);
mulg(u0, v1);
subg(v1,scratch7);
gtog(scratch7, v1);
}
pushg(1);
if (!isone(u1))
{
gtog(u1,x);
if(x->sign<0) addg(p, x);
pushg(4);
return(0);
}
if(x->sign<0)
addg(p, x);
pushg(4);
return(1);
}
int
binvg(
giant p,
giant x
)
{
modg(p, x);
return(binvaux(p,x));
}
int
invg(
giant p,
giant x
)
{
modg(p, x);
return(invaux(p,x));
}
int
invaux(
giant p,
giant x
)
{
giant scratch7, u0, u1, v0, v1;
if ((x->sign==1)&&(x->n[0]==1))
return(1);
u0 = popg();
u1 = popg();
v0 = popg();
v1 = popg();
itog(1,u1);
gtog(p, v0);
gtog(x, v1);
itog(0,x);
scratch7 = popg();
while (!isZero(v1))
{
gtog(v0, u0);
divg(v1, u0);
gtog(u0, scratch7);
mulg(v1, scratch7);
subg(v0, scratch7);
negg(scratch7);
gtog(v1, v0);
gtog(scratch7, v1);
gtog(u1, scratch7);
mulg(u0, scratch7);
subg(x, scratch7);
negg(scratch7);
gtog(u1,x);
gtog(scratch7, u1);
}
pushg(1);
if ((v0->sign!=1)||(v0->n[0]!=1))
{
gtog(v0,x);
pushg(4);
return(0);
}
if(x->sign<0)
addg(p, x);
pushg(4);
return(1);
}
int
mersenneinvg(
int q,
giant x
)
{
int k;
giant u0, u1, v1;
u0 = popg();
u1 = popg();
v1 = popg();
itog(1, u0);
itog(0, u1);
itog(1, v1);
gshiftleft(q, v1);
subg(u0, v1);
mersennemod(q, x);
while (1)
{
k = -1;
if (isZero(x))
{
gtog(v1, x);
pushg(3);
return(0);
}
while (bitval(x, ++k) == 0);
gshiftright(k, x);
if (k)
{
gshiftleft(q-k, u0);
mersennemod(q, u0);
}
if (isone(x))
break;
addg(u1, u0);
mersennemod(q, u0);
negg(u1);
addg(u0, u1);
mersennemod(q, u1);
if (!gcompg(v1,x)) {
pushg(3);
return(0);
}
addg(v1, x);
negg(v1);
addg(x, v1);
mersennemod(q, v1);
}
gtog(u0, x);
mersennemod(q,x);
pushg(3);
return(1);
}
void
cgcdg(
giant a,
giant v
)
{
giant u, r;
v->sign = abs(v->sign);
if (isZero(a))
return;
u = popg();
r = popg();
gtog(a, u);
u->sign = abs(u->sign);
while (!isZero(v))
{
gtog(u, r);
modg(v, r);
gtog(v, u);
gtog(r, v);
}
gtog(u,v);
pushg(2);
}
void
gcdg(
giant x,
giant y
)
{
if (bitlen(y)<= GCDLIMIT)
bgcdg(x,y);
else
ggcd(x,y);
}
void
bgcdg(
giant a,
giant b
)
{
int k = isZero(b), m = isZero(a);
giant u, v, t;
if (k || m)
{
if (m)
{
if (k)
itog(1,b);
return;
}
if (k)
{
if (m)
itog(1,b);
else
gtog(a,b);
return;
}
}
u = popg();
v = popg();
t = popg();
gtog(a, u);
u->sign = abs(a->sign);
gtog(b, v);
v->sign = abs(b->sign);
k = numtrailzeros(u);
m = numtrailzeros(v);
if (k>m)
k = m;
gshiftright(k,u);
gshiftright(k,v);
if (u->n[0] & 1)
{
gtog(v, t);
negg(t);
}
else
{
gtog(u,t);
}
while (!isZero(t))
{
m = numtrailzeros(t);
gshiftright(m, t);
if (t->sign > 0)
{
gtog(t, u);
subg(v,t);
}
else
{
gtog(t, v);
negg(v);
addg(u,t);
}
}
gtog(u,b);
gshiftleft(k, b);
pushg(3);
}
void
powerg(
int m,
int n,
giant g
)
{
giant scratch2 = popg();
itog(1, g);
itog(m, scratch2);
while (n)
{
if (n & 1)
mulg(scratch2, g);
n >>= 1;
if (n)
squareg(scratch2);
}
pushg(1);
}
#if 0
void
jtest(
giant n
)
{
if (n->sign)
{
if (n->n[n->sign-1] == 0)
{
fprintf(stderr,"%d %d tilt",n->sign, (int)(n->n[n->sign-1]));
exit(7);
}
}
}
#endif
void
make_recip(
giant d,
giant r
)
{
int b;
giant tmp, tmp2;
if (isZero(d) || (d->sign < 0))
{
exit(SIGN);
}
tmp = popg();
tmp2 = popg();
itog(1, r);
subg(r, d);
b = bitlen(d);
addg(r, d);
gshiftleft(b, r);
gtog(r, tmp2);
while (1)
{
gtog(r, tmp);
squareg(tmp);
gshiftright(b, tmp);
mulg(d, tmp);
gshiftright(b, tmp);
addg(r, r);
subg(tmp, r);
if (gcompg(r, tmp2) <= 0)
break;
gtog(r, tmp2);
}
itog(1, tmp);
gshiftleft(2*b, tmp);
gtog(r, tmp2);
mulg(d, tmp2);
subg(tmp2, tmp);
itog(1, tmp2);
while (tmp->sign < 0)
{
subg(tmp2, r);
addg(d, tmp);
}
pushg(2);
}
void
divg_via_recip(
giant d,
giant r,
giant n
)
{
int s = 2*(bitlen(r)-1), sign = gsign(n);
giant tmp, tmp2;
if (isZero(d) || (d->sign < 0))
{
exit(SIGN);
}
tmp = popg();
tmp2 = popg();
n->sign = abs(n->sign);
itog(0, tmp2);
while (1)
{
gtog(n, tmp);
mulg(r, tmp);
gshiftright(s, tmp);
addg(tmp, tmp2);
mulg(d, tmp);
subg(tmp, n);
if (gcompg(n,d) >= 0)
{
subg(d,n);
iaddg(1, tmp2);
}
if (gcompg(n,d) < 0)
break;
}
gtog(tmp2, n);
n->sign *= sign;
pushg(2);
}
#if 1
void
modg_via_recip(
giant d,
giant r,
giant n
)
{
int s = (bitlen(r)-1), sign = n->sign;
giant tmp, tmp2;
if (isZero(d) || (d->sign < 0))
{
exit(SIGN);
}
tmp = popg();
tmp2 = popg();
n->sign = abs(n->sign);
while (1)
{
gtog(n, tmp); gshiftright(s-1, tmp);
mulg(r, tmp);
gshiftright(s+1, tmp);
mulg(d, tmp);
subg(tmp, n);
if (gcompg(n,d) >= 0)
subg(d,n);
if (gcompg(n,d) < 0)
break;
}
if (sign >= 0)
goto done;
if (isZero(n))
goto done;
negg(n);
addg(d,n);
done:
pushg(2);
return;
}
#else
void
modg_via_recip(
giant d,
giant r,
giant n
)
{
int s = 2*(bitlen(r)-1), sign = n->sign;
giant tmp, tmp2;
if (isZero(d) || (d->sign < 0))
{
exit(SIGN);
}
tmp = popg();
tmp2 = popg()
n->sign = abs(n->sign);
while (1)
{
gtog(n, tmp);
mulg(r, tmp);
gshiftright(s, tmp);
mulg(d, tmp);
subg(tmp, n);
if (gcompg(n,d) >= 0)
subg(d,n);
if (gcompg(n,d) < 0)
break;
}
if (sign >= 0)
goto done;
if (isZero(n))
goto done;
negg(n);
addg(d,n);
done:
pushg(2);
return;
}
#endif
void
modg(
giant d,
giant n
)
{
if (cur_recip == NULL) {
cur_recip = newgiant(current_max_size);
cur_den = newgiant(current_max_size);
gtog(d, cur_den);
make_recip(d, cur_recip);
} else if (gcompg(d, cur_den)) {
gtog(d, cur_den);
make_recip(d, cur_recip);
}
modg_via_recip(d, cur_recip, n);
}
#if 0
int
feemulmod (
giant a,
giant b,
int q,
int k
)
{
giant carry, kk, scratch;
int i, j;
int asize = abs(a->sign), bsize = abs(b->sign);
unsigned short *aptr,*bptr,*destptr;
unsigned int words;
int kpower, curk;
if ((q % 16) || (k <= 0) || (k >= 65535)) {
return (0);
}
carry = popg();
kk = popg();
scratch = popg();
for (i=0; i<asize+bsize; i++) scratch->n[i]=0;
words = q >> 4;
bptr = b->n;
for (i = 0; i < bsize; i++) {
mult = *bptr++;
if (mult) {
kpower = i/words;
if (kpower >= 1) itog (kpower,kk);
for (j = 1; j < kpower; k++) smulg(kpower,kk);
itog(0,carry);
aptr = a->n;
for (j = 0; j < bsize; b++) {
gtog(kk,scratch);
smulg(*aptr++,scratch);
smulg(mult,scratch);
iaddg(*destptr,scratch);
addg(carry,scratch);
*destptr++ = scratch->n[0];
gshiftright(scratch,16);
gtog(scratch,carry);
if (destptr - scratch->n >= words) {
smulg(k, carry);
smulg(k, kk);
destptr -= words;
}
}
}
}
int i,j,m;
unsigned int prod,carry=0;
int asize = abs(a->sign), bsize = abs(b->sign);
unsigned short *aptr,*bptr,*destptr;
unsigned short mult;
int words, excess;
int temp;
giant scratch = popg(), scratch2 = popg(), scratch3 = popg();
short *carryptr = scratch->n;
int kpower,kpowerlimit, curk;
if ((q % 16) || (k <= 0) || (k >= 65535)) {
return (0);
}
scratch
for (i=0; i<asize+bsize; i++) scratch->n[i]=0;
words = q >> 4;
bptr = b->n;
for (i=0; i<bsize; ++i)
{
mult = *bptr++;
if (mult)
{
kpower = i/words;
aptr = a->n;
destptr = scratch->n + i;
if (kpower == 0) {
carry = 0;
} else if (kpower <= kpowerlimit) {
carry = 0;
curk = k;
for (j = 1; j < kpower; j++) curk *= k;
} else {
itog (k,scratch);
for (j = 1; j < kpower; j++) smulg(k,scratch);
itog(0,scratch2);
}
for (j = 0; j < asize; j++) {
if(kpower == 0) {
prod = *aptr++ * mult + *destptr + carry;
*destptr++ = (unsigned short)(prod & 0xFFFF);
carry = prod >> 16;
} else if (kpower < kpowerlimit) {
prod = kcur * *aptr++;
temp = prod >> 16;
prod &= 0xFFFF;
temp *= mult;
prod *= mult;
temp += prod >> 16;
prod &= 0xFFFF;
prod += *destptr + carry;
carry = prod >> 16 + temp;
*destptr++ = (unsigned short)(prod & 0xFFFF);
} else {
gtog(scratch,scratch3);
smulg(*aptr++,scratch3);
smulg(mult,scratch3);
iaddg(*destptr,scratch3);
addg(scratch3,scratch2);
*destptr++ = scratch2->n[0];
memmove(scratch2->n,scratch2->n+1,2*(scratch2->size-1));
scratch2->sign--;
}
if (destptr - scratch->n > words) {
if (kpower == 0) {
curk = k;
carry *= k;
} else if (kpower < kpowerlimit) {
curk *= k;
carry *= curk;
} else if (kpower == kpowerlimit) {
itog (k,scratch);
for (j = 1; j < kpower; j++) smulg(k,scratch);
itog(carry,scratch2);
smulg(k,scratch2);
} else {
smulg(k,scratch);
smulg(k,scratch2);
}
kpower++;
destptr -= words;
}
}
if (kpower <= kpowerlimit) {
iaddg(carry,scratch);
} else {
addg(scratch2,scratch);
}
while(scratch->sign > q)
gtog(scratch,scratch2)
}
}
scratch->sign = destptr - scratch->n;
if (!carry)
--(scratch->sign);
scratch->sign *= gsign(a)*gsign(b);
gtog(scratch,a);
pushg(3);
return (1);
}
#endif
int
idivg(
int divisor,
giant theg
)
{
int n;
int base = 1<<(8*sizeof(short));
n = radixdiv(base,divisor,theg);
return(n);
}
void
divg(
giant d,
giant n
)
{
if (cur_recip == NULL) {
cur_recip = newgiant(current_max_size);
cur_den = newgiant(current_max_size);
gtog(d, cur_den);
make_recip(d, cur_recip);
} else if (gcompg(d, cur_den)) {
gtog(d, cur_den);
make_recip(d, cur_recip);
}
divg_via_recip(d, cur_recip, n);
}
void
powermod(
giant x,
int n,
giant g
)
{
giant scratch2 = popg();
gtog(x, scratch2);
itog(1, x);
while (n)
{
if (n & 1)
{
mulg(scratch2, x);
modg(g, x);
}
n >>= 1;
if (n)
{
squareg(scratch2);
modg(g, scratch2);
}
}
pushg(1);
}
void
powermodg(
giant x,
giant n,
giant g
)
{
int len, pos;
giant scratch2 = popg();
gtog(x, scratch2);
itog(1, x);
len = bitlen(n);
pos = 0;
while (1)
{
if (bitval(n, pos++))
{
mulg(scratch2, x);
modg(g, x);
}
if (pos>=len)
break;
squareg(scratch2);
modg(g, scratch2);
}
pushg(1);
}
void
fermatpowermod(
giant x,
int n,
int q
)
{
giant scratch2 = popg();
gtog(x, scratch2);
itog(1, x);
while (n)
{
if (n & 1)
{
mulg(scratch2, x);
fermatmod(q, x);
}
n >>= 1;
if (n)
{
squareg(scratch2);
fermatmod(q, scratch2);
}
}
pushg(1);
}
void
fermatpowermodg(
giant x,
giant n,
int q
)
{
int len, pos;
giant scratch2 = popg();
gtog(x, scratch2);
itog(1, x);
len = bitlen(n);
pos = 0;
while (1)
{
if (bitval(n, pos++))
{
mulg(scratch2, x);
fermatmod(q, x);
}
if (pos>=len)
break;
squareg(scratch2);
fermatmod(q, scratch2);
}
pushg(1);
}
void
mersennepowermod(
giant x,
int n,
int q
)
{
giant scratch2 = popg();
gtog(x, scratch2);
itog(1, x);
while (n)
{
if (n & 1)
{
mulg(scratch2, x);
mersennemod(q, x);
}
n >>= 1;
if (n)
{
squareg(scratch2);
mersennemod(q, scratch2);
}
}
pushg(1);
}
void
mersennepowermodg(
giant x,
giant n,
int q
)
{
int len, pos;
giant scratch2 = popg();
gtog(x, scratch2);
itog(1, x);
len = bitlen(n);
pos = 0;
while (1)
{
if (bitval(n, pos++))
{
mulg(scratch2, x);
mersennemod(q, x);
}
if (pos>=len)
break;
squareg(scratch2);
mersennemod(q, scratch2);
}
pushg(1);
}
void
gshiftleft(
int bits,
giant g
)
{
int rem = bits&15, crem = 16-rem, words = bits>>4;
int size = abs(g->sign), j, k, sign = gsign(g);
unsigned short carry, dat;
if (!bits)
return;
if (!size)
return;
if (bits < 0) {
gshiftright(-bits,g);
return;
}
if (size+words+1 > current_max_size) {
error = OVFLOW;
exit(error);
}
if (rem == 0) {
memmove(g->n + words, g->n, size * sizeof(short));
for (j = 0; j < words; j++) g->n[j] = 0;
g->sign += (g->sign < 0)?(-words):(words);
} else {
k = size+words;
carry = 0;
for (j=size-1; j>=0; j--) {
dat = g->n[j];
g->n[k--] = (unsigned short)((dat >> crem) | carry);
carry = (unsigned short)(dat << rem);
}
do {
g->n[k--] = carry;
carry = 0;
} while(k>=0);
k = size+words;
if (g->n[k] == 0)
--k;
g->sign = sign*(k+1);
}
}
void
gshiftright(
int bits,
giant g
)
{
register int j,size=abs(g->sign);
register unsigned int carry;
int words = bits >> 4;
int remain = bits & 15, cremain = (16-remain);
if (bits==0)
return;
if (isZero(g))
return;
if (bits < 0) {
gshiftleft(-bits,g);
return;
}
if (words >= size) {
g->sign = 0;
return;
}
if (remain == 0) {
memmove(g->n,g->n + words,(size - words) * sizeof(short));
g->sign += (g->sign < 0)?(words):(-words);
} else {
size -= words;
if (size)
{
for(j=0;j<size-1;++j)
{
carry = g->n[j+words+1] << cremain;
g->n[j] = (unsigned short)((g->n[j+words] >> remain ) | carry);
}
g->n[size-1] = (unsigned short)(g->n[size-1+words] >> remain);
}
if (g->n[size-1] == 0)
--size;
if (g->sign > 0)
g->sign = size;
else
g->sign = -size;
}
}
void
extractbits(
int n,
giant src,
giant dest
)
{
register int words = n >> 4, numbytes = words*sizeof(short);
register int bits = n & 15;
if (n<=0)
return;
if (words >= abs(src->sign))
gtog(src,dest);
else
{
memcpy((char *)(dest->n), (char *)(src->n), numbytes);
if (bits)
{
dest->n[words] = (unsigned short)(src->n[words] & ((1<<bits)-1));
++words;
}
while ((dest->n[words-1] == 0) && (words > 0))
{
--words;
}
if (src->sign<0)
dest->sign = -words;
else
dest->sign = words;
}
}
int
allzeros(
int shorts,
int bits,
giant g
)
{
int i=shorts;
while (i>0)
{
if (g->n[--i])
return(0);
}
return((int)(!(g->n[shorts] & ((1<<bits)-1))));
}
void
fermatnegate(
int n,
giant g
)
{
register int shorts = n>>4,
bits = n & 15,
i = shorts,
mask = 1<<bits;
register unsigned carry,temp;
for (temp=(unsigned)shorts; (int)temp>g->sign-1; --temp)
{
g->n[temp] = 0;
}
if (g->n[shorts] & mask)
{
g->sign = 1;
g->n[0] = 1;
return;
}
if (allzeros(shorts,bits,g))
return;
while (i>0)
{ --i;
g->n[i] = (unsigned short)(~(g->n[i+1]));
}
g->n[shorts] ^= mask-1;
carry = 2;
i = 0;
while (carry)
{
temp = g->n[i]+carry;
g->n[i++] = (unsigned short)(temp & 0xffff);
carry = temp>>16;
}
while(!g->n[shorts])
{
--shorts;
}
g->sign = shorts+1;
}
void
mersennemod (
int n,
giant g
)
{
int the_sign, s;
giant scratch3 = popg(), scratch4 = popg();
if ((the_sign = gsign(g)) < 0) absg(g);
while (bitlen(g) > n) {
gtog(g,scratch3);
gshiftright(n,scratch3);
addg(scratch3,g);
gshiftleft(n,scratch3);
subg(scratch3,g);
}
if(!isZero(g)) {
if ((s = gsign(g)) < 0) absg(g);
itog(1,scratch3);
gshiftleft(n,scratch3);
itog(1,scratch4);
subg(scratch4,scratch3);
if(gcompg(g,scratch3) >= 0) subg(scratch3,g);
if (s < 0) {
g->sign = -g->sign;
addg(scratch3,g);
}
if (the_sign < 0) {
g->sign = -g->sign;
addg(scratch3,g);
}
}
pushg(2);
}
void
fermatmod (
int n,
giant g
)
{
int the_sign, s;
giant scratch3 = popg();
if ((the_sign = gsign(g)) < 0) absg(g);
while (bitlen(g) > n) {
gtog(g,scratch3);
gshiftright(n,scratch3);
subg(scratch3,g);
gshiftleft(n,scratch3);
subg(scratch3,g);
}
if((bitlen(g) < n) && (the_sign * (g->sign) >= 0)) goto leave;
if(!isZero(g)) {
if ((s = gsign(g)) < 0) absg(g);
itog(1,scratch3);
gshiftleft(n,scratch3);
iaddg(1,scratch3);
if(gcompg(g,scratch3) >= 0) subg(scratch3,g);
if (s * the_sign < 0) {
g->sign = -g->sign;
addg(scratch3,g);
}
}
leave:
pushg(1);
}
void
fer_mod (
int n,
giant g,
giant modulus
)
{
int the_sign, s;
giant scratch3 = popg();
if ((the_sign = gsign(g)) < 0) absg(g);
while (bitlen(g) > n) {
gtog(g,scratch3);
gshiftright(n,scratch3);
subg(scratch3,g);
gshiftleft(n,scratch3);
subg(scratch3,g);
}
if((bitlen(g) < n) && (the_sign * (g->sign) >= 0)) goto leave;
if(!isZero(g)) {
if ((s = gsign(g)) < 0) absg(g);
if(gcompg(g,modulus) >= 0) subg(modulus,g);
if (s * the_sign < 0) {
g->sign = -g->sign;
addg(modulus,g);
}
}
leave:
pushg(1);
}
void
smulg(
unsigned short i,
giant g
)
{
unsigned short carry = 0;
int size = abs(g->sign);
register int j,k,mul = abs(i);
unsigned short *digit = g->n;
for (j=0; j<size; ++j)
{
k = *digit * mul + carry;
carry = (unsigned short)(k>>16);
*digit = (unsigned short)(k & 0xffff);
++digit;
}
if (carry)
{
if (++j >= current_max_size)
{
error = OVFLOW;
exit(error);
}
*digit = carry;
}
if ((g->sign>0) ^ (i>0))
g->sign = -j;
else
g->sign = j;
}
void
squareg(
giant b
)
{
auxmulg(b,b);
}
void
mulg(
giant a,
giant b
)
{
auxmulg(a,b);
}
void
auxmulg(
giant a,
giant b
)
{
float grammartime;
int square = (a==b);
int sizea, sizeb;
switch (mulmode)
{
case GRAMMAR_MUL:
if (square) grammarsquareg(b);
else grammarmulg(a,b);
break;
case FFT_MUL:
if (square)
FFTsquareg(b);
else
FFTmulg(a,b);
break;
case KARAT_MUL:
if (square) karatsquareg(b);
else karatmulg(a,b);
break;
case AUTO_MUL:
sizea = abs(a->sign);
sizeb = abs(b->sign);
if((sizea > KARAT_BREAK) && (sizea <= FFT_BREAK) &&
(sizeb > KARAT_BREAK) && (sizeb <= FFT_BREAK)){
if(square) karatsquareg(b);
else karatmulg(a,b);
} else {
grammartime = (float)sizea;
grammartime *= (float)sizeb;
if (grammartime < FFT_BREAK * FFT_BREAK)
{
if (square) grammarsquareg(b);
else grammarmulg(a,b);
}
else
{
if (square) FFTsquareg(b);
else FFTmulg(a,b);
}
}
break;
}
}
void
justg(giant x) {
int s = x->sign, sg = 1;
if(s<0) {
sg = -1;
s = -s;
}
--s;
while(x->n[s] == 0) {
--s;
if(s < 0) break;
}
x->sign = sg*(s+1);
}
void
karatmulg(giant x, giant y)
{
int s = abs(x->sign), t = abs(y->sign), w, bits,
sg = gsign(x)*gsign(y);
giant a, b, c, d, e, f;
if((s <= KARAT_BREAK) || (t <= KARAT_BREAK)) {
grammarmulg(x,y);
return;
}
w = (s + t + 2)/4; bits = 16*w;
a = popg(); b = popg(); c = popg();
d = popg(); e = popg(); f = popg();
gtog(x,a); absg(a); if (w <= s) {a->sign = w; justg(a);}
gtog(x,b); absg(b);
gshiftright(bits, b);
gtog(y,c); absg(c); if (w <= t) {c->sign = w; justg(c);}
gtog(y,d); absg(d);
gshiftright(bits,d);
gtog(a,e); normal_addg(b,e);
gtog(c,f); normal_addg(d,f);
karatmulg(e,f);
karatmulg(c,a);
karatmulg(d,b);
normal_subg(a,f);
normal_subg(b,f);
gshiftleft(bits, b);
normal_addg(f, b);
gshiftleft(bits, b);
normal_addg(a, b);
gtog(b, y); y->sign *= sg;
pushg(6);
return;
}
void
karatsquareg(giant x)
{
int s = abs(x->sign), w, bits;
giant a, b, c;
if(s <= KARAT_BREAK) {
grammarsquareg(x);
return;
}
w = (s+1)/2; bits = 16*w;
a = popg(); b = popg(); c = popg();
gtog(x, a); a->sign = w; justg(a);
gtog(x, b); absg(b);
gshiftright(bits, b);
gtog(a,c); normal_addg(b,c);
karatsquareg(c);
karatsquareg(a);
karatsquareg(b);
normal_subg(b, c);
normal_subg(a, c);
gshiftleft(bits, b);
normal_addg(c,b);
gshiftleft(bits, b);
normal_addg(a, b);
gtog(b, x);
pushg(3);
return;
}
void
grammarmulg(
giant a,
giant b
)
{
int i,j;
unsigned int prod,carry=0;
int asize = abs(a->sign), bsize = abs(b->sign);
unsigned short *aptr,*bptr,*destptr;
unsigned short mult;
giant scratch = popg();
for (i=0; i<asize+bsize; ++i)
{
scratch->n[i]=0;
}
bptr = &(b->n[0]);
for (i=0; i<bsize; ++i)
{
mult = *(bptr++);
if (mult)
{
carry = 0;
aptr = &(a->n[0]);
destptr = &(scratch->n[i]);
for (j=0; j<asize; ++j)
{
prod = *(aptr++) * mult + *destptr + carry;
*(destptr++) = (unsigned short)(prod & 0xffff);
carry = prod >> 16;
}
*destptr = (unsigned short)carry;
}
}
bsize+=asize;
if (!carry)
--bsize;
scratch->sign = gsign(a)*gsign(b)*bsize;
gtog(scratch,b);
pushg(1);
}
void
grammarsquareg (
giant a
)
{
unsigned int cur_term;
unsigned int prod, carry=0, temp;
int asize = abs(a->sign), max = asize * 2 - 1;
unsigned short *ptr = a->n, *ptr1, *ptr2;
giant scratch;
if(asize == 0) {
itog(0,a);
return;
}
scratch = popg();
asize--;
temp = *ptr;
temp *= temp;
scratch->n[0] = temp;
carry = temp >> 16;
for (cur_term = 1; cur_term < max; cur_term++) {
ptr1 = ptr2 = ptr;
if (cur_term <= asize) {
ptr2 += cur_term;
} else {
ptr1 += cur_term - asize;
ptr2 += asize;
}
prod = carry & 0xFFFF;
carry >>= 16;
while(ptr1 < ptr2) {
temp = *ptr1++ * *ptr2--;
prod += (temp << 1) & 0xFFFF;
carry += (temp >> 15);
}
if (ptr1 == ptr2) {
temp = *ptr1;
temp *= temp;
prod += temp & 0xFFFF;
carry += (temp >> 16);
}
carry += prod >> 16;
scratch->n[cur_term] = (unsigned short) (prod);
}
if (carry) {
scratch->n[cur_term] = carry;
scratch->sign = cur_term+1;
} else scratch->sign = cur_term;
gtog(scratch,a);
pushg(1);
}
int initL = 0;
int
lpt(
int n,
int *lambda
)
{
register int i = 1;
*lambda = 0;
while (i<n)
{
i<<=1;
++(*lambda);
}
return(i);
}
void
addsignal(
giant x,
double *z,
int n
)
{
register int j, k, m, car, last;
register double f, g,err;
maxFFTerror = 0;
last = 0;
for (j=0;j<n;j++)
{
f = gfloor(z[j]+0.5);
if(f != 0.0) last = j;
if (checkFFTerror)
{
err = fabs(f - z[j]);
if (err > maxFFTerror)
maxFFTerror = err;
}
z[j] =0;
k = 0;
do
{
g = gfloor(f*TWOM16);
z[j+k] += f-g*TWO16;
++k;
f=g;
} while(f != 0.0);
}
car = 0;
for(j=0;j < last + 1;j++)
{
m = (int)(z[j]+car);
x->n[j] = (unsigned short)(m & 0xffff);
car = (m>>16);
}
if (car)
x->n[j] = (unsigned short)car;
else
--j;
while(!(x->n[j])) --j;
x->sign = j+1;
}
void
FFTsquareg(
giant x
)
{
int j,size = abs(x->sign);
register int L;
if (size<4)
{
grammarmulg(x,x);
return;
}
L = lpt(size+size, &j);
if(!z) z = (double *)malloc(MAX_SHORTS * sizeof(double));
giant_to_double(x, size, z, L);
fft_real_to_hermitian(z, L);
square_hermitian(z, L);
fftinv_hermitian_to_real(z, L);
addsignal(x,z,L);
x->sign = abs(x->sign);
}
void
FFTmulg(
giant y,
giant x
)
{
int lambda, sizex = abs(x->sign), sizey = abs(y->sign);
int finalsign = gsign(x)*gsign(y);
register int L;
if ((sizex<=4)||(sizey<=4))
{
grammarmulg(y,x);
return;
}
L = lpt(sizex+sizey, &lambda);
if(!z) z = (double *)malloc(MAX_SHORTS * sizeof(double));
if(!z2) z2 = (double *)malloc(MAX_SHORTS * sizeof(double));
giant_to_double(x, sizex, z, L);
giant_to_double(y, sizey, z2, L);
fft_real_to_hermitian(z, L);
fft_real_to_hermitian(z2, L);
mul_hermitian(z2, z, L);
fftinv_hermitian_to_real(z, L);
addsignal(x,z,L);
x->sign = finalsign*abs(x->sign);
}
void
scramble_real(
double *x,
int n
)
{
register int i,j,k;
register double tmp;
for (i=0,j=0;i<n-1;i++)
{
if (i<j)
{
tmp = x[j];
x[j]=x[i];
x[i]=tmp;
}
k = n/2;
while (k<=j)
{
j -= k;
k>>=1;
}
j += k;
}
}
void
fft_real_to_hermitian(
double *z,
int n
)
{
register double cc1, ss1, cc3, ss3;
register int is, id, i0, i1, i2, i3, i4, i5, i6, i7, i8,
a, a3, b, b3, nminus = n-1, dil, expand;
register double *x, e;
int nn = n>>1;
double t1, t2, t3, t4, t5, t6;
register int n2, n4, n8, i, j;
init_sinCos(n);
expand = cur_run/n;
scramble_real(z, n);
x = z-1;
is = 1;
id = 4;
do
{
for (i0=is;i0<=n;i0+=id)
{
i1 = i0+1;
e = x[i0];
x[i0] = e + x[i1];
x[i1] = e - x[i1];
}
is = (id<<1)-1;
id <<= 2;
} while(is<n);
n2 = 2;
while(nn>>=1)
{
n2 <<= 1;
n4 = n2>>2;
n8 = n2>>3;
is = 0;
id = n2<<1;
do
{
for (i=is;i<n;i+=id)
{
i1 = i+1;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
t1 = x[i4]+x[i3];
x[i4] -= x[i3];
x[i3] = x[i1] - t1;
x[i1] += t1;
if (n4==1)
continue;
i1 += n8;
i2 += n8;
i3 += n8;
i4 += n8;
t1 = (x[i3]+x[i4])*SQRTHALF;
t2 = (x[i3]-x[i4])*SQRTHALF;
x[i4] = x[i2] - t1;
x[i3] = -x[i2] - t1;
x[i2] = x[i1] - t2;
x[i1] += t2;
}
is = (id<<1) - n2;
id <<= 2;
} while(is<n);
dil = n/n2;
a = dil;
for (j=2;j<=n8;j++)
{
a3 = (a+(a<<1))&nminus;
b = a*expand;
b3 = a3*expand;
cc1 = s_cos(b);
ss1 = s_sin(b);
cc3 = s_cos(b3);
ss3 = s_sin(b3);
a = (a+dil)&nminus;
is = 0;
id = n2<<1;
do
{
for(i=is;i<n;i+=id)
{
i1 = i+j;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
i5 = i + n4 - j + 2;
i6 = i5 + n4;
i7 = i6 + n4;
i8 = i7 + n4;
t1 = x[i3]*cc1 + x[i7]*ss1;
t2 = x[i7]*cc1 - x[i3]*ss1;
t3 = x[i4]*cc3 + x[i8]*ss3;
t4 = x[i8]*cc3 - x[i4]*ss3;
t5 = t1 + t3;
t6 = t2 + t4;
t3 = t1 - t3;
t4 = t2 - t4;
t2 = x[i6] + t6;
x[i3] = t6 - x[i6];
x[i8] = t2;
t2 = x[i2] - t3;
x[i7] = -x[i2] - t3;
x[i4] = t2;
t1 = x[i1] + t5;
x[i6] = x[i1] - t5;
x[i1] = t1;
t1 = x[i5] + t4;
x[i5] -= t4;
x[i2] = t1;
}
is = (id<<1) - n2;
id <<= 2;
} while(is<n);
}
}
}
void
fftinv_hermitian_to_real(
double *z,
int n
)
{
register double cc1, ss1, cc3, ss3;
register int is, id, i0, i1, i2, i3, i4, i5, i6, i7, i8,
a, a3, b, b3, nminus = n-1, dil, expand;
register double *x, e;
int nn = n>>1;
double t1, t2, t3, t4, t5;
int n2, n4, n8, i, j;
init_sinCos(n);
expand = cur_run/n;
x = z-1;
n2 = n<<1;
while(nn >>= 1)
{
is = 0;
id = n2;
n2 >>= 1;
n4 = n2>>2;
n8 = n4>>1;
do
{
for(i=is;i<n;i+=id)
{
i1 = i+1;
i2 = i1 + n4;
i3 = i2 + n4;
i4 = i3 + n4;
t1 = x[i1] - x[i3];
x[i1] += x[i3];
x[i2] += x[i2];
x[i3] = t1 - 2.0*x[i4];
x[i4] = t1 + 2.0*x[i4];
if (n4==1)
continue;
i1 += n8;
i2 += n8;
i3 += n8;
i4 += n8;
t1 = (x[i2]-x[i1])*SQRTHALF;
t2 = (x[i4]+x[i3])*SQRTHALF;
x[i1] += x[i2];
x[i2] = x[i4]-x[i3];
x[i3] = -2.0*(t2+t1);
x[i4] = 2.0*(t1-t2);
}
is = (id<<1) - n2;
id <<= 2;
} while (is<n-1);
dil = n/n2;
a = dil;
for (j=2;j<=n8;j++)
{
a3 = (a+(a<<1))&nminus;
b = a*expand;
b3 = a3*expand;
cc1 = s_cos(b);
ss1 = s_sin(b);
cc3 = s_cos(b3);
ss3 = s_sin(b3);
a = (a+dil)&nminus;
is = 0;
id = n2<<1;
do
{
for(i=is;i<n;i+=id)
{
i1 = i+j;
i2 = i1+n4;
i3 = i2+n4;
i4 = i3+n4;
i5 = i+n4-j+2;
i6 = i5+n4;
i7 = i6+n4;
i8 = i7+n4;
t1 = x[i1] - x[i6];
x[i1] += x[i6];
t2 = x[i5] - x[i2];
x[i5] += x[i2];
t3 = x[i8] + x[i3];
x[i6] = x[i8] - x[i3];
t4 = x[i4] + x[i7];
x[i2] = x[i4] - x[i7];
t5 = t1 - t4;
t1 += t4;
t4 = t2 - t3;
t2 += t3;
x[i3] = t5*cc1 + t4*ss1;
x[i7] = -t4*cc1 + t5*ss1;
x[i4] = t1*cc3 - t2*ss3;
x[i8] = t2*cc3 + t1*ss3;
}
is = (id<<1) - n2;
id <<= 2;
} while(is<n-1);
}
}
is = 1;
id = 4;
do
{
for (i0=is;i0<=n;i0+=id)
{
i1 = i0+1;
e = x[i0];
x[i0] = e + x[i1];
x[i1] = e - x[i1];
}
is = (id<<1) - 1;
id <<= 2;
} while(is<n);
scramble_real(z, n);
e = 1/(double)n;
for (i=0;i<n;i++)
{
z[i] *= e;
}
}
void
mul_hermitian(
double *a,
double *b,
int n
)
{
register int k, half = n>>1;
register double aa, bb, am, bm;
b[0] *= a[0];
b[half] *= a[half];
for (k=1;k<half;k++)
{
aa = a[k];
bb = b[k];
am = a[n-k];
bm = b[n-k];
b[k] = aa*bb - am*bm;
b[n-k] = aa*bm + am*bb;
}
}
void
square_hermitian(
double *b,
int n
)
{
register int k, half = n>>1;
register double c, d;
b[0] *= b[0];
b[half] *= b[half];
for (k=1;k<half;k++)
{
c = b[k];
d = b[n-k];
b[n-k] = 2.0*c*d;
b[k] = (c+d)*(c-d);
}
}
void
giant_to_double
(
giant x,
int sizex,
double *z,
int L
)
{
register int j;
for (j=sizex;j<L;j++)
{
z[j]=0.0;
}
for (j=0;j<sizex;j++)
{
z[j] = x->n[j];
}
}
void
gswap(
giant *p,
giant *q
)
{
giant t;
t = *p;
*p = *q;
*q = t;
}
void
onestep(
giant x,
giant y,
gmatrix A
)
{
giant s4 = popg();
gtog(x,s4);
gtog(y,x);
gtog(s4,y);
divg(x,s4);
punch(s4,A);
mulg(x,s4);
subg(s4,y);
pushg(1);
}
void
mulvM(
gmatrix A,
giant x,
giant y
)
{
giant s0 = popg(), s1 = popg();
gtog(A->ur,s0);
gtog( A->lr,s1);
dotproduct(x,y,A->ul,s0);
dotproduct(x,y,A->ll,s1);
gtog(s0,x);
gtog(s1,y);
pushg(2);
}
void
mulmM(
gmatrix A,
gmatrix B
)
{
giant s0 = popg();
giant s1 = popg();
giant s2 = popg();
giant s3 = popg();
gtog(B->ul,s0);
gtog(B->ur,s1);
gtog(B->ll,s2);
gtog(B->lr,s3);
dotproduct(A->ur,A->ul,B->ll,s0);
dotproduct(A->ur,A->ul,B->lr,s1);
dotproduct(A->ll,A->lr,B->ul,s2);
dotproduct(A->ll,A->lr,B->ur,s3);
gtog(s0,B->ul);
gtog(s1,B->ur);
gtog(s2,B->ll);
gtog(s3,B->lr);
pushg(4);
}
void
writeM(
gmatrix A
)
{
printf(" ul:");
gout(A->ul);
printf(" ur:");
gout(A->ur);
printf(" ll:");
gout(A->ll);
printf(" lr:");
gout(A->lr);
}
void
punch(
giant q,
gmatrix A
)
{
giant s0 = popg();
gtog(A->ll,s0);
mulg(q,A->ll);
gswap(&A->ul,&A->ll);
subg(A->ul,A->ll);
gtog(s0,A->ul);
gtog(A->lr,s0);
mulg(q,A->lr);
gswap(&A->ur,&A->lr);
subg(A->ur,A->lr);
gtog(s0,A->ur);
pushg(1);
}
static void
dotproduct(
giant a,
giant b,
giant c,
giant d
)
{
giant s4 = popg();
gtog(c,s4);
mulg(a, s4);
mulg(b,d);
addg(s4,d);
pushg(1);
}
void
ggcd(
giant xx,
giant yy
)
{
giant x = popg(), y = popg();
gmatrix A = newgmatrix();
gtog(xx,x); gtog(yy,y);
for(;;)
{
fix(&x,&y);
if (bitlen(y) <= GCDLIMIT )
break;
A->ul = popg();
A->ur = popg();
A->ll = popg();
A->lr = popg();
itog(1,A->ul);
itog(0,A->ur);
itog(0,A->ll);
itog(1,A->lr);
hgcd(0,x,y,A);
mulvM(A,x,y);
pushg(4);
fix(&x,&y);
if (bitlen(y) <= GCDLIMIT )
break;
modg(y,x);
gswap(&x,&y);
}
bgcdg(x,y);
gtog(y,yy);
pushg(2);
free(A);
}
void
fix(
giant *p,
giant *q
)
{
if( gsign(*p) < 0 )
negg(*p);
if( gsign(*q) < 0 )
negg(*q);
if( gcompg(*p,*q) < 0 )
gswap(p,q);
}
void
hgcd(
int n,
giant xx,
giant yy,
gmatrix A
)
{
giant x, y;
if (isZero(yy))
return;
x = popg();
y = popg();
gtog(xx,x);
gtog(yy,y);
gshiftright(n,x);
gshiftright(n,y);
if (bitlen(x) <= INTLIMIT )
{
shgcd(gtoi(x),gtoi(y),A);
}
else
{
gmatrix B = newgmatrix();
int m = bitlen(x)/2;
hgcd(m,x,y,A);
mulvM(A,x,y);
if (gsign(x) < 0)
{
negg(x); negg(A->ul); negg(A->ur);
}
if (gsign(y) < 0)
{
negg(y); negg(A->ll); negg(A->lr);
}
if (gcompg(x,y) < 0)
{
gswap(&x,&y);
gswap(&A->ul,&A->ll);
gswap(&A->ur,&A->lr);
}
if (!isZero(y))
{
onestep(x,y,A);
m /= 2;
B->ul = popg();
B->ur = popg();
B->ll = popg();
B->lr = popg();
itog(1,B->ul);
itog(0,B->ur);
itog(0,B->ll);
itog(1,B->lr);
hgcd(m,x,y,B);
mulmM(B,A);
pushg(4);
}
free(B);
}
pushg(2);
}
void
shgcd(
register int x,
register int y,
gmatrix A
)
{
register int q, t, start = x;
int Aul = 1, Aur = 0, All = 0, Alr = 1;
while (y != 0 && y > start/y)
{
q = x/y;
t = y;
y = x%y;
x = t;
t = All;
All = Aul-q*t;
Aul = t;
t = Alr;
Alr = Aur-q*t;
Aur = t;
}
itog(Aul,A->ul);
itog(Aur,A->ur);
itog(All,A->ll);
itog(Alr,A->lr);
}