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-2024 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 A68_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_WIN32)
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 (A68_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" A68_LD, (MP_INT_T) MP_EXPONENT (z));
139 fprintf (stdout, " S" A68_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) A68_MP (varying_mp_digits)));
154 }
155 return A68_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 = A68_SP;
189 (void) sub_mp (p, z, mp_one (digs), x, digs);
190 A68_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 = A68_SP;
199 (void) sub_mp (p, z, x, mp_one (digs), digs);
200 A68_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 = A68_SP;
209 (void) add_mp (p, z, x, mp_one (digs), digs);
210 A68_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 A68_FALSE;
223 }
224 }
225 return A68_TRUE;
226 } else {
227 return A68_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 = A68_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 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 (A68_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, MOID (p));
381 exit_genie (p, A68_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) > A68_MAX_INT / weight) {
389 diagnostic (A68_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, M_INT);
390 exit_genie (p, A68_RUNTIME_ERROR);
391 }
392 INT_T term = (MP_INT_T) MP_DIGIT (z, j) * weight;
393 if (sum > A68_MAX_INT - term) {
394 diagnostic (A68_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, M_INT);
395 exit_genie (p, A68_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 (x == 0.0) {
409 return z;
410 }
411 // Small integers can be done better by int_to_mp.
412 if (ABS (x) < MP_RADIX && trunc (x) == x) {
413 return int_to_mp (p, z, (INT_T) trunc (x), digs);
414 }
415 int sign_x = SIGN (x);
416 // Scale to [0, 0.1>.
417 REAL_T a = ABS (x);
418 INT_T expo = (int) log10 (a);
419 a /= ten_up (expo);
420 expo--;
421 if (a >= 1) {
422 a /= 10;
423 expo++;
424 }
425 // Transport digs of x to the mantissa of z.
426 INT_T sum = 0, weight = (MP_RADIX / 10);
427 int j = 1;
428 for (int k = 0; a != 0.0 && j <= digs && k < A68_REAL_DIG; k++) {
429 REAL_T u = a * 10;
430 REAL_T v = floor (u);
431 a = u - v;
432 sum += weight * (INT_T) v;
433 weight /= 10;
434 if (weight < 1) {
435 MP_DIGIT (z, j++) = (MP_T) sum;
436 sum = 0;
437 weight = (MP_RADIX / 10);
438 }
439 }
440 // Store the last digs.
441 if (j <= digs) {
442 MP_DIGIT (z, j) = (MP_T) sum;
443 }
444 (void) align_mp (z, &expo, digs);
445 MP_EXPONENT (z) = (MP_T) expo;
446 MP_DIGIT (z, 1) *= sign_x;
447 check_mp_exp (p, z);
448 return z;
449 }
450
451 //! @brief Convert multi-precision number to real.
452
453 REAL_T mp_to_real (NODE_T * p, MP_T * z, int digs)
454 {
455 // This routine looks a lot like "strtod".
456 (void) p;
457 if (MP_EXPONENT (z) * (MP_T) LOG_MP_RADIX <= (MP_T) A68_REAL_MIN_EXP) {
458 return 0;
459 } else {
460 REAL_T terms[1 + MP_MAX_DIGITS];
461 REAL_T weight = ten_up ((int) (MP_EXPONENT (z) * LOG_MP_RADIX));
462 unt lim = MIN (digs, MP_MAX_DIGITS);
463 for (int k = 1; k <= lim; k++) {
464 terms[k] = ABS (MP_DIGIT (z, k)) * weight;
465 weight /= MP_RADIX;
466 }
467 // Sum terms from small to large.
468 REAL_T sum = 0;
469 for (int k = lim; k >= 1; k--) {
470 sum += terms[k];
471 }
472 CHECK_REAL (p, sum);
473 return MP_DIGIT (z, 1) >= 0 ? sum : -sum;
474 }
475 }
476
477 //! @brief Normalise positive intermediate, fast.
478
479 static inline void norm_mp_light (MP_T * w, int k, int digs)
480 {
481 // Bring every digit back to [0 .. MP_RADIX>.
482 MP_T *z = &MP_DIGIT (w, digs);
483 for (int j = digs; j >= k; j--, z--) {
484 if (z[0] >= MP_RADIX) {
485 z[0] -= (MP_T) MP_RADIX;
486 z[-1] += 1;
487 } else if (z[0] < 0) {
488 z[0] += (MP_T) MP_RADIX;
489 z[-1] -= 1;
490 }
491 }
492 }
493
494 //! @brief Normalise positive intermediate.
495
496 static inline void norm_mp (MP_T * w, int k, int digs)
497 {
498 // Bring every digit back to [0 .. MP_RADIX>.
499 MP_T *z = &MP_DIGIT (w, digs);
500 for (int j = digs; j >= k; j--, z--) {
501 if (z[0] >= (MP_T) MP_RADIX) {
502 MP_T carry = (MP_T) ((MP_INT_T) (z[0] / (MP_T) MP_RADIX));
503 z[0] -= carry * (MP_T) MP_RADIX;
504 z[-1] += carry;
505 } else if (z[0] < (MP_T) 0) {
506 MP_T carry = (MP_T) 1 + (MP_T) ((MP_INT_T) ((-z[0] - 1) / (MP_T) MP_RADIX));
507 z[0] += carry * (MP_T) MP_RADIX;
508 z[-1] -= carry;
509 }
510 }
511 }
512
513 //! @brief Round multi-precision number.
514
515 static inline void round_internal_mp (MP_T * z, MP_T * w, int digs)
516 {
517 // Assume that w has precision of at least 2 + digs.
518 int last = (MP_DIGIT (w, 1) == 0 ? 2 + digs : 1 + digs);
519 if (MP_DIGIT (w, last) >= MP_RADIX / 2) {
520 MP_DIGIT (w, last - 1) += 1;
521 }
522 if (MP_DIGIT (w, last - 1) >= MP_RADIX) {
523 norm_mp (w, 2, last); // Hardly ever happens - no need to optimise
524 }
525 if (MP_DIGIT (w, 1) == 0) {
526 (void) move_mp_part (&MP_DIGIT (z, 1), &MP_DIGIT (w, 2), digs);
527 MP_EXPONENT (z) = MP_EXPONENT (w) - 1;
528 } else if (z != w) {
529 (void) move_mp_part (&MP_EXPONENT (z), &MP_EXPONENT (w), (1 + digs));
530 }
531 // Zero is zero is zero.
532 if (MP_DIGIT (z, 1) == 0) {
533 MP_EXPONENT (z) = (MP_T) 0;
534 }
535 }
536
537 //! @brief Truncate at decimal point.
538
539 MP_T *trunc_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
540 {
541 if (MP_EXPONENT (x) < 0) {
542 SET_MP_ZERO (z, digs);
543 } else if (MP_EXPONENT (x) >= (MP_T) digs) {
544 errno = EDOM;
545 diagnostic (A68_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, (IS (MOID (p), PROC_SYMBOL) ? SUB_MOID (p) : MOID (p)));
546 exit_genie (p, A68_RUNTIME_ERROR);
547 } else {
548 (void) move_mp (z, x, digs);
549 for (int k = (int) (MP_EXPONENT (x) + 2); k <= digs; k++) {
550 MP_DIGIT (z, k) = (MP_T) 0;
551 }
552 }
553 return z;
554 }
555
556 //! @brief Floor - largest integer smaller than x.
557
558 MP_T *floor_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
559 {
560 if (MP_EXPONENT (x) < 0) {
561 SET_MP_ZERO (z, digs);
562 } else if (MP_EXPONENT (x) >= (MP_T) digs) {
563 errno = EDOM;
564 diagnostic (A68_RUNTIME_ERROR, p, ERROR_OUT_OF_BOUNDS, (IS (MOID (p), PROC_SYMBOL) ? SUB_MOID (p) : MOID (p)));
565 exit_genie (p, A68_RUNTIME_ERROR);
566 } else {
567 (void) move_mp (z, x, digs);
568 for (int k = (int) (MP_EXPONENT (x) + 2); k <= digs; k++) {
569 MP_DIGIT (z, k) = (MP_T) 0;
570 }
571 }
572 if (MP_DIGIT (x, 1) < 0 && ! same_mp (p, z, x, digs)) {
573 (void) minus_one_mp (p, z, z, digs);
574 }
575 return z;
576 }
577
578 //! @brief Shorten and round.
579
580 MP_T *shorten_mp (NODE_T * p, MP_T * z, int digs, MP_T * x, int digs_x)
581 {
582 if (digs > digs_x) {
583 return lengthen_mp (p, z, digs, x, digs_x);
584 } else if (digs == digs_x) {
585 return move_mp (z, x, digs);
586 } else {
587 // Reserve extra digs for proper rounding.
588 ADDR_T pop_sp = A68_SP;
589 int digs_h = digs + 2;
590 BOOL_T negative = (BOOL_T) (MP_DIGIT (x, 1) < 0);
591 MP_T *w = nil_mp (p, digs_h);
592 if (negative) {
593 MP_DIGIT (x, 1) = -MP_DIGIT (x, 1);
594 }
595 MP_STATUS (w) = (MP_T) 0;
596 MP_EXPONENT (w) = MP_EXPONENT (x) + 1;
597 MP_DIGIT (w, 1) = (MP_T) 0;
598 (void) move_mp_part (&MP_DIGIT (w, 2), &MP_DIGIT (x, 1), digs + 1);
599 round_internal_mp (z, w, digs);
600 if (negative) {
601 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
602 }
603 A68_SP = pop_sp;
604 return z;
605 }
606 }
607
608 //! @brief Lengthen x and assign to z.
609
610 MP_T *lengthen_mp (NODE_T * p, MP_T * z, int digs_z, MP_T * x, int digs_x)
611 {
612 if (digs_z < digs_x) {
613 return shorten_mp (p, z, digs_z, x, digs_x);
614 } else if (digs_z == digs_x) {
615 return move_mp (z, x, digs_z);
616 } else {
617 if (z != x) {
618 (void) move_mp_part (&MP_DIGIT (z, 1), &MP_DIGIT (x, 1), digs_x);
619 MP_EXPONENT (z) = MP_EXPONENT (x);
620 MP_STATUS (z) = MP_STATUS (x);
621 }
622 for (int j = 1 + digs_x; j <= digs_z; j++) {
623 MP_DIGIT (z, j) = (MP_T) 0;
624 }
625 }
626 return z;
627 }
628
629 //! @brief Set "z" to the sum of positive "x" and positive "y".
630
631 MP_T *add_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
632 {
633 MP_STATUS (z) = (MP_T) INIT_MASK;
634 // Trivial cases.
635 if (MP_DIGIT (x, 1) == (MP_T) 0) {
636 (void) move_mp (z, y, digs);
637 return z;
638 } else if (MP_DIGIT (y, 1) == 0) {
639 (void) move_mp (z, x, digs);
640 return z;
641 }
642 // We want positive arguments.
643 ADDR_T pop_sp = A68_SP;
644 MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
645 MP_DIGIT (x, 1) = ABS (x_1);
646 MP_DIGIT (y, 1) = ABS (y_1);
647 if (x_1 >= 0 && y_1 < 0) {
648 (void) sub_mp (p, z, x, y, digs);
649 } else if (x_1 < 0 && y_1 >= 0) {
650 (void) sub_mp (p, z, y, x, digs);
651 } else if (x_1 < 0 && y_1 < 0) {
652 (void) add_mp (p, z, x, y, digs);
653 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
654 } else {
655 // Add.
656 int digs_h = 2 + digs;
657 MP_T *w = nil_mp (p, digs_h);
658 if (MP_EXPONENT (x) == MP_EXPONENT (y)) {
659 MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
660 for (int j = 1; j <= digs; j++) {
661 MP_DIGIT (w, j + 1) = MP_DIGIT (x, j) + MP_DIGIT (y, j);
662 }
663 MP_DIGIT (w, digs_h) = (MP_T) 0;
664 } else if (MP_EXPONENT (x) > MP_EXPONENT (y)) {
665 int shl_y = (int) MP_EXPONENT (x) - (int) MP_EXPONENT (y);
666 MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
667 for (int j = 1; j < digs_h; j++) {
668 int i_y = j - shl_y;
669 MP_T x_j = (j > digs ? 0 : MP_DIGIT (x, j));
670 MP_T y_j = (i_y <= 0 || i_y > digs ? 0 : MP_DIGIT (y, i_y));
671 MP_DIGIT (w, j + 1) = x_j + y_j;
672 }
673 } else {
674 int shl_x = (int) MP_EXPONENT (y) - (int) MP_EXPONENT (x);
675 MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (y);
676 for (int j = 1; j < digs_h; j++) {
677 int i_x = j - shl_x;
678 MP_T x_j = (i_x <= 0 || i_x > digs ? 0 : MP_DIGIT (x, i_x));
679 MP_T y_j = (j > digs ? 0 : MP_DIGIT (y, j));
680 MP_DIGIT (w, j + 1) = x_j + y_j;
681 }
682 }
683 norm_mp_light (w, 2, digs_h);
684 round_internal_mp (z, w, digs);
685 check_mp_exp (p, z);
686 }
687 // Restore and exit.
688 A68_SP = pop_sp;
689 MP_T z_1 = MP_DIGIT (z, 1);
690 MP_DIGIT (x, 1) = x_1;
691 MP_DIGIT (y, 1) = y_1;
692 MP_DIGIT (z, 1) = z_1; // In case z IS x OR z IS y
693 return z;
694 }
695
696 //! @brief Set "z" to the difference of positive "x" and positive "y".
697
698 MP_T *sub_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
699 {
700 MP_STATUS (z) = (MP_T) INIT_MASK;
701 // Trivial cases.
702 if (MP_DIGIT (x, 1) == (MP_T) 0) {
703 (void) move_mp (z, y, digs);
704 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
705 return z;
706 } else if (MP_DIGIT (y, 1) == (MP_T) 0) {
707 (void) move_mp (z, x, digs);
708 return z;
709 }
710 // We want positive arguments.
711 ADDR_T pop_sp = A68_SP;
712 MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
713 MP_DIGIT (x, 1) = ABS (x_1);
714 MP_DIGIT (y, 1) = ABS (y_1);
715 if (x_1 >= 0 && y_1 < 0) {
716 (void) add_mp (p, z, x, y, digs);
717 } else if (x_1 < 0 && y_1 >= 0) {
718 (void) add_mp (p, z, y, x, digs);
719 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
720 } else if (x_1 < 0 && y_1 < 0) {
721 (void) sub_mp (p, z, y, x, digs);
722 } else {
723 // Subtract.
724 BOOL_T negative = A68_FALSE;
725 int fnz, digs_h = 2 + digs;
726 MP_T *w = nil_mp (p, digs_h);
727 if (MP_EXPONENT (x) == MP_EXPONENT (y)) {
728 MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
729 for (int j = 1; j <= digs; j++) {
730 MP_DIGIT (w, j + 1) = MP_DIGIT (x, j) - MP_DIGIT (y, j);
731 }
732 MP_DIGIT (w, digs_h) = (MP_T) 0;
733 } else if (MP_EXPONENT (x) > MP_EXPONENT (y)) {
734 int shl_y = (int) MP_EXPONENT (x) - (int) MP_EXPONENT (y);
735 MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (x);
736 for (int j = 1; j < digs_h; j++) {
737 int i_y = j - shl_y;
738 MP_T x_j = (j > digs ? 0 : MP_DIGIT (x, j));
739 MP_T y_j = (i_y <= 0 || i_y > digs ? 0 : MP_DIGIT (y, i_y));
740 MP_DIGIT (w, j + 1) = x_j - y_j;
741 }
742 } else {
743 int shl_x = (int) MP_EXPONENT (y) - (int) MP_EXPONENT (x);
744 MP_EXPONENT (w) = (MP_T) 1 + MP_EXPONENT (y);
745 for (int j = 1; j < digs_h; j++) {
746 int i_x = j - shl_x;
747 MP_T x_j = (i_x <= 0 || i_x > digs ? 0 : MP_DIGIT (x, i_x));
748 MP_T y_j = (j > digs ? 0 : MP_DIGIT (y, j));
749 MP_DIGIT (w, j + 1) = x_j - y_j;
750 }
751 }
752 // Correct if we subtract large from small.
753 if (MP_DIGIT (w, 2) <= 0) {
754 fnz = -1;
755 for (int j = 2; j <= digs_h && fnz < 0; j++) {
756 if (MP_DIGIT (w, j) != 0) {
757 fnz = j;
758 }
759 }
760 negative = (BOOL_T) (MP_DIGIT (w, fnz) < 0);
761 if (negative) {
762 for (int j = fnz; j <= digs_h; j++) {
763 MP_DIGIT (w, j) = -MP_DIGIT (w, j);
764 }
765 }
766 }
767 // Normalise.
768 norm_mp_light (w, 2, digs_h);
769 fnz = -1;
770 for (int j = 1; j <= digs_h && fnz < 0; j++) {
771 if (MP_DIGIT (w, j) != 0) {
772 fnz = j;
773 }
774 }
775 if (fnz > 1) {
776 int j2 = fnz - 1;
777 for (int j = 1; j <= digs_h - j2; j++) {
778 MP_DIGIT (w, j) = MP_DIGIT (w, j + j2);
779 MP_DIGIT (w, j + j2) = (MP_T) 0;
780 }
781 MP_EXPONENT (w) -= j2;
782 }
783 // Round.
784 round_internal_mp (z, w, digs);
785 if (negative) {
786 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
787 }
788 check_mp_exp (p, z);
789 }
790 // Restore and exit.
791 A68_SP = pop_sp;
792 MP_T z_1 = MP_DIGIT (z, 1);
793 MP_DIGIT (x, 1) = x_1;
794 MP_DIGIT (y, 1) = y_1;
795 MP_DIGIT (z, 1) = z_1; // In case z IS x OR z IS y
796 return z;
797 }
798
799 //! @brief Set "z" to the product of "x" and "y".
800
801 MP_T *mul_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
802 {
803 if (IS_ZERO_MP (x) || IS_ZERO_MP (y)) {
804 SET_MP_ZERO (z, digs);
805 return z;
806 }
807 // Grammar school algorithm with intermittent normalisation.
808 ADDR_T pop_sp = A68_SP;
809 int digs_h = 2 + digs;
810 MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
811 MP_DIGIT (x, 1) = ABS (x_1);
812 MP_DIGIT (y, 1) = ABS (y_1);
813 MP_STATUS (z) = (MP_T) INIT_MASK;
814 MP_T *w = lit_mp (p, 0, MP_EXPONENT (x) + MP_EXPONENT (y) + 1, digs_h);
815 int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (2 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
816 for (int i = digs; i >= 1; i--) {
817 MP_T yi = MP_DIGIT (y, i);
818 if (yi != 0) {
819 int k = digs_h - i;
820 int j = (k > digs ? digs : k);
821 MP_T *u = &MP_DIGIT (w, i + j), *v = &MP_DIGIT (x, j);
822 if ((digs - i + 1) % oflow == 0) {
823 norm_mp (w, 2, digs_h);
824 }
825 while (j-- >= 1) {
826 (u--)[0] += yi * (v--)[0];
827 }
828 }
829 }
830 norm_mp (w, 2, digs_h);
831 round_internal_mp (z, w, digs);
832 // Restore and exit.
833 A68_SP = pop_sp;
834 MP_T z_1 = MP_DIGIT (z, 1);
835 MP_DIGIT (x, 1) = x_1;
836 MP_DIGIT (y, 1) = y_1;
837 MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
838 check_mp_exp (p, z);
839 return z;
840 }
841
842 //! @brief Set "z" to the quotient of "x" and "y".
843
844 MP_T *div_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
845 {
846 // This routine is based on
847 //
848 // D. M. Smith, "A Multiple-Precision Division Algorithm"
849 // Mathematics of Computation 66 (1996) 157-163.
850 //
851 // This is O(N^2) but runs faster than straightforward methods by skipping
852 // most of the intermediate normalisation and recovering from wrong
853 // guesses without separate correction steps.
854 // Depending on application, div_mp cost is circa 3 times that of mul_mp.
855 // Therefore Newton-Raphson division makes no sense here.
856 if (IS_ZERO_MP (y)) {
857 errno = ERANGE;
858 return NaN_MP;
859 }
860 // Determine normalisation interval assuming that q < 2b in each step.
861 #if (A68_LEVEL <= 2)
862 int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (3 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
863 #else
864 int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (2 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
865 #endif
866 MP_T x_1 = MP_DIGIT (x, 1), y_1 = MP_DIGIT (y, 1);
867 MP_DIGIT (x, 1) = ABS (x_1);
868 MP_DIGIT (y, 1) = ABS (y_1);
869 MP_STATUS (z) = (MP_T) INIT_MASK;
870 // Slight optimisation when the denominator has few digits.
871 int nzdigs = digs;
872 while (MP_DIGIT (y, nzdigs) == 0 && nzdigs > 1) {
873 nzdigs--;
874 }
875 if (nzdigs == 1 && MP_EXPONENT (y) == 0) {
876 (void) div_mp_digit (p, z, x, MP_DIGIT (y, 1), digs);
877 MP_T z_1 = MP_DIGIT (z, 1);
878 MP_DIGIT (x, 1) = x_1;
879 MP_DIGIT (y, 1) = y_1;
880 MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
881 check_mp_exp (p, z);
882 return z;
883 }
884 // Working nominator in which the quotient develops.
885 ADDR_T pop_sp = A68_SP;
886 int wdigs = 4 + digs;
887 MP_T *w = lit_mp (p, 0, MP_EXPONENT (x) - MP_EXPONENT (y), wdigs);
888 (void) move_mp_part (&MP_DIGIT (w, 2), &MP_DIGIT (x, 1), digs);
889 // Estimate the denominator. For small MP_RADIX add: MP_DIGIT (y, 4) / MP_REAL_RADIX.
890 MP_REAL_T den = (MP_DIGIT (y, 1) * MP_REAL_RADIX + MP_DIGIT (y, 2)) * MP_REAL_RADIX + MP_DIGIT (y, 3);
891 MP_T *t = &MP_DIGIT (w, 2);
892 for (int k = 1, len = digs + 2, first = 3; k <= digs + 2; k++, len++, first++, t++) {
893 // Estimate quotient digit.
894 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);
895 if (nom == 0) {
896 q = 0;
897 } else {
898 // Correct the nominator.
899 q = (MP_T) (MP_INT_T) (nom / den);
900 int lim = MINIMUM (len, wdigs);
901 if (nzdigs <= lim - first + 1) {
902 lim = first + nzdigs - 1;
903 }
904 MP_T *u = t, *v = &MP_DIGIT (y, 1);
905 for (int j = first; j <= lim; j++) {
906 (u++)[0] -= q * (v++)[0];
907 }
908 }
909 t[0] += t[-1] * MP_RADIX;
910 t[-1] = q;
911 if (k % oflow == 0 || k == digs + 2) {
912 norm_mp (w, first, wdigs);
913 }
914 }
915 norm_mp (w, 2, digs);
916 round_internal_mp (z, w, digs);
917 // Restore and exit.
918 A68_SP = pop_sp;
919 MP_T z_1 = MP_DIGIT (z, 1);
920 MP_DIGIT (x, 1) = x_1;
921 MP_DIGIT (y, 1) = y_1;
922 MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
923 check_mp_exp (p, z);
924 return z;
925 }
926
927 //! @brief Set "z" to the integer quotient of "x" and "y".
928
929 MP_T *over_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
930 {
931 if (MP_DIGIT (y, 1) == 0) {
932 errno = ERANGE;
933 return NaN_MP;
934 }
935 ADDR_T pop_sp = A68_SP;
936 int digs_g = FUN_DIGITS (digs);
937 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);
938 (void) div_mp (p, z_g, x_g, y_g, digs_g);
939 trunc_mp (p, z_g, z_g, digs_g);
940 (void) shorten_mp (p, z, digs, z_g, digs_g);
941 MP_STATUS (z) = (MP_T) INIT_MASK;
942 A68_SP = pop_sp;
943 return z;
944 }
945
946 //! @brief Set "z" to x mod y.
947
948 MP_T *mod_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
949 {
950 if (MP_DIGIT (y, 1) == 0) {
951 errno = EDOM;
952 return NaN_MP;
953 }
954 int digs_g = FUN_DIGITS (digs);
955 ADDR_T pop_sp = A68_SP;
956 MP_T *x_g = len_mp (p, x, digs, digs_g);
957 MP_T *y_g = len_mp (p, y, digs, digs_g);
958 MP_T *z_g = nil_mp (p, digs_g);
959 // x mod y = x - y * trunc (x / y).
960 (void) over_mp (p, z_g, x_g, y_g, digs_g);
961 (void) mul_mp (p, z_g, y_g, z_g, digs_g);
962 (void) sub_mp (p, z_g, x_g, z_g, digs_g);
963 (void) shorten_mp (p, z, digs, z_g, digs_g);
964 // Restore and exit.
965 A68_SP = pop_sp;
966 return z;
967 }
968
969 //! @brief Set "z" to the product of x and digit y.
970
971 MP_T *mul_mp_digit (NODE_T * p, MP_T * z, MP_T * x, MP_T y, int digs)
972 {
973 // This is an O(N) routine for multiplication by a short value.
974 MP_T x_1 = MP_DIGIT (x, 1);
975 int digs_h = 2 + digs;
976 ADDR_T pop_sp = A68_SP;
977 MP_DIGIT (x, 1) = ABS (x_1);
978 MP_STATUS (z) = (MP_T) INIT_MASK;
979 MP_T y_1 = y;
980 y = ABS (y_1);
981 if (y == 2) {
982 (void) add_mp (p, z, x, x, digs);
983 } else {
984 MP_T *w = lit_mp (p, 0, MP_EXPONENT (x) + 1, digs_h);
985 MP_T *u = &MP_DIGIT (w, 1 + digs), *v = &MP_DIGIT (x, digs);
986 int j = digs;
987 while (j-- >= 1) {
988 (u--)[0] += y * (v--)[0];
989 }
990 norm_mp (w, 2, digs_h);
991 round_internal_mp (z, w, digs);
992 }
993 // Restore and exit.
994 A68_SP = pop_sp;
995 MP_T z_1 = MP_DIGIT (z, 1);
996 MP_DIGIT (x, 1) = x_1;
997 MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
998 check_mp_exp (p, z);
999 return z;
1000 }
1001
1002 //! @brief Set "z" to x/2.
1003
1004 MP_T *half_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1005 {
1006 ADDR_T pop_sp = A68_SP;
1007 int digs_h = 2 + digs;
1008 MP_T x_1 = MP_DIGIT (x, 1);
1009 MP_DIGIT (x, 1) = ABS (x_1);
1010 MP_STATUS (z) = (MP_T) INIT_MASK;
1011 // Calculate x * 0.5.
1012 MP_T *w = lit_mp (p, 0, MP_EXPONENT (x), digs_h);
1013 MP_T *u = &MP_DIGIT (w, 1 + digs), *v = &MP_DIGIT (x, digs);
1014 int j = digs;
1015 while (j-- >= 1) {
1016 (u--)[0] += (MP_RADIX / 2) * (v--)[0];
1017 }
1018 norm_mp (w, 2, digs_h);
1019 round_internal_mp (z, w, digs);
1020 // Restore and exit.
1021 A68_SP = pop_sp;
1022 MP_T z_1 = MP_DIGIT (z, 1);
1023 MP_DIGIT (x, 1) = x_1;
1024 MP_DIGIT (z, 1) = (x_1 >= 0 ? z_1 : -z_1);
1025 check_mp_exp (p, z);
1026 return z;
1027 }
1028
1029 //! @brief Set "z" to x/10.
1030
1031 MP_T *tenth_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1032 {
1033 ADDR_T pop_sp = A68_SP;
1034 int digs_h = 2 + digs;
1035 MP_T x_1 = MP_DIGIT (x, 1);
1036 MP_DIGIT (x, 1) = ABS (x_1);
1037 MP_STATUS (z) = (MP_T) INIT_MASK;
1038 // Calculate x * 0.1.
1039 MP_T *w = lit_mp (p, 0, MP_EXPONENT (x), digs_h);
1040 MP_T *u = &MP_DIGIT (w, 1 + digs), *v = &MP_DIGIT (x, digs);
1041 int j = digs;
1042 while (j-- >= 1) {
1043 (u--)[0] += (MP_RADIX / 10) * (v--)[0];
1044 }
1045 norm_mp (w, 2, digs_h);
1046 round_internal_mp (z, w, digs);
1047 // Restore and exit.
1048 A68_SP = pop_sp;
1049 MP_T z_1 = MP_DIGIT (z, 1);
1050 MP_DIGIT (x, 1) = x_1;
1051 MP_DIGIT (z, 1) = (x_1 >= 0 ? z_1 : -z_1);
1052 check_mp_exp (p, z);
1053 return z;
1054 }
1055
1056 //! @brief Set "z" to the quotient of x and digit y.
1057
1058 MP_T *div_mp_digit (NODE_T * p, MP_T * z, MP_T * x, MP_T y, int digs)
1059 {
1060 if (y == 0) {
1061 errno = ERANGE;
1062 return NaN_MP;
1063 }
1064 // Determine normalisation interval assuming that q < 2b in each step.
1065 #if (A68_LEVEL <= 2)
1066 int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (3 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
1067 #else
1068 int oflow = (int) FLOOR_MP ((MP_REAL_T) MAX_REPR_INT / (2 * MP_REAL_RADIX * MP_REAL_RADIX)) - 1;
1069 #endif
1070 // Work with positive operands.
1071 ADDR_T pop_sp = A68_SP;
1072 MP_T x_1 = MP_DIGIT (x, 1), y_1 = y;
1073 MP_DIGIT (x, 1) = ABS (x_1);
1074 MP_STATUS (z) = (MP_T) INIT_MASK;
1075 y = ABS (y_1);
1076 if (y == 2) {
1077 (void) half_mp (p, z, x, digs);
1078 } else if (y == 10) {
1079 (void) tenth_mp (p, z, x, digs);
1080 } else {
1081 int wdigs = 4 + digs;
1082 MP_T *w = lit_mp (p, 0, MP_EXPONENT (x), wdigs);
1083 (void) move_mp_part (&MP_DIGIT (w, 2), &MP_DIGIT (x, 1), digs);
1084 // Estimate the denominator.
1085 MP_REAL_T den = (MP_REAL_T) y * MP_REAL_RADIX * MP_REAL_RADIX;
1086 MP_T *t = &MP_DIGIT (w, 2);
1087 for (int k = 1, first = 3; k <= digs + 2; k++, first++, t++) {
1088 // Estimate quotient digit and correct.
1089 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);
1090 MP_REAL_T q = (MP_T) (MP_INT_T) (nom / den);
1091 t[0] += t[-1] * MP_RADIX - q * y;
1092 t[-1] = q;
1093 if (k % oflow == 0 || k == digs + 2) {
1094 norm_mp (w, first, wdigs);
1095 }
1096 }
1097 norm_mp (w, 2, digs);
1098 round_internal_mp (z, w, digs);
1099 }
1100 // Restore and exit.
1101 A68_SP = pop_sp;
1102 MP_T z_1 = MP_DIGIT (z, 1);
1103 MP_DIGIT (x, 1) = x_1;
1104 MP_DIGIT (z, 1) = ((x_1 * y_1) >= 0 ? z_1 : -z_1);
1105 check_mp_exp (p, z);
1106 return z;
1107 }
1108
1109 //! @brief Set "z" to the integer quotient of "x" and "y".
1110
1111 MP_T *over_mp_digit (NODE_T * p, MP_T * z, MP_T * x, MP_T y, int digs)
1112 {
1113 if (y == 0) {
1114 errno = ERANGE;
1115 return NaN_MP;
1116 }
1117 int digs_g = FUN_DIGITS (digs);
1118 ADDR_T pop_sp = A68_SP;
1119 MP_T *x_g = len_mp (p, x, digs, digs_g), *z_g = nil_mp (p, digs_g);
1120 (void) div_mp_digit (p, z_g, x_g, y, digs_g);
1121 trunc_mp (p, z_g, z_g, digs_g);
1122 (void) shorten_mp (p, z, digs, z_g, digs_g);
1123 // Restore and exit.
1124 A68_SP = pop_sp;
1125 return z;
1126 }
1127
1128 //! @brief Set "z" to the reciprocal of "x".
1129
1130 MP_T *rec_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1131 {
1132 if (IS_ZERO_MP (x)) {
1133 errno = ERANGE;
1134 return NaN_MP;
1135 }
1136 ADDR_T pop_sp = A68_SP;
1137 (void) div_mp (p, z, mp_one (digs), x, digs);
1138 A68_SP = pop_sp;
1139 return z;
1140 }
1141
1142 //! @brief LONG REAL long pi
1143
1144 void genie_pi_mp (NODE_T * p)
1145 {
1146 int digs = DIGITS (MOID (p));
1147 MP_T *z = nil_mp (p, digs);
1148 (void) mp_pi (p, z, MP_PI, digs);
1149 MP_STATUS (z) = (MP_T) INIT_MASK;
1150 }
1151
1152 //! @brief Set "z" to "x" ** "n".
1153
1154 MP_T *pow_mp_int (NODE_T * p, MP_T * z, MP_T * x, INT_T n, int digs)
1155 {
1156 ADDR_T pop_sp = A68_SP;
1157 int bit, digs_g = FUN_DIGITS (digs);
1158 MP_T *x_g = len_mp (p, x, digs, digs_g), *z_g = lit_mp (p, 1, 0, digs_g);
1159 BOOL_T negative = (BOOL_T) (n < 0);
1160 if (negative) {
1161 n = -n;
1162 }
1163 bit = 1;
1164 while ((int) bit <= (int) n) {
1165 if (n & bit) {
1166 (void) mul_mp (p, z_g, z_g, x_g, digs_g);
1167 }
1168 (void) mul_mp (p, x_g, x_g, x_g, digs_g);
1169 bit <<= 1;
1170 }
1171 (void) shorten_mp (p, z, digs, z_g, digs_g);
1172 A68_SP = pop_sp;
1173 if (negative) {
1174 (void) rec_mp (p, z, z, digs);
1175 }
1176 check_mp_exp (p, z);
1177 return z;
1178 }
1179
1180 //! @brief Set "z" to "x" ** "y".
1181
1182 MP_T *pow_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
1183 {
1184 PRELUDE_ERROR (ln_mp (p, z, x, digs) == NaN_MP, p, ERROR_INVALID_ARGUMENT, MOID (p));
1185 (void) mul_mp (p, z, y, z, digs);
1186 (void) exp_mp (p, z, z, digs);
1187 return z;
1188 }
1189
1190 //! @brief Set "z" to 10 ** "n".
1191
1192 MP_T *ten_up_mp (NODE_T * p, MP_T * z, int n, int digs)
1193 {
1194 #if (A68_LEVEL >= 3)
1195 static MP_T y[LOG_MP_RADIX] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
1196 #else
1197 static MP_T y[LOG_MP_RADIX] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
1198 #endif
1199 if (n >= 0) {
1200 set_mp (z, y[n % LOG_MP_RADIX], n / LOG_MP_RADIX, digs);
1201 } else {
1202 set_mp (z, y[(LOG_MP_RADIX + n % LOG_MP_RADIX) % LOG_MP_RADIX], (n + 1) / LOG_MP_RADIX - 1, digs);
1203 }
1204 check_mp_exp (p, z);
1205 return z;
1206 }
1207
1208 //! @brief Comparison of "x" and "y".
1209
1210 void eq_mp (NODE_T * p, A68_BOOL * z, MP_T * x, MP_T * y, int digs)
1211 {
1212 ADDR_T pop_sp = A68_SP;
1213 MP_T *v = nil_mp (p, digs);
1214 (void) sub_mp (p, v, x, y, digs);
1215 STATUS (z) = INIT_MASK;
1216 VALUE (z) = (MP_DIGIT (v, 1) == 0 ? A68_TRUE : A68_FALSE);
1217 A68_SP = pop_sp;
1218 }
1219
1220 //! @brief Comparison of "x" and "y".
1221
1222 void ne_mp (NODE_T * p, A68_BOOL * z, MP_T * x, MP_T * y, int digs)
1223 {
1224 ADDR_T pop_sp = A68_SP;
1225 MP_T *v = nil_mp (p, digs);
1226 (void) sub_mp (p, v, x, y, digs);
1227 STATUS (z) = INIT_MASK;
1228 VALUE (z) = (MP_DIGIT (v, 1) != 0 ? A68_TRUE : A68_FALSE);
1229 A68_SP = pop_sp;
1230 }
1231
1232 //! @brief Comparison of "x" and "y".
1233
1234 void lt_mp (NODE_T * p, A68_BOOL * z, MP_T * x, MP_T * y, int digs)
1235 {
1236 ADDR_T pop_sp = A68_SP;
1237 MP_T *v = nil_mp (p, digs);
1238 (void) sub_mp (p, v, x, y, digs);
1239 STATUS (z) = INIT_MASK;
1240 VALUE (z) = (MP_DIGIT (v, 1) < 0 ? A68_TRUE : A68_FALSE);
1241 A68_SP = pop_sp;
1242 }
1243
1244 //! @brief Comparison of "x" and "y".
1245
1246 void le_mp (NODE_T * p, A68_BOOL * z, MP_T * x, MP_T * y, int digs)
1247 {
1248 ADDR_T pop_sp = A68_SP;
1249 MP_T *v = nil_mp (p, digs);
1250 (void) sub_mp (p, v, x, y, digs);
1251 STATUS (z) = INIT_MASK;
1252 VALUE (z) = (MP_DIGIT (v, 1) <= 0 ? A68_TRUE : A68_FALSE);
1253 A68_SP = pop_sp;
1254 }
1255
1256 //! @brief Comparison of "x" and "y".
1257
1258 void gt_mp (NODE_T * p, A68_BOOL * z, MP_T * x, MP_T * y, int digs)
1259 {
1260 ADDR_T pop_sp = A68_SP;
1261 MP_T *v = nil_mp (p, digs);
1262 (void) sub_mp (p, v, x, y, digs);
1263 STATUS (z) = INIT_MASK;
1264 VALUE (z) = (MP_DIGIT (v, 1) > 0 ? A68_TRUE : A68_FALSE);
1265 A68_SP = pop_sp;
1266 }
1267
1268 //! @brief Comparison of "x" and "y".
1269
1270 void ge_mp (NODE_T * p, A68_BOOL * z, MP_T * x, MP_T * y, int digs)
1271 {
1272 ADDR_T pop_sp = A68_SP;
1273 MP_T *v = nil_mp (p, digs);
1274 (void) sub_mp (p, v, x, y, digs);
1275 STATUS (z) = INIT_MASK;
1276 VALUE (z) = (MP_DIGIT (v, 1) >= 0 ? A68_TRUE : A68_FALSE);
1277 A68_SP = pop_sp;
1278 }
1279
1280 //! @brief round (x).
1281
1282 MP_T *round_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1283 {
1284 MP_T *y = nil_mp (p, digs);
1285 SET_MP_HALF (y, digs);
1286 if (MP_DIGIT (x, 1) >= 0) {
1287 (void) add_mp (p, z, x, y, digs);
1288 (void) trunc_mp (p, z, z, digs);
1289 } else {
1290 (void) sub_mp (p, z, x, y, digs);
1291 (void) trunc_mp (p, z, z, digs);
1292 }
1293 MP_STATUS (z) = (MP_T) INIT_MASK;
1294 return z;
1295 }
1296
1297 //! @brief Entier (x).
1298
1299 MP_T *entier_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1300 {
1301 if (MP_DIGIT (x, 1) >= 0) {
1302 (void) trunc_mp (p, z, x, digs);
1303 } else {
1304 MP_T *y = nil_mp (p, digs);
1305 (void) move_mp (y, z, digs);
1306 (void) trunc_mp (p, z, x, digs);
1307 (void) sub_mp (p, y, y, z, digs);
1308 if (MP_DIGIT (y, 1) != 0) {
1309 SET_MP_ONE (y, digs);
1310 (void) sub_mp (p, z, z, y, digs);
1311 }
1312 }
1313 MP_STATUS (z) = (MP_T) INIT_MASK;
1314 return z;
1315 }
© 2002-2024 J.M. van der Veer (jmvdveer@xs4all.nl)
|