markb@giga.slc.unisys.com (Mark Baranowski) (08/22/90)
echo x - README sed 's/^X//' >README <<'*-*-END-of-README-*-*' XOverview: X X This library is a collection of routines that allows you to X add, subtract, multiply, and divide arbitrarily long integers X (BigNums). This library also contains routines for converting X ascii strings to BigNums, converting BigNums to ascii strings, X and comparing two BigNums against each other. X X XFeatures: X X Input/output, multiplication, division, addition, subtraction, X and comparison (i.e. <, <=, >, >=, !=, ==). X X Arbitrarily long integers are obtainable by adjusting the X constant BN_SIZE in the file BN.h to the number of 32-bit X chunks desired and then recompiling. This size may be from 1 X up to any desired maximum. X X Overflow is detected for multiplication, addition, and X subtraction. The global variable BN_overflow is set to TRUE X if an overflow occurs and remains TRUE until reset by the X user. It is up to the user whether or not to ignore X BN_overflow. X X The remainder is available immediately following any division X via the global variable BN_remainder. This variable is valid X only until the next BigNum operation. X X All arguments are passed by value and the result is returned X by value. Although this slightly increases the overhead, the X user interface is simplified. X X XSynopsis: X X #include "BN.h" X X X /* Remainder following division: */ X BN BN_remainder; /* Valid only until next operation */ X X X /* Overflow condition following add, sub, or mult: */ X Boolean BN_overflow; /* Valid until reset by the user */ X X X /* Convert an ascii string to a BigNum. X User is notified and BN_overflow is set if any overflow occurs. */ X BN atoBN(str) X char *str; X X X /* Convert a BigNum to an ascii string. */ X int BNtoa(bignum,str) X BN bignum; X char *str; X X X /* Convert a 32 bit integer to a BigNum. */ X BN itoBN(num) X int num; X X X /* Convert a BigNum to a 32 bit integer. X User is warned if any truncation occurs. */ X int BNtoi(bignum) X BN bignum; X X X /* Multiply two BigNums. X BN_overflow is set if any overflow occurs. */ X BN BN_mult(multiplicand,multiplier) X BN multiplicand,multiplier; X X X /* Divide the dividend by the divisor. X The User is notified and program terminated if division by X 0 occurs. The global variable BN_remainder contains the X remainder of the most recent division. */ X BN BN_div(dividend,divisor) X BN dividend,divisor; X X X /* Subtract arg2 from arg1. X BN_overflow is set if any overflow occurs. */ X BN BN_sub(arg1,arg2) X BN arg1,arg2; X X X /* Add two BigNums. X BN_overflow is set if any overflow occurs. */ X BN BN_add(arg1,arg2) X BN arg1,arg2; X X X /* Negate a BigNum. */ X BN BN_neg(arg) X BN arg; X X X /* Test if arg1 is greater than or equal to arg2. */ X Boolean BN_ge(arg1,arg2) X BN arg1,arg2; X X X /* Test if arg1 is less than or equal to arg2. */ X Boolean BN_le(arg1,arg2) X BN arg1,arg2; X X X /* Test if arg1 is greater than arg2. */ X Boolean BN_gt(arg1,arg2) X BN arg1,arg2; X X X /* Test if arg1 is less than arg2. */ X Boolean BN_lt(arg1,arg2) X BN arg1,arg2; X X X /* Test if arg1 is not equal to arg2. */ X Boolean BN_ne(arg1,arg2) X BN arg1,arg2; X X X /* Test if arg1 is equal to arg2. */ X Boolean BN_eq(arg1,arg2) X BN arg1,arg2; X X XExample: X X /**************************/ X /* Using 32-bit integers: */ X X main() X { X int i,j,n; X X j = 10000000000000000; X X /* Find first factorial greater than j: */ X i = 1; X n = 1; X while (i <= j) X { X i = i * n; X n++; X } X printf("%d\n", i); X } X X X /******************/ X /* Using BigNums: */ X X #include "BN.h" X main() X { X BN i,j,n; X char str[11*BN_SIZE]; X X j = atoBN("10000000000000000"); X X /* Find first factorial greater than j: */ X i = itoBN(1); X n = itoBN(1); X while (BN_le (i, j)) X { X i = BN_mult(i, n); X n = BN_add (n, itoBN(1)); X X /* Check for overflow: */ X if (BN_overflow) X { X printf("BigNum overflow\n"); X exit (1); X } X } X X BNtoa(i,str); X printf("%s\n", str); X } X X X XTo compile: X X cc yourprogram.c BN.c X X XRanges: X X Different ranges can be obtained by changing the constant X BN_SIZE in "BN.h". The following table gives the maximum X power of 10 and the maximum factorial obtainable for a given X BN_SIZE: X X X BN_SIZE 10^N N! X ------- ---- -- X 1 10 12 X 2 19 20 X 5 48 39 X 10 97 67 X 20 193 116 X 50 482 245 *-*-END-of-README-*-* echo x - BN.c sed 's/^X//' >BN.c <<'*-*-END-of-BN.c-*-*' X/****************************************************************************** X BN.c -- A library for performing arithmetic operations on arbitrarily X long integers. X X Author: Mark Baranowski (markb@signus.utah.edu) X X Version: 1.0 X Date: Aug. 20, 1990 X X This program is provided "as is" without any warranty of its X correctness or of its fitness for a particular purpose. You may X freely distribute and modify this program as long as this header X remains intact. X X *****************************************************************************/ X X#include "BN.h" X Xtypedef void Nil; X#define Local static X X/* Globally accessable remainder following division: */ XBN BN_remainder; /* Valid only until next operation */ X X/* Globally accessable overflow condition following add, sub, or mult: */ XBoolean BN_overflow; /* Valid until reset by the user */ X X/* Locally defined functions: */ XLocal Nil lshift1(/* BN *arg */); XLocal Nil rshift1(/* BN *arg */); X X/* Locally defined BigNum Constants: */ XLocal BN BN_0 = { 0, /* 0, 0, ..., 0 */}; XLocal BN BN_1 = { 1, /* 0, 0, ..., 0 */}; XLocal BN BN_10 = {10, /* 0, 0, ..., 0 */}; X X#define MASKSIGN(bignum) (bignum.N[BN_SIZE-1] & 0x80000000) X Xextern void exit(); X X/****************************************************************************** X X atoBN -- Convert an ascii string to a BigNum. X X User is notified and BN_overflow is set if any overflow occurs. X X *****************************************************************************/ X XBN atoBN(str) X char *str; X{ X BN bignum; X Boolean negate; X Boolean save_overflow; X X /* Make sure we don't confuse a pre-existing overflow for an overflow that X might occur here: */ X save_overflow = BN_overflow; X BN_overflow = FALSE; X X /* Build bignum: */ X X if (str[0] == '-') X { X str++; X negate = TRUE; X } X else X { X negate = FALSE; X } X X bignum = BN_0; X while (*str) X { X if (*str < '0' || *str > '9') X { X (void)printf ("atoBN: non-numeric character in constant\n"); X exit (1); X } X bignum = BN_add(BN_mult(BN_10,bignum), itoBN(*str++ - '0')); X } X if (negate) bignum = BN_neg(bignum); X X /* Notify user of any overflows and restore overflow condition if it X existed previously: */ X if (BN_overflow) (void)printf("atoBN: string overflowed bignum\n"); X BN_overflow = BN_overflow || save_overflow; X X return (bignum); X} X X X/****************************************************************************** X X BNtoa -- Convert a BigNum to an ascii string. X X *****************************************************************************/ X Xint BNtoa(bignum,str) X BN bignum; X char *str; X{ X register int i,j,k; X register char tmp; X X /* If negative then output '-' and change to a positive number: */ X if (MASKSIGN(bignum)) X { X *str++ = '-'; X bignum = BN_neg(bignum); X } X X /* Strip off numbers in reverse order: */ X i = 0; X do X { X bignum = BN_div(bignum, BN_10); X str[i++] = '0' + BN_remainder.N[0]; X } while (BN_gt(bignum, BN_0)); X str[i] = '\0'; X X /* Reverse the order of output string: */ X j = 0; X k = i - 1; X while (j < k) X { X tmp = str[j]; X str[j++] = str[k]; X str[k--] = tmp; X } X X /* Return the string size: */ X return (i); X} X X X/****************************************************************************** X X itoBN -- Convert a 32 bit integer to a BigNum. X X *****************************************************************************/ X XBN itoBN(num) X int num; X{ X BN bignum; X int i, filler; X X /* Assign num to first 32 bits of bignum and add the appropriate X filler for the remaining bits depending on the sign: */ X X bignum.N[0] = num; X if (num < 0) X filler = -1; X else X filler = 0; X X for (i = 1; i < BN_SIZE; i++) X bignum.N[i] = filler; X X return (bignum); X} X X X/****************************************************************************** X X BNtoi -- Convert a BigNum to a 32 bit integer. X X User is warned if any truncation occurs. X X *****************************************************************************/ X Xint BNtoi(bignum) X BN bignum; X{ X# define BN_abs(bn) (MASKSIGN(bn) ? BN_neg(bn) : bn) X X if (BN_gt (BN_abs(bignum), itoBN(0x7fffffff))) X (void)printf ("BNtoi: integer truncated\n"); X X /* Return the lowest 32 bits of a bignum: */ X return ((int)bignum.N[0]); X} X X X/****************************************************************************** X X BN_mult -- Multiply two BigNums. X X BN_overflow is set if any overflow occurs. X X *****************************************************************************/ X XBN BN_mult(multiplicand,multiplier) X BN multiplicand,multiplier; X{ X BN result; X Boolean s1,s2; X register Boolean danger; X register int i,j; X X /* Note whether either argument is negative and make sure both of them X are positive: */ X s1 = MASKSIGN(multiplicand); X s2 = MASKSIGN(multiplier); X if (s1) multiplicand = BN_neg(multiplicand); X if (s2) multiplier = BN_neg(multiplier); X X /* Perform multiplication by shifting the multiplicand and performing an X addition whenever a '1' appears in the multiplier: */ X danger = FALSE; X result = BN_0; X for (i = 0; i < BN_SIZE; i++) X { X for (j = 0; j < 32; j++) X { X if (BN_eq (multiplier, BN_0)) break; X if (multiplier.N[0] & 1) X { X /* BN_add also detects certain overflows. */ X result = BN_add(result,multiplicand); X /* Overflow occurs either by addition or by adding the multiplicand X after shifting it out of range. */ X BN_overflow = BN_overflow || danger; X } X lshift1(&multiplicand); X rshift1(&multiplier); X danger = danger || (MASKSIGN(multiplicand) ? TRUE : FALSE); X } X } X X /* Ensure the result has the appropriate sign: */ X if (s1 != s2) result = BN_neg(result); X X return (result); X} X X X/****************************************************************************** X X BN_div -- Divide the dividend by the divisor. X X The User is notified and program terminated if division by 0 occurs. X The global variable BN_remainder contains the remainder of the most X recent division. X X *****************************************************************************/ X XBN BN_div(dividend,divisor) X BN dividend,divisor; X{ X BN result,power; X register int nshifts; X Boolean s1,s2; X X if (BN_eq(divisor, BN_0)) X { X (void)printf("BN_div: division by 0\n"); X exit (1); X } X X /* Note whether either argument is negative and make sure both of them X are positive: */ X s1 = MASKSIGN(dividend); X s2 = MASKSIGN(divisor); X if (s1) dividend = BN_neg(dividend); X if (s2) divisor = BN_neg(divisor); X X /* Perform division by shifting the divisor up by powers of two until X it is greater than the dividend. Next shift the divisor down in X powers of two--each time the dividend can be divided by the divisor X tally the current power of two and reduce the dividend accordingly: */ X power = BN_1; X nshifts = 0; X while (MASKSIGN(divisor) == 0 && BN_ge(dividend,divisor)) X { X lshift1(&divisor); X lshift1(&power); X nshifts++; X } X result = BN_0; X while (nshifts > 0) X { X rshift1(&divisor); X rshift1(&power); X nshifts--; X if (BN_ge(dividend,divisor)) X { X result = BN_add(result,power); X dividend = BN_sub(dividend,divisor); X } X } X X /* Ensure the result has the appropriate sign and make the remainder X globally available: */ X if (s1 != s2) result = BN_neg(result); X BN_remainder = dividend; X X return (result); X} X X X/****************************************************************************** X X BN_sub -- Subtract arg2 from arg1. X X BN_overflow is set if any overflow occurs. X X *****************************************************************************/ X XBN BN_sub(arg1,arg2) X BN arg1,arg2; X{ X /* Perform subtraction by negating the subtrahend and then adding X both numbers: */ X return (BN_add(arg1,BN_neg(arg2))); X} X X X/****************************************************************************** X X BN_add -- Add two BigNums. X X BN_overflow is set if any overflow occurs. X X *****************************************************************************/ X XBN BN_add(arg1,arg2) X BN arg1,arg2; X{ X BN result; X register int i; X register int ssum; X register int a1,a2,carry; X int criterion; X X /* Perform addition in 32 bit chunks by first striping off the sign X bits of each chunk, adding the numbers, manually appending the X sign bit, and then generating a carry if necessary: */ X X carry = 0; X for (i = 0; i < BN_SIZE; i++) X { X a1 = arg1.N[i] & 0x7fffffff; X a2 = arg2.N[i] & 0x7fffffff; X result.N[i] = a1 + a2 + carry; X X ssum = 0; X if (arg1.N[i] & 0x80000000) ssum++; X if (arg2.N[i] & 0x80000000) ssum++; X if (result.N[i] & 0x80000000) ssum++; X X if (ssum == 0) X { X carry = 0; X } X else if (ssum == 1) X { X result.N[i] |= 0x80000000; X carry = 0; X } X else if (ssum == 2) X { X result.N[i] &= 0x7fffffff; X carry = 1; X } X else /* (ssum == 3) */ X { X carry = 1; X } X } X X /* Check for overflow. The overflow "criterion" is always an even X number unless an overflow occurs: */ X criterion = ((MASKSIGN(arg1) ? 1 : 0) + X (MASKSIGN(arg2) ? 1 : 0) + X (MASKSIGN(result) ? 1 : 0) + X carry); X BN_overflow = BN_overflow || (criterion == 1 || criterion == 3); X X return (result); X} X X X/****************************************************************************** X X BN_neg -- Negate a BigNum. X X *****************************************************************************/ X XBN BN_neg(arg) X BN arg; X{ X register int i; X X /* Perform negation a la two's complement (i.e. perform a bitwise X complement and then add 1): */ X X for (i = 0; i < BN_SIZE; i++) X arg.N[i] = ~arg.N[i]; X X return (BN_add(arg,BN_1)); X} X X X/****************************************************************************** X X BN_ge -- Test if arg1 is greater than or equal to arg2. X X *****************************************************************************/ X XBoolean BN_ge(arg1,arg2) X BN arg1,arg2; X{ X return (!BN_lt(arg1,arg2)); X} X X X/****************************************************************************** X X BN_le -- Test if arg1 is less than or equal to arg2. X X *****************************************************************************/ X XBoolean BN_le(arg1,arg2) X BN arg1,arg2; X{ X return (BN_lt(arg1,arg2) || BN_eq(arg1,arg2)); X} X X X/****************************************************************************** X X BN_gt -- Test if arg1 is greater than arg2. X X *****************************************************************************/ X XBoolean BN_gt(arg1,arg2) X BN arg1,arg2; X{ X return (!BN_lt(arg1,arg2) && !BN_eq(arg1,arg2)); X} X X X/****************************************************************************** X X BN_lt -- Test if arg1 is less than arg2. X X *****************************************************************************/ X XBoolean BN_lt(arg1,arg2) X BN arg1,arg2; X{ X Boolean s1,s2; X register int i; X X /* Test if arg1 is less than arg2 by first comparing signs. If both X signs are different, then the rest is easy. If both signs are the X same then begin looking from the high order bits down to the low X order bits until a difference is detected: */ X X s1 = (MASKSIGN(arg1) ? TRUE : FALSE); X s2 = (MASKSIGN(arg2) ? TRUE : FALSE); X X if (s1 && !s2) X return (TRUE); X else if (!s1 && s2) X return (FALSE); X else X for (i = BN_SIZE-1; i >= 0; i--) X if (arg1.N[i] != arg2.N[i]) X { X if (arg1.N[i] < arg2.N[i]) X return (TRUE); X else /* (arg1.N[i] > arg2.N[i]) */ X return (FALSE); X } X return (FALSE); X} X X X/****************************************************************************** X X BN_ne -- Test if arg1 is not equal to arg2. X X *****************************************************************************/ X XBoolean BN_ne(arg1,arg2) X BN arg1,arg2; X{ X return(!BN_eq(arg1,arg2)); X} X X X/****************************************************************************** X X BN_eq -- Test if arg1 is equal to arg2. X X *****************************************************************************/ X XBoolean BN_eq(arg1,arg2) X BN arg1,arg2; X{ X register int i; X X for (i = 0; i < BN_SIZE; i++) X if (arg1.N[i] != arg2.N[i]) return (FALSE); X X return (TRUE); X} X X X/****************************************************************************** X X lshift1 -- Perform a logical left shift one bit possition. X X *****************************************************************************/ X XLocal Nil lshift1(arg) X register BN *arg; X{ X register int i; X X for (i = BN_SIZE - 1; i > 0; i--) X { X arg->N[i] = arg->N[i] << 1; X if (arg->N[i-1] & 0x80000000) X arg->N[i] |= 1; X } X X arg->N[0] = arg->N[0] << 1; X X return; X} X X X/****************************************************************************** X X rshift1 -- Perform a logical right shift one bit possition. X X *****************************************************************************/ X XLocal Nil rshift1(arg) X register BN *arg; X{ X register int i; X X for (i = 0; i < BN_SIZE - 1; i++) X { X arg->N[i] = arg->N[i] >> 1; X if (arg->N[i+1] & 1) X arg->N[i] |= 0x80000000; X } X X arg->N[BN_SIZE-1] = arg->N[BN_SIZE-1] >> 1; X X return; X} *-*-END-of-BN.c-*-* echo x - BN.h sed 's/^X//' >BN.h <<'*-*-END-of-BN.h-*-*' X/****************************************************************************** X BN.h -- A library for performing arithmetic operations on arbitrarily X long integers. X X Author: Mark Baranowski (markb@signus.utah.edu) X X Version: 1.0 X Date: Aug. 20, 1990 X X This program is provided "as is" without any warranty of its X correctness or of its fitness for a particular purpose. You may X freely distribute and modify this program as long as this header X remains intact. X X *****************************************************************************/ X X/* Number of 32 bit chunks used by BigNum--BN_SIZE must be at least 1 and X can be any desired maximum: */ X#define BN_SIZE 5 X X/* BN data structure: */ Xstruct BN X{ X unsigned int N[BN_SIZE]; X}; Xtypedef struct BN BN; X Xtypedef int Boolean; X#define TRUE 1 X#define FALSE 0 X X/* BigNum remainder following division: */ Xextern BN BN_remainder; /* Valid only until next operation */ X X/* BigNum overflow condition following add, subtract, or multiply: */ Xextern Boolean BN_overflow; /* Valid until reset by the user */ X X/* Globally defined funtions: */ Xextern BN atoBN (/* char *str */); Xextern int BNtoa (/* BN bignum, char *str */); Xextern BN itoBN (/* int num */); Xextern int BNtoi (/* BN bignum */); X Xextern BN BN_mult(/* BN arg1, BN arg2 */); Xextern BN BN_div (/* BN arg1, BN arg2 */); Xextern BN BN_sub (/* BN arg1, BN arg2 */); Xextern BN BN_add (/* BN arg1, BN arg2 */); Xextern BN BN_neg (/* BN arg1 */); X Xextern Boolean BN_ge(/* BN arg1, BN arg2 */); Xextern Boolean BN_le(/* BN arg1, BN arg2 */); Xextern Boolean BN_gt(/* BN arg1, BN arg2 */); Xextern Boolean BN_lt(/* BN arg1, BN arg2 */); Xextern Boolean BN_ne(/* BN arg1, BN arg2 */); Xextern Boolean BN_eq(/* BN arg1, BN arg2 */); *-*-END-of-BN.h-*-* exit -- Internet: markb@slc.unisys.com uucp: ...!giga!markb markb@signus.utah.edu Brought to you by the Great State of Utah: Home of Gary Gilmore, Syncrete, Cold Fusion, Mark Hoffman, Sonja Johnson, John Singer, and Sen. Orrin Hatch.