/************************************************************************/ /* */ /* Multi-Precise Arithmetic Package */ /* */ /* Author: Robert D. Silverman */ /* MITRE Corp. */ /* Burlington Rd. */ /* Bedford, Mass. */ /* */ /* Notes: */ /* All multi-precise numbers are stored in radix "RADIX" */ /* They are stored with their length in word 0. The length */ /* includes word 0. */ /* They are stored with most significant digits to the left and */ /* least to the right. */ /* */ /* SIZE(x) is the size of the multi-precise number x */ /* LASTDIGIT(x) is the least significant word */ /* FIRST(x) is the most significant */ /* */ /************************************************************************/ #include #include #define RADIX 1073741824 #define SQRT_RAD 32768 #define MPDIM 50 #define MPCDIM 200 #define MPBITS 30 #define LOGTWO 50 #define EOS '\0' #define BLANK ' ' #define SIZE(x) x[0] #define LASTDIGIT(x) x[1] #define FIRST(x) x[SIZE(x)-1] #define INIT(x) for (ini=0; ini= B */ /* OUTPUT CAN BE INPUT */ /************************************************************************/ subt(a,b,c) int a[],b[],c[]; { /* start of subt */ register int i,borrow,as,bs; borrow = 0; as = SIZE(a); bs = SIZE(b); for (i=1; i 1 && c[i] == 0) { SIZE(c)--; i--; } } /* end of subt */ /************************************************************************/ /* */ /* routine to do single precision multiply */ /* */ /************************************************************************/ mul_single(a,b,c) int a[],b,c[]; { /* start of mul_single */ register int carry,i,d; int x[2]; d = SIZE(a); carry = 0; for (i=1; i0; i--) { divrem(r,a[i],b,x); result[i] = x[0]; r = x[1]; } SIZE(result) = as; i = SIZE(result); while (i>1 && result[i-1] == 0) { SIZE(result)--; i = SIZE(result); } return(r); } /* end of div_single */ /************************************************************************/ /* */ /* routine to do single precision remaindering */ /* */ /************************************************************************/ smod(a,b) int a[],b; { /* start of smod */ register int i,r,as; int x[2]; r = 0; as = SIZE(a); for (i=as-1; i>0; i--) { divrem(r,a[i],b,x); r = x[1]; } return(r); } /* end of smod */ /************************************************************************/ /* */ /* routine to multiply C = A*B */ /* OUTPUT CAN'T BE INPUT */ /************************************************************************/ mult(a,b,c) int a[],b[],c[]; { /* start of mult */ register int carry,i,j,k; int x[2],da,db; da = SIZE(a); db = SIZE(b); for (i=1; i< da; i++) c[i] = 0; /* initialize output */ for (j=1; j= RADIX) { x[0] -= RADIX; x[1]++; } carry = x[1]; c[k] = x[0]; } k = da + j - 1; c[k] = carry; } SIZE(c) = da + db - 1; if (FIRST(c) == 0) SIZE(c)--; } /* end of mult */ /************************************************************************/ /* */ /* routine to perform division */ /* */ /************************************************************************/ div(a,b,q,r) int a[],b[],q[],r[]; { /* start of div */ register int i,j,k; int qhat,d,m,db,t[2],len; int x[2],borrow,tmp; int junk1[MPDIM],junk2[MPDIM],junk3[MPDIM]; /* if divisor is single precision call div_single */ if (SIZE(b) < 3) { r[1] = div_single(a,b[1],q); SIZE(r) = 2; return; } /* now we do full blown long division (expensive!) */ m = SIZE(a) - SIZE(b) + 2; db = SIZE(b); if (m > 1) { /* outer loop */ d = RADIX/(1 + FIRST(b)); mul_single(a,d,junk1); mul_single(b,d,junk2); /* normalize input parameters */ junk2[SIZE(junk2)] = 0; /* pad with high order zero */ len = SIZE(junk1) - 1; if (len == SIZE(a)-1) { len++; junk1[len] = 0; } for (j=0; j i || (x[1] == i && x[0] > junk1[len-2])) { qhat--; i += junk2[db-1]; muladd(qhat,junk2[db-2],0,x); } mul_single(junk2,qhat,junk3); /* multiply and subtract*/ if (SIZE(junk2) == SIZE(junk3)) { k = SIZE(junk2); SIZE(junk3) = k+1; junk3[k] = 0; } borrow = 0; k = len - SIZE(junk2) + 1; for (i=1; i 2 && junk1[db-1] == 0) db--; SIZE(junk1) = db; i = div_single(junk1,d,r); /* unnormalize */ if (q[m-1] == 0) SIZE(q) = m-1; else SIZE(q) = m; } else { /* q = 0, r = a */ mpcopy(a,r); q[0] = 2; q[1] = 0; } } /* end of div */ /************************************************************************/ /* */ /* routine to convert multi-precision integer to string of */ /* digits of length MPITOC <= size */ /* */ /************************************************************************/ mpitoc(input,str,size) int input[]; char str[]; int size; { /* start of mpitoc */ register int i,j,k; int junk1[MPCDIM],temp,d; static char digits[10] = { '0','1','2','3','4','5','6','7','8','9' }; char tstr[MPCDIM]; mpcopy(input,junk1); for (i=0; i SIZE(b)) return(1); if (SIZE(b) > SIZE(a)) return(-1); for (i=SIZE(a)-1; i > 0; i--) { if (a[i] > b[i]) return(1); if (a[i] < b[i]) return(-1); } return(0); } /* end of mpcmp */ /************************************************************************/ /* */ /* routine to return [sqrt(n)] */ /* */ /************************************************************************/ mpsqrt(a,b) int a[],b[]; { /* start of mpsqrt */ int junkc[MPCDIM],junk3[MPCDIM]; register int i,da,db; da = SIZE(a); db = 1 + da/2; SIZE(b) = db; for (i=1; i= RADIX) { /* this is rare */ b[db] = 1; FIRST(b) -= RADIX; SIZE(b) = db+1; } while(1) /* loop to infinity */ { div(a,b,junkc,junk3); /* junkc = a/b */ add(b,junkc,junkc); /* junkc += b */ da = div_single(junkc,2,junkc); /* divide by 2 */ if (mpcmp(junkc,b) == 0) break; /* converged */ mpcopy(junkc,b); } } /* end of mpsqrt */ /************************************************************************/ /* */ /* this routine removes the largest possible power of 2 from N */ /* */ /************************************************************************/ mpodiv(a) int a[]; { /* start of mpodiv */ register int i,j,index; int d,accum; if (SIZE(a) < 2 && a[1] == 0) return(-1); i=1; while (a[i] == 0) i++; i -= 1; if (i > 0) { d = SIZE(a) - i; for (j=1; j<=d; j++) { index = j+i; a[j] = a[index]; } SIZE(a) = d; } accum = 1; j = 0; d = a[1]; while (d % 2 == 0) { j++; d = d/2; accum = 2*accum; } if (j > 0) div_single(a,accum,a); return(MPBITS*i + j); } /* end of mpodiv */ /************************************************************************/ /* */ /* routine to compute GCD(a,b) */ /* */ /************************************************************************/ gcd(a,b,c) int a[],b[],c[]; { /* start of gcd */ register int i,j; int junk1[MPCDIM],temp[MPCDIM]; if (mpcmp(a,b) == 0) /* if the two are equal */ { mpcopy(a,c); return; } mpcopy(a,c); mpcopy(b,junk1); i = mpodiv(c); if (SIZE(b) == 2 && b[1] == 0) /* b is zero */ { printf("Attempt to take GCD with 0\n"); return; } if (SIZE(a) == 2 && a[1] == 0) /* a is zero */ { printf("Attempt to take GCD with 0\n"); return; } i = mpcmp(c,junk1); while (i != 0) { if (i < 0) { subt(junk1,c,temp); mpcopy(temp,junk1); j = mpodiv(junk1); } else { subt(c,junk1,temp); mpcopy(temp,c); j = mpodiv(c); } i = mpcmp(c,junk1); } } /* end of gcd */ /************************************************************************/ /* */ /* routine to compute b^e MOD m for MP integers b,e, and m */ /* */ /************************************************************************/ mpower(b,e,m,a) int b[],e[],m[],a[]; { /* start of mpower */ int junkc[MPDIM],junk4[MPDIM]; int bits[MPBITS+1],ebit; register int i,n,j,es; mpcopy(b,a); es = SIZE(e)-1; for (n=es; n>0; n--) { ebit = e[n]; i = 0; for (j=1; j<=MPBITS; j++) { i++; bits[i] = ebit & 1; ebit = ebit/2; if (n == SIZE(e)-1 && ebit == 0) { i--; break; } } for (; i>0; i--) { mult(a,a,junkc); div(junkc,m,junk4,a); if (bits[i] != 0) { mult(a,b,junkc); div(junkc,m,junk4,a); } } } } /* end of mpower */ /************************************************************************/ /* */ /* ARITHMETIC PRIMITIVES: (A*B + C)/D */ /* (A*B + C) MOD D */ /* */ /************************************************************************/ mulold(a,b,c,m) int a,b,c,m[2]; { /* start of muladd */ long d; d = a*b + c; m[0] = d % RADIX; m[1] = d/RADIX; } /* end of muladd */ /************************************************************************/ /* */ /* ARITHMETIC PRIMITIVE: q = (A*RADIX + B)/C */ /* */ /************************************************************************/ divold(a,b,q) int a[2],b,*q; { /* start of divrem */ long t; t = a[1]*RADIX + a[0]; *q = t/b; return(t % b); } /* end of divrem */ /************************************************************************/ /* */ /* routine to compute A**N for single precision A and N */ /* and a MP result */ /* */ /************************************************************************/ cpower(base,power,result) int base, power, result[]; { /* start of cpower */ int temp[MPCDIM], temp1[MPCDIM], bits[LOGTWO], max; register int i; INIT(result); INIT(temp); binary(power,bits,&max); SIZE(temp) = 2; temp[1] = base; SIZE(result) = 2; result[1] = base; for (i=max-1; i>=0; i--) { if (bits[i] == 1) { mult(result,result,temp1); mult(temp1,temp,result); } else { mult(result,result,temp1); mpcopy(temp1,result); } } } /* end of cpower */ /************************************************************************/ /* */ /* routine to return binary representation of an integer */ /* */ /************************************************************************/ binary(n,bits,max) int n,bits[],*max; { /* start of binary */ register int i,p,q,r; for (i=0; i<32; i++) bits[i] = 0; p = n; while (p > 0) { q = 1; r = 0; while (q <= p/2) { q = 2*q; r++; } bits[r] = 1; p = p-q; } i = 31; while (bits[i] != 1) i--; *max = i; } /* end of binary */ /************************************************************************/ /* */ /* routine to display a number in internal format on the sceen */ /* */ /************************************************************************/ internal(num) int num[]; { /* start of internal */ int i; for (i=SIZE(num)-1; i>0; i--) printf("%d, ",num[i]); printf("\n"); } /* end of internal */ /************************************************************************/ /* */ /* routine to dump current number into a file */ /* */ /************************************************************************/ fileit(number,ptr) int number[]; FILE *ptr; { register int nd; char junk[MPCDIM]; nd = mpitoc(number,junk,MPCDIM); fprintf(ptr,"%s\n",junk); } /************************************************************************/ /* */ /* prime testing routine */ /* */ /************************************************************************/ prime_test(number) int number[]; { /* start of prime_test */ int temp[MPDIM],temp1[MPDIM],base[MPDIM],q[MPDIM],nminus1[MPDIM], quo[MPDIM],k,x,j,i,limit; double mplog( int number[]); /* if We assume the generalized Riemann Hypothesis then we can do */ /* a very fast tes */ x = 101; /* base for exponentiation */ mpcopy(number,temp); /* make temporary copy */ SIZE(base) = 2; FIRST(base) = x; /* use x as base for Fermat test*/ if (LASTDIGIT(number) % 2 == 0) return(1); /* make sure it's odd */ /* if (LASTDIGIT(number)<2 && SIZE(number)==2) return(1); /* check for 0 or 1 */ if (SIZE(number)==2) { /* special code for small numbers */ k = LASTDIGIT(number)<2; if (k<2) return(1); /* check for 0 or 1 */ if (k<8) return(0); /* check for 3, 5, 7 */ for (i=3; i<50000; i++) { if (k%i == 0) return(1); if (i*i > k) return(0); } } temp[1]--; /* use N-1 as the exponent */ mpcopy(temp,nminus1); /* make copy of N-1 */ mpower(base,temp,number,temp1); /* base^(N-1) MOD N */ /* check the residue: if it isn't 1 the number is definitely */ /* composite. Otherwise proceed with the test */ if (SIZE(temp1) > 2 || LASTDIGIT(temp1) != 1) return(1); /* start by removing largest possible power of 2 from N-1 */ /* compute k and q for N = 1 + q*2^k */ k = 0; while (LASTDIGIT(temp) % 2 == 0) { div_single(temp,2,temp1); mpcopy(temp1,temp); k++; } mpcopy(temp,q); /* now we try to find a primitive root of N. Once we do compute */ /* x^((N-1)/p) until = -1 MOD N. */ /* We are guaranteed to find a primitive root quickly because */ /* of the Riemann Hypothesis. */ limit = (int)mplog(number); /* need only limit */ /* iterations, maximum */ for (i=0; i SQRT_RAD && mod_mult(i,i,p) == N) return(i); } return(0); /* no solution */ } /* end of modsqrt */ /************************************************************************/ /* */ /* routine to compute base^exp MOD mod for MP integer base */ /* and single precision exp and mod. */ /* */ /************************************************************************/ spower(base,exp,mod) int base[],exp,mod; { /* start of spower */ int junk[MPDIM]; int bits[MPBITS+1]; register int i,j,base0,base1; base1 = div_single(base,mod,junk); base0 = base1; i = 0; for (j=1; j<=MPBITS; j++) /* find binary representation of exp */ { i++; bits[i] = exp & 1; /* exp MOD 2 */ exp = exp/2; if (exp == 0) { i--; break; } } for (; i>0; i--) { if (base1 <= SQRT_RAD) base1 = base1*base1 % mod; else base1 = mod_mult(base1,base1,mod); if (bits[i] != 0) { if (base1 <= SQRT_RAD && base0 <= SQRT_RAD) base1 = base0*base1 % mod; else base1 = mod_mult(base1,base0,mod); } } return(base1); } /* end of spower */ /************************************************************************/ /* */ /* routine to compute base^exp MOD mod for SP integer base */ /* and single precision exp and mod. */ /* */ /************************************************************************/ fastpow(base,exp,mod) int base,exp,mod; { /* start of fastpow */ int junk[MPDIM]; int bits[MPBITS+1]; register int i,j,base0,base1; base1 = base % mod; base0 = base1; i = 0; for (j=1; j<=MPBITS; j++) /* find binary representation of exp */ { i++; bits[i] = exp & 1; /* exp MOD 2 */ exp = exp/2; if (exp == 0) { i--; break; } } for (; i>0; i--) { if (base1 <= SQRT_RAD) base1 = base1*base1 % mod; else base1 = mod_mult(base1,base1,mod); if (bits[i] != 0) { if (base1 <= SQRT_RAD && base0 <= SQRT_RAD) base1 = base0*base1 % mod; else base1 = mod_mult(base1,base0,mod); } } return(base1); } /* end of fastpow */ /************************************************************************/ /* */ /* returns Multiplicative inverse of a mod modulus */ /* */ /************************************************************************/ single_modinv(a,modulus) int a,modulus; { /* start of single_modinv */ register int ps,ps1,ps2=1,parity,dividend,divisor,rem,q; q = modulus/a; rem = modulus - a*q; dividend = a; divisor = rem; ps1 = q; parity = 0; while (rem != 0) { q = 1; rem = dividend - divisor; if (rem >= divisor) { q = 2; rem -= divisor; if (rem >= divisor) { q = 3; rem -= divisor; if (rem >= divisor) { q = 4; rem -= divisor; if (rem >= divisor) { q = dividend/divisor; rem = dividend - q*divisor; } } } } ps = q*ps1 + ps2; ps2 = ps1; ps1 = ps; dividend = divisor; divisor = rem; parity = 1 - parity; } if (parity == 0) return(ps2); else return(modulus-ps2); } /* end of single_modinv */ /************************************************************************/ /* */ /* routine to return the next prime after N */ /* */ /************************************************************************/ next_prime(N) int N; { /* start of next_prime */ register int p,q,r; if (N == 2) return(3); if (N == 3) return(5); p = N; while (1) { p += 2; if ((p % 3) == 0) continue; if ((p % 5) == 0) continue; q = 5; r = (int)sqrt((double)p) + 1; while (q <= r) { q += 2; if (p % q == 0) goto nextp; q += 4; if (p % q == 0) goto nextp; } return(p); nextp:; } } /* end of next_prime */ double mplog(int num[MPDIM]) { if (SIZE(num) <= 2) { return log((double)FIRST (num)); } else { return (double)(SIZE(num)-3)*log((double)RADIX) + log( ((double)FIRST (num))*(double)RADIX + (double)(num[SIZE(num)-2]) ); } }