--- /dev/null
+/**************************************************************
+ *
+ * giants.c
+ *
+ * Library for large-integer arithmetic.
+ *
+ * The large-gcd implementation is due to J. P. Buhler.
+ * Special mod routines use ideas of R. McIntosh.
+ * Contributions from G. Woltman, A. Powell acknowledged.
+ *
+ * Updates:
+ * 18 Jul 99 REC Added routine fer_mod(), for use when Fermat
+ giant itself is available.
+ * 17 Jul 99 REC Fixed sign bug in fermatmod()
+ * 17 Apr 99 REC Fixed various comment/line wraps
+ * 25 Mar 99 REC G. Woltman/A. Powell fixes Karat. routines
+ * 05 Mar 99 REC Moved invaux, binvaux giants to stack
+ * 05 Mar 99 REC Moved gread/gwrite giants to stack
+ * 05 Mar 99 REC No static on cur_den, cur_recip (A. Powell)
+ * 28 Feb 99 REC Error detection added atop newgiant().
+ * 27 Feb 99 REC Reduced wasted work in addsignal().
+ * 27 Feb 99 REC Reduced wasted work in FFTmulg().
+ * 19 Feb 99 REC Generalized iaddg() per R. Mcintosh.
+ * 2 Feb 99 REC Fixed comments.
+ * 6 Dec 98 REC Fixed yet another Karatsuba glitch.
+ * 1 Dec 98 REC Fixed errant case of addg().
+ * 28 Nov 98 REC Installed A. Powell's (correct) variant of
+ Karatsuba multiply.
+ * 15 May 98 REC Modified gwrite() to handle huge integers.
+ * 13 May 98 REC Changed to static stack declarations
+ * 11 May 98 REC Installed Karatsuba multiply, to handle
+ * medregion 'twixt grammar- and FFT-multiply.
+ * 1 May 98 JF gshifts now handle bits < 0 correctly.
+ * 30 Apr 98 JF 68k assembler code removed,
+ * stack giant size now invariant and based
+ * on first call of newgiant(),
+ * stack memory leaks fixed.
+ * 29 Apr 98 JF function prototyes cleaned up,
+ * GCD no longer uses personal stack,
+ * optimized shifts for bits%16 == 0.
+ * 27 Apr 98 JF scratch giants now replaced with stack
+ * 20 Apr 98 JF grammarsquareg fixed for asize == 0.
+ * scratch giants now static.
+ * 29 Jan 98 JF Corrected out-of-range errors in
+ * mersennemod and fermatmod.
+ * 23 Dec 97 REC Sped up divide routines via split-shift.
+ * 18 Nov 97 JF Improved mersennemod, fermatmod.
+ * 9 Nov 97 JF Sped up grammarsquareg.
+ * 20 May 97 RDW Fixed Win32 compiler warnings.
+ * 18 May 97 REC Installed new, fast divide.
+ * 17 May 97 REC Reduced memory for FFT multiply.
+ * 26 Apr 97 REC Creation.
+ *
+ * c. 1997,1998 Perfectly Scientific, Inc.
+ * All Rights Reserved.
+ *
+ **************************************************************/
+
+
+/* Include Files. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "giants.h"
+
+
+/* Compiler options. */
+
+#ifdef _WIN32
+#pragma warning( disable : 4127 4706 ) /* disable conditional is constant warning */
+#endif
+
+
+/* Global variables. */
+
+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;
+
+/* stack handling functions. */
+static giant popg(void);
+static void pushg(int);
+
+
+/* Private function prototypes. */
+
+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);
+
+
+
+/**************************************************************
+ *
+ * Functions
+ *
+ **************************************************************/
+
+
+/**************************************************************
+ *
+ * Initialization and utility functions
+ *
+ **************************************************************/
+
+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) {
+/* Initialize the stack if we're just starting out.
+ * Note that all stack giants will be whatever current_max_size is
+ * when newgiant() is first called. */
+ 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) {
+/* Expand the stack if we need to. */
+ 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) {
+/* Prune the stack if it's too big. Disabled, so the stack can only expand */
+ /* cur_stack_size -= STACK_GROW;
+ for (i = cur_stack_size - STACK_GROW; i < cur_stack_size; i++)
+ free(stack[i]);
+ stack = (giant *) realloc (stack,cur_stack_size * sizeof(giant)); */
+ }
+
+/* Malloc our giant. */
+ 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 newgiant() is being called for the first time, set the
+ * size of the stack giants. */
+ if (stack_glen == 0) stack_glen = current_max_size;
+
+ return(thegiant);
+}
+
+
+gmatrix
+newgmatrix(
+ void
+)
+/* Allocates space for a gmatrix struct, but does not
+ * allocate space for the giants. */
+{
+ 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
+)
+/* Returns TR if thegiant == 0. */
+{
+ 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
+)
+/* destgiant becomes equal to srcgiant. */
+{
+ int numbytes = sizeof(int) + abs(srcgiant->sign)*sizeof(short);
+
+ memcpy((char *)destgiant,(char *)srcgiant,numbytes);
+}
+
+
+void
+itog(
+ int i,
+ giant g
+)
+/* The giant g becomes set to the integer value i. */
+{
+ 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
+)
+/* Calculate the value of an int-sized giant NOT exceeding 31 bits. */
+{
+ 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
+)
+/* Returns the sign of g. */
+{
+ if (isZero(g))
+ return(0);
+ if (g->sign >0)
+ return(1);
+ return(-1);
+}
+
+
+#if 0
+int gcompg(a,b)
+/* Returns -1,0,1 if a<b, a=b, a>b, respectively. */
+ 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
+)
+/* Returns -1,0,1 if a<b, a=b, a>b, respectively. */
+{
+ 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; /* Take absolute value of 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;
+}
+
+
+/**************************************************************
+ *
+ * Private I/O Functions
+ *
+ **************************************************************/
+
+
+int
+radixdiv(
+ int base,
+ int divisor,
+ giant thegiant
+)
+/* Divides giant of arbitrary base by divisor.
+ * Returns remainder. Used by idivg and gread. */
+{
+ 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
+)
+/* Used by gwriteln. */
+{
+ 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
+)
+/* Outputs thegiant to filepointer. Output is terminated by a newline. */
+{
+ 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);
+}
+
+
+
+/**************************************************************
+ *
+ * Private Math Functions
+ *
+ **************************************************************/
+
+
+void
+negg(
+ giant g
+)
+/* g becomes -g. */
+{
+ g->sign = -g->sign;
+}
+
+
+void
+absg(
+ giant g
+)
+{
+ /* g becomes the absolute value of g. */
+ if (g->sign <0)
+ g->sign=-g->sign;
+}
+
+
+void
+iaddg(
+ int i,
+ giant g
+)
+/* Giant g becomes g + (int)i. */
+{
+ 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;
+ }
+}
+
+
+/* New subtract routines.
+ The basic subtract "subg()" uses the following logic table:
+
+ a b if(b > a) if(a > b)
+
+ + + b := b - a b := -(a - b)
+ - + b := b + (-a) N.A.
+ + - N.A. b := -((-b) + a)
+ - - b := (-a) - (-b) b := -((-b) - (-a))
+
+ The basic addition routine "addg()" uses:
+
+ a b if(b > -a) if(-a > b)
+
+ + + b := b + a N.A.
+ - + b := b - (-a) b := -((-a) - b)
+ + - b := a - (-b) b := -((-b) - a)
+ - - N.A. b := -((-b) + (-a))
+
+ In this way, internal routines "normal_addg," "normal_subg,"
+ and "reverse_subg;" each of which assumes non-negative
+ operands and a non-negative result, are now used for greater
+ efficiency.
+ */
+
+void
+normal_addg(
+ giant a,
+ giant b
+)
+/* b := a + b, both a,b assumed non-negative. */
+{
+ 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
+)
+/* b := b - a; requires b, a non-negative and b >= a. */
+{
+ 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
+)
+/* b := a - b; requires b, a non-negative and a >= 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; /* REC, 21 Apr 1996. */
+ 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
+)
+/* b := b + a, any signs any result. */
+{
+ 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
+)
+/* b := b - a, any signs, any result. */
+{
+ 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
+)
+/* Returns the number of trailing zero bits in 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
+)
+/* u becomes greatest power of two not exceeding u/v. */
+{
+ 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
+)
+/* Binary inverse method. Returns zero if no inverse exists,
+ * in which case x becomes GCD(x,p). */
+{
+
+ 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
+)
+/* Returns zero if no inverse exists, in which case x becomes
+ * GCD(x,p). */
+{
+
+ 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
+)
+/* Classical Euclid GCD. v becomes gcd(a, 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
+)
+/* Binary form of the gcd. b becomes the gcd of a,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();
+
+ /* Now neither a nor b is zero. */
+ 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
+)
+/* g becomes m^n, NO mod performed. */
+{
+ 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
+)
+/* r becomes the steady-state reciprocal
+ * 2^(2b)/d, where b = bit-length of d-1. */
+{
+ 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
+)
+/* n := n/d, where r is the precalculated
+ * steady-state reciprocal of d. */
+{
+ 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
+)
+/* This is the fastest mod of the present collection.
+ * n := n % d, where r is the precalculated
+ * steady-state reciprocal of d. */
+
+{
+ 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
+)
+/* n becomes n%d. n is arbitrary, but the denominator d must be positive! */
+{
+ 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
+)
+/* a becomes (a*b) (mod 2^q-k) where q % 16 == 0 and k is "small" (0 < k < 65535).
+ * Returns 0 if unsuccessful, otherwise 1. */
+{
+ 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;
+ }
+ }
+
+ /* Next, deal with the carry term. Needs to be improved to
+ handle overflow carry cases. */
+ 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
+)
+{
+ /* theg becomes theg/divisor. Returns remainder. */
+ int n;
+ int base = 1<<(8*sizeof(short));
+
+ n = radixdiv(base,divisor,theg);
+ return(n);
+}
+
+
+void
+divg(
+ giant d,
+ giant n
+)
+/* n becomes n/d. n is arbitrary, but the denominator d must be positive! */
+{
+ 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
+)
+/* x becomes x^n (mod 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
+)
+/* x becomes x^n (mod 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
+)
+/* x becomes x^n (mod 2^q+1). */
+{
+ 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
+)
+/* x becomes x^n (mod 2^q+1). */
+{
+ 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
+)
+/* x becomes x^n (mod 2^q-1). */
+{
+ 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
+)
+/* x becomes x^n (mod 2^q-1). */
+{
+ 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
+)
+/* shift g left bits bits. Equivalent to g = g*2^bits. */
+{
+ 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
+)
+/* shift g right bits bits. Equivalent to g = g/2^bits. */
+{
+ 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
+)
+/* dest becomes lowermost n bits of src. Equivalent to dest = src % 2^n. */
+{
+ 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
+)
+/* negate g. g is mod 2^n+1. */
+{
+ 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)
+ { /* if high bit is set, -g = 1. */
+ g->sign = 1;
+ g->n[0] = 1;
+ return;
+ }
+ if (allzeros(shorts,bits,g))
+ return; /* if g=0, -g = 0. */
+
+ 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
+)
+/* g := g (mod 2^n - 1) */
+{
+ 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
+)
+/* g := g (mod 2^n + 1), */
+{
+ 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
+)
+/* Same as fermatmod(), except modulus = 2^n should be passed
+if available (i.e. if already allocated and set). */
+{
+ 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
+)
+/* g becomes g * i. */
+{
+ 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
+)
+/* b becomes b^2. */
+{
+ auxmulg(b,b);
+}
+
+
+void
+mulg(
+ giant a,
+ giant b
+)
+/* b becomes a*b. */
+{
+ auxmulg(a,b);
+}
+
+
+void
+auxmulg(
+ giant a,
+ giant b
+)
+/* Optimized general multiply, b becomes a*b. Modes are:
+ * AUTO_MUL: switch according to empirical speed criteria.
+ * GRAMMAR_MUL: force grammar-school algorithm.
+ * KARAT_MUL: force Karatsuba divide-conquer method.
+ * FFT_MUL: force floating point FFT method. */
+{
+ 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);
+}
+
+/* Next, improved Karatsuba routines from A. Powell,
+ improvements by G. Woltman. */
+
+void
+karatmulg(giant x, giant y)
+/* y becomes x*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); /* e := (a + b) */
+ gtog(c,f); normal_addg(d,f); /* f := (c + d) */
+ karatmulg(e,f); /* f := (a + b)(c + d) */
+ karatmulg(c,a); /* a := a c */
+ karatmulg(d,b); /* b := b d */
+ normal_subg(a,f);
+ /* f := (a + b)(c + d) - a c */
+ normal_subg(b,f);
+ /* f := (a + b)(c + d) - a c - b d */
+ 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)
+/* x becomes x^2. */
+{
+ 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
+)
+/* b becomes a*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
+)
+/* a := a^2. */
+{
+ 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);
+}
+
+
+/**************************************************************
+ *
+ * FFT multiply Functions
+ *
+ **************************************************************/
+
+int initL = 0;
+
+int
+lpt(
+ int n,
+ int *lambda
+)
+/* Returns least power of two greater than n. */
+{
+ 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
+)
+{
+ /* x becomes y*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
+)
+/* Output is {Re(z^[0]),...,Re(z^[n/2),Im(z^[n/2-1]),...,Im(z^[1]).
+ * This is a decimation-in-time, split-radix algorithm.
+ */
+{
+ 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; /* FORTRAN compatibility. */
+ 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
+)
+/* Input is {Re(z^[0]),...,Re(z^[n/2),Im(z^[n/2-1]),...,Im(z^[1]).
+ * This is a decimation-in-frequency, split-radix algorithm.
+ */
+{
+ 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
+)
+/* Do one step of the euclidean algorithm and modify
+ * the matrix A accordingly. */
+{
+ 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
+)
+/* Multiply vector by Matrix; changes x,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
+)
+/* Multiply matrix by Matrix; changes second matrix. */
+{
+ 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
+)
+/* Multiply the matrix A on the left by [0,1,1,-q]. */
+{
+ 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
+)
+/* Replace last argument with the dot product of two 2-vectors. */
+{
+ giant s4 = popg();
+
+ gtog(c,s4);
+ mulg(a, s4);
+ mulg(b,d);
+ addg(s4,d);
+
+ pushg(1);
+}
+
+
+void
+ggcd(
+ giant xx,
+ giant yy
+)
+/* A giant gcd. Modifies its arguments. */
+{
+ 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
+)
+/* Insure that x > y >= 0. */
+{
+ 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
+)
+/* hgcd(n,x,y,A) chops n bits off x and y and computes th
+ * 2 by 2 matrix A such that A[x y] is the pair of terms
+ * in the remainder sequence starting with x,y that is
+ * half the size of x. Note that the argument A is modified
+ * but that the arguments xx and yy are left unchanged.
+ */
+{
+ 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
+)
+/*
+ * Do a half gcd on the integers a and b, putting the result in A
+ * It is fairly easy to use the 2 by 2 matrix description of the
+ * extended Euclidean algorithm to prove that the quantity q*t
+ * never overflows.
+ */
+{
+ 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);
+}