home *** CD-ROM | disk | FTP | other *** search
- /* bignum.c - C routines for bignumbers */
-
- /*
- Wesley Loewer's Big Numbers. (C) 1994, Wesley B. Loewer
-
- The bignumber format is simply a signed integer of variable length. The
- bytes are stored in reverse order (least significant byte first, most
- significant byte last). The sign bit is the highest bit of the most
- significant byte. Negatives are stored in 2's complement form. The byte
- length of the bignumbers must be a multiple of 4 for 386+ operations, and
- a multiple of 2 for 8086/286 and non 80x86 machines.
-
- Some of the arithmetic operations coded here may alter some of the
- operands used. Therefore, take note of the SIDE-EFFECTS listed with each
- procedure. If the value of an operand needs to be retained, just use
- copy_bn() first. This was done for speed sake to avoid unnecessary
- copying. If space is at such a premium that copying it would be
- difficult, some of the operations only change the sign of the value. In
- this case, the original could be optained by calling neg_a_bn().
-
- Most of the bignumber routines operate on true integers. Some of the
- procedures, however, are designed specifically for fixed decimal point
- operations. The results and how the results are interpreted depend on
- where the implied decimal point is located. The routines that depend on
- where the decimal is located are: strtobn(), bntostr(), bntoint(), inttobn(),
- bntofloat(), floattobn(), inv_bn(), div_bn(). The number of bytes
- designated for the integer part may be 1, 2, or 4.
-
-
- BIGNUMBER FORMAT:
- The following is a discription of the bignumber format and associated
- variables. The number is stored in reverse order (Least Significant Byte,
- LSB, stored first in memory, Most Significant Byte, MSB, stored last).
- Each '_' below represents a block of memory used for arithmetic (1 block =
- 4 bytes on 386+, 2 bytes on 286-). All lengths are given in bytes.
-
- LSB MSB
- _ _ _ _ _ _ _ _ _ _ _ _
- n <----------- bnlength ----------->
- intlength ---> <---
-
- bnlength = the length in bytes of the bignumber
- intlength = the number of bytes used to represent the integer part of
- the bignumber. Possible values are 1, 2, or 4. This
- determines the largest number that can be represented by
- the bignumber.
- intlength = 1, max value = 127.99...
- intlength = 2, max value = 32,767.99...
- intlength = 4, max value = 2,147,483,647.99...
-
-
- FULL DOUBLE PRECISION MULTIPLICATION:
- ( full_mult_bn(), full_square_bn() )
-
- The product of two bignumbers, n1 and n2, will be a result, r, which is
- a double wide bignumber. The integer part will also be twice as wide,
- thereby eliminating the possiblity of overflowing the number.
-
- LSB MSB
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
- r <--------------------------- 2*bnlength ----------------------------->
- 2*intlength ---> <---
-
- If this double wide bignumber, r, needs to be converted to a normal,
- single width bignumber, this is easily done with pointer arithmetic. The
- converted value starts at r+shiftfactor (where shiftfactor =
- bnlength-intlength) and continues for bnlength bytes. The lower order
- bytes and the upper integer part of the double wide number can then be
- ignored.
-
- LSB MSB
- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
- r <--------------------------- 2*bnlength ----------------------------->
- 2*intlength ---> <---
- LSB MSB
- r+shiftfactor <---------- bnlength ------------>
- intlength ---> <---
-
-
- PARTIAL PRECISION MULTIPLICATION:
- ( mult_bn(), square_bn() )
-
- In most cases, full double precision multiplication is not necessary. The
- lower order bytes are usually thrown away anyway. The non-"full"
- multiplication routines only calculate rlength bytes in the result. The
- value of rlength must be in the range: 2*bnlength <= rlength < bnlength.
- The amount by which rlength exceeds bnlength accounts for the extra bytes
- that must be multiplied so that the first bnlength bytes are correct.
- These extra bytes are refered to in the code as the "padding," that is:
- rlength=bnlength+padding.
-
- All three of the values, bnlength, rlength, and therefore padding, must be
- multiples of the size of memory blocks being used for arithmetic (2 on
- 8086/286 and 4 on 386+). Typically, the padding is 2*blocksize. In the
- case where bnlength=blocksize, padding can only be blocksize to keep
- rlength from being too big.
-
- The product of two bignumbers, n1 and n2, will then be a result, r, which
- is of length rlength. The integer part will be twice as wide, thereby
- eliminating the possiblity of overflowing the number.
-
- LSB MSB
- _ _ _ _ _ _ _ _ _ _ _ _ _ _
- r <---- rlength = bnlength+padding ------>
- 2*intlength ---> <---
-
- If r needs to be converted to a normal, single width bignumber, this is
- easily done with pointer arithmetic. The converted value starts at
- r+shiftfactor (where shiftfactor = padding-intlength) and continues for
- bnlength bytes. The lower order bytes and the upper integer part of the
- double wide number can then be ignored.
-
- LSB MSB
- _ _ _ _ _ _ _ _ _ _ _ _ _ _
- r <---- rlength = bnlength+padding ------>
- 2*intlength ---> <---
- LSB MSB
- r+shiftfactor <---------- bnlength --------->
- intlength ---> <---
- */
-
- /************************************************************************/
- /* There are three parts to the bignum library: */
- /* */
- /* 1) bignum.c - initialization, general routines, routines that would */
- /* not be speeded up much with assembler. */
- /* */
- /* 2) bignuma.asm - hand coded assembler routines. */
- /* */
- /* 3) bignumc.c - portable C versions of routines in bignuma.asm */
- /* */
- /************************************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <malloc.h>
- #include "prototyp.h"
- #include "bignum.h"
- #ifdef sign
- #undef sign
- #endif
-
- /* globals */
- extern int bnstep, bnlength, intlength, rlength, padding, shiftfactor, decimals;
- extern int bflength, rbflength, bfshiftfactor, bfdecimals;
-
- /* used internally by bignum.c routines */
- extern bn_t bntmp1, bntmp2, bntmp3, bntmp4, bntmp5, bntmp6; /* rlength */
- extern bn_t bntmpcpy1, bntmpcpy2; /* bnlength */
-
- /* used by other routines */
- extern bn_t bn_pi;
- extern bn_t bnxmin, bnxmax, bnymin, bnymax, bnx3rd, bny3rd; /* bnlength */
- extern bn_t bnxdel, bnydel, bnxdel2, bnydel2, bnclosenuff; /* bnlength */
- extern bn_t bntmpsqrx, bntmpsqry, bntmp; /* rlength */
- extern _BNCMPLX bnold, bnnew, bnparm, bnsaved; /* bnlength */
-
- #define LOG10_256 2.4082399653118
- #define LOG_256 5.5451774444795
-
- /*************************************************************************
- * The original bignumber code was written specifically for a Little Endian
- * system (80x86). The following is not particularly efficient, but was
- * simple to incorporate. If speed with a Big Endian machine is critical,
- * the bignumber format could be reversed.
- **************************************************************************/
- #ifdef BIG_ENDIAN /* Big Endian: &variable == &(most significant byte), moterola style */
- U32 access32(BYTE far *addr)
- {
- return addr[0] | ((U32)addr[1]<<8) | ((U32)addr[2]<<16) | ((U32)addr[3]<<24);
- }
-
- U16 access16(BYTE far *addr)
- {
- return (U16)addr[0] | ((U16)addr[1]<<8);
- }
-
- S16 accessS16(S16 far *addr)
- {
- return (S16)((BYTE *)addr)[0] | ((S16)((BYTE *)addr)[1]<<8);
- }
-
- U32 set32(BYTE far *addr, U32 val)
- {
- addr[0] = val&0xff;
- addr[1] = (val>>8)&0xff;
- addr[2] = (val>>16)&0xff;
- addr[3] = (val>>24)&0xff;
- return val;
- }
-
- U16 set16(BYTE far *addr, U16 val)
- {
- addr[0] = val&0xff;
- addr[1] = (val>>8)&0xff;
- return val;
- }
-
- S16 setS16(S16 far *addr, S16 val)
- {
- ((BYTE *)addr)[0] = val&0xff;
- ((BYTE *)addr)[1] = (val>>8)&0xff;
- return val;
- }
- #endif
-
- #if (_MSC_VER >= 700)
- #pragma code_seg ("bignum1_text") /* place following in an overlay */
- #endif
-
- /************************************************************************/
- /* convert_bn -- convert bignum numbers from old to new lengths */
- int convert_bn(bn_t new, bn_t old, int newbnlength, int newintlength,
- int oldbnlength, int oldintlength)
- {
- int savebnlength, saveintlength;
-
- /* save lengths so not dependent on external environment */
- saveintlength = intlength;
- savebnlength = bnlength;
-
- intlength = newintlength;
- bnlength = newbnlength;
- clear_bn(new);
-
- if(newbnlength - newintlength > oldbnlength - oldintlength)
- {
-
- /* This will keep the integer part from overflowing past the array. */
- bnlength = oldbnlength - oldintlength + min(oldintlength, newintlength);
-
- _fmemcpy(new+newbnlength-newintlength-oldbnlength+oldintlength,
- old,bnlength);
- }
- else
- {
- bnlength = newbnlength - newintlength + min(oldintlength, newintlength);
- _fmemcpy(new,old+oldbnlength-oldintlength-newbnlength+newintlength,
- bnlength);
- }
- intlength = saveintlength;
- bnlength = savebnlength;
- return(0);
- }
-
- /********************************************************************/
- /* bn_hexdump() - for debugging, dumps to stdout */
-
- void bn_hexdump(bn_t r)
- {
- int i;
-
- for (i=0; i<bnlength; i++)
- printf("%02X ", r[i]);
- printf("\n");
- return;
- }
-
- /**********************************************************************/
- /* strtobn() - converts a string into a bignumer */
- /* r - pointer to a bignumber */
- /* s - string in the floating point format [-][digits].[digits] */
- /* note: the string may not be empty or have extra space and may */
- /* not use scientific notation (2.3e4). */
-
- bn_t strtobn(bn_t r, char *s)
- {
- unsigned l;
- bn_t onesbyte;
- int signflag=0;
- long longval;
-
- clear_bn(r);
- onesbyte = r + bnlength - intlength;
-
- if (s[0] == '+') /* for + sign */
- {
- s++;
- }
- else if (s[0] == '-') /* for neg sign */
- {
- signflag = 1;
- s++;
- }
-
- if (strchr(s, '.') != NULL) /* is there a decimal point? */
- {
- l = strlen(s) - 1; /* start with the last digit */
- while (s[l] >= '0' && s[l] <= '9') /* while a digit */
- {
- *onesbyte = (BYTE)(s[l--] - '0');
- div_a_bn_int(r, 10);
- }
-
- if (s[l] == '.')
- {
- longval = atol(s);
- switch (intlength)
- { /* only 1, 2, or 4 are allowed */
- case 1:
- *onesbyte = (BYTE)longval;
- break;
- case 2:
- set16(onesbyte, (U16)longval);
- break;
- case 4:
- set32(onesbyte, longval);
- break;
- }
- }
- }
- else
- {
- longval = atol(s);
- switch (intlength)
- { /* only 1, 2, or 4 are allowed */
- case 1:
- *onesbyte = (BYTE)longval;
- break;
- case 2:
- set16(onesbyte, (U16)longval);
- break;
- case 4:
- set32(onesbyte, longval);
- break;
- }
- }
-
-
- if (signflag)
- neg_a_bn(r);
-
- return r;
- }
-
- /********************************************************************/
- /* strlen_needed() - returns string length needed to hold bignumber */
-
- int strlen_needed()
- {
- int length;
-
- /* first space for integer part */
- switch(intlength)
- {
- case 1:
- length = 3; /* max 127 */
- break;
- case 2:
- length = 5; /* max 32767 */
- break;
- case 4:
- length = 10; /* max 2147483647 */
- break;
- }
- length += decimals; /* decimal part */
- length += 2; /* decimal point and sign */
- length += 4; /* null and a little extra for safety */
- return(length);
- }
-
- /********************************************************************/
- /* bntostr() - converts a bignumber into a string */
- /* s - string, must be large enough to hold the number. */
- /* r - bignumber */
- /* will covert to a floating point notation */
- /* SIDE-EFFECT: the bignumber, r, is destroyed. */
- /* Copy it first if necessary. */
-
- char *unsafe_bntostr(char *s, int dec, bn_t r)
- {
- int l=0, d;
- bn_t onesbyte;
- long longval;
-
- if (dec == 0)
- dec = decimals;
- onesbyte = r + bnlength - intlength;
-
- if (is_bn_neg(r))
- {
- neg_a_bn(r);
- *(s++) = '-';
- }
- switch (intlength)
- { /* only 1, 2, or 4 are allowed */
- case 1:
- longval = *onesbyte;
- break;
- case 2:
- longval = access16(onesbyte);
- break;
- case 4:
- longval = access32(onesbyte);
- break;
- }
- ltoa(longval, s, 10);
- l = strlen(s);
- s[l++] = '.';
- for (d=0; d < dec; d++)
- {
- *onesbyte = 0; /* clear out highest byte */
- mult_a_bn_int(r, 10);
- if (is_bn_zero(r))
- break;
- s[l++] = (BYTE)(*onesbyte + '0');
- }
- s[l] = '\0'; /* don't forget nul char */
-
- return s;
- }
-
- #if (_MSC_VER >= 700)
- #pragma code_seg ( ) /* back to normal segment */
- #endif
-
- /*********************************************************************/
- /* b = l */
- /* Converts a long to a bignumber */
- bn_t inttobn(bn_t r, long longval)
- {
- bn_t onesbyte;
-
- clear_bn(r);
- onesbyte = r + bnlength - intlength;
- switch (intlength)
- { /* only 1, 2, or 4 are allowed */
- case 1:
- *onesbyte = (BYTE)longval;
- break;
- case 2:
- set16(onesbyte, (U16)longval);
- break;
- case 4:
- set32(onesbyte, longval);
- break;
- }
- return r;
- }
-
- /*********************************************************************/
- /* l = floor(b), floor rounds down */
- /* Converts the integer part a bignumber to a long */
- long bntoint(bn_t n)
- {
- bn_t onesbyte;
- long longval;
-
- onesbyte = n + bnlength - intlength;
- switch (intlength)
- { /* only 1, 2, or 4 are allowed */
- case 1:
- longval = *onesbyte;
- break;
- case 2:
- longval = access16(onesbyte);
- break;
- case 4:
- longval = access32(onesbyte);
- break;
- }
- return longval;
- }
-
- /*********************************************************************/
- /* b = f */
- /* Converts a double to a bignumber */
- bn_t floattobn(bn_t r, LDBL f)
- {
- #ifndef USE_BIGNUM_C_CODE
- floattobf(bftmp1, f);
- return bftobn(r, bftmp1);
-
- #else
- bn_t onesbyte;
- int i;
- int signflag=0;
-
- clear_bn(r);
- onesbyte = r + bnlength - intlength;
-
- if (f < 0)
- {
- signflag = 1;
- f = -f;
- }
-
- switch (intlength)
- { /* only 1, 2, or 4 are allowed */
- case 1:
- *onesbyte = (BYTE)f;
- break;
- case 2:
- set16(onesbyte, (U16)f);
- break;
- case 4:
- set32(onesbyte, (U32)f);
- break;
- }
-
- f -= (long)f; /* keep only the decimal part */
- for (i = bnlength - intlength - 1; i >= 0 && f != 0.0; i--)
- {
- f *= 256;
- r[i] = (BYTE)f; /* keep use the integer part */
- f -= (BYTE)f; /* now throw away the integer part */
- }
-
- if (signflag)
- neg_a_bn(r);
-
- return r;
- #endif /* USE_BIGNUM_C_CODE */
- }
-
- /********************************************************************/
- /* sign(r) */
- int sign_bn(bn_t n)
- {
- return is_bn_neg(n) ? -1 : is_bn_not_zero(n) ? 1 : 0;
- }
-
- /********************************************************************/
- /* r = |n| */
- bn_t abs_bn(bn_t r, bn_t n)
- {
- copy_bn(r,n);
- if (is_bn_neg(r))
- neg_a_bn(r);
- return r;
- }
-
- /********************************************************************/
- /* r = |r| */
- bn_t abs_a_bn(bn_t r)
- {
- if (is_bn_neg(r))
- neg_a_bn(r);
- return r;
- }
-
- /********************************************************************/
- /* r = 1/n */
- /* uses bntmp1 - bntmp3 - global temp bignumbers */
- /* SIDE-EFFECTS: */
- /* n ends up as |n| Make copy first if necessary. */
- bn_t unsafe_inv_bn(bn_t r, bn_t n)
- {
- int signflag=0, i;
- long maxval;
- LDBL f;
-
- /* use Newton's recursive method for zeroing in on 1/n : r=r(2-rn) */
-
- if (is_bn_neg(n))
- { /* will be a lot easier to deal with just positives */
- signflag = 1;
- neg_a_bn(n);
- }
-
- f = bntofloat(n);
- if (f == 0) /* division by zero */
- {
- max_bn(r);
- return r;
- }
- f = 1/f; /* approximate inverse */
- maxval = (1L << ((intlength<<3)-1)) - 1;
- if (f > maxval) /* check for overflow */
- {
- max_bn(r);
- return r;
- }
- else if (f <= -maxval)
- {
- max_bn(r);
- neg_a_bn(r);
- return r;
- }
-
- floattobn(r, f); /* start with approximate inverse */
- clear_bn(bntmp2); /* will be used as 1.0 and 2.0 */
-
- for (i=0; i<25; i++) /* safety net, this shouldn't ever be needed */
- {
- unsafe_mult_bn(bntmp1, r, n); /* bntmp1=rn */
- bntmp2[bnlength-intlength] = 1; /* bntmp2 = 1.0 */
- if ( !cmp_bn(bntmp2, bntmp1+shiftfactor) ) /* if not different */
- break; /* they must be the same */
- bntmp2[bnlength-intlength] = 2; /* bntmp2 = 2.0 */
- sub_bn(bntmp3, bntmp2, bntmp1+shiftfactor); /* bntmp3=2-rn */
- unsafe_mult_bn(bntmp1, r, bntmp3); /* bntmp1=r(2-rn) */
- copy_bn(r, bntmp1+shiftfactor); /* r = bntmp1 */
- }
-
- if (signflag)
- {
- neg_a_bn(r);
- }
- return r;
- }
-
- /********************************************************************/
- /* r = n1/n2 */
- /* r - result of length bnlength */
- /* uses bntmp1 - bntmp3 - global temp bignumbers */
- /* SIDE-EFFECTS: */
- /* n1, n2 end up as |n1|, |n2| */
- /* Make copies first if necessary. */
- bn_t unsafe_div_bn(bn_t r, bn_t n1, bn_t n2)
- {
- int scale1, scale2, scale, sign=0, i;
- long maxval;
- LDBL a, b, f;
-
- /* first, check for valid data */
- a = bntofloat(n1);
- if (a == 0) /* division into zero */
- {
- clear_bn(r); /* return 0 */
- return r;
- }
- b = bntofloat(n2);
- if (b == 0) /* division by zero */
- {
- max_bn(r);
- return r;
- }
- f = a/b; /* approximate quotient */
- maxval = (1L << ((intlength<<3)-1)) - 1;
- if (f > maxval) /* check for overflow */
- {
- max_bn(r);
- return r;
- }
- else if (f <= -maxval)
- {
- max_bn(r);
- neg_a_bn(r);
- return r;
- }
- /* appears to be ok, do division */
-
- if (is_bn_neg(n1))
- {
- neg_a_bn(n1);
- sign = !sign;
- }
- if (is_bn_neg(n2))
- {
- neg_a_bn(n2);
- sign = !sign;
- }
-
- /* scale n1 and n2 so: |n| >= 1/256 */
- /* scale = (int)(log(1/fabs(a))/LOG_256) = LOG_256(1/|a|) */
- i = bnlength-1;
- while (i >= 0 && n1[i] == 0)
- i--;
- scale1 = bnlength - i - 2;
- if (scale1 < 0)
- scale1 = 0;
- i = bnlength-1;
- while (i >= 0 && n2[i] == 0)
- i--;
- scale2 = bnlength - i - 2;
- if (scale2 < 0)
- scale2 = 0;
-
- /* important!, use memmove(), not memcpy() */
- _fmemmove(n1+scale1, n1, bnlength-scale1); /* shift bytes over */
- _fmemset(n1, 0, scale1); /* zero out the rest */
- _fmemmove(n2+scale2, n2, bnlength-scale2); /* shift bytes over */
- _fmemset(n2, 0, scale2); /* zero out the rest */
-
- unsafe_inv_bn(r, n2);
- unsafe_mult_bn(bntmp1, n1, r);
- copy_bn(r, bntmp1+shiftfactor); /* r = bntmp1 */
-
- if (scale1 != scale2)
- {
- /* Rescale r back to what it should be. Overflow has already been checked */
- if (scale1 > scale2) /* answer is too big, adjust it */
- {
- scale = scale1-scale2;
- _fmemmove(r, r+scale, bnlength-scale); /* shift bytes over */
- _fmemset(r+bnlength-scale, 0, scale); /* zero out the rest */
- }
- else if (scale1 < scale2) /* answer is too small, adjust it */
- {
- scale = scale2-scale1;
- _fmemmove(r+scale, r, bnlength-scale); /* shift bytes over */
- _fmemset(r, 0, scale); /* zero out the rest */
- }
- /* else scale1 == scale2 */
-
- }
-
- if (sign)
- neg_a_bn(r);
-
- return r;
- }
-
- /********************************************************************/
- /* sqrt(r) */
- /* uses bntmp1 - bntmp4 - global temp bignumbers */
- /* SIDE-EFFECTS: */
- /* n ends up as |n| */
- bn_t sqrt_bn(bn_t r, bn_t n)
- {
- int i, comp, almost_match=0;
- LDBL f;
-
- /* use Newton's recursive method for zeroing in on sqrt(n): r=.5(r+n/r) */
-
- if (is_bn_neg(n))
- { /* sqrt of a neg, return 0 */
- clear_bn(r);
- return r;
- }
-
- f = bntofloat(n);
- if (f == 0) /* division by zero will occur */
- {
- clear_bn(r); /* sqrt(0) = 0 */
- return r;
- }
- f = sqrtl(f); /* approximate square root */
- /* no need to check overflow */
-
- floattobn(r, f); /* start with approximate sqrt */
- copy_bn(bntmp4, r);
-
- for (i=0; i<25; i++) /* safety net, this shouldn't ever be needed */
- {
- unsafe_div_bn(bntmp4, n, r);
- add_a_bn(r, bntmp4);
- half_a_bn(r);
- if ((comp=abs(cmp_bn(r, bntmp4))) <= 2 ) /* if match or almost match */
- {
- if (comp == 0) /* perfect match */
- break;
- if (almost_match == 2) /* did they almost match before? */
- { /* yes, this must be the third time */
- /* average last 4 bytes */
- *(U32 far *)r = (*(U32 far *)r + *(U32 far *)bntmp4) / 2;
- break;
- }
- else /* this is the first or second time they almost matched */
- almost_match++;
- }
- }
-
- return r;
- }
-
- /********************************************************************/
- /* exp(r) */
- /* uses bntmp1, bntmp2, bntmp3 - global temp bignumbers */
- bn_t exp_bn(bn_t r, bn_t n)
- {
- U16 fact=1;
-
- if (is_bn_zero(n))
- {
- inttobn(r, 1);
- return r;
- }
-
- /* use Taylor Series (very slow convergence) */
- inttobn(r, 1); /* start with r=1.0 */
- copy_bn(bntmp2, r);
- for (;;)
- {
- /* copy n, if n is negative, mult_bn() alters n */
- unsafe_mult_bn(bntmp3, bntmp2, copy_bn(bntmp1, n));
- copy_bn(bntmp2, bntmp3+shiftfactor);
- div_a_bn_int(bntmp2, fact);
- if (!is_bn_not_zero(bntmp2))
- break; /* too small to register */
- add_a_bn(r, bntmp2);
- fact++;
- }
- return r;
- }
-
- /********************************************************************/
- /* ln(r) */
- /* uses bntmp1 - bntmp6 - global temp bignumbers */
- /* SIDE-EFFECTS: */
- /* n ends up as |n| */
- bn_t unsafe_ln_bn(bn_t r, bn_t n)
- {
- int i, comp, almost_match=0;
- long maxval;
- LDBL f;
-
- /* use Newton's recursive method for zeroing in on ln(n): r=r+n*exp(-r)-1 */
-
- if (is_bn_neg(n) || is_bn_zero(n))
- { /* error, return largest neg value */
- max_bn(r);
- neg_a_bn(r);
- return r;
- }
-
- f = bntofloat(n);
- f = logl(f); /* approximate ln(x) */
- maxval = (1L << ((intlength<<3)-1)) - 1;
- if (f > maxval) /* check for overflow */
- {
- max_bn(r);
- return r;
- }
- else if (f <= -maxval)
- {
- max_bn(r);
- neg_a_bn(r);
- return r;
- }
- /* appears to be ok, do ln */
-
- floattobn(r, f); /* start with approximate ln */
- neg_a_bn(r); /* -r */
- copy_bn(bntmp5, r); /* -r */
- inttobn(bntmp4, 1);
-
- for (i=0; i<25; i++) /* safety net, this shouldn't ever be needed */
- {
- exp_bn(bntmp6, r); /* exp(-r) */
- unsafe_mult_bn(bntmp2, bntmp6, n); /* n*exp(-r) */
- sub_a_bn(bntmp2+shiftfactor, bntmp4); /* n*exp(-r) - 1 */
- sub_a_bn(r, bntmp2+shiftfactor); /* -r - (n*exp(-r) - 1) */
-
- if ((comp=abs(cmp_bn(r, bntmp5))) <= 2 ) /* if match or almost match */
- {
- if (comp == 0) /* perfect match */
- break;
- if (almost_match == 2) /* did they almost match twice before? */
- { /* yes, this must be the third time */
- /* average last 4 bytes */
- *(U32 far *)r = (*(U32 far *)r + *(U32 far *)bntmp5) / 2;
- break;
- }
- else /* this is the first or second time they almost matched */
- almost_match++;
- }
- copy_bn(bntmp5, r); /* -r */
- }
- neg_a_bn(r); /* -(-r) */
- return r;
- }
-
- /********************************************************************/
- /* sincos_bn(r) */
- /* uses bntmp1 - bntmp2 - global temp bignumbers */
- /* SIDE-EFFECTS: */
- /* n ends up as |n| mod (pi/4) */
- bn_t unsafe_sincos_bn(bn_t s, bn_t c, bn_t n)
- {
- U16 fact=2;
- int k=0, i, halves;
- int signcos=0, signsin=0, switch_sincos=0;
-
- /* assure range 0 <= x < pi/4 */
-
- if (is_bn_zero(n))
- {
- clear_bn(s); /* sin(0) = 0 */
- inttobn(c, 1); /* cos(0) = 1 */
- return s;
- }
-
- if (is_bn_neg(n))
- {
- signsin = !signsin; /* sin(-x) = -sin(x), odd; cos(-x) = cos(x), even */
- neg_a_bn(n);
- }
- /* n >= 0 */
-
- double_bn(bntmp1, bn_pi); /* 2*pi */
- /* this could be done with remainders, but it would probably be slower */
- while (cmp_bn(n, bntmp1) >= 0) /* while n >= 2*pi */
- sub_a_bn(n, bntmp1);
- /* 0 <= n < 2*pi */
-
- copy_bn(bntmp1, bn_pi); /* pi */
- if (cmp_bn(n, bntmp1) >= 0) /* if n >= pi */
- {
- sub_a_bn(n, bntmp1);
- signsin = !signsin;
- signcos = !signcos;
- }
- /* 0 <= n < pi */
-
- half_bn(bntmp1, bn_pi); /* pi/2 */
- if (cmp_bn(n, bntmp1) > 0) /* if n > pi/2 */
- {
- sub_bn(n, bn_pi, n); /* pi - n */
- signcos = !signcos;
- }
- /* 0 <= n < pi/2 */
-
- half_bn(bntmp1, bn_pi); /* pi/2 */
- half_a_bn(bntmp1); /* pi/4 */
- if (cmp_bn(n, bntmp1) > 0) /* if n > pi/4 */
- {
- half_bn(bntmp1, bn_pi); /* pi/2 */
- sub_bn(n, bntmp1, n); /* pi/2 - n */
- switch_sincos = !switch_sincos;
- }
- /* 0 <= n < pi/4 */
-
- /* this looks redundant, but n could now be zero when it wasn't before */
- if (is_bn_zero(n))
- {
- clear_bn(s); /* sin(0) = 0 */
- inttobn(c, 1); /* cos(0) = 1 */
- return s;
- }
-
-
- /* at this point, the double angle trig identities could be used as many */
- /* times as desired to reduce the range to pi/8, pi/16, etc... Each time */
- /* the range is cut in half, the number of iterations required is reduced */
- /* by "quite a bit." It's just a matter of testing to see what gives the */
- /* optimal results. */
- /* halves = bnlength / 10; */ /* this is experimental */
- halves = 1;
- for (i = 0; i < halves; i++)
- half_a_bn(n);
-
- /* use Taylor Series (very slow convergence) */
- copy_bn(s, n); /* start with s=n */
- inttobn(c, 1); /* start with c=1 */
- copy_bn(bntmp1, n); /* the current x^n/n! */
-
- for (;;)
- {
- /* even terms for cosine */
- unsafe_mult_bn(bntmp2, bntmp1, n);
- copy_bn(bntmp1, bntmp2+shiftfactor);
- div_a_bn_int(bntmp1, fact++);
- if (!is_bn_not_zero(bntmp1))
- break; /* too small to register */
- if (k) /* alternate between adding and subtracting */
- add_a_bn(c, bntmp1);
- else
- sub_a_bn(c, bntmp1);
-
- /* odd terms for sine */
- unsafe_mult_bn(bntmp2, bntmp1, n);
- copy_bn(bntmp1, bntmp2+shiftfactor);
- div_a_bn_int(bntmp1, fact++);
- if (!is_bn_not_zero(bntmp1))
- break; /* too small to register */
- if (k) /* alternate between adding and subtracting */
- add_a_bn(s, bntmp1);
- else
- sub_a_bn(s, bntmp1);
- k = !k; /* toggle */
- }
-
- /* now need to undo what was done by cutting angles in half */
- inttobn(bntmp1, 1);
- for (i = 0; i < halves; i++)
- {
- unsafe_mult_bn(bntmp2, s, c); /* no need for safe mult */
- double_bn(s, bntmp2+shiftfactor); /* sin(2x) = 2*sin(x)*cos(x) */
- unsafe_square_bn(bntmp2,c);
- double_a_bn(bntmp2+shiftfactor);
- sub_bn(c, bntmp2+shiftfactor, bntmp1); /* cos(2x) = 2*cos(x)*cos(x) - 1 */
- }
-
- if (switch_sincos)
- {
- copy_bn(bntmp1, s);
- copy_bn(s, c);
- copy_bn(c, bntmp1);
- }
- if (signsin)
- neg_a_bn(s);
- if (signcos)
- neg_a_bn(c);
- return s; /* return sine I guess */
- }
-
- /********************************************************************/
- /* atan(r) */
- /* uses bntmp1 - bntmp5 - global temp bignumbers */
- /* SIDE-EFFECTS: */
- /* n ends up as |n| */
- bn_t unsafe_atan_bn(bn_t r, bn_t n)
- {
- int i, comp, almost_match=0, signflag=0;
- LDBL f;
-
- /* use Newton's recursive method for zeroing in on atan(n): r=r-cos(r)(sin(r)-n*cos(r)) */
-
- if (is_bn_neg(n))
- {
- signflag = 1;
- neg_a_bn(n);
- }
-
- f = bntofloat(n);
- f = atanl(f); /* approximate arctangent */
- /* no need to check overflow */
-
- floattobn(r, f); /* start with approximate atan */
- copy_bn(bntmp3, r);
-
- for (i=0; i<25; i++) /* safety net, this shouldn't ever be needed */
- {
- unsafe_sincos_bn(bntmp4, bntmp5, bntmp3); /* sin(r), cos(r) */
- copy_bn(bntmp3, r); /* restore bntmp3 from sincos_bn() */
- copy_bn(bntmp1, bntmp5);
- unsafe_mult_bn(bntmp2, n, bntmp1); /* n*cos(r) */
- sub_a_bn(bntmp4, bntmp2+shiftfactor); /* sin(r) - n*cos(r) */
- unsafe_mult_bn(bntmp1, bntmp5, bntmp4); /* cos(r) * (sin(r) - n*cos(r)) */
- sub_a_bn(r, bntmp1+shiftfactor); /* r - cos(r) * (sin(r) - n*cos(r)) */
-
- if ((comp=abs(cmp_bn(r, bntmp3))) <= 2 ) /* if match or almost match */
- {
- if (comp == 0) /* perfect match */
- break;
- if (almost_match == 2) /* did they almost match before? */
- { /* yes, this must be the third time */
- /* average last 4 bytes */
- *(U32 far *)r = (*(U32 far *)r + *(U32 far *)bntmp3) / 2;
- break;
- }
- else /* this is the first or second time they almost matched */
- almost_match++;
- }
- copy_bn(bntmp3, r); /* make a copy for later comparison */
- }
-
- if (signflag)
- neg_a_bn(r);
- return r;
- }
-
- /********************************************************************/
- /* atan2(r,ny,nx) */
- /* uses bntmp1 - bntmp6 - global temp bigfloats */
- bn_t unsafe_atan2_bn(bn_t r, bn_t ny, bn_t nx)
- {
- int signx, signy;
-
- signx = sign_bn(nx);
- signy = sign_bn(ny);
-
- if (signy == 0)
- {
- if (signx < 0)
- copy_bn(r, bn_pi); /* negative x axis, 180 deg */
- else /* signx >= 0 positive x axis, 0 */
- clear_bn(r);
- return(r);
- }
- if (signx == 0)
- {
- copy_bn(r, bn_pi); /* y axis */
- half_a_bn(r); /* +90 deg */
- if (signy < 0)
- neg_a_bn(r); /* -90 deg */
- return(r);
- }
-
- if (signy < 0)
- neg_a_bn(ny);
- if (signx < 0)
- neg_a_bn(nx);
- unsafe_div_bn(bntmp6,ny,nx);
- unsafe_atan_bn(r, bntmp6);
- if (signx < 0)
- sub_bn(r,bn_pi,r);
- if(signy < 0)
- neg_a_bn(r);
- return(r);
- }
-
-
- /**********************************************************************/
- /* The rest of the functions are "safe" versions of the routines that */
- /* have side effects which alter the parameters */
- /**********************************************************************/
-
- /**********************************************************************/
- bn_t full_mult_bn(bn_t r, bn_t n1, bn_t n2)
- {
- int sign1, sign2;
-
- sign1 = is_bn_neg(n1);
- sign2 = is_bn_neg(n2);
- unsafe_full_mult_bn(r, n1, n2);
- if (sign1)
- neg_a_bn(n1);
- if (sign2)
- neg_a_bn(n2);
- return r;
- }
-
- /**********************************************************************/
- bn_t mult_bn(bn_t r, bn_t n1, bn_t n2)
- {
- int sign1, sign2;
-
- /* TW ENDFIX */
- sign1 = is_bn_neg(n1);
- sign2 = is_bn_neg(n2);
- unsafe_mult_bn(r, n1, n2);
- if (sign1)
- neg_a_bn(n1);
- if (sign2)
- neg_a_bn(n2);
- return r;
- }
-
- /**********************************************************************/
- bn_t full_square_bn(bn_t r, bn_t n)
- {
- int sign;
-
- sign = is_bn_neg(n);
- unsafe_full_square_bn(r, n);
- if (sign)
- neg_a_bn(n);
- return r;
- }
-
- /**********************************************************************/
- bn_t square_bn(bn_t r, bn_t n)
- {
- int sign;
-
- sign = is_bn_neg(n);
- unsafe_square_bn(r, n);
- if (sign)
- neg_a_bn(n);
- return r;
- }
-
- /**********************************************************************/
- bn_t div_bn_int(bn_t r, bn_t n, U16 u)
- {
- int sign;
-
- sign = is_bn_neg(n);
- unsafe_div_bn_int(r, n, u);
- if (sign)
- neg_a_bn(n);
- return r;
- }
-
- #if (_MSC_VER >= 700)
- #pragma code_seg ("bignum1_text") /* place following in an overlay */
- #endif
-
- /**********************************************************************/
- char *bntostr(char *s, int dec, bn_t r)
- {
- return unsafe_bntostr(s, dec, copy_bn(bntmpcpy2, r));
- }
-
- #if (_MSC_VER >= 700)
- #pragma code_seg ( ) /* back to normal segment */
- #endif
-
- /**********************************************************************/
- bn_t inv_bn(bn_t r, bn_t n)
- {
- int sign;
-
- sign = is_bn_neg(n);
- unsafe_inv_bn(r, n);
- if (sign)
- neg_a_bn(n);
- return r;
- }
-
- /**********************************************************************/
- bn_t div_bn(bn_t r, bn_t n1, bn_t n2)
- {
- copy_bn(bntmpcpy1, n1);
- copy_bn(bntmpcpy2, n2);
- return unsafe_div_bn(r, bntmpcpy1, bntmpcpy2);
- }
-
- /**********************************************************************/
- bn_t ln_bn(bn_t r, bn_t n)
- {
- #if 0
- int sign;
-
- sign = is_bn_neg(n);
- unsafe_ln_bn(r, n);
- if (sign)
- neg_a_bn(n);
- #endif
- copy_bn(bntmpcpy1, n); /* allows r and n to overlap memory */
- unsafe_ln_bn(r, bntmpcpy1);
- return r;
- }
-
- /**********************************************************************/
- bn_t sincos_bn(bn_t s, bn_t c, bn_t n)
- {
- return unsafe_sincos_bn(s, c, copy_bn(bntmpcpy1, n));
- }
-
- /**********************************************************************/
- bn_t atan_bn(bn_t r, bn_t n)
- {
- int sign;
-
- sign = is_bn_neg(n);
- unsafe_atan_bn(r, n);
- if (sign)
- neg_a_bn(n);
- return r;
- }
-
- /**********************************************************************/
- bn_t atan2_bn(bn_t r, bn_t ny, bn_t nx)
- {
- copy_bn(bntmpcpy1, ny);
- copy_bn(bntmpcpy2, nx);
- unsafe_atan2_bn(r, bntmpcpy1, bntmpcpy2);
- return r;
- }
-
-
- /**********************************************************************/
- /* Tim's miscellaneous stuff follows */
-
- /**********************************************************************/
- int is_bn_zero(bn_t n)
- {
- return !is_bn_not_zero(n);
- }
-