/* MIRANDA INTEGER PACKAGE */ /* package for unbounded precision integers */ /************************************************************************** * Copyright (C) Research Software Limited 1985-90. All rights reserved. * * The Miranda system is distributed as free software under the terms in * * the file "COPYING" which is included in the distribution. * * * * Revised to C11 standard and made 64bit compatible, January 2020 * *------------------------------------------------------------------------*/ #include "data.h" #include "lex.h" #include "big.h" #include /* contains remainder from last call to longdiv or shortdiv */ static word b_rem; extern char *dicp; #define maxval (1ll<<60) static double logIBASE, log10IBASE; word big_one; static word big_plus(word, word, int); static word big_sub(word, word); static word len(word); static word longdiv(word, word); static word ms2d(word); static word msd(word); static word shift(word, word); static word shortdiv(word, word); static word stimes(word, word); void bigsetup(void) { logIBASE = log((double)IBASE); log10IBASE = log10((double)IBASE); big_one = make(INT, 1, 0); } int isnat(word x) { return (tag[x] == INT && big_is_positive(x)); } /* store C long long as mira bigint */ word sto_int(long long i) { word s = 0; if (i < 0) { s = SIGNBIT; i = -i; } word x = make(INT, (s | i) & MAXDIGIT, 0); for (word *p = &rest(x); i >>= DIGITWIDTH; p = &rest(*p)) { *p = make(INT, i & MAXDIGIT, 0); } return x; } /* mira bigint to C long long */ long long get_int(word x) { long long n = digit0(x); word sign = big_is_negative(x); if (!(x = rest(x))) { return (sign ? -n : n); } for (word w = DIGITWIDTH; x && w < 60; w += DIGITWIDTH) { n += (long long)digit(x) << w; x = rest(x); } if (x) { n = maxval; /* overflow, return large value */ } return (sign ? -n : n); } word bignegate(word x) { if (bigzero(x)) { return x; } return make(INT, (hd[x] & SIGNBIT) ? (hd[x] & MAXDIGIT) : (SIGNBIT | hd[x]), tl[x]); } word bigplus(word x, word y) { if (big_is_positive(x)) { if (big_is_positive(y)) { return (big_plus(x, y, 0)); } else { return (big_sub(x, y)); } } else if (big_is_positive(y)) { return (big_sub(y, x)); } return (big_plus(x, y, SIGNBIT)); /* both negative */ } /* ignore input signs, treat x,y as positive */ word big_plus(word x, word y, int signbit) { word d = digit0(x) + digit0(y); word carry = ((d & IBASE) != 0); word r = make(INT, (signbit | d) & MAXDIGIT, 0); /* result */ word *z = &rest(r); /* pointer to rest of result */ x = rest(x); y = rest(y); while (x && y) { /* this loop has been unwrapped once, see above */ d = carry + digit(x) + digit(y); carry = ((d & IBASE) != 0); *z = make(INT, d & MAXDIGIT, 0); x = rest(x); y = rest(y); z = &rest(*z); } if (y) { x = y; /* by convention x is the longer one */ } while (x) { d = carry + digit(x); carry = ((d & IBASE) != 0); *z = make(INT, d & MAXDIGIT, 0); x = rest(x); z = &rest(*z); } if (carry) { *z = make(INT, 1, 0); } return r; } word bigsub(word x, word y) { if (big_is_positive(x)) { if (big_is_positive(y)) { return (big_sub(x, y)); } else { return (big_plus(x, y, 0)); /* poz x, negative y */ } } else if (big_is_positive(y)) { return (big_plus(x, y, SIGNBIT)); /* negative x, poz y */ } return (big_sub(y, x)); /* both negative */ } /* ignore input signs, treat x,y as positive */ word big_sub(word x, word y) { word d = digit0(x) - digit0(y); word borrow = (d & IBASE) != 0; word r = make(INT, d & MAXDIGIT, 0); /* result */ word *z = &rest(r); word *p = NULL; /* pointer to trailing zeros, if any */ x = rest(x); y = rest(y); while (x && y) { /* this loop has been unwrapped once, see above */ d = digit(x) - digit(y) - borrow; borrow = (d & IBASE) != 0; d = d & MAXDIGIT; *z = make(INT, d, 0); if (d) p = NULL; else if (!p) p = z; x = rest(x); y = rest(y); z = &rest(*z); } while (y) { /* at most one of these two loops will be invoked */ d = -digit(y) - borrow; borrow = ((d & IBASE) != 0); d = d & MAXDIGIT; *z = make(INT, d, 0); if (d) p = NULL; else if (!p) p = z; y = rest(y); z = &rest(*z); } while (x) { /* alternative loop */ d = digit(x) - borrow; borrow = ((d & IBASE) != 0); d = d & MAXDIGIT; *z = make(INT, d, 0); if (d) { p = NULL; } else if (!p) { p = z; } x = rest(x); z = &rest(*z); } if (borrow) { /* result is negative - take complement and add 1 */ p = NULL; d = (digit(r) ^ MAXDIGIT) + 1; borrow = ((d & IBASE) != 0); /* borrow now means `carry' (sorry) */ digit(r) = SIGNBIT | d; /* set sign bit of result */ z = &rest(r); while (*z) { d = (digit(*z) ^ MAXDIGIT) + borrow; borrow = ((d & IBASE) != 0); digit(*z) = d = d & MAXDIGIT; if (d) { p = NULL; } else if (!p) { p = z; } z = &rest(*z); } } if (p) { *p = 0; /* remove redundant (ie trailing) zeros */ } return r; } /* returns +ve,0,-ve as x greater than, equal, less than y */ int bigcmp(word x, word y) { word s = big_is_negative(x); if (big_is_negative(y) != s) { return (s ? -1 : 1); } word r = digit0(x) - digit0(y); for (;;) { x = rest(x); y = rest(y); if (!x) { if (y) { return (s ? 1 : -1); } else { return (s ? -r : r); } } if (!y) { return (s ? -1 : 1); } word d = digit(x) - digit(y); if (d) { r = d; } } } /* naive multiply - quadratic */ word bigtimes(word x, word y) { word r = make(INT, 0, 0); if (len(x) < len(y)) { word hold = x; x = y; y = hold; } /* important optimisation */ if (bigzero(x)) { return r; /* short cut */ } word d = digit0(y); word s = big_is_negative(y); word n = 0; for (;;) { if (d) { r = bigplus(r, shift(n, stimes(x, d))); } n++; y = rest(y); if (!y) { return (s != big_is_negative(x) ? bignegate(r) : r); } d = digit(y); } } /* multiply big x by n'th power of IBASE */ /* NB - we assume x non-zero, else unnormalised result */ word shift(word n, word x) { while (n--) { x = make(INT, 0, x); } return x; } /* multiply big x (>=0) by digit n (>0) */ word stimes(word x, word n) { unsigned d = n * digit0(x); /* ignore sign of x */ word carry = d >> DIGITWIDTH; word r = make(INT, d & MAXDIGIT, 0); word *y = &rest(r); while ((x = rest(x))) { d = n * digit(x) + carry; *y = make(INT, d & MAXDIGIT, 0); y = &rest(*y); carry = d >> DIGITWIDTH; } if (carry) { *y = make(INT, carry, 0); } return r; } /* may assume y~=0 */ word bigdiv(word x, word y) { /* make x,y positive and remember signs */ word s1 = big_is_negative(y); if (s1) { y = make(INT, digit0(y), rest(y)); } word s2 = s1; if (big_is_negative(x)) { x = make(INT, digit0(x), rest(x)); s2 = !s1; } /* effect: s1 set iff y negative, s2 set iff signs mixed */ word q = rest(y) ? longdiv(x, y) : shortdiv(x, digit(y)); if (s2) { if (!bigzero(b_rem)) { x = q; while ((digit(x) += 1) == IBASE) { /* add 1 to q in situ */ digit(x) = 0; if (!rest(x)) { rest(x) = make(INT, 1, 0); break; } else { x = rest(x); } } } if (!bigzero(q)) { digit(q) = SIGNBIT | digit(q); } } return q; } /* may assume y~=0 */ word bigmod(word x, word y) { word s1 = big_is_negative(y); /* make x,y positive and remember signs */ if (s1) { y = make(INT, digit0(y), rest(y)); } word s2 = s1; if (big_is_negative(x)) { x = make(INT, digit0(x), rest(x)); s2 = !s1; } /* effect: s1 set iff y negative, s2 set iff signs mixed */ if (rest(y)) { longdiv(x, y); } else { shortdiv(x, digit(y)); } if (s2) { if (!bigzero(b_rem)) { b_rem = bigsub(y, b_rem); } } return (s1 ? bignegate(b_rem) : b_rem); } /* NB - above have entier based handling of signed cases (as Miranda) in which remainder has sign of divisor. To get this:- if signs of divi(sor/dend) mixed negate quotient and if remainder non-zero take complement and add one to magnitude of quotient */ /* for alternative, truncate based handling of signed cases (usual in C):- magnitudes invariant under change of sign, remainder has sign of dividend, quotient negative if signs of divi(sor/dend) mixed */ /* divide big x by single digit n returning big quotient and setting external b_rem as side effect */ /* may assume - x>=0,n>0 */ word shortdiv(word x, word n) { word d = digit(x); word q = 0; while ((x = rest(x))) { /* reverse rest(x) into q */ q = make(INT, d, q); d = digit(x); /* leaving most sig. digit in d */ } x = q; d = d / n; if (d || !q) { q = make(INT, d, 0); /* put back first digit (if not leading 0) */ } else { q = 0; } word s_rem = d % n; while (x) { /* in situ division of q by n AND destructive reversal */ d = s_rem * IBASE + digit(x); digit(x) = d / n; s_rem = d % n; word tmp = x; x = rest(x); rest(tmp) = q; q = tmp; } b_rem = make(INT, s_rem, 0); return q; } /* divide big x by big y returning quotient, leaving remainder in extern variable b_rem */ /* may assume - x>=0,y>0 */ word longdiv(word x, word y) { if (bigcmp(x, y) < 0) { b_rem = x; return (make(INT, 0, 0)); } word y1 = msd(y); word scale = IBASE / (y1 + 1); if (scale > 1) { /* rescale if necessary */ x = stimes(x, scale); y = stimes(y, scale); y1 = msd(y); } word n = 0; word ly = len(y); while (bigcmp(x, y = make(INT, 0, y)) >= 0) { n++; } y = rest(y); /* want largest y not exceeding x */ ly += n; word q = 0; for (;;) { word d = 0; word lx = len(x); if (lx == ly) { if (bigcmp(x, y) >= 0) { x = bigsub(x, y); d = 1; } } else if (lx > ly) { d = ms2d(x) / y1; if (d > MAXDIGIT) { d = MAXDIGIT; } if ((d -= 2) > 0) { x = bigsub(x, stimes(y, d)); } else { d = 0; } if (bigcmp(x, y) >= 0) { x = bigsub(x, y); d++; if (bigcmp(x, y) >= 0) { x = bigsub(x, y); d++; } } } q = make(INT, d, q); if (n-- == 0) { b_rem = (scale == 1) ? x : shortdiv(x, scale); return q; } ly--; y = rest(y); } } /* see Bird & Wadler p82 for explanation */ /* no of digits in big x */ word len(word x) { word n = 1; while ((x = rest(x))) { n++; } return n; } /* most significant digit of big x */ word msd(word x) { while (rest(x)) { x = rest(x); } return digit(x); /* sign? */ } /* most significant 2 digits of big x (len>=2) */ word ms2d(word x) { word d = digit(x); x = rest(x); while (rest(x)) { d = digit(x); x = rest(x); } return (digit(x) * IBASE + d); } /* assumes y poz */ word bigpow(word x, word y) { word r = make(INT, 1, 0); while (rest(y)) { word i = DIGITWIDTH; word d = digit(y); while (i--) { if (d & 1) { r = bigtimes(r, x); } x = bigtimes(x, x); d >>= 1; } y = rest(y); } word d = digit(y); if (d & 1) { r = bigtimes(r, x); } while (d >>= 1) { x = bigtimes(x, x); if (d & 1) { r = bigtimes(r, x); } } return r; } double bigtodbl(word x) { double b = 1.0; double r = (double)digit0(x); x = rest(x); while (x) { b = b * IBASE; r = r + b * digit(x); x = rest(x); } return big_is_negative(x) ? -r : r; } /* small end first */ /* note: can return oo, -oo but is used without surrounding sto_/set)dbl() only in compare() */ /* not currently used */ long double bigtoldbl(word x) { long double b = 1.0L; long double r = (long double)digit0(x); x = rest(x); while (x) { b = b * IBASE; r = r + b * digit(x); x = rest(x); } return big_is_negative(x) ? -r : r; } /* not compatible with std=c90, lib fns eg sqrtl broken */ /* entier */ word dbltobig(double x) { word r = make(INT, 0, 0); word *p = &r; double y = fabs(floor(x)); while (y > 0.0) { double n = fmod(y, (double)IBASE); digit(*p) = (word) n; y = (y - n) / (double)IBASE; if (y > 0.0) { rest(*p) = make(INT, 0, 0), p = &rest(*p); } } if (x < 0) { digit(r) = SIGNBIT | digit(r); } return r; } /* produces junk in low order digits if x exceeds range in which integer can be held without error as a double -- NO, see next comment */ /* hugs, ghci, mira produce same integer for floor/entier hugenum, has 2^971 as factor so the low order bits are NOT JUNK -- 9.1.12 */ /* note on suppressed fix: choice of 1e9 arbitrary, chosen to prevent eg entier(100*0.29) = 28 but has undesirable effects, causing eg entier 1.9999999999 = 2 underlying problem is that computable floor on true Reals is _|_ at the exact integers. There are inherent deficiences in 64 bit fp, no point in trying to mask this */ /* logarithm of big x */ double biglog(word x) { if (big_is_negative(x) || bigzero(x)) { errno = EDOM; math_error("log"); } word n = 0; double r = digit(x); while ((x = rest(x))) { n++; r = digit(x) + r / IBASE; } return (log(r) + n * logIBASE); } /* logarithm of big x */ double biglog10(word x) { if (big_is_negative(x) || bigzero(x)) { errno = EDOM; math_error("log10"); } word n = 0; double r = digit(x); while ((x = rest(x))) { n++, r = digit(x) + r / IBASE; } return (log10(r) + n * log10IBASE); } /* NB does NOT check for malformed number, assumes already done */ /* p is a pointer to a null terminated string of digits */ /* read a big number (in decimal) */ word bigscan(char *p) { word s = 0; word r = make(INT, 0, 0); if (*p == '-') { s = 1; p++; /* optional leading `-' (for NUMVAL) */ } while (*p) { word d = *p - '0'; word f = 10; p++; while (*p && f < PTEN) { d = 10 * d + *p - '0'; f = 10 * f; p++; } /* rest of loop does r=f*r+d; (in situ) */ d = f * digit(r) + d; word carry = d >> DIGITWIDTH; word *x = &rest(r); digit(r) = d & MAXDIGIT; while (*x) { d = f * digit(*x) + carry; digit(*x) = d & MAXDIGIT; carry = d >> DIGITWIDTH; x = &rest(*x); } if (carry) { *x = make(INT, carry, 0); } } if (s && !bigzero(r)) { digit(r) = digit(r) | SIGNBIT; } return r; } /* code to handle (unsigned) exponent commented out */ /* assumes redundant leading zeros removed */ /* read unsigned hex number in '\0'-terminated string p to q */ word bigxscan(char *p, char *q) { if (*p == '0' && !p[1]) { return make(INT, 0, 0); } word r; /* will hold result */ word *x = &r; while (q > p) { unsigned long long hold; q = (q - p < 15) ? p : (q - 15); /* read upto 15 hex digits from small end */ sscanf(q, "%llx", &hold); *q = '\0'; word count = 4; /* 15 hex digits => 4 bignum digits */ while (count-- && !(hold == 0 && q == p)) { *x = make(INT, hold & MAXDIGIT, 0); hold >>= DIGITWIDTH; x = &rest(*x); } } return r; } /* read unsigned octal number in '\0'-terminated string p to q */ word bigoscan(char *p, char *q) /* assumes redundant leading zeros removed */ { word r; /* will hold result */ word *x = &r; while (q > p) { unsigned hold; q = q - p < 5 ? p : q - 5; /* read (upto) 5 octal digits from small end */ sscanf(q, "%o", &hold); *q = '\0'; *x = make(INT, hold, 0), x = &rest(*x); } return r; } word digitval(char c) { return isdigit(c) ? c - '0' : isupper(c) ? 10 + c - 'A' : 10 + c - 'a'; } /* numeral (as Miranda string) to big number */ /* does NOT check for malformed numeral, assumes done and that z fully evaluated */ word strtobig(word z, int base) { word s = 0; word r = make(INT, 0, 0); word PBASE = PTEN; if (base == 16) { PBASE = PSIXTEEN; } else if (base == 8) { PBASE = PEIGHT; } if (z != NIL && hd[z] == '-') { s = 1; z = tl[z]; /* optional leading `-' (for NUMVAL) */ } if (base != 10) { z = tl[tl[z]]; /* remove "0x" or "0o" */ } while (z != NIL) { word d = digitval(hd[z]), f = base; z = tl[z]; while (z != NIL && f < PBASE) { d = base * d + digitval(hd[z]); f = base * f; z = tl[z]; } /* rest of loop does r=f*r+d; (in situ) */ d = f * digit(r) + d; word carry = d >> DIGITWIDTH; word *x = &rest(r); digit(r) = d & MAXDIGIT; while (*x) { d = f * digit(*x) + carry, digit(*x) = d & MAXDIGIT, carry = d >> DIGITWIDTH, x = &rest(*x); } if (carry) { *x = make(INT, carry, 0); } } if (s && !bigzero(r)) { digit(r) = digit(r) | SIGNBIT; } return r; } word bigtostr(word x) /* number to decimal string (as Miranda list) */ { word sign = big_is_negative(x); word s = NIL; #ifdef DEBUG extern int debug; if (debug & 04) { /* print octally */ word d = digit0(x); for (;;) { word i = OCTW; while (i-- || d) { s = cons('0' + (d & 07), s), d >>= 3; } x = rest(x); if (x) { s = cons(' ', s), d = digit(x); } else { return (sign ? cons('-', s) : s); } } } #endif if (rest(x) == 0) { extern char *dicp; sprintf(dicp, "%ld", getsmallint(x)); return (str_conv(dicp)); } word x1 = make(INT, digit0(x), 0); /* reverse x into x1 */ while ((x = rest(x))) { x1 = make(INT, digit(x), x1); } x = x1; for (;;) { /* in situ division of (reversed order) x by PTEN */ word d = digit(x); word rem = d % PTEN; d = d / PTEN; x1 = rest(x); if (d) { digit(x) = d; } else { x = x1; /* remove leading zero from result */ } while (x1) { d = rem * IBASE + digit(x1); digit(x1) = d / PTEN; rem = d % PTEN; x1 = rest(x1); } /* end of in situ division (also uses x1 as temporary) */ if (x) { word i = TENW; while (i--) { s = cons('0' + rem % 10, s), rem = rem / 10; } } else { while (rem) { s = cons('0' + rem % 10, s), rem = rem / 10; } return (sign ? cons('-', s) : s); } } } /* integer to hexadecimal string (as Miranda list) */ word bigtostrx(word x) { word r = NIL; word sign = big_is_negative(x); while (x) { word count = 4; /* 60 bits => 20 octal digits => 4 bignum digits */ unsigned long long factor = 1; unsigned long long hold = 0; while (count-- && x) { /* calculate value of (upto) 4 bignum digits */ hold = hold + factor * digit0(x), factor <<= 15, x = rest(x); } sprintf(dicp, "%.15llx", hold); /* 15 hex digits = 60 bits */ char *q = dicp + 15; while (--q >= dicp) { r = cons(*q, r); } } while (digit(r) == '0' && rest(r) != NIL) { r = rest(r); /* remove redundant leading 0's */ } r = cons('0', cons('x', r)); if (sign) { r = cons('-', r); } return r; } /* integer to octal string (as Miranda list) */ word bigtostr8(word x) { word r = NIL; word sign = big_is_negative(x); while (x) { char *q = dicp + 5; sprintf(dicp, "%.5lo", digit0(x)); while (--q >= dicp) { r = cons(*q, r); } x = rest(x); } while (digit(r) == '0' && rest(r) != NIL) { r = rest(r); /* remove redundant leading 0's */ } r = cons('0', cons('o', r)); if (sign) { r = cons('-', r); } return r; } /* END OF MIRANDA INTEGER PACKAGE */