mp.c

     
   1  //! @file mp.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2026 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! [LONG] LONG INT, REAL routines.
  25  
  26  // Multiprecision calculations are useful in these cases:
  27  // Ill-conditioned linear systems
  28  // Summation of large series
  29  // Long-time or large-scale simulations
  30  // Small-scale phenomena
  31  // 'Experimental mathematics'
  32  // The routines in this library follow algorithms as described in the
  33  // literature, notably
  34  // 
  35  //   D.M. Smith, "Efficient Multiple-Precision Evaluation of Elementary Functions"
  36  //   Mathematics of Computation 52 (1989) 131-134
  37  // 
  38  //   D.M. Smith, "A Multiple-Precision Division Algorithm"
  39  //   Mathematics of Computation 66 (1996) 157-163
  40  //   The GNU MPFR library documentation
  41  // 
  42  // Multiprecision libraries are (freely) available, but this one is particularly 
  43  // designed to work with Algol68G. It implements following modes:
  44  // 
  45  //    LONG INT, LONG REAL, LONG COMPLEX, LONG BITS
  46  //    LONG LONG INT, LONG LONG REAL, LONG LONG COMPLEX, LONG LONG BITS
  47  // 
  48  // Note that recent implementations of GCC make available 64-bit LONG INT and 
  49  // 128-bit LONG REAL. This suits many multiprecision needs already. 
  50  // On such platforms, below code is used for LONG LONG modes only.
  51  // Now that 64-bit integers are commonplace, this library has been adapted to
  52  // exploit them. Having some more digits per word gives performance gain.
  53  // This is implemented through macros to keep the library compatible with
  54  // old platforms with 32-bit integers and 64-bit doubles.
  55  // 
  56  // Currently, LONG modes have a fixed precision, and LONG LONG modes have
  57  // user-definable precision. Precisions span about 30 decimal digits for
  58  // LONG modes up to (default) about 60 decimal digits for LONG LONG modes, a
  59  // range that is thought to be adequate for most multiprecision applications.
  60  // Although the maximum length of a number is in principle unbound, this 
  61  // implementation is not designed for more than a few hundred decimal places. 
  62  // At higher precisions, expect a performance penalty with respect to
  63  // state of the art implementations that may for instance use convolution for
  64  // multiplication. 
  65  // 
  66  // This library takes a sloppy approach towards LONG INT and LONG BITS which are
  67  // implemented as LONG REAL and truncated where appropriate. This keeps the code
  68  // short at the penalty of some performance loss.
  69  // 
  70  // As is common practice, mp numbers are represented by a row of digits
  71  // in a large base. Layout of a mp number "z" is:
  72  // 
  73  //    MP_T *z;
  74  //    MP_STATUS (z)        Status word
  75  //    MP_EXPONENT (z)      Exponent with base MP_RADIX
  76  //    MP_DIGIT (z, 1 .. N) Digits 1 .. N
  77  // 
  78  // Note that this library assumes IEEE 754 compatible implementation of
  79  // type "double". It also assumes a 32- (or 64-) bit type "int".
  80  // 
  81  // Most legacy multiple precision libraries stored numbers as [] int*4.
  82  // However, since division and multiplication are O(N ** 2) operations, it is
  83  // advantageous to keep the base as high as possible. Modern computers handle
  84  // doubles at similar or better speed as integers, therefore this library
  85  // opts for storing numbers as [] words were a word is real*8 (legacy) or 
  86  // int*8, trading space for speed.
  87  // 
  88  // Set a base such that "base^2" can be exactly represented by a word.
  89  // To facilitate transput, we require a base that is a power of 10.
  90  // 
  91  // If we choose the base right then in multiplication and division we do not need
  92  // to normalise intermediate results at each step since a number of additions
  93  // can be made before overflow occurs. That is why we specify "MAX_REPR_INT".
  94  // 
  95  // Mind that the precision of a mp number is at worst just
  96  // (LONG_MP_DIGITS - 1) * LOG_MP_RADIX + 1, since the most significant mp digit
  97  // is also in range [0 .. MP_RADIX>. Do not specify less than 2 digits.
  98  // 
  99  // Since this software is distributed without any warranty, it is your
 100  // responsibility to validate the behaviour of the routines and their accuracy
 101  // using the source code provided. See the GNU General Public License for details.
 102  
 103  #include "a68g.h"
 104  #include "a68g-genie.h"
 105  #include "a68g-prelude.h"
 106  #include "a68g-mp.h"
 107  
 108  // Internal mp constants.
 109  
 110  //! @brief Set number of digits for long long numbers.
 111  
 112  void set_long_mp_digits (int n)
 113  {
 114    A68G_MP (varying_mp_digits) = n;
 115  }
 116  
 117  //! @brief Convert precision to digits for long long number.
 118  
 119  int width_to_mp_digits (int n)
 120  {
 121    return 1 + n / LOG_MP_RADIX;
 122  }
 123  
 124  //! @brief Unformatted write of z to stdout; debugging routine.
 125  
 126  #if defined (BUILD_LINUX)
 127  
 128  void raw_write_mp (char *str, MP_T * z, int digs)
 129  {
 130    fprintf (stdout, "\n(%d digits)%s", digs, str);
 131    for (int i = 1; i <= digs; i++) {
 132      #if (A68G_LEVEL >= 3)
 133        fprintf (stdout, " %09lld", (MP_INT_T) MP_DIGIT (z, i));
 134      #else
 135        fprintf (stdout, " %07d", (MP_INT_T) MP_DIGIT (z, i));
 136      #endif
 137    }
 138    fprintf (stdout, " E" A68G_LD, (MP_INT_T) MP_EXPONENT (z));
 139    fprintf (stdout, " S" A68G_LD, (MP_INT_T) MP_STATUS (z));
 140    fprintf (stdout, "\n");
 141    ASSERT (fflush (stdout) == 0);
 142  }
 143  
 144  #endif
 145  
 146  //! @brief Whether z is a valid representation for its mode.
 147  
 148  BOOL_T check_mp_int (MP_T * z, const MOID_T * m)
 149  {
 150    if (m == M_LONG_INT || m == M_LONG_BITS) {
 151      return (BOOL_T) ((MP_EXPONENT (z) >= (MP_T) 0) && (MP_EXPONENT (z) < (MP_T) LONG_MP_DIGITS));
 152    } else if (m == M_LONG_LONG_INT || m == M_LONG_LONG_BITS) {
 153      return (BOOL_T) ((MP_EXPONENT (z) >= (MP_T) 0) && (MP_EXPONENT (z) < (MP_T) A68G_MP (varying_mp_digits)));
 154    }
 155    return A68G_FALSE;
 156  }
 157  
 158  //! @brief |x|
 159  
 160  MP_T *abs_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 161  {
 162    (void) p;
 163    if (x != z) {
 164      (void) move_mp (z, x, digs);
 165    }
 166    MP_DIGIT (z, 1) = ABS (MP_DIGIT (z, 1));
 167    MP_STATUS (z) = (MP_T) INIT_MASK;
 168    return z;
 169  }
 170  
 171  //! @brief -x
 172  
 173  MP_T *minus_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 174  {
 175    (void) p;
 176    if (x != z) {
 177      (void) move_mp (z, x, digs);
 178    }
 179    MP_DIGIT (z, 1) = -(MP_DIGIT (z, 1));
 180    MP_STATUS (z) = (MP_T) INIT_MASK;
 181    return z;
 182  }
 183  
 184  //! @brief 1 - x
 185  
 186  MP_T *one_minus_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 187  {
 188    ADDR_T pop_sp = A68G_SP;
 189    (void) sub_mp (p, z, mp_one (digs), x, digs);
 190    A68G_SP = pop_sp;
 191    return z;
 192  }
 193  
 194  //! @brief x - 1
 195  
 196  MP_T *minus_one_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 197  {
 198    ADDR_T pop_sp = A68G_SP;
 199    (void) sub_mp (p, z, x, mp_one (digs), digs);
 200    A68G_SP = pop_sp;
 201    return z;
 202  }
 203  
 204  //! @brief x + 1
 205  
 206  MP_T *plus_one_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 207  {
 208    ADDR_T pop_sp = A68G_SP;
 209    (void) add_mp (p, z, x, mp_one (digs), digs);
 210    A68G_SP = pop_sp;
 211    return z;
 212  }
 213  
 214  //! @brief Test whether x = y.
 215  
 216  BOOL_T same_mp (NODE_T * p, MP_T * x, MP_T * y, int digs)
 217  {
 218    (void) p;
 219    if ((MP_STATUS (x) == MP_STATUS (y)) && (MP_EXPONENT (x) == MP_EXPONENT (y))) {
 220      for (int k = digs; k >= 1; k--) {
 221        if (MP_DIGIT (x, k) != MP_DIGIT (y, k)) {
 222          return A68G_FALSE;
 223        }
 224      }
 225      return A68G_TRUE;
 226    } else {
 227      return A68G_FALSE;
 228    }
 229  }
 230  
 231  //! @brief Align 10-base z in a MP_RADIX mantissa.
 232  
 233  MP_T *align_mp (MP_T * z, INT_T * expo, int digs)
 234  {
 235    INT_T shift;
 236    if (*expo >= 0) {
 237      shift = LOG_MP_RADIX - (*expo) % LOG_MP_RADIX - 1;
 238      (*expo) /= LOG_MP_RADIX;
 239    } else {
 240      shift = (-(*expo) - 1) % LOG_MP_RADIX;
 241      (*expo) = ((*expo) + 1) / LOG_MP_RADIX;
 242      (*expo)--;
 243    }
 244  // Optimising below code does not make the library noticeably faster.
 245    for (int i = 1; i <= shift; i++) {
 246      INT_T carry = 0;
 247      for (INT_T j = 1; j <= digs; j++) {
 248        MP_INT_T k = ((MP_INT_T) MP_DIGIT (z, j)) % 10;
 249        MP_DIGIT (z, j) = (MP_T) ((MP_INT_T) (MP_DIGIT (z, j) / 10) + carry * (MP_RADIX / 10));
 250        carry = k;
 251      }
 252    }
 253    return z;
 254  }
 255  
 256  //! @brief Transform string into multi-precision number.
 257  
 258  MP_T *strtomp (NODE_T * p, MP_T * z, char *s, int digs)
 259  {
 260    BOOL_T ok = A68G_TRUE;
 261    errno = 0;
 262    SET_MP_ZERO (z, digs);
 263    while (IS_SPACE (s[0])) {
 264      s++;
 265    }
 266  // Get the sign.
 267    int sign = (s[0] == '-' ? -1 : 1);
 268    if (s[0] == '+' || s[0] == '-') {
 269      s++;
 270    }
 271  // Scan mantissa digs and put them into "z".
 272    while (s[0] == '0') {
 273      s++;
 274    }
 275    int i = 0, dig = 1;
 276    INT_T sum = 0, dot = -1, one = -1, pow = 0, W = MP_RADIX / 10;
 277    while (s[i] != NULL_CHAR && dig <= digs && (IS_DIGIT (s[i]) || s[i] == POINT_CHAR)) {
 278      if (s[i] == POINT_CHAR) {
 279        dot = i;
 280      } else {
 281        int value = (int) s[i] - (int) '0';
 282        if (one < 0 && value > 0) {
 283          one = pow;
 284        }
 285        sum += W * value;
 286        if (one >= 0) {
 287          W /= 10;
 288        }
 289        pow++;
 290        if (W < 1) {
 291          MP_DIGIT (z, dig++) = (MP_T) sum;
 292          sum = 0;
 293          W = MP_RADIX / 10;
 294        }
 295      }
 296      i++;
 297    }
 298  // Store the last digs.
 299    if (dig <= digs) {
 300      MP_DIGIT (z, dig++) = (MP_T) sum;
 301    }
 302  // See if there is an exponent.
 303    INT_T expo;
 304    if (s[i] != NULL_CHAR && TO_UPPER (s[i]) == TO_UPPER (EXPONENT_CHAR)) {
 305      char *end;
 306      expo = (int) strtol (&(s[++i]), &end, 10);
 307      ok = (BOOL_T) (end[0] == NULL_CHAR);
 308    } else {
 309      expo = 0;
 310      ok = (BOOL_T) (s[i] == NULL_CHAR);
 311    }
 312  // Calculate effective exponent.
 313    if (dot >= 0) {
 314      if (one > dot) {
 315        expo -= one - dot + 1;
 316      } else {
 317        expo += dot - 1;
 318      }
 319    } else {
 320      expo += pow - 1;
 321    }
 322    (void) align_mp (z, &expo, digs);
 323    MP_EXPONENT (z) = (MP_DIGIT (z, 1) == 0 ? 0 : (MP_T) expo);
 324    MP_DIGIT (z, 1) *= sign;
 325    check_mp_exp (p, z);
 326    if (errno == 0 && ok) {
 327      return z;
 328    } else {
 329      return NaN_MP;
 330    }
 331  }
 332  
 333  //! @brief Convert integer to multi-precison number.
 334  
 335  MP_T *int_to_mp (NODE_T * p, MP_T * z, INT_T k, int digs)
 336  {
 337    int sign_k = 1;
 338    if (k < 0) {
 339      k = -k;
 340      sign_k = -1;
 341    }
 342    INT_T m = k, n = 0;
 343    while ((m /= MP_RADIX) != 0) {
 344      n++;
 345    }
 346    set_mp (z, 0, n, digs);
 347    for (int j = 1 + n; j >= 1; j--) {
 348      MP_DIGIT (z, j) = (MP_T) (k % MP_RADIX);
 349      k /= MP_RADIX;
 350    }
 351    MP_DIGIT (z, 1) = sign_k * MP_DIGIT (z, 1);
 352    check_mp_exp (p, z);
 353    return z;
 354  }
 355  
 356  //! @brief Convert unt to multi-precison number.
 357  
 358  MP_T *unt_to_mp (NODE_T * p, MP_T * z, UNSIGNED_T k, int digs)
 359  {
 360    int m = k, n = 0;
 361    while ((m /= MP_RADIX) != 0) {
 362      n++;
 363    }
 364    set_mp (z, 0, n, digs);
 365    for (int j = 1 + n; j >= 1; j--) {
 366      MP_DIGIT (z, j) = (MP_T) (k % MP_RADIX);
 367      k /= MP_RADIX;
 368    }
 369    check_mp_exp (p, z);
 370    return z;
 371  }
 372  
 373  //! @brief Convert multi-precision number to integer.
 374  
 375  INT_T mp_to_int (NODE_T * p, MP_T * z, int digs)
 376  {
 377  // This routines looks a lot like "strtol". 
 378    INT_T expo = (int) MP_EXPONENT (z), sum = 0, weight = 1;
 379    if (expo >= digs) {
 380      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, MOID (p));
 381      exit_genie (p, A68G_RUNTIME_ERROR);
 382    }
 383    BOOL_T negative = (BOOL_T) (MP_DIGIT (z, 1) < 0);
 384    if (negative) {
 385      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 386    }
 387    for (int j = 1 + expo; j >= 1; j--) {
 388      if ((MP_INT_T) MP_DIGIT (z, j) > A68G_MAX_INT / weight) {
 389        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, M_INT);
 390        exit_genie (p, A68G_RUNTIME_ERROR);
 391      }
 392      INT_T term = (MP_INT_T) MP_DIGIT (z, j) * weight;
 393      if (sum > A68G_MAX_INT - term) {
 394        diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, M_INT);
 395        exit_genie (p, A68G_RUNTIME_ERROR);
 396      }
 397      sum += term;
 398      weight *= MP_RADIX;
 399    }
 400    return negative ? -sum : sum;
 401  }
 402  
 403  //! @brief Convert REAL_T to multi-precison number.
 404  
 405  MP_T *real_to_mp (NODE_T * p, MP_T * z, REAL_T x, int digs)
 406  {
 407    SET_MP_ZERO (z, digs);
 408    if (a68g_isinf_real (x)) {
 409      if (x == A68G_POSINF_REAL) {
 410        MP_STATUS (z) = (PLUS_INF_MASK | INIT_MASK);
 411      } else {
 412        MP_STATUS (z) = (MINUS_INF_MASK | INIT_MASK);
 413      }
 414      return z;
 415    }
 416    if (x == 0.0) {
 417      return z;
 418    }
 419  // Small integers can be done better by int_to_mp.
 420    if (ABS (x) < MP_RADIX && trunc (x) == x) {
 421      return int_to_mp (p, z, (INT_T) trunc (x), digs);
 422    }
 423    int sign_x = SIGN (x);
 424  // Scale to [0, 0.1>.
 425    REAL_T a = ABS (x);
 426    INT_T expo = (int) log10 (a);
 427    a /= ten_up (expo);
 428    expo--;
 429    if (a >= 1) {
 430      a /= 10;
 431      expo++;
 432    }
 433  // Transport digs of x to the mantissa of z.
 434    INT_T sum = 0, weight = (MP_RADIX / 10);
 435    int j = 1;
 436    for (int k = 0; a != 0.0 && j <= digs && k < A68G_REAL_DIG; k++) {
 437      REAL_T u = a * 10;
 438      REAL_T v = floor (u);
 439      a = u - v;
 440      sum += weight * (INT_T) v;
 441      weight /= 10;
 442      if (weight < 1) {
 443        MP_DIGIT (z, j++) = (MP_T) sum;
 444        sum = 0;
 445        weight = (MP_RADIX / 10);
 446      }
 447    }
 448  // Store the last digs.
 449    if (j <= digs) {
 450      MP_DIGIT (z, j) = (MP_T) sum;
 451    }
 452    (void) align_mp (z, &expo, digs);
 453    MP_EXPONENT (z) = (MP_T) expo;
 454    MP_DIGIT (z, 1) *= sign_x;
 455    check_mp_exp (p, z);
 456    return z;
 457  }
 458  
 459  //! @brief Convert multi-precision number to real.
 460  
 461  REAL_T mp_to_real (NODE_T * p, MP_T * z, int digs)
 462  {
 463  // This routine looks a lot like "strtod".
 464    (void) p;
 465    if (PLUS_INF_MP (z)) {
 466      return A68G_POSINF_REAL;
 467    }
 468    if (MINUS_INF_MP (z)) {
 469      return A68G_MININF_REAL;
 470    }
 471    if (MP_EXPONENT (z) * (MP_T) LOG_MP_RADIX <= (MP_T) A68G_REAL_MIN_EXP) {
 472      return 0;
 473    } else {
 474      REAL_T terms[1 + MP_MAX_DIGITS];
 475      REAL_T weight = ten_up ((int) (MP_EXPONENT (z) * LOG_MP_RADIX));
 476      int lim = MIN (digs, MP_MAX_DIGITS);
 477      for (int k = 1; k <= lim; k++) {
 478        terms[k] = ABS (MP_DIGIT (z, k)) * weight;
 479        weight /= MP_RADIX;
 480      }
 481  // Sum terms from small to large.
 482      REAL_T sum = 0;
 483      for (int k = lim; k >= 1; k--) {
 484        sum += terms[k];
 485      }
 486      CHECK_REAL (p, sum);
 487      return MP_DIGIT (z, 1) >= 0 ? sum : -sum;
 488    }
 489  }
 490  
 491  //! @brief Normalise positive intermediate, fast.
 492  
 493  static inline void norm_mp_light (MP_T * w, int k, int digs)
 494  {
 495  // Bring every digit back to [0 .. MP_RADIX>.
 496    MP_T *z = &MP_DIGIT (w, digs);
 497    for (int j = digs; j >= k; j--, z--) {
 498      if (z[0] >= MP_RADIX) {
 499        z[0] -= (MP_T) MP_RADIX;
 500        z[-1] += 1;
 501      } else if (z[0] < 0) {
 502        z[0] += (MP_T) MP_RADIX;
 503        z[-1] -= 1;
 504      }
 505    }
 506  }
 507  
 508  //! @brief Normalise positive intermediate.
 509  
 510  static inline void norm_mp (MP_T * w, int k, int digs)
 511  {
 512  // Bring every digit back to [0 .. MP_RADIX>.
 513    MP_T *z = &MP_DIGIT (w, digs);
 514    for (int j = digs; j >= k; j--, z--) {
 515      if (z[0] >= (MP_T) MP_RADIX) {
 516        MP_T carry = (MP_T) ((MP_INT_T) (z[0] / (MP_T) MP_RADIX));
 517        z[0] -= carry * (MP_T) MP_RADIX;
 518        z[-1] += carry;
 519      } else if (z[0] < (MP_T) 0) {
 520        MP_T carry = (MP_T) 1 + (MP_T) ((MP_INT_T) ((-z[0] - 1) / (MP_T) MP_RADIX));
 521        z[0] += carry * (MP_T) MP_RADIX;
 522        z[-1] -= carry;
 523      }
 524    }
 525  }
 526  
 527  //! @brief Round multi-precision number.
 528  
 529  static inline void round_internal_mp (MP_T * z, MP_T * w, int digs)
 530  {
 531  // Assume that w has precision of at least 2 + digs.
 532    int last = (MP_DIGIT (w, 1) == 0 ? 2 + digs : 1 + digs);
 533    if (MP_DIGIT (w, last) >= MP_RADIX / 2) {
 534      MP_DIGIT (w, last - 1) += 1;
 535    }
 536    if (MP_DIGIT (w, last - 1) >= MP_RADIX) {
 537      norm_mp (w, 2, last); // Hardly ever happens - no need to optimise
 538    }
 539    if (MP_DIGIT (w, 1) == 0) {
 540      (void) move_mp_part (&MP_DIGIT (z, 1), &MP_DIGIT (w, 2), digs);
 541      MP_EXPONENT (z) = MP_EXPONENT (w) - 1;
 542    } else if (z != w) {
 543      (void) move_mp_part (&MP_EXPONENT (z), &MP_EXPONENT (w), (1 + digs));
 544    }
 545  // Zero is zero is zero.
 546    if (MP_DIGIT (z, 1) == 0) {
 547      MP_EXPONENT (z) = (MP_T) 0;
 548    }
 549  }
 550  
 551  //! @brief Truncate at decimal point.
 552  
 553  MP_T *trunc_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 554  {
 555    if (MP_EXPONENT (x) < 0) {
 556      SET_MP_ZERO (z, digs);
 557    } else if (MP_EXPONENT (x) >= (MP_T) digs) {
 558      errno = EDOM;
 559      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, (IS (MOID (p), PROC_SYMBOL) ? SUB_MOID (p) : MOID (p)));
 560      exit_genie (p, A68G_RUNTIME_ERROR);
 561    } else {
 562      (void) move_mp (z, x, digs);
 563      for (int k = (int) (MP_EXPONENT (x) + 2); k <= digs; k++) {
 564        MP_DIGIT (z, k) = (MP_T) 0;
 565      }
 566    }
 567    return z;
 568  }
 569  
 570  //! @brief Floor - largest integer smaller than x.
 571  
 572  MP_T *floor_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
 573  {
 574    if (MP_EXPONENT (x) < 0) {
 575      SET_MP_ZERO (z, digs);
 576    } else if (MP_EXPONENT (x) >= (MP_T) digs) {
 577      errno = EDOM;
 578      diagnostic (A68G_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, (IS (MOID (p), PROC_SYMBOL) ? SUB_MOID (p) : MOID (p)));
 579      exit_genie (p, A68G_RUNTIME_ERROR);
 580    } else {
 581      (void) move_mp (z, x, digs);
 582      for (int k = (int) (MP_EXPONENT (x) + 2); k <= digs; k++) {
 583        MP_DIGIT (z, k) = (MP_T) 0;
 584      }
 585    }
 586    if (MP_DIGIT (x, 1) < 0 && ! same_mp (p, z, x, digs)) {
 587      (void) minus_one_mp (p, z, z, digs);
 588    }
 589    return z;
 590  }
 591  
 592  //! @brief Shorten and round.
 593  
 594  MP_T *shorten_mp (NODE_T * p, MP_T * z, int digs, MP_T * x, int digs_x)
 595  {
 596    if (digs > digs_x) {
 597      return lengthen_mp (p, z, digs, x, digs_x);
 598    } else if (digs == digs_x) {
 599      return move_mp (z, x, digs);
 600    } else {
 601  // Reserve extra digs for proper rounding.
 602      ADDR_T pop_sp = A68G_SP;
 603      int digs_h = digs + 2;
 604      BOOL_T negative = (BOOL_T) (MP_DIGIT (x, 1) < 0);
 605      MP_T *w = nil_mp (p, digs_h);
 606      if (negative) {
 607        MP_DIGIT (x, 1) = -MP_DIGIT (x, 1);
 608      }
 609      MP_STATUS (w) = (MP_T) 0;
 610      MP_EXPONENT (w) = MP_EXPONENT (x) + 1;
 611      MP_DIGIT (w, 1) = (MP_T) 0;
 612      (void) move_mp_part (&MP_DIGIT (w, 2), &MP_DIGIT (x, 1), digs + 1);
 613      round_internal_mp (z, w, digs);
 614      if (negative) {
 615        MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 616      }
 617      MP_STATUS (z) = MP_STATUS (x);
 618      A68G_SP = pop_sp;
 619      return z;
 620    }
 621  }
 622  
 623  //! @brief Lengthen x and assign to z.
 624  
 625  MP_T *lengthen_mp (NODE_T * p, MP_T * z, int digs_z, MP_T * x, int digs_x)
 626  {
 627    if (digs_z < digs_x) {
 628      return shorten_mp (p, z, digs_z, x, digs_x);
 629    } else if (digs_z == digs_x) {
 630      return move_mp (z, x, digs_z);
 631    } else {
 632      if (z != x) {
 633        (void) move_mp_part (&MP_DIGIT (z, 1), &MP_DIGIT (x, 1), digs_x);
 634        MP_EXPONENT (z) = MP_EXPONENT (x);
 635        MP_STATUS (z) = MP_STATUS (x);
 636      }
 637      for (int j = 1 + digs_x; j <= digs_z; j++) {
 638        MP_DIGIT (z, j) = (MP_T) 0;
 639      }
 640    }
 641    return z;
 642  }
 643  
 644  //! @brief Set "z" to the sum of positive "x" and positive "y".
 645  
 646  MP_T *add_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
 647  {
 648    MP_STATUS (z) = (MP_T) INIT_MASK;
 649  // Trivial cases.
 650    if (MP_DIGIT (x, 1) == (MP_T) 0) {
 651      (void) move_mp (z, y, digs);
 652      return z;
 653    } else if (MP_DIGIT (y, 1) == 0) {
 654      (void) move_mp (z, x, digs);
 655      return z;
 656    }
 657  // We want positive arguments.
 658    ADDR_T pop_sp = A68G_SP;
 659    MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
 660    MP_DIGIT (x, 1) = ABS (x_1);
 661    MP_DIGIT (y, 1) = ABS (y_1);
 662    if (x_1 >= 0 && y_1 < 0) {
 663      (void) sub_mp (p, z, x, y, digs);
 664    } else if (x_1 < 0 && y_1 >= 0) {
 665      (void) sub_mp (p, z, y, x, digs);
 666    } else if (x_1 < 0 && y_1 < 0) {
 667      (void) add_mp (p, z, x, y, digs);
 668      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 669    } else {
 670  // Add.
 671      int digs_h = 2 + digs;
 672      MP_T *w = nil_mp (p, digs_h);
 673      if (MP_EXPONENT (x) == MP_EXPONENT (y)) {
 674        MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
 675        for (int j = 1; j <= digs; j++) {
 676          MP_DIGIT (w, j + 1) = MP_DIGIT (x, j) + MP_DIGIT (y, j);
 677        }
 678        MP_DIGIT (w, digs_h) = (MP_T) 0;
 679      } else if (MP_EXPONENT (x) > MP_EXPONENT (y)) {
 680        int shl_y = (int) MP_EXPONENT (x) - (int) MP_EXPONENT (y);
 681        MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
 682        for (int j = 1; j < digs_h; j++) {
 683          int i_y = j - shl_y;
 684          MP_T x_j = (j > digs ? 0 : MP_DIGIT (x, j));
 685          MP_T y_j = (i_y <= 0 || i_y > digs ? 0 : MP_DIGIT (y, i_y));
 686          MP_DIGIT (w, j + 1) = x_j + y_j;
 687        }
 688      } else {
 689        int shl_x = (int) MP_EXPONENT (y) - (int) MP_EXPONENT (x);
 690        MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (y);
 691        for (int j = 1; j < digs_h; j++) {
 692          int i_x = j - shl_x;
 693          MP_T x_j = (i_x <= 0 || i_x > digs ? 0 : MP_DIGIT (x, i_x));
 694          MP_T y_j = (j > digs ? 0 : MP_DIGIT (y, j));
 695          MP_DIGIT (w, j + 1) = x_j + y_j;
 696        }
 697      }
 698      norm_mp_light (w, 2, digs_h);
 699      round_internal_mp (z, w, digs);
 700      check_mp_exp (p, z);
 701    }
 702  // Restore and exit.
 703    A68G_SP = pop_sp;
 704    MP_T z_1 = MP_DIGIT (z, 1);
 705    MP_DIGIT (x, 1) = x_1;
 706    MP_DIGIT (y, 1) = y_1;
 707    MP_DIGIT (z, 1) = z_1;        // In case z IS x OR z IS y
 708    return z;
 709  }
 710  
 711  //! @brief Set "z" to the difference of positive "x" and positive "y".
 712  
 713  MP_T *sub_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
 714  {
 715    MP_STATUS (z) = (MP_T) INIT_MASK;
 716  // Trivial cases.
 717    if (MP_DIGIT (x, 1) == (MP_T) 0) {
 718      (void) move_mp (z, y, digs);
 719      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 720      return z;
 721    } else if (MP_DIGIT (y, 1) == (MP_T) 0) {
 722      (void) move_mp (z, x, digs);
 723      return z;
 724    }
 725  // We want positive arguments.
 726    ADDR_T pop_sp = A68G_SP;
 727    MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
 728    MP_DIGIT (x, 1) = ABS (x_1);
 729    MP_DIGIT (y, 1) = ABS (y_1);
 730    if (x_1 >= 0 && y_1 < 0) {
 731      (void) add_mp (p, z, x, y, digs);
 732    } else if (x_1 < 0 && y_1 >= 0) {
 733      (void) add_mp (p, z, y, x, digs);
 734      MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 735    } else if (x_1 < 0 && y_1 < 0) {
 736      (void) sub_mp (p, z, y, x, digs);
 737    } else {
 738  // Subtract.
 739      BOOL_T negative = A68G_FALSE;
 740      int fnz, digs_h = 2 + digs;
 741      MP_T *w = nil_mp (p, digs_h);
 742      if (MP_EXPONENT (x) == MP_EXPONENT (y)) {
 743        MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
 744        for (int j = 1; j <= digs; j++) {
 745          MP_DIGIT (w, j + 1) = MP_DIGIT (x, j) - MP_DIGIT (y, j);
 746        }
 747        MP_DIGIT (w, digs_h) = (MP_T) 0;
 748      } else if (MP_EXPONENT (x) > MP_EXPONENT (y)) {
 749        int shl_y = (int) MP_EXPONENT (x) - (int) MP_EXPONENT (y);
 750        MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
 751        for (int j = 1; j < digs_h; j++) {
 752          int i_y = j - shl_y;
 753          MP_T x_j = (j > digs ? 0 : MP_DIGIT (x, j));
 754          MP_T y_j = (i_y <= 0 || i_y > digs ? 0 : MP_DIGIT (y, i_y));
 755          MP_DIGIT (w, j + 1) = x_j - y_j;
 756        }
 757      } else {
 758        int shl_x = (int) MP_EXPONENT (y) - (int) MP_EXPONENT (x);
 759        MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (y);
 760        for (int j = 1; j < digs_h; j++) {
 761          int i_x = j - shl_x;
 762          MP_T x_j = (i_x <= 0 || i_x > digs ? 0 : MP_DIGIT (x, i_x));
 763          MP_T y_j = (j > digs ? 0 : MP_DIGIT (y, j));
 764          MP_DIGIT (w, j + 1) = x_j - y_j;
 765        }
 766      }
 767  // Correct if we subtract large from small.
 768      if (MP_DIGIT (w, 2) <= 0) {
 769        fnz = -1;
 770        for (int j = 2; j <= digs_h && fnz < 0; j++) {
 771          if (MP_DIGIT (w, j) != 0) {
 772            fnz = j;
 773          }
 774        }
 775        negative = (BOOL_T) (MP_DIGIT (w, fnz) < 0);
 776        if (negative) {
 777          for (int j = fnz; j <= digs_h; j++) {
 778            MP_DIGIT (w, j) = -MP_DIGIT (w, j);
 779          }
 780        }
 781      }
 782  // Normalise.
 783      norm_mp_light (w, 2, digs_h);
 784      fnz = -1;
 785      for (int j = 1; j <= digs_h && fnz < 0; j++) {
 786        if (MP_DIGIT (w, j) != 0) {
 787          fnz = j;
 788        }
 789      }
 790      if (fnz > 1) {
 791        int j2 = fnz - 1;
 792        for (int j = 1; j <= digs_h - j2; j++) {
 793          MP_DIGIT (w, j) = MP_DIGIT (w, j + j2);
 794          MP_DIGIT (w, j + j2) = (MP_T) 0;
 795        }
 796        MP_EXPONENT (w) -= j2;
 797      }
 798  // Round.
 799      round_internal_mp (z, w, digs);
 800      if (negative) {
 801        MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
 802      }
 803      check_mp_exp (p, z);
 804    }
 805  // Restore and exit.
 806    A68G_SP = pop_sp;
 807    MP_T z_1 = MP_DIGIT (z, 1);
 808    MP_DIGIT (x, 1) = x_1;
 809    MP_DIGIT (y, 1) = y_1;
 810    MP_DIGIT (z, 1) = z_1;        // In case z IS x OR z IS y
 811    return z;
 812  }
 813  
 814  //! @brief Set "z" to the product of "x" and "y".
 815  
 816  MP_T *mul_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
 817  {
 818    if (IS_ZERO_MP (x) || IS_ZERO_MP (y)) {
 819      SET_MP_ZERO (z, digs);
 820      return z;
 821    }
 822  // Grammar school algorithm with intermittent normalisation.
 823    ADDR_T pop_sp = A68G_SP;
 824    int digs_h = 2 + digs;
 825    MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
 826    MP_DIGIT (x, 1) = ABS (x_1);
 827    MP_DIGIT (y, 1) = ABS (y_1);
 828    MP_STATUS (z) = (MP_T) INIT_MASK;
 829    MP_T *w = lit_mp (p, 0, MP_EXPONENT (x) + MP_EXPONENT (y) + 1, digs_h);
 830    int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (2 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
 831    for (int i = digs; i >= 1; i--) {
 832      MP_T yi = MP_DIGIT (y, i);
 833      if (yi != 0) {
 834        int k = digs_h - i;
 835        int j = (k > digs ? digs : k);
 836        MP_T *u = &MP_DIGIT (w, i + j), *v = &MP_DIGIT (x, j);
 837        if ((digs - i + 1) % oflow == 0) {
 838          norm_mp (w, 2, digs_h);
 839        }
 840        while (j-- >= 1) {
 841          (u--)[0] += yi * (v--)[0];
 842        }
 843      }
 844    }
 845    norm_mp (w, 2, digs_h);
 846    round_internal_mp (z, w, digs);
 847  // Restore and exit.
 848    A68G_SP = pop_sp;
 849    MP_T z_1 = MP_DIGIT (z, 1);
 850    MP_DIGIT (x, 1) = x_1;
 851    MP_DIGIT (y, 1) = y_1;
 852    MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
 853    check_mp_exp (p, z);
 854    return z;
 855  }
 856  
 857  //! @brief Set "z" to the quotient of "x" and "y".
 858  
 859  MP_T *div_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
 860  {
 861  // This routine is based on
 862  // 
 863  //    D. M. Smith, "A Multiple-Precision Division Algorithm"
 864  //    Mathematics of Computation 66 (1996) 157-163.
 865  // 
 866  // This is O(N^2) but runs faster than straightforward methods by skipping
 867  // most of the intermediate normalisation and recovering from wrong
 868  // guesses without separate correction steps.
 869  // Depending on application, div_mp cost is circa 3 times that of mul_mp.
 870  // Therefore Newton-Raphson division makes no sense here.
 871    if (IS_ZERO_MP (y)) {
 872      errno = ERANGE;
 873      return NaN_MP;
 874    }
 875  // Determine normalisation interval assuming that q < 2b in each step.
 876    #if (A68G_LEVEL <= 2)
 877      int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (3 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
 878    #else
 879      int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (2 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
 880    #endif
 881    MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
 882    MP_DIGIT (x, 1) = ABS (x_1);
 883    MP_DIGIT (y, 1) = ABS (y_1);
 884    MP_STATUS (z) = (MP_T) INIT_MASK;
 885  // Slight optimisation when the denominator has few digits.
 886    int nzdigs = digs;
 887    while (MP_DIGIT (y, nzdigs) == 0 && nzdigs > 1) {
 888      nzdigs--;
 889    }
 890    if (nzdigs == 1 && MP_EXPONENT (y) == 0) {
 891      (void) div_mp_digit (p, z, x, MP_DIGIT (y, 1), digs);
 892      MP_T z_1 = MP_DIGIT (z, 1);
 893      MP_DIGIT (x, 1) = x_1;
 894      MP_DIGIT (y, 1) = y_1;
 895      MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
 896      check_mp_exp (p, z);
 897      return z;
 898    }
 899  // Working nominator in which the quotient develops.
 900    ADDR_T pop_sp = A68G_SP;
 901    int wdigs = 4 + digs;
 902    MP_T *w = lit_mp (p, 0, MP_EXPONENT (x) - MP_EXPONENT (y), wdigs);
 903    (void) move_mp_part (&MP_DIGIT (w, 2), &MP_DIGIT (x, 1), digs);
 904  // Estimate the denominator. For small MP_RADIX add: MP_DIGIT (y, 4) / MP_REAL_RADIX.
 905    MP_REAL_T den = (MP_DIGIT (y, 1) * MP_REAL_RADIX + MP_DIGIT (y, 2)) * MP_REAL_RADIX + MP_DIGIT (y, 3);
 906    MP_T *t = &MP_DIGIT (w, 2);
 907    for (int k = 1, len = digs + 2, first = 3; k <= digs + 2; k++, len++, first++, t++) {
 908  // Estimate quotient digit.
 909      MP_REAL_T q, nom = ((t[-1] * MP_REAL_RADIX + t[0]) * MP_REAL_RADIX + t[1]) * MP_REAL_RADIX + (wdigs >= (first + 2) ? t[2] : 0);
 910      if (nom == 0) {
 911        q = 0;
 912      } else {
 913  // Correct the nominator.
 914        q = (MP_T) (MP_INT_T) (nom / den);
 915        int lim = MINIMUM (len, wdigs);
 916        if (nzdigs <= lim - first + 1) {
 917          lim = first + nzdigs - 1;
 918        }
 919        MP_T *u = t, *v = &MP_DIGIT (y, 1);
 920        for (int j = first; j <= lim; j++) {
 921          (u++)[0] -= q * (v++)[0];
 922        }
 923      }
 924      t[0] += t[-1] * MP_RADIX;
 925      t[-1] = q;
 926      if (k % oflow == 0 || k == digs + 2) {
 927        norm_mp (w, first, wdigs);
 928      }
 929    }
 930    norm_mp (w, 2, digs);
 931    round_internal_mp (z, w, digs);
 932  // Restore and exit.
 933    A68G_SP = pop_sp;
 934    MP_T z_1 = MP_DIGIT (z, 1);
 935    MP_DIGIT (x, 1) = x_1;
 936    MP_DIGIT (y, 1) = y_1;
 937    MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
 938    check_mp_exp (p, z);
 939    return z;
 940  }
 941  
 942  //! @brief Set "z" to the integer quotient of "x" and "y".
 943  
 944  MP_T *over_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
 945  {
 946    if (MP_DIGIT (y, 1) == 0) {
 947      errno = ERANGE;
 948      return NaN_MP;
 949    }
 950    ADDR_T pop_sp = A68G_SP;
 951    int digs_g = FUN_DIGITS (digs);
 952    MP_T *x_g = len_mp (p, x, digs, digs_g), *y_g = len_mp (p, y, digs, digs_g), *z_g = nil_mp (p, digs_g);
 953    (void) div_mp (p, z_g, x_g, y_g, digs_g);
 954    trunc_mp (p, z_g, z_g, digs_g);
 955    (void) shorten_mp (p, z, digs, z_g, digs_g);
 956    MP_STATUS (z) = (MP_T) INIT_MASK;
 957    A68G_SP = pop_sp;
 958    return z;
 959  }
 960  
 961  //! @brief Set "z" to x mod y.
 962  
 963  MP_T *mod_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
 964  {
 965    if (MP_DIGIT (y, 1) == 0) {
 966      errno = EDOM;
 967      return NaN_MP;
 968    }
 969    int digs_g = FUN_DIGITS (digs);
 970    ADDR_T pop_sp = A68G_SP;
 971    MP_T *x_g = len_mp (p, x, digs, digs_g);
 972    MP_T *y_g = len_mp (p, y, digs, digs_g);
 973    MP_T *z_g = nil_mp (p, digs_g);
 974  // x mod y = x - y * trunc (x / y).
 975    (void) over_mp (p, z_g, x_g, y_g, digs_g);
 976    (void) mul_mp (p, z_g, y_g, z_g, digs_g);
 977    (void) sub_mp (p, z_g, x_g, z_g, digs_g);
 978    (void) shorten_mp (p, z, digs, z_g, digs_g);
 979  // Restore and exit.
 980    A68G_SP = pop_sp;
 981    return z;
 982  }
 983  
 984  //! @brief Set "z" to the product of x and digit y.
 985  
 986  MP_T *mul_mp_digit (NODE_T * p, MP_T * z, MP_T * x, MP_T y, int digs)
 987  {
 988  // This is an O(N) routine for multiplication by a short value.
 989    MP_T x_1 = MP_DIGIT (x, 1);
 990    int digs_h = 2 + digs;
 991    ADDR_T pop_sp = A68G_SP;
 992    MP_DIGIT (x, 1) = ABS (x_1);
 993    MP_STATUS (z) = (MP_T) INIT_MASK;
 994    MP_T y_1 = y;
 995    y = ABS (y_1);
 996    if (y == 2) {
 997      (void) add_mp (p, z, x, x, digs);
 998    } else {
 999      MP_T *w = lit_mp (p, 0, MP_EXPONENT (x) + 1, digs_h);
1000      MP_T *u = &MP_DIGIT (w, 1 + digs), *v = &MP_DIGIT (x, digs);
1001      int j = digs;
1002      while (j-- >= 1) {
1003        (u--)[0] += y * (v--)[0];
1004      }
1005      norm_mp (w, 2, digs_h);
1006      round_internal_mp (z, w, digs);
1007    }
1008  // Restore and exit.
1009    A68G_SP = pop_sp;
1010    MP_T z_1 = MP_DIGIT (z, 1);
1011    MP_DIGIT (x, 1) = x_1;
1012    MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
1013    check_mp_exp (p, z);
1014    return z;
1015  }
1016  
1017  //! @brief Set "z" to x/2.
1018  
1019  MP_T *half_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1020  {
1021    ADDR_T pop_sp = A68G_SP;
1022    int digs_h = 2 + digs;
1023    MP_T x_1 = MP_DIGIT (x, 1);
1024    MP_DIGIT (x, 1) = ABS (x_1);
1025    MP_STATUS (z) = (MP_T) INIT_MASK;
1026  // Calculate x * 0.5.
1027    MP_T *w = lit_mp (p, 0, MP_EXPONENT (x), digs_h);
1028    MP_T *u = &MP_DIGIT (w, 1 + digs), *v = &MP_DIGIT (x, digs);
1029    int j = digs;
1030    while (j-- >= 1) {
1031      (u--)[0] += (MP_RADIX / 2) * (v--)[0];
1032    }
1033    norm_mp (w, 2, digs_h);
1034    round_internal_mp (z, w, digs);
1035  // Restore and exit.
1036    A68G_SP = pop_sp;
1037    MP_T z_1 = MP_DIGIT (z, 1);
1038    MP_DIGIT (x, 1) = x_1;
1039    MP_DIGIT (z, 1) = (x_1 >= 0 ? z_1 : -z_1);
1040    check_mp_exp (p, z);
1041    return z;
1042  }
1043  
1044  //! @brief Set "z" to x/10.
1045  
1046  MP_T *tenth_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1047  {
1048    ADDR_T pop_sp = A68G_SP;
1049    int digs_h = 2 + digs;
1050    MP_T x_1 = MP_DIGIT (x, 1);
1051    MP_DIGIT (x, 1) = ABS (x_1);
1052    MP_STATUS (z) = (MP_T) INIT_MASK;
1053  // Calculate x * 0.1.
1054    MP_T *w = lit_mp (p, 0, MP_EXPONENT (x), digs_h);
1055    MP_T *u = &MP_DIGIT (w, 1 + digs), *v = &MP_DIGIT (x, digs);
1056    int j = digs;
1057    while (j-- >= 1) {
1058      (u--)[0] += (MP_RADIX / 10) * (v--)[0];
1059    }
1060    norm_mp (w, 2, digs_h);
1061    round_internal_mp (z, w, digs);
1062  // Restore and exit.
1063    A68G_SP = pop_sp;
1064    MP_T z_1 = MP_DIGIT (z, 1);
1065    MP_DIGIT (x, 1) = x_1;
1066    MP_DIGIT (z, 1) = (x_1 >= 0 ? z_1 : -z_1);
1067    check_mp_exp (p, z);
1068    return z;
1069  }
1070  
1071  //! @brief Set "z" to the quotient of x and digit y.
1072  
1073  MP_T *div_mp_digit (NODE_T * p, MP_T * z, MP_T * x, MP_T y, int digs)
1074  {
1075    if (y == 0) {
1076      errno = ERANGE;
1077      return NaN_MP;
1078    }
1079  // Determine normalisation interval assuming that q < 2b in each step.
1080    #if (A68G_LEVEL <= 2)
1081      int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (3 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
1082    #else
1083      int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (2 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
1084    #endif
1085  // Work with positive operands.
1086    ADDR_T pop_sp = A68G_SP;
1087    MP_T x_1 = MP_DIGIT (x, 1), y_1 = y;
1088    MP_DIGIT (x, 1) = ABS (x_1);
1089    MP_STATUS (z) = (MP_T) INIT_MASK;
1090    y = ABS (y_1);
1091    if (y == 2) {
1092      (void) half_mp (p, z, x, digs);
1093    } else if (y == 10) {
1094      (void) tenth_mp (p, z, x, digs);
1095    } else {
1096      int wdigs = 4 + digs;
1097      MP_T *w = lit_mp (p, 0, MP_EXPONENT (x), wdigs);
1098      (void) move_mp_part (&MP_DIGIT (w, 2), &MP_DIGIT (x, 1), digs);
1099  // Estimate the denominator.
1100      MP_REAL_T den = (MP_REAL_T) y * MP_REAL_RADIX * MP_REAL_RADIX;
1101      MP_T *t = &MP_DIGIT (w, 2);
1102      for (int k = 1, first = 3; k <= digs + 2; k++, first++, t++) {
1103  // Estimate quotient digit and correct.
1104        MP_REAL_T nom = ((t[-1] * MP_REAL_RADIX + t[0]) * MP_REAL_RADIX + t[1]) * MP_REAL_RADIX + (wdigs >= (first + 2) ? t[2] : 0);
1105        MP_REAL_T q = (MP_T) (MP_INT_T) (nom / den);
1106        t[0] += t[-1] * MP_RADIX - q * y;
1107        t[-1] = q;
1108        if (k % oflow == 0 || k == digs + 2) {
1109          norm_mp (w, first, wdigs);
1110        }
1111      }
1112      norm_mp (w, 2, digs);
1113      round_internal_mp (z, w, digs);
1114    }
1115  // Restore and exit.
1116    A68G_SP = pop_sp;
1117    MP_T z_1 = MP_DIGIT (z, 1);
1118    MP_DIGIT (x, 1) = x_1;
1119    MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
1120    check_mp_exp (p, z);
1121    return z;
1122  }
1123  
1124  //! @brief Set "z" to the integer quotient of "x" and "y".
1125  
1126  MP_T *over_mp_digit (NODE_T * p, MP_T * z, MP_T * x, MP_T y, int digs)
1127  {
1128    if (y == 0) {
1129      errno = ERANGE;
1130      return NaN_MP;
1131    }
1132    int digs_g = FUN_DIGITS (digs);
1133    ADDR_T pop_sp = A68G_SP;
1134    MP_T *x_g = len_mp (p, x, digs, digs_g), *z_g = nil_mp (p, digs_g);
1135    (void) div_mp_digit (p, z_g, x_g, y, digs_g);
1136    trunc_mp (p, z_g, z_g, digs_g);
1137    (void) shorten_mp (p, z, digs, z_g, digs_g);
1138  // Restore and exit.
1139    A68G_SP = pop_sp;
1140    return z;
1141  }
1142  
1143  //! @brief Set "z" to the reciprocal of "x".
1144  
1145  MP_T *rec_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1146  {
1147    if (IS_ZERO_MP (x)) {
1148      errno = ERANGE;
1149      return NaN_MP;
1150    }
1151    ADDR_T pop_sp = A68G_SP;
1152    (void) div_mp (p, z, mp_one (digs), x, digs);
1153    A68G_SP = pop_sp;
1154    return z;
1155  }
1156  
1157  //! @brief LONG REAL long pi
1158  
1159  void genie_pi_mp (NODE_T * p)
1160  {
1161    int digs = DIGITS (MOID (p));
1162    MP_T *z = nil_mp (p, digs);
1163    (void) mp_pi (p, z, MP_PI, digs);
1164    MP_STATUS (z) = (MP_T) INIT_MASK;
1165  }
1166  
1167  //! @brief Set "z" to "x" ** "n".
1168  
1169  MP_T *pow_mp_int (NODE_T * p, MP_T * z, MP_T * x, INT_T n, int digs)
1170  {
1171    ADDR_T pop_sp = A68G_SP;
1172    int bit, digs_g = FUN_DIGITS (digs);
1173    MP_T *x_g = len_mp (p, x, digs, digs_g), *z_g = lit_mp (p, 1, 0, digs_g);
1174    BOOL_T negative = (BOOL_T) (n < 0);
1175    if (negative) {
1176      n = -n;
1177    }
1178    bit = 1;
1179    while ((int) bit <= (int) n) {
1180      if (n & bit) {
1181        (void) mul_mp (p, z_g, z_g, x_g, digs_g);
1182      }
1183      (void) mul_mp (p, x_g, x_g, x_g, digs_g);
1184      bit <<= 1;
1185    }
1186    (void) shorten_mp (p, z, digs, z_g, digs_g);
1187    A68G_SP = pop_sp;
1188    if (negative) {
1189      (void) rec_mp (p, z, z, digs);
1190    }
1191    check_mp_exp (p, z);
1192    return z;
1193  }
1194  
1195  //! @brief Set "z" to "x" ** "y".
1196  
1197  MP_T *pow_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
1198  {
1199    PRELUDE_ERROR (ln_mp (p, z, x, digs) == NaN_MP, p, ERROR_INVALID_ARGUMENT, MOID (p));
1200    (void) mul_mp (p, z, y, z, digs);
1201    (void) exp_mp (p, z, z, digs);
1202    return z;
1203  }
1204  
1205  //! @brief Set "z" to 10 ** "n".
1206  
1207  MP_T *ten_up_mp (NODE_T * p, MP_T * z, int n, int digs)
1208  {
1209    #if (A68G_LEVEL >= 3)
1210      static MP_T y[LOG_MP_RADIX] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
1211    #else
1212      static MP_T y[LOG_MP_RADIX] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
1213    #endif
1214    if (n >= 0) {
1215      set_mp (z, y[n % LOG_MP_RADIX], n / LOG_MP_RADIX, digs);
1216    } else {
1217      set_mp (z, y[(LOG_MP_RADIX + n % LOG_MP_RADIX) % LOG_MP_RADIX], (n + 1) / LOG_MP_RADIX - 1, digs);
1218    }
1219    check_mp_exp (p, z);
1220    return z;
1221  }
1222  
1223  //! @brief Comparison of "x" and "y".
1224  
1225  void eq_mp (NODE_T * p, A68G_BOOL * z, MP_T * x, MP_T * y, int digs)
1226  {
1227    ADDR_T pop_sp = A68G_SP;
1228    MP_T *v = nil_mp (p, digs);
1229    (void) sub_mp (p, v, x, y, digs);
1230    STATUS (z) = INIT_MASK;
1231    VALUE (z) = (MP_DIGIT (v, 1) == 0 ? A68G_TRUE : A68G_FALSE);
1232    A68G_SP = pop_sp;
1233  }
1234  
1235  //! @brief Comparison of "x" and "y".
1236  
1237  void ne_mp (NODE_T * p, A68G_BOOL * z, MP_T * x, MP_T * y, int digs)
1238  {
1239    ADDR_T pop_sp = A68G_SP;
1240    MP_T *v = nil_mp (p, digs);
1241    (void) sub_mp (p, v, x, y, digs);
1242    STATUS (z) = INIT_MASK;
1243    VALUE (z) = (MP_DIGIT (v, 1) != 0 ? A68G_TRUE : A68G_FALSE);
1244    A68G_SP = pop_sp;
1245  }
1246  
1247  //! @brief Comparison of "x" and "y".
1248  
1249  void lt_mp (NODE_T * p, A68G_BOOL * z, MP_T * x, MP_T * y, int digs)
1250  {
1251    ADDR_T pop_sp = A68G_SP;
1252    MP_T *v = nil_mp (p, digs);
1253    (void) sub_mp (p, v, x, y, digs);
1254    STATUS (z) = INIT_MASK;
1255    VALUE (z) = (MP_DIGIT (v, 1) < 0 ? A68G_TRUE : A68G_FALSE);
1256    A68G_SP = pop_sp;
1257  }
1258  
1259  //! @brief Comparison of "x" and "y".
1260  
1261  void le_mp (NODE_T * p, A68G_BOOL * z, MP_T * x, MP_T * y, int digs)
1262  {
1263    ADDR_T pop_sp = A68G_SP;
1264    MP_T *v = nil_mp (p, digs);
1265    (void) sub_mp (p, v, x, y, digs);
1266    STATUS (z) = INIT_MASK;
1267    VALUE (z) = (MP_DIGIT (v, 1) <= 0 ? A68G_TRUE : A68G_FALSE);
1268    A68G_SP = pop_sp;
1269  }
1270  
1271  //! @brief Comparison of "x" and "y".
1272  
1273  void gt_mp (NODE_T * p, A68G_BOOL * z, MP_T * x, MP_T * y, int digs)
1274  {
1275    ADDR_T pop_sp = A68G_SP;
1276    MP_T *v = nil_mp (p, digs);
1277    (void) sub_mp (p, v, x, y, digs);
1278    STATUS (z) = INIT_MASK;
1279    VALUE (z) = (MP_DIGIT (v, 1) > 0 ? A68G_TRUE : A68G_FALSE);
1280    A68G_SP = pop_sp;
1281  }
1282  
1283  //! @brief Comparison of "x" and "y".
1284  
1285  void ge_mp (NODE_T * p, A68G_BOOL * z, MP_T * x, MP_T * y, int digs)
1286  {
1287    ADDR_T pop_sp = A68G_SP;
1288    MP_T *v = nil_mp (p, digs);
1289    (void) sub_mp (p, v, x, y, digs);
1290    STATUS (z) = INIT_MASK;
1291    VALUE (z) = (MP_DIGIT (v, 1) >= 0 ? A68G_TRUE : A68G_FALSE);
1292    A68G_SP = pop_sp;
1293  }
1294  
1295  //! @brief round (x).
1296  
1297  MP_T *round_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1298  {
1299    MP_T *y = nil_mp (p, digs);
1300    SET_MP_HALF (y, digs);
1301    if (MP_DIGIT (x, 1) >= 0) {
1302      (void) add_mp (p, z, x, y, digs);
1303      (void) trunc_mp (p, z, z, digs);
1304    } else {
1305      (void) sub_mp (p, z, x, y, digs);
1306      (void) trunc_mp (p, z, z, digs);
1307    }
1308    MP_STATUS (z) = (MP_T) INIT_MASK;
1309    return z;
1310  }
1311  
1312  //! @brief Entier (x).
1313  
1314  MP_T *entier_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1315  {
1316    if (MP_DIGIT (x, 1) >= 0) {
1317      (void) trunc_mp (p, z, x, digs);
1318    } else {
1319      MP_T *y = nil_mp (p, digs);
1320      (void) move_mp (y, z, digs);
1321      (void) trunc_mp (p, z, x, digs);
1322      (void) sub_mp (p, y, y, z, digs);
1323      if (MP_DIGIT (y, 1) != 0) {
1324        SET_MP_ONE (y, digs);
1325        (void) sub_mp (p, z, z, y, digs);
1326      }
1327    }
1328    MP_STATUS (z) = (MP_T) INIT_MASK;
1329    return z;
1330  }
     


© 2002-2026 J.M. van der Veer (jmvdveer@xs4all.nl)