/* * * KerN describe in C. * [Bepaul] 18 Juin 1988 * */ /* * Description of types and constants. * * Several conventions are used in the commentary: * A "BigNum" is the name for an infinite-precision number. * Capital letters (e.g., "N") are used to refer to the value of BigNums. * The notation "N[d]" is used to refer to a particular digit of N. * The notation "Size(N)" refers to the number of digits in N, * which is typically passed to the subroutine as "nl". * The notation "Length(N)" refers to the number of digits in N, * not including any leading zeros. * The word "Base" is used for the number 2 ** BN_DIGIT_SIZE, where * BN_DIGIT_SIZE is the number of bits in a single BigNum digit. * The expression "BBase(N)" is used for Base ** NumDigits(N). * The term "leading zeros" refers to any zeros before the most * significant digit of a number. */ #include "bignum.h" #include "bignumintern.h" /* * Creation and access to type and length fields. */ extern char *malloc(); /* Allocates a BigNum structure and returns a pointer to it */ BigNum BnAlloc(size) int size; { register BigNum n; n = (BigNum) (malloc(sizeof(struct BigNumHeader) + size * sizeof(BigNumDigit)) + sizeof(struct BigNumHeader)); BN_LENGTH(n) = size; return(n); } /* Allocates a BigNum, inserts its Type, and returns a pointer to it */ BigNum BnCreate(type, size) BigNumType type; int size; { register BigNum n; n = BnAlloc(size); BN_TYPE(n) = type; BnSetToZero(n, 0, size); return(n); } /* Frees a BigNum structure */ BnFree(n) BigNum n; { return(free(((struct BigNumHeader *) n) - 1)); } /* Returns the BigNum's Type */ BigNumType BnGetType(n) BigNum n; { return(BN_TYPE(n)); } /* Sets the BigNum's Type */ BnSetType(n, type) BigNum n; BigNumType type; { BN_TYPE(n) = type; } /* Returns the number of digits allocated for the BigNum */ BnGetSize(n) BigNum n; { return(BN_LENGTH(n)); } /* * non arithmetic access to digits */ /* Sets all the specified digits of the BigNum to 0 */ BnSetToZero(n, nd, nl) BigNum n; int nd, nl; { register BigNum rn = &n[nd]; register int rnl = nl; while(rnl) { rnl--; *(rn++) = 0; } } /* Copies N => M */ BnAssign(m, md, n, nd, nl) register BigNum m, n; register int md, nd, nl; { if((n != m) || (md <= nd) || (nd + nl <= md)) { n = &n[nd]; m = &m[md]; while(nl != 0) { nl--; *m++ = *n++; } } else { n = &n[nd + nl]; m = &m[md + nl]; while(nl != 0) { nl--; *--m = *--n; } } } /* Sets a single digit of N to the passed value */ BnSetDigit(n, nd, digit) BigNum n; int nd, digit; { n[nd] = digit; } /* Returns a single digit of N, if the digit can be represented in just * BN_WORD_SIZE bits. */ BnGetDigit(n, nd) register BigNum n; int nd; { return(n[nd]); } /* Returns the number of digits of N, not counting leading zeros */ BnNumDigits(n, nd, nl) register BigNum n; register int nd, nl; { n = &n[nd + nl]; while((nl != 0) && (*(--n) == 0)) nl--; if(nl == 0) return(1); return(nl); } /* Returns the number of leading zero bits in a digit of N */ BnNumLeadingZeroBitsInDigit(n, nd) BigNum n; int nd; { int register p = 0; register BigNumDigit c = n[nd], mask = 1 << (BN_DIGIT_SIZE - 1); if (c == 0) return(BN_DIGIT_SIZE); while((c & mask) == 0) { p++; mask >>= 1; } return(p); } /* * Predicates on one digit. */ /* Returns TRUE iff the digit can be represented in just BN_WORD_SIZE bits */ Boolean BnDoesDigitFitInWord (n, nd) BigNum n; int nd; { /* The C compiler must evaluate the predicate at compile time */ if(BN_DIGIT_SIZE > BN_WORD_SIZE) { if(n[nd] >= 1 << BN_WORD_SIZE) return(0); else return(1); } else { return(1); } } /* Returns TRUE iff N[d] = 0 */ Boolean BnIsDigitZero(n, nd) BigNum n; int nd; { return((n[nd] == 0)); } /* Returns TRUE iff Base/2 <= N[d] < Base; i.e., if N[d]'s leading bit is 1 */ Boolean BnIsDigitNormalized(n, nd) BigNum n; int nd; { if(n[nd] & (1 << (BN_DIGIT_SIZE - 1))) return(1); else return(0); } /* Returns TRUE iff N[d] is odd */ Boolean BnIsDigitOdd(n, nd) BigNum n; int nd; { return(n[nd] & 1); } /* Returns 1 if M[d] > N[d], -1 if M[d] < N[d], 0 otherwise. */ Sign BnCompareDigits(m, md, n, nd) BigNum m, n; int md, nd; { register BigNumDigit cm = m[md]; register BigNumDigit cn = n[nd]; if(cm < cn) return(-1); if(cm == cn) return(0); return(1); } /* * Logical operations. */ /* Performs the computation BBase(N) - N - 1 => N */ BnComplement(n, nd, nl) register BigNum n; register int nd, nl; { n = &n[nd]; while(nl != 0) { nl--; *(n++) ^= -1; } } /* Performs the logical computation M And N => M */ BnAndDigits(m, md, n, nd) BigNum m, n; int md, nd; { m[md] &= n[nd]; } /* Performs the logical computation M Or N => M */ BnOrDigits(m, md, n, nd) BigNum m, n; int md, nd; { m[md] |= n[nd]; } /* Performs the logical computation M Xor N => M */ BnXorDigits(m, md, n, nd) BigNum m, n; int md, nd; { m[md] ^= n[nd]; } /* * Shift operations */ /* Shifts M left by "nbits", filling with 0s. * The leftmost "nbits" of M replace the digit N[d]. * Assumes 0 <= nbits < BN_DIGIT_SIZE. */ BnShiftLeft(m, md, ml, n, nd, nbits) register BigNum m; register int ml, nbits; BigNum n; int md, nd; { if(nbits == 0) { n[nd] = 0; } else { register int rnbits = BN_DIGIT_SIZE - nbits; register BigNumDigit res = 0, save; m = &m[md]; while(ml != 0) { ml--; save = *m; *m++ = (save << nbits) | res; res = save >> rnbits; } n[nd] = res; } } /* Shifts M right by "nbits", filling with 0s. * The rightmost "nbits" of M replace the digit N[d]. * Assumes 0 <= nbits < BN_DIGIT_SIZE. */ BnShiftRight(m, md, ml, n, nd, nbits) register BigNum m; register int ml, nbits; BigNum n; int md, nd; { if(nbits == 0) { n[nd] = 0; } else { register int lnbits = BN_DIGIT_SIZE - nbits; register BigNumDigit res = 0, save; m = &m[md + ml]; while(ml != 0) { ml--; save = *(--m); *m = (save >> nbits) | res; res = save << lnbits; } n[nd] = res; } } /* * Additions. */ /* Performs the sum N + CarryIn => N. Returns the CarryOut. */ BigNumCarry BnAddCarry(n, nd, nl, carryin) register BigNum n; register int nd, nl; BigNumCarry carryin; { if(carryin == 0) return(0); if(nl == 0) return(1); n = &n[nd]; while((nl != 0) && !(++(*n++))) nl--; if(nl != 0) return(0); return(1); } /* Performs the sum M + N + CarryIn => M. Returns the CarryOut. * Assumes Size(M) >= Size(N). */ BigNumCarry BnAdd(m, md, ml, n, nd, nl, carryin) register BigNum m, n; register int ml, nl; int md, nd; BigNumCarry carryin; { register BigNumProduct d = carryin; m = &m[md]; n = &n[nd]; ml -= nl; while(nl != 0) { nl--; if(sizeof(BigNumProduct) > sizeof(BigNumDigit)) { d += *m + *(n++); *(m++) = d; d >>= BN_DIGIT_SIZE; } else { register BigNumProduct save; save = *m; d += save; if(d < save) { *(m++) = *(n++); d = 1; } else { save = *(n++); d += save; *(m++) = d; d = (d < save) ? 1 : 0; } } } if(d == 0) return(0); if(ml == 0) return(1); while((ml != 0) && !(++(*m++))) ml--; if(ml != 0) return(0); return(1); } /* * Subtraction. */ /* Performs the difference N + CarryIn - 1 => N. Returns the CarryOut. */ BigNumCarry BnSubtractBorrow(n, nd, nl, carryin) register BigNum n; register int nd, nl; BigNumCarry carryin; { if(carryin == 1) return(1); if(nl == 0) return(0); n = &n[nd]; while((nl != 0) && !((*n++)--)) nl--; if(nl != 0) return(1); return(0); } /* Performs the difference M - N + CarryIn - 1 => M. Returns the CarryOut. * Assumes Size(M) >= Size(N). */ BigNumCarry BnSubtract(m, md, ml, n, nd, nl, carryin) register BigNum m, n; register int ml, nl; int md, nd; BigNumCarry carryin; { register BigNumProduct d = carryin; ml -= nl; m = &m[md]; n = &n[nd]; while(nl != 0) { nl--; if(sizeof(BigNumProduct) > sizeof(BigNumDigit)) { register BigNumDigit invn; invn = *(n++) ^ -1; d += *m + invn; *(m++) = d; d >>= BN_DIGIT_SIZE; } else { register BigNumProduct save; register BigNumDigit invn; save = *m; invn = *(n++) ^ -1; d += save; if(d < save) { *(m++) = invn; d = 1; } else { d += invn; *(m++) = d; d = (d < invn) ? 1 : 0; } } } if(d == 1) return(1); if(ml == 0) return(0); while((ml != 0) && !((*m++)--)) ml--; if(ml != 0) return(1); return(0); } /* * Multiplication. */ /* Performs the product: * Q = P + M * N[d] * BB = BBase(P) * Q mod BB => P * Q div BB => CarryOut * Returns the CarryOut. Assumes Size(P) >= Size(M) + 1. */ #define LOW(x) (x & ((1 << (BN_DIGIT_SIZE / 2)) - 1)) #define HIGHT(x) (x >> (BN_DIGIT_SIZE / 2)) #define L2H(x) (x << (BN_DIGIT_SIZE / 2)) BigNumCarry BnMultiplyDigit(p, pd, pl, m, md, ml, n, nd) register BigNum p, m; register int pl, ml; BigNum n; int pd, md, nd; { register BigNumDigit c = n[nd]; register BigNumProduct d = 0; if(c == 0) return(0); if(c == 1) return(BnAdd(p, pd, pl, m, md, ml, 0)); p = &p[pd]; m = &m[md]; pl -= ml; while(ml != 0) { ml--; if(sizeof(BigNumProduct) > sizeof(BigNumDigit)) { d += *p + (c * (*(m++))); *(p++) = d; d >>= BN_DIGIT_SIZE; } else { BigNumDigit save, Lm, Hm, Lc, Hc, X0, X1, X2, X3; save = *(m++); Lc = LOW(c); Hc = HIGHT(c); Lm = LOW(save); Hm = HIGHT(save); X0 = Lc * Lm; X1 = Lc * Hm; X2 = Hc * Lm; X3 = Hc * Hm; d += X0; if(d < X0) X3++; d += L2H(X1); if(d < L2H(X1)) X3++; d += L2H(X2); if(d < L2H(X2)) X3++; d += *p; if(d < *p) X3++; *(p++) = d; d = X3 + HIGHT(X1) + HIGHT(X2); } } if(sizeof(BigNumProduct) > sizeof(BigNumDigit)) { while(pl != 0) { pl--; d += *p; *(p++) = d; d >>= BN_DIGIT_SIZE; } return(d); } else { BigNumDigit save; save = *p; d += save; *(p++) = d; if(d >= save) return(0); pl--; while((pl != 0) && !(++(*p++))) pl--; if(pl != 0) return(0); return(1); } } /* * Division. */ /* Performs the quotient: * N div D[d] => Q * N mod D[d] => R * Assumes leading digit of N < D[d], and D[d] > 0. */ extern double pow(); BnDivideDigit(q, qd, r, rd, n, nd, nl, d, dd) register BigNum q, n; BigNum r, d; register int nl; int qd, rd, nd, dd; { register BigNumDigit c = d[dd]; register BigNumProduct quad; n = &n[nd + nl]; nl--; q = &q[qd + nl]; quad = *(--n); while(nl != 0) { nl--; if(sizeof(BigNumProduct) > sizeof(BigNumDigit)) { quad = (quad << BN_DIGIT_SIZE) | *(--n); *(--q) = quad / c; quad = quad % c; } else { printf("Not yet implemented\n"); exit(0); } } r[rd] = quad; }