mp-math.c
1 //! @file mp-math.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 REAL math functions.
25
26 #include "a68g.h"
27
28 #include "a68g-double.h"
29 #include "a68g-mp.h"
30 #include "a68g-prelude.h"
31
32 //! @brief Test on |"z"| > 0.001 for argument reduction in "sin" and "exp".
33
34 static inline BOOL_T eps_mp (MP_T * z, int digs)
35 {
36 if (MP_DIGIT (z, 1) == 0) {
37 return A68_FALSE;
38 } else if (MP_EXPONENT (z) > -1) {
39 return A68_TRUE;
40 } else if (MP_EXPONENT (z) < -1) {
41 return A68_FALSE;
42 } else {
43 // More or less optimised for LONG and default LONG LONG precisions.
44 return (BOOL_T) (digs <= 10 ? ABS (MP_DIGIT (z, 1)) > 0.01 * MP_RADIX : ABS (MP_DIGIT (z, 1)) > 0.001 * MP_RADIX);
45 }
46 }
47
48 //! @brief PROC (LONG REAL) LONG REAL sqrt
49
50 MP_T *sqrt_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
51 {
52 ADDR_T pop_sp = A68_SP;
53 if (MP_DIGIT (x, 1) == 0) {
54 A68_SP = pop_sp;
55 SET_MP_ZERO (z, digs);
56 return z;
57 }
58 if (MP_DIGIT (x, 1) < 0) {
59 A68_SP = pop_sp;
60 errno = EDOM;
61 return NaN_MP;
62 }
63 int gdigs = FUN_DIGITS (digs), hdigs;
64 BOOL_T reciprocal = A68_FALSE;
65 MP_T *z_g = nil_mp (p, gdigs), *x_g = len_mp (p, x, digs, gdigs);
66 MP_T *tmp = nil_mp (p, gdigs);
67 // Scaling for small x; sqrt (x) = 1 / sqrt (1 / x).
68 if ((reciprocal = (BOOL_T) (MP_EXPONENT (x_g) < 0)) == A68_TRUE) {
69 (void) rec_mp (p, x_g, x_g, gdigs);
70 }
71 if (ABS (MP_EXPONENT (x_g)) >= 2) {
72 // For extreme arguments we want accurate results as well.
73 int expo = (int) MP_EXPONENT (x_g);
74 MP_EXPONENT (x_g) = (MP_T) (expo % 2);
75 (void) sqrt_mp (p, z_g, x_g, gdigs);
76 MP_EXPONENT (z_g) += (MP_T) (expo / 2);
77 } else {
78 // Argument is in range. Estimate the root as double.
79 #if (A68_LEVEL >= 3)
80 DOUBLE_T x_d = mp_to_double (p, x_g, gdigs);
81 (void) double_to_mp (p, z_g, sqrt_double (x_d), gdigs);
82 #else
83 REAL_T x_d = mp_to_real (p, x_g, gdigs);
84 (void) real_to_mp (p, z_g, sqrt (x_d), gdigs);
85 #endif
86 // Newton's method: x<n+1> = (x<n> + a / x<n>) / 2.
87 int decimals = DOUBLE_ACCURACY;
88 do {
89 decimals <<= 1;
90 hdigs = MINIMUM (1 + decimals / LOG_MP_RADIX, gdigs);
91 (void) div_mp (p, tmp, x_g, z_g, hdigs);
92 (void) add_mp (p, tmp, z_g, tmp, hdigs);
93 (void) half_mp (p, z_g, tmp, hdigs);
94 } while (decimals < 2 * gdigs * LOG_MP_RADIX);
95 }
96 if (reciprocal) {
97 (void) rec_mp (p, z_g, z_g, digs);
98 }
99 (void) shorten_mp (p, z, digs, z_g, gdigs);
100 // Exit.
101 A68_SP = pop_sp;
102 return z;
103 }
104
105 //! @brief PROC (LONG REAL) LONG REAL curt
106
107 MP_T *curt_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
108 {
109 ADDR_T pop_sp = A68_SP;
110 if (MP_DIGIT (x, 1) == 0) {
111 A68_SP = pop_sp;
112 SET_MP_ZERO (z, digs);
113 return z;
114 }
115 BOOL_T change_sign = A68_FALSE;
116 if (MP_DIGIT (x, 1) < 0) {
117 change_sign = A68_TRUE;
118 MP_DIGIT (x, 1) = -MP_DIGIT (x, 1);
119 }
120 int gdigs = FUN_DIGITS (digs), hdigs;
121 BOOL_T reciprocal = A68_FALSE;
122 MP_T *z_g = nil_mp (p, gdigs), *x_g = len_mp (p, x, digs, gdigs);
123 MP_T *tmp = nil_mp (p, gdigs);
124 // Scaling for small x; curt (x) = 1 / curt (1 / x).
125 if ((reciprocal = (BOOL_T) (MP_EXPONENT (x_g) < 0)) == A68_TRUE) {
126 (void) rec_mp (p, x_g, x_g, gdigs);
127 }
128 if (ABS (MP_EXPONENT (x_g)) >= 3) {
129 // For extreme arguments we want accurate results as well.
130 int expo = (int) MP_EXPONENT (x_g);
131 MP_EXPONENT (x_g) = (MP_T) (expo % 3);
132 (void) curt_mp (p, z_g, x_g, gdigs);
133 MP_EXPONENT (z_g) += (MP_T) (expo / 3);
134 } else {
135 // Argument is in range. Estimate the root as double.
136 #if (A68_LEVEL >= 3)
137 DOUBLE_T x_d = mp_to_double (p, x_g, gdigs);
138 (void) double_to_mp (p, z_g, cbrt_double (x_d), gdigs);
139 #else
140 REAL_T x_d = mp_to_real (p, x_g, gdigs);
141 (void) real_to_mp (p, z_g, cbrt (x_d), gdigs);
142 #endif
143 // Newton's method: x<n+1> = (2 x<n> + a / x<n> ^ 2) / 3.
144 int decimals = DOUBLE_ACCURACY;
145 do {
146 decimals <<= 1;
147 hdigs = MINIMUM (1 + decimals / LOG_MP_RADIX, gdigs);
148 (void) mul_mp (p, tmp, z_g, z_g, hdigs);
149 (void) div_mp (p, tmp, x_g, tmp, hdigs);
150 (void) add_mp (p, tmp, z_g, tmp, hdigs);
151 (void) add_mp (p, tmp, z_g, tmp, hdigs);
152 (void) div_mp_digit (p, z_g, tmp, (MP_T) 3, hdigs);
153 } while (decimals < gdigs * LOG_MP_RADIX);
154 }
155 if (reciprocal) {
156 (void) rec_mp (p, z_g, z_g, digs);
157 }
158 (void) shorten_mp (p, z, digs, z_g, gdigs);
159 // Exit.
160 A68_SP = pop_sp;
161 if (change_sign) {
162 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
163 }
164 return z;
165 }
166
167 //! @brief PROC (LONG REAL) LONG REAL hypot
168
169 MP_T *hypot_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
170 {
171 // sqrt (x^2 + y^2).
172 ADDR_T pop_sp = A68_SP;
173 MP_T *t = nil_mp (p, digs), *u = nil_mp (p, digs), *v = nil_mp (p, digs);
174 (void) move_mp (u, x, digs);
175 (void) move_mp (v, y, digs);
176 MP_DIGIT (u, 1) = ABS (MP_DIGIT (u, 1));
177 MP_DIGIT (v, 1) = ABS (MP_DIGIT (v, 1));
178 if (IS_ZERO_MP (u)) {
179 (void) move_mp (z, v, digs);
180 } else if (IS_ZERO_MP (v)) {
181 (void) move_mp (z, u, digs);
182 } else {
183 SET_MP_ONE (t, digs);
184 (void) sub_mp (p, z, u, v, digs);
185 if (MP_DIGIT (z, 1) > 0) {
186 (void) div_mp (p, z, v, u, digs);
187 (void) mul_mp (p, z, z, z, digs);
188 (void) add_mp (p, z, t, z, digs);
189 (void) sqrt_mp (p, z, z, digs);
190 (void) mul_mp (p, z, u, z, digs);
191 } else {
192 (void) div_mp (p, z, u, v, digs);
193 (void) mul_mp (p, z, z, z, digs);
194 (void) add_mp (p, z, t, z, digs);
195 (void) sqrt_mp (p, z, z, digs);
196 (void) mul_mp (p, z, v, z, digs);
197 }
198 }
199 A68_SP = pop_sp;
200 return z;
201 }
202
203 //! @brief PROC (LONG REAL) LONG REAL exp
204
205 MP_T *exp_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
206 {
207 // Argument is reduced by using exp (z / (2 ** n)) ** (2 ** n) = exp(z).
208 int gdigs = FUN_DIGITS (digs);
209 ADDR_T pop_sp = A68_SP;
210 if (MP_DIGIT (x, 1) == 0) {
211 SET_MP_ONE (z, digs);
212 return z;
213 }
214 MP_T *x_g = len_mp (p, x, digs, gdigs);
215 MP_T *pwr = nil_mp (p, gdigs), *fac = nil_mp (p, gdigs), *sum = nil_mp (p, gdigs);
216 MP_T *tmp = nil_mp (p, gdigs);
217 int m = 0;
218 // Scale x down.
219 while (eps_mp (x_g, gdigs)) {
220 m++;
221 (void) half_mp (p, x_g, x_g, gdigs);
222 }
223 // Calculate Taylor sum exp (z) = 1 + z / 1 ! + z ** 2 / 2 ! + ..
224 SET_MP_ONE (sum, gdigs);
225 (void) add_mp (p, sum, sum, x_g, gdigs);
226 (void) mul_mp (p, pwr, x_g, x_g, gdigs);
227 (void) half_mp (p, tmp, pwr, gdigs);
228 (void) add_mp (p, sum, sum, tmp, gdigs);
229 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
230 (void) div_mp_digit (p, tmp, pwr, (MP_T) 6, gdigs);
231 (void) add_mp (p, sum, sum, tmp, gdigs);
232 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
233 (void) div_mp_digit (p, tmp, pwr, (MP_T) 24, gdigs);
234 (void) add_mp (p, sum, sum, tmp, gdigs);
235 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
236 (void) div_mp_digit (p, tmp, pwr, (MP_T) 120, gdigs);
237 (void) add_mp (p, sum, sum, tmp, gdigs);
238 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
239 (void) div_mp_digit (p, tmp, pwr, (MP_T) 720, gdigs);
240 (void) add_mp (p, sum, sum, tmp, gdigs);
241 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
242 (void) div_mp_digit (p, tmp, pwr, (MP_T) 5040, gdigs);
243 (void) add_mp (p, sum, sum, tmp, gdigs);
244 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
245 (void) div_mp_digit (p, tmp, pwr, (MP_T) 40320, gdigs);
246 (void) add_mp (p, sum, sum, tmp, gdigs);
247 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
248 (void) div_mp_digit (p, tmp, pwr, (MP_T) 362880, gdigs);
249 (void) add_mp (p, sum, sum, tmp, gdigs);
250 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
251 (void) set_mp (fac, (MP_T) (MP_T) 3628800, 0, gdigs);
252 int n = 10;
253 BOOL_T iter = (BOOL_T) (MP_DIGIT (pwr, 1) != 0);
254 while (iter) {
255 (void) div_mp (p, tmp, pwr, fac, gdigs);
256 if (MP_EXPONENT (tmp) <= (MP_EXPONENT (sum) - gdigs)) {
257 iter = A68_FALSE;
258 } else {
259 (void) add_mp (p, sum, sum, tmp, gdigs);
260 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
261 n++;
262 (void) mul_mp_digit (p, fac, fac, (MP_T) n, gdigs);
263 }
264 }
265 // Square exp (x) up.
266 while (m--) {
267 (void) mul_mp (p, sum, sum, sum, gdigs);
268 }
269 (void) shorten_mp (p, z, digs, sum, gdigs);
270 // Exit.
271 A68_SP = pop_sp;
272 return z;
273 }
274
275 //! @brief PROC (LONG REAL) LONG REAL exp (x) - 1
276 // assuming "x" to be close to 0.
277
278 MP_T *expm1_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
279 {
280 int gdigs = FUN_DIGITS (digs);
281 ADDR_T pop_sp = A68_SP;
282 if (MP_DIGIT (x, 1) == 0) {
283 SET_MP_ONE (z, digs);
284 return z;
285 }
286 MP_T *x_g = len_mp (p, x, digs, gdigs);
287 MP_T *pwr = nil_mp (p, gdigs), *fac = nil_mp (p, gdigs), *sum = nil_mp (p, gdigs);
288 MP_T *tmp = nil_mp (p, gdigs);
289 // Calculate Taylor sum expm1 (z) = z / 1 ! + z ** 2 / 2 ! + ...
290 (void) add_mp (p, sum, sum, x_g, gdigs);
291 (void) mul_mp (p, pwr, x_g, x_g, gdigs);
292 (void) half_mp (p, tmp, pwr, gdigs);
293 (void) add_mp (p, sum, sum, tmp, gdigs);
294 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
295 (void) div_mp_digit (p, tmp, pwr, (MP_T) 6, gdigs);
296 (void) add_mp (p, sum, sum, tmp, gdigs);
297 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
298 (void) div_mp_digit (p, tmp, pwr, (MP_T) 24, gdigs);
299 (void) add_mp (p, sum, sum, tmp, gdigs);
300 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
301 (void) div_mp_digit (p, tmp, pwr, (MP_T) 120, gdigs);
302 (void) add_mp (p, sum, sum, tmp, gdigs);
303 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
304 (void) div_mp_digit (p, tmp, pwr, (MP_T) 720, gdigs);
305 (void) add_mp (p, sum, sum, tmp, gdigs);
306 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
307 (void) div_mp_digit (p, tmp, pwr, (MP_T) 5040, gdigs);
308 (void) add_mp (p, sum, sum, tmp, gdigs);
309 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
310 (void) div_mp_digit (p, tmp, pwr, (MP_T) 40320, gdigs);
311 (void) add_mp (p, sum, sum, tmp, gdigs);
312 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
313 (void) div_mp_digit (p, tmp, pwr, (MP_T) 362880, gdigs);
314 (void) add_mp (p, sum, sum, tmp, gdigs);
315 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
316 (void) set_mp (fac, (MP_T) (MP_T) 3628800, 0, gdigs);
317 int n = 10;
318 BOOL_T iter = (BOOL_T) (MP_DIGIT (pwr, 1) != 0);
319 while (iter) {
320 (void) div_mp (p, tmp, pwr, fac, gdigs);
321 if (MP_EXPONENT (tmp) <= (MP_EXPONENT (sum) - gdigs)) {
322 iter = A68_FALSE;
323 } else {
324 (void) add_mp (p, sum, sum, tmp, gdigs);
325 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
326 n++;
327 (void) mul_mp_digit (p, fac, fac, (MP_T) n, gdigs);
328 }
329 }
330 (void) shorten_mp (p, z, digs, sum, gdigs);
331 // Exit.
332 A68_SP = pop_sp;
333 return z;
334 }
335
336 //! @brief ln scale
337
338 MP_T *mp_ln_scale (NODE_T * p, MP_T * z, int digs)
339 {
340 ADDR_T pop_sp = A68_SP;
341 int gdigs = FUN_DIGITS (digs);
342 MP_T *z_g = nil_mp (p, gdigs);
343 // First see if we can restore a previous calculation.
344 if (gdigs <= A68_MP (mp_ln_scale_size)) {
345 (void) move_mp (z_g, A68_MP (mp_ln_scale), gdigs);
346 } else {
347 // No luck with the kept value, we generate a longer one.
348 (void) set_mp (z_g, (MP_T) 1, 1, gdigs);
349 (void) ln_mp (p, z_g, z_g, gdigs);
350 A68_MP (mp_ln_scale) = (MP_T *) get_heap_space ((unt) SIZE_MP (gdigs));
351 (void) move_mp (A68_MP (mp_ln_scale), z_g, gdigs);
352 A68_MP (mp_ln_scale_size) = gdigs;
353 }
354 (void) shorten_mp (p, z, digs, z_g, gdigs);
355 A68_SP = pop_sp;
356 return z;
357 }
358
359 //! @brief ln 10
360
361 MP_T *mp_ln_10 (NODE_T * p, MP_T * z, int digs)
362 {
363 ADDR_T pop_sp = A68_SP;
364 int gdigs = FUN_DIGITS (digs);
365 MP_T *z_g = nil_mp (p, gdigs);
366 // First see if we can restore a previous calculation.
367 if (gdigs <= A68_MP (mp_ln_10_size)) {
368 (void) move_mp (z_g, A68_MP (mp_ln_10), gdigs);
369 } else {
370 // No luck with the kept value, we generate a longer one.
371 (void) set_mp (z_g, (MP_T) 10, 0, gdigs);
372 (void) ln_mp (p, z_g, z_g, gdigs);
373 A68_MP (mp_ln_10) = (MP_T *) get_heap_space ((unt) SIZE_MP (gdigs));
374 (void) move_mp (A68_MP (mp_ln_10), z_g, gdigs);
375 A68_MP (mp_ln_10_size) = gdigs;
376 }
377 (void) shorten_mp (p, z, digs, z_g, gdigs);
378 A68_SP = pop_sp;
379 return z;
380 }
381
382 //! @brief PROC (LONG REAL) LONG REAL ln
383
384 MP_T *ln_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
385 {
386 // Depending on the argument we choose either Taylor or Newton.
387 ADDR_T pop_sp = A68_SP;
388 int gdigs = FUN_DIGITS (digs);
389 BOOL_T neg, scale;
390 MP_T expo = 0;
391 if (MP_DIGIT (x, 1) <= 0) {
392 errno = EDOM;
393 return NaN_MP;
394 }
395 MP_T *x_g = len_mp (p, x, digs, gdigs), *z_g = nil_mp (p, gdigs);
396 // We use ln (1 / x) = - ln (x).
397 neg = (BOOL_T) (MP_EXPONENT (x_g) < 0);
398 if (neg) {
399 (void) rec_mp (p, x_g, x_g, digs);
400 }
401 // We want correct results for extreme arguments. We scale when "x_g" exceeds
402 // "MP_RADIX ** +- 2", using ln (x * MP_RADIX ** n) = ln (x) + n * ln (MP_RADIX).
403 scale = (BOOL_T) (ABS (MP_EXPONENT (x_g)) >= 2);
404 if (scale) {
405 expo = MP_EXPONENT (x_g);
406 MP_EXPONENT (x_g) = (MP_T) 0;
407 }
408 if (MP_EXPONENT (x_g) == 0 && MP_DIGIT (x_g, 1) == 1 && MP_DIGIT (x_g, 2) == 0) {
409 // Taylor sum for x close to unity.
410 // ln (x) = (x - 1) - (x - 1) ** 2 / 2 + (x - 1) ** 3 / 3 - ...
411 // This is faster for small x and avoids cancellation.
412 MP_T *pwr = nil_mp (p, gdigs), *tmp = nil_mp (p, gdigs);
413 (void) minus_one_mp (p, x_g, x_g, gdigs);
414 (void) mul_mp (p, pwr, x_g, x_g, gdigs);
415 (void) move_mp (z_g, x_g, gdigs);
416 int n = 2;
417 BOOL_T iter = (BOOL_T) (MP_DIGIT (pwr, 1) != 0);
418 while (iter) {
419 (void) div_mp_digit (p, tmp, pwr, (MP_T) n, gdigs);
420 if (MP_EXPONENT (tmp) <= (MP_EXPONENT (z_g) - gdigs)) {
421 iter = A68_FALSE;
422 } else {
423 MP_DIGIT (tmp, 1) = (EVEN (n) ? -MP_DIGIT (tmp, 1) : MP_DIGIT (tmp, 1));
424 (void) add_mp (p, z_g, z_g, tmp, gdigs);
425 (void) mul_mp (p, pwr, pwr, x_g, gdigs);
426 n++;
427 }
428 }
429 } else {
430 // Newton's method: x<n+1> = x<n> - 1 + a / exp (x<n>).
431 MP_T *tmp = nil_mp (p, gdigs);
432 // Construct an estimate.
433 #if (A68_LEVEL >= 3)
434 (void) double_to_mp (p, z_g, log_double (mp_to_double (p, x_g, gdigs)), gdigs);
435 #else
436 (void) real_to_mp (p, z_g, log (mp_to_real (p, x_g, gdigs)), gdigs);
437 #endif
438 int decimals = DOUBLE_ACCURACY;
439 do {
440 decimals <<= 1;
441 int hdigs = MINIMUM (1 + decimals / LOG_MP_RADIX, gdigs);
442 (void) exp_mp (p, tmp, z_g, hdigs);
443 (void) div_mp (p, tmp, x_g, tmp, hdigs);
444 (void) minus_one_mp (p, z_g, z_g, hdigs);
445 (void) add_mp (p, z_g, z_g, tmp, hdigs);
446 } while (decimals < gdigs * LOG_MP_RADIX);
447 }
448 // Inverse scaling.
449 if (scale) {
450 // ln (x * MP_RADIX ** n) = ln (x) + n * ln (MP_RADIX).
451 MP_T *ln_base = nil_mp (p, gdigs);
452 (void) mp_ln_scale (p, ln_base, gdigs);
453 (void) mul_mp_digit (p, ln_base, ln_base, (MP_T) expo, gdigs);
454 (void) add_mp (p, z_g, z_g, ln_base, gdigs);
455 }
456 if (neg) {
457 MP_DIGIT (z_g, 1) = -MP_DIGIT (z_g, 1);
458 }
459 (void) shorten_mp (p, z, digs, z_g, gdigs);
460 // Exit.
461 A68_SP = pop_sp;
462 return z;
463 }
464
465 //! @brief PROC (LONG REAL) LONG REAL log
466
467 MP_T *log_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
468 {
469 ADDR_T pop_sp = A68_SP;
470 MP_T *ln_10 = nil_mp (p, digs);
471 if (ln_mp (p, z, x, digs) == NaN_MP) {
472 errno = EDOM;
473 return NaN_MP;
474 }
475 (void) mp_ln_10 (p, ln_10, digs);
476 (void) div_mp (p, z, z, ln_10, digs);
477 A68_SP = pop_sp;
478 return z;
479 }
480
481 //! @brief sinh ("z") and cosh ("z")
482
483 MP_T *hyp_mp (NODE_T * p, MP_T * sh, MP_T * ch, MP_T * z, int digs)
484 {
485 ADDR_T pop_sp = A68_SP;
486 MP_T *x_g = nil_mp (p, digs), *y_g = nil_mp (p, digs), *z_g = nil_mp (p, digs);
487 (void) move_mp (z_g, z, digs);
488 (void) exp_mp (p, x_g, z_g, digs);
489 (void) rec_mp (p, y_g, x_g, digs);
490 (void) add_mp (p, ch, x_g, y_g, digs);
491 // Avoid cancellation for sinh.
492 if ((MP_DIGIT (x_g, 1) == 1 && MP_DIGIT (x_g, 2) == 0) || (MP_DIGIT (y_g, 1) == 1 && MP_DIGIT (y_g, 2) == 0)) {
493 (void) expm1_mp (p, x_g, z_g, digs);
494 MP_DIGIT (z_g, 1) = -MP_DIGIT (z_g, 1);
495 (void) expm1_mp (p, y_g, z_g, digs);
496 }
497 (void) sub_mp (p, sh, x_g, y_g, digs);
498 (void) half_mp (p, sh, sh, digs);
499 (void) half_mp (p, ch, ch, digs);
500 A68_SP = pop_sp;
501 return z;
502 }
503
504 //! @brief PROC (LONG REAL) LONG REAL sinh
505
506 MP_T *sinh_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
507 {
508 ADDR_T pop_sp = A68_SP;
509 int gdigs = FUN_DIGITS (digs);
510 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
511 (void) hyp_mp (p, z_g, y_g, x_g, gdigs);
512 (void) shorten_mp (p, z, digs, z_g, gdigs);
513 A68_SP = pop_sp;
514 return z;
515 }
516
517 //! @brief PROC (LONG REAL) LONG REAL asinh
518
519 MP_T *asinh_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
520 {
521 if (IS_ZERO_MP (x)) {
522 SET_MP_ZERO (z, digs);
523 return z;
524 } else {
525 ADDR_T pop_sp = A68_SP;
526 int gdigs;
527 if (MP_EXPONENT (x) >= -1) {
528 gdigs = FUN_DIGITS (digs);
529 } else {
530 // Extra precision when x^2+1 gets close to 1.
531 gdigs = 2 * FUN_DIGITS (digs);
532 }
533 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
534 (void) mul_mp (p, z_g, x_g, x_g, gdigs);
535 SET_MP_ONE (y_g, gdigs);
536 (void) add_mp (p, y_g, z_g, y_g, gdigs);
537 (void) sqrt_mp (p, y_g, y_g, gdigs);
538 (void) add_mp (p, y_g, y_g, x_g, gdigs);
539 (void) ln_mp (p, z_g, y_g, gdigs);
540 if (IS_ZERO_MP (z_g)) {
541 (void) move_mp (z, x, digs);
542 } else {
543 (void) shorten_mp (p, z, digs, z_g, gdigs);
544 }
545 A68_SP = pop_sp;
546 return z;
547 }
548 }
549
550 //! @brief PROC (LONG REAL) LONG REAL cosh
551
552 MP_T *cosh_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
553 {
554 ADDR_T pop_sp = A68_SP;
555 int gdigs = FUN_DIGITS (digs);
556 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
557 (void) hyp_mp (p, y_g, z_g, x_g, gdigs);
558 (void) shorten_mp (p, z, digs, z_g, gdigs);
559 A68_SP = pop_sp;
560 return z;
561 }
562
563 //! @brief PROC (LONG REAL) LONG REAL acosh
564
565 MP_T *acosh_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
566 {
567 ADDR_T pop_sp = A68_SP;
568 int gdigs;
569 if (MP_DIGIT (x, 1) == 1 && MP_DIGIT (x, 2) == 0) {
570 // Extra precision when x^2-1 gets close to 0.
571 gdigs = 2 * FUN_DIGITS (digs);
572 } else {
573 gdigs = FUN_DIGITS (digs);
574 }
575 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
576 (void) mul_mp (p, z_g, x_g, x_g, gdigs);
577 SET_MP_ONE (y_g, gdigs);
578 (void) sub_mp (p, y_g, z_g, y_g, gdigs);
579 (void) sqrt_mp (p, y_g, y_g, gdigs);
580 (void) add_mp (p, y_g, y_g, x_g, gdigs);
581 (void) ln_mp (p, z_g, y_g, gdigs);
582 (void) shorten_mp (p, z, digs, z_g, gdigs);
583 A68_SP = pop_sp;
584 return z;
585 }
586
587 //! @brief PROC (LONG REAL) LONG REAL tanh
588
589 MP_T *tanh_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
590 {
591 ADDR_T pop_sp = A68_SP;
592 int gdigs = FUN_DIGITS (digs);
593 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
594 (void) hyp_mp (p, y_g, z_g, x_g, gdigs);
595 (void) div_mp (p, z_g, y_g, z_g, gdigs);
596 (void) shorten_mp (p, z, digs, z_g, gdigs);
597 A68_SP = pop_sp;
598 return z;
599 }
600
601 //! @brief PROC (LONG REAL) LONG REAL atanh
602
603 MP_T *atanh_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
604 {
605 ADDR_T pop_sp = A68_SP;
606 int gdigs = FUN_DIGITS (digs);
607 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
608 SET_MP_ONE (y_g, gdigs);
609 (void) add_mp (p, z_g, y_g, x_g, gdigs);
610 (void) sub_mp (p, y_g, y_g, x_g, gdigs);
611 (void) div_mp (p, y_g, z_g, y_g, gdigs);
612 (void) ln_mp (p, z_g, y_g, gdigs);
613 (void) half_mp (p, z_g, z_g, gdigs);
614 (void) shorten_mp (p, z, digs, z_g, gdigs);
615 A68_SP = pop_sp;
616 return z;
617 }
618
619 //! @brief PROC (LONG REAL) LONG REAL sin
620
621 MP_T *sin_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
622 {
623 // Use triple-angle relation to reduce argument.
624 ADDR_T pop_sp = A68_SP;
625 int gdigs = FUN_DIGITS (digs);
626 // We will use "pi".
627 MP_T *pi = nil_mp (p, gdigs), *tpi = nil_mp (p, gdigs), *hpi = nil_mp (p, gdigs);
628 (void) mp_pi (p, pi, MP_PI, gdigs);
629 (void) mp_pi (p, tpi, MP_TWO_PI, gdigs);
630 (void) mp_pi (p, hpi, MP_HALF_PI, gdigs);
631 // Argument reduction (1): sin (x) = sin (x mod 2 pi).
632 MP_T *x_g = len_mp (p, x, digs, gdigs);
633 (void) mod_mp (p, x_g, x_g, tpi, gdigs);
634 // Argument reduction (2): sin (-x) = sin (x)
635 // sin (x) = - sin (x - pi); pi < x <= 2 pi
636 // sin (x) = sin (pi - x); pi / 2 < x <= pi
637 BOOL_T neg = (BOOL_T) (MP_DIGIT (x_g, 1) < 0);
638 if (neg) {
639 MP_DIGIT (x_g, 1) = -MP_DIGIT (x_g, 1);
640 }
641 MP_T *tmp = nil_mp (p, gdigs);
642 (void) sub_mp (p, tmp, x_g, pi, gdigs);
643 BOOL_T flip = (BOOL_T) (MP_DIGIT (tmp, 1) > 0);
644 if (flip) { // x > pi
645 (void) sub_mp (p, x_g, x_g, pi, gdigs);
646 }
647 (void) sub_mp (p, tmp, x_g, hpi, gdigs);
648 if (MP_DIGIT (tmp, 1) > 0) { // x > pi / 2
649 (void) sub_mp (p, x_g, pi, x_g, gdigs);
650 }
651 // Argument reduction (3): (follows from De Moivre's theorem)
652 // sin (3x) = sin (x) * (3 - 4 sin ^ 2 (x))
653 int m = 0;
654 while (eps_mp (x_g, gdigs)) {
655 m++;
656 (void) div_mp_digit (p, x_g, x_g, (MP_T) 3, gdigs);
657 }
658 // Taylor sum.
659 MP_T *sqr = nil_mp (p, gdigs), *z_g = nil_mp (p, gdigs);
660 MP_T *pwr = nil_mp (p, gdigs), *fac = nil_mp (p, gdigs);
661 (void) mul_mp (p, sqr, x_g, x_g, gdigs);
662 (void) mul_mp (p, pwr, sqr, x_g, gdigs);
663 (void) move_mp (z_g, x_g, gdigs);
664 (void) div_mp_digit (p, tmp, pwr, (MP_T) 6, gdigs);
665 (void) sub_mp (p, z_g, z_g, tmp, gdigs);
666 (void) mul_mp (p, pwr, pwr, sqr, gdigs);
667 (void) div_mp_digit (p, tmp, pwr, (MP_T) 120, gdigs);
668 (void) add_mp (p, z_g, z_g, tmp, gdigs);
669 (void) mul_mp (p, pwr, pwr, sqr, gdigs);
670 (void) div_mp_digit (p, tmp, pwr, (MP_T) 5040, gdigs);
671 (void) sub_mp (p, z_g, z_g, tmp, gdigs);
672 (void) mul_mp (p, pwr, pwr, sqr, gdigs);
673 (void) set_mp (fac, (MP_T) 362880, 0, gdigs);
674 int n = 9;
675 BOOL_T even = A68_TRUE, iter = (BOOL_T) (MP_DIGIT (pwr, 1) != 0);
676 while (iter) {
677 (void) div_mp (p, tmp, pwr, fac, gdigs);
678 if (MP_EXPONENT (tmp) <= (MP_EXPONENT (z_g) - gdigs)) {
679 iter = A68_FALSE;
680 } else {
681 if (even) {
682 (void) add_mp (p, z_g, z_g, tmp, gdigs);
683 even = A68_FALSE;
684 } else {
685 (void) sub_mp (p, z_g, z_g, tmp, gdigs);
686 even = A68_TRUE;
687 }
688 (void) mul_mp (p, pwr, pwr, sqr, gdigs);
689 (void) mul_mp_digit (p, fac, fac, (MP_T) (++n), gdigs);
690 (void) mul_mp_digit (p, fac, fac, (MP_T) (++n), gdigs);
691 }
692 }
693 // Inverse scaling using sin (3x) = sin (x) * (3 - 4 sin ** 2 (x)).
694 // Use existing mp's for intermediates.
695 (void) set_mp (fac, (MP_T) 3, 0, gdigs);
696 while (m--) {
697 (void) mul_mp (p, pwr, z_g, z_g, gdigs);
698 (void) mul_mp_digit (p, pwr, pwr, (MP_T) 4, gdigs);
699 (void) sub_mp (p, pwr, fac, pwr, gdigs);
700 (void) mul_mp (p, z_g, pwr, z_g, gdigs);
701 }
702 (void) shorten_mp (p, z, digs, z_g, gdigs);
703 if (neg ^ flip) {
704 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
705 }
706 // Exit.
707 A68_SP = pop_sp;
708 return z;
709 }
710
711 //! @brief PROC (LONG REAL) LONG REAL cas
712
713 MP_T *cas_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
714 {
715 // Hartley kernel, which Hartley named cas (cosine and sine).
716 ADDR_T pop_sp = A68_SP;
717 MP_T *sinx = nil_mp (p, digs), *cosx = nil_mp (p, digs);
718 cos_mp (p, cosx, x, digs);
719 sin_mp (p, sinx, x, digs);
720 (void) add_mp (p, z, cosx, sinx, digs);
721 A68_SP = pop_sp;
722 return z;
723 }
724
725 //! @brief PROC (LONG REAL) LONG REAL cos
726
727 MP_T *cos_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
728 {
729 // Use cos (x) = sin (pi / 2 - x).
730 // Compute x mod 2 pi before subtracting to avoid cancellation.
731 ADDR_T pop_sp = A68_SP;
732 int gdigs = FUN_DIGITS (digs);
733 MP_T *hpi = nil_mp (p, gdigs), *tpi = nil_mp (p, gdigs);
734 MP_T *x_g = len_mp (p, x, digs, gdigs), *y = nil_mp (p, digs);
735 (void) mp_pi (p, hpi, MP_HALF_PI, gdigs);
736 (void) mp_pi (p, tpi, MP_TWO_PI, gdigs);
737 (void) mod_mp (p, x_g, x_g, tpi, gdigs);
738 (void) sub_mp (p, x_g, hpi, x_g, gdigs);
739 (void) shorten_mp (p, y, digs, x_g, gdigs);
740 (void) sin_mp (p, z, y, digs);
741 A68_SP = pop_sp;
742 return z;
743 }
744
745 //! @brief PROC (LONG REAL) LONG REAL tan
746
747 MP_T *tan_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
748 {
749 // Use tan (x) = sin (x) / sqrt (1 - sin ^ 2 (x)).
750 ADDR_T pop_sp = A68_SP;
751 int gdigs = FUN_DIGITS (digs);
752 MP_T *pi = nil_mp (p, gdigs), *hpi = nil_mp (p, gdigs);
753 (void) mp_pi (p, pi, MP_PI, gdigs);
754 (void) mp_pi (p, hpi, MP_HALF_PI, gdigs);
755 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs);
756 MP_T *sns = nil_mp (p, digs), *cns = nil_mp (p, digs);
757 // Argument mod pi.
758 BOOL_T neg;
759 (void) mod_mp (p, x_g, x_g, pi, gdigs);
760 if (MP_DIGIT (x_g, 1) >= 0) {
761 (void) sub_mp (p, y_g, x_g, hpi, gdigs);
762 neg = (BOOL_T) (MP_DIGIT (y_g, 1) > 0);
763 } else {
764 (void) add_mp (p, y_g, x_g, hpi, gdigs);
765 neg = (BOOL_T) (MP_DIGIT (y_g, 1) < 0);
766 }
767 (void) shorten_mp (p, x, digs, x_g, gdigs);
768 // tan(x) = sin(x) / sqrt (1 - sin ** 2 (x)).
769 (void) sin_mp (p, sns, x, digs);
770 (void) mul_mp (p, cns, sns, sns, digs);
771 (void) one_minus_mp (p, cns, cns, digs);
772 (void) sqrt_mp (p, cns, cns, digs);
773 if (div_mp (p, z, sns, cns, digs) == NaN_MP) {
774 errno = EDOM;
775 A68_SP = pop_sp;
776 return NaN_MP;
777 }
778 A68_SP = pop_sp;
779 if (neg) {
780 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
781 }
782 return z;
783 }
784
785 //! @brief PROC (LONG REAL) LONG REAL arcsin
786
787 MP_T *asin_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
788 {
789 ADDR_T pop_sp = A68_SP;
790 int gdigs = FUN_DIGITS (digs);
791 MP_T *x_g = len_mp (p, x, digs, gdigs), *z_g = nil_mp (p, gdigs);
792 MP_T *y = nil_mp (p, digs);
793 (void) mul_mp (p, z_g, x_g, x_g, gdigs);
794 (void) one_minus_mp (p, z_g, z_g, gdigs);
795 if (sqrt_mp (p, z_g, z_g, digs) == NaN_MP) {
796 errno = EDOM;
797 A68_SP = pop_sp;
798 return NaN_MP;
799 }
800 if (MP_DIGIT (z_g, 1) == 0) {
801 (void) mp_pi (p, z, MP_HALF_PI, digs);
802 MP_DIGIT (z, 1) = (MP_DIGIT (x_g, 1) >= 0 ? MP_DIGIT (z, 1) : -MP_DIGIT (z, 1));
803 A68_SP = pop_sp;
804 return z;
805 }
806 if (div_mp (p, x_g, x_g, z_g, gdigs) == NaN_MP) {
807 errno = EDOM;
808 A68_SP = pop_sp;
809 return NaN_MP;
810 }
811 (void) shorten_mp (p, y, digs, x_g, gdigs);
812 (void) atan_mp (p, z, y, digs);
813 A68_SP = pop_sp;
814 return z;
815 }
816
817 //! @brief PROC (LONG REAL) LONG REAL arccos
818
819 MP_T *acos_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
820 {
821 ADDR_T pop_sp = A68_SP;
822 int gdigs = FUN_DIGITS (digs);
823 BOOL_T neg = (BOOL_T) (MP_DIGIT (x, 1) < 0);
824 if (MP_DIGIT (x, 1) == 0) {
825 (void) mp_pi (p, z, MP_HALF_PI, digs);
826 A68_SP = pop_sp;
827 return z;
828 }
829 MP_T *x_g = len_mp (p, x, digs, gdigs), *z_g = nil_mp (p, gdigs);
830 MP_T *y = nil_mp (p, digs);
831 (void) mul_mp (p, z_g, x_g, x_g, gdigs);
832 (void) one_minus_mp (p, z_g, z_g, gdigs);
833 if (sqrt_mp (p, z_g, z_g, digs) == NaN_MP) {
834 errno = EDOM;
835 A68_SP = pop_sp;
836 return NaN_MP;
837 }
838 if (div_mp (p, x_g, z_g, x_g, gdigs) == NaN_MP) {
839 errno = EDOM;
840 A68_SP = pop_sp;
841 return NaN_MP;
842 }
843 (void) shorten_mp (p, y, digs, x_g, gdigs);
844 (void) atan_mp (p, z, y, digs);
845 if (neg) {
846 (void) mp_pi (p, y, MP_PI, digs);
847 (void) add_mp (p, z, z, y, digs);
848 }
849 A68_SP = pop_sp;
850 return z;
851 }
852
853 //! @brief PROC (LONG REAL) LONG REAL arctan
854
855 MP_T *atan_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
856 {
857 // Depending on the argument we choose either Taylor or Newton.
858 ADDR_T pop_sp = A68_SP;
859 if (MP_DIGIT (x, 1) == 0) {
860 A68_SP = pop_sp;
861 SET_MP_ZERO (z, digs);
862 return z;
863 }
864 int gdigs = FUN_DIGITS (digs);
865 MP_T *x_g = len_mp (p, x, digs, gdigs), *z_g = nil_mp (p, gdigs);
866 BOOL_T neg = (BOOL_T) (MP_DIGIT (x_g, 1) < 0);
867 if (neg) {
868 MP_DIGIT (x_g, 1) = -MP_DIGIT (x_g, 1);
869 }
870 // For larger arguments we use atan(x) = pi/2 - atan(1/x).
871 BOOL_T flip = (BOOL_T) (((MP_EXPONENT (x_g) > 0) || (MP_EXPONENT (x_g) == 0 && MP_DIGIT (x_g, 1) > 1)) && (MP_DIGIT (x_g, 1) != 0));
872 if (flip) {
873 (void) rec_mp (p, x_g, x_g, gdigs);
874 }
875 if (MP_EXPONENT (x_g) < -1 || (MP_EXPONENT (x_g) == -1 && MP_DIGIT (x_g, 1) < MP_RADIX / 100)) {
876 // Taylor sum for x close to zero.
877 // arctan (x) = x - x ** 3 / 3 + x ** 5 / 5 - x ** 7 / 7 + ..
878 // This is faster for small x and avoids cancellation
879 MP_T *pwr = nil_mp (p, gdigs), *sqr = nil_mp (p, gdigs), *tmp = nil_mp (p, gdigs);
880 (void) mul_mp (p, sqr, x_g, x_g, gdigs);
881 (void) mul_mp (p, pwr, sqr, x_g, gdigs);
882 (void) move_mp (z_g, x_g, gdigs);
883 int n = 3;
884 BOOL_T even = A68_FALSE, iter = (BOOL_T) (MP_DIGIT (pwr, 1) != 0);
885 while (iter) {
886 (void) div_mp_digit (p, tmp, pwr, (MP_T) n, gdigs);
887 if (MP_EXPONENT (tmp) <= (MP_EXPONENT (z_g) - gdigs)) {
888 iter = A68_FALSE;
889 } else {
890 if (even) {
891 (void) add_mp (p, z_g, z_g, tmp, gdigs);
892 even = A68_FALSE;
893 } else {
894 (void) sub_mp (p, z_g, z_g, tmp, gdigs);
895 even = A68_TRUE;
896 }
897 (void) mul_mp (p, pwr, pwr, sqr, gdigs);
898 n += 2;
899 }
900 }
901 } else {
902 // Newton's method: x<n+1> = x<n> - cos (x<n>) * (sin (x<n>) - a cos (x<n>)).
903 MP_T *sns = nil_mp (p, gdigs), *cns = nil_mp (p, gdigs), *tmp = nil_mp (p, gdigs);
904 // Construct an estimate.
905 #if (A68_LEVEL >= 3)
906 (void) double_to_mp (p, z_g, atan_double (mp_to_double (p, x_g, gdigs)), gdigs);
907 #else
908 (void) real_to_mp (p, z_g, atan (mp_to_real (p, x_g, gdigs)), gdigs);
909 #endif
910 int decimals = DOUBLE_ACCURACY;
911 do {
912 decimals <<= 1;
913 int hdigs = MINIMUM (1 + decimals / LOG_MP_RADIX, gdigs);
914 (void) sin_mp (p, sns, z_g, hdigs);
915 (void) mul_mp (p, tmp, sns, sns, hdigs);
916 (void) one_minus_mp (p, tmp, tmp, hdigs);
917 (void) sqrt_mp (p, cns, tmp, hdigs);
918 (void) mul_mp (p, tmp, x_g, cns, hdigs);
919 (void) sub_mp (p, tmp, sns, tmp, hdigs);
920 (void) mul_mp (p, tmp, tmp, cns, hdigs);
921 (void) sub_mp (p, z_g, z_g, tmp, hdigs);
922 } while (decimals < gdigs * LOG_MP_RADIX);
923 }
924 if (flip) {
925 MP_T *hpi = nil_mp (p, gdigs);
926 (void) sub_mp (p, z_g, mp_pi (p, hpi, MP_HALF_PI, gdigs), z_g, gdigs);
927 }
928 (void) shorten_mp (p, z, digs, z_g, gdigs);
929 MP_DIGIT (z, 1) = (neg ? -MP_DIGIT (z, 1) : MP_DIGIT (z, 1));
930 // Exit.
931 A68_SP = pop_sp;
932 return z;
933 }
934
935 //! @brief PROC (LONG REAL, LONG REAL) LONG REAL atan2
936
937 MP_T *atan2_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
938 {
939 ADDR_T pop_sp = A68_SP;
940 MP_T *t = nil_mp (p, digs);
941 if (MP_DIGIT (x, 1) == 0 && MP_DIGIT (y, 1) == 0) {
942 errno = EDOM;
943 A68_SP = pop_sp;
944 return NaN_MP;
945 } else {
946 BOOL_T flip = (BOOL_T) (MP_DIGIT (y, 1) < 0);
947 MP_DIGIT (y, 1) = ABS (MP_DIGIT (y, 1));
948 if (IS_ZERO_MP (x)) {
949 (void) mp_pi (p, z, MP_HALF_PI, digs);
950 } else {
951 BOOL_T flop = (BOOL_T) (MP_DIGIT (x, 1) <= 0);
952 MP_DIGIT (x, 1) = ABS (MP_DIGIT (x, 1));
953 (void) div_mp (p, z, y, x, digs);
954 (void) atan_mp (p, z, z, digs);
955 if (flop) {
956 (void) mp_pi (p, t, MP_PI, digs);
957 (void) sub_mp (p, z, t, z, digs);
958 }
959 }
960 if (flip) {
961 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
962 }
963 }
964 A68_SP = pop_sp;
965 return z;
966 }
967
968 //! @brief PROC (LONG REAL) LONG REAL csc
969
970 MP_T *csc_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
971 {
972 sin_mp (p, z, x, digs);
973 if (rec_mp (p, z, z, digs) == NaN_MP) {
974 return NaN_MP;
975 }
976 return z;
977 }
978
979 //! @brief PROC (LONG REAL) LONG REAL cscdg
980
981 MP_T *cscdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
982 {
983 sindg_mp (p, z, x, digs);
984 if (rec_mp (p, z, z, digs) == NaN_MP) {
985 return NaN_MP;
986 }
987 return z;
988 }
989
990 //! @brief PROC (LONG REAL) LONG REAL acsc
991
992 MP_T *acsc_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
993 {
994 if (rec_mp (p, z, x, digs) == NaN_MP) {
995 return NaN_MP;
996 }
997 return asin_mp (p, z, z, digs);
998 }
999
1000 //! @brief PROC (LONG REAL) LONG REAL acscdg
1001
1002 MP_T *acscdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1003 {
1004 if (rec_mp (p, z, x, digs) == NaN_MP) {
1005 return NaN_MP;
1006 }
1007 return asindg_mp (p, z, z, digs);
1008 }
1009
1010 //! @brief PROC (LONG REAL) LONG REAL sec
1011
1012 MP_T *sec_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1013 {
1014 cos_mp (p, z, x, digs);
1015 if (rec_mp (p, z, z, digs) == NaN_MP) {
1016 return NaN_MP;
1017 }
1018 return z;
1019 }
1020
1021 //! @brief PROC (LONG REAL) LONG REAL secdg
1022
1023 MP_T *secdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1024 {
1025 cosdg_mp (p, z, x, digs);
1026 if (rec_mp (p, z, z, digs) == NaN_MP) {
1027 return NaN_MP;
1028 }
1029 return z;
1030 }
1031
1032 //! @brief PROC (LONG REAL) LONG REAL asec
1033
1034 MP_T *asec_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1035 {
1036 if (rec_mp (p, z, x, digs) == NaN_MP) {
1037 return NaN_MP;
1038 }
1039 return acos_mp (p, z, z, digs);
1040 }
1041
1042 //! @brief PROC (LONG REAL) LONG REAL asec
1043
1044 MP_T *asecdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1045 {
1046 if (rec_mp (p, z, x, digs) == NaN_MP) {
1047 return NaN_MP;
1048 }
1049 return acosdg_mp (p, z, z, digs);
1050 }
1051
1052 //! @brief PROC (LONG REAL) LONG REAL cot
1053
1054 MP_T *cot_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1055 {
1056 // Use tan (x) = sin (x) / sqrt (1 - sin ^ 2 (x)).
1057 ADDR_T pop_sp = A68_SP;
1058 int gdigs = FUN_DIGITS (digs);
1059 MP_T *pi = nil_mp (p, gdigs), *hpi = nil_mp (p, gdigs);
1060 (void) mp_pi (p, pi, MP_PI, gdigs);
1061 (void) mp_pi (p, hpi, MP_HALF_PI, gdigs);
1062 MP_T *x_g = len_mp (p, x, digs, gdigs), *y_g = nil_mp (p, gdigs);
1063 MP_T *sns = nil_mp (p, digs), *cns = nil_mp (p, digs);
1064 // Argument mod pi.
1065 BOOL_T neg;
1066 (void) mod_mp (p, x_g, x_g, pi, gdigs);
1067 if (MP_DIGIT (x_g, 1) >= 0) {
1068 (void) sub_mp (p, y_g, x_g, hpi, gdigs);
1069 neg = (BOOL_T) (MP_DIGIT (y_g, 1) > 0);
1070 } else {
1071 (void) add_mp (p, y_g, x_g, hpi, gdigs);
1072 neg = (BOOL_T) (MP_DIGIT (y_g, 1) < 0);
1073 }
1074 (void) shorten_mp (p, x, digs, x_g, gdigs);
1075 // tan(x) = sin(x) / sqrt (1 - sin ** 2 (x)).
1076 (void) sin_mp (p, sns, x, digs);
1077 (void) mul_mp (p, cns, sns, sns, digs);
1078 (void) one_minus_mp (p, cns, cns, digs);
1079 (void) sqrt_mp (p, cns, cns, digs);
1080 if (div_mp (p, z, cns, sns, digs) == NaN_MP) {
1081 errno = EDOM;
1082 A68_SP = pop_sp;
1083 return NaN_MP;
1084 }
1085 A68_SP = pop_sp;
1086 if (neg) {
1087 MP_DIGIT (z, 1) = -MP_DIGIT (z, 1);
1088 }
1089 return z;
1090 }
1091
1092 //! @brief PROC (LONG REAL) LONG REAL arccot
1093
1094 MP_T *acot_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1095 {
1096 ADDR_T pop_sp = A68_SP;
1097 MP_T *f = nil_mp (p, digs);
1098 if (rec_mp (p, f, x, digs) == NaN_MP) {
1099 errno = EDOM;
1100 A68_SP = pop_sp;
1101 return NaN_MP;
1102 } else {
1103 (void) atan_mp (p, z, f, digs);
1104 A68_SP = pop_sp;
1105 return z;
1106 }
1107 }
1108
1109 //! @brief PROC (LONG REAL) LONG REAL sindg
1110
1111 MP_T *sindg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1112 {
1113 ADDR_T pop_sp = A68_SP;
1114 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1115 (void) mp_pi (p, f, MP_PI_OVER_180, digs);
1116 (void) mul_mp (p, g, x, f, digs);
1117 (void) sin_mp (p, z, g, digs);
1118 A68_SP = pop_sp;
1119 return z;
1120 }
1121
1122 //! @brief PROC (LONG REAL) LONG REAL cosdg
1123
1124 MP_T *cosdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1125 {
1126 ADDR_T pop_sp = A68_SP;
1127 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1128 (void) mp_pi (p, f, MP_PI_OVER_180, digs);
1129 (void) mul_mp (p, g, x, f, digs);
1130 (void) cos_mp (p, z, g, digs);
1131 A68_SP = pop_sp;
1132 return z;
1133 }
1134
1135 //! @brief PROC (LONG REAL) LONG REAL tandg
1136
1137 MP_T *tandg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1138 {
1139 ADDR_T pop_sp = A68_SP;
1140 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1141 (void) mp_pi (p, f, MP_PI_OVER_180, digs);
1142 (void) mul_mp (p, g, x, f, digs);
1143 if (tan_mp (p, z, g, digs) == NaN_MP) {
1144 errno = EDOM;
1145 A68_SP = pop_sp;
1146 return NaN_MP;
1147 } else {
1148 A68_SP = pop_sp;
1149 return z;
1150 }
1151 }
1152
1153 //! @brief PROC (LONG REAL) LONG REAL cotdg
1154
1155 MP_T *cotdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1156 {
1157 ADDR_T pop_sp = A68_SP;
1158 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1159 (void) mp_pi (p, f, MP_PI_OVER_180, digs);
1160 (void) mul_mp (p, g, x, f, digs);
1161 if (cot_mp (p, z, g, digs) == NaN_MP) {
1162 errno = EDOM;
1163 A68_SP = pop_sp;
1164 return NaN_MP;
1165 } else {
1166 A68_SP = pop_sp;
1167 return z;
1168 }
1169 }
1170
1171 //! @brief PROC (LONG REAL) LONG REAL arcsindg
1172
1173 MP_T *asindg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1174 {
1175 ADDR_T pop_sp = A68_SP;
1176 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1177 (void) asin_mp (p, f, x, digs);
1178 (void) mp_pi (p, g, MP_180_OVER_PI, digs);
1179 (void) mul_mp (p, z, f, g, digs);
1180 A68_SP = pop_sp;
1181 return z;
1182 }
1183
1184 //! @brief PROC (LONG REAL) LONG REAL arccosdg
1185
1186 MP_T *acosdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1187 {
1188 ADDR_T pop_sp = A68_SP;
1189 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1190 (void) acos_mp (p, f, x, digs);
1191 (void) mp_pi (p, g, MP_180_OVER_PI, digs);
1192 (void) mul_mp (p, z, f, g, digs);
1193 A68_SP = pop_sp;
1194 return z;
1195 }
1196
1197 //! @brief PROC (LONG REAL) LONG REAL arctandg
1198
1199 MP_T *atandg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1200 {
1201 ADDR_T pop_sp = A68_SP;
1202 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1203 (void) atan_mp (p, f, x, digs);
1204 (void) mp_pi (p, g, MP_180_OVER_PI, digs);
1205 (void) mul_mp (p, z, f, g, digs);
1206 A68_SP = pop_sp;
1207 return z;
1208 }
1209
1210 //! @brief PROC (LONG REAL) LONG REAL arccotdg
1211
1212 MP_T *acotdg_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1213 {
1214 ADDR_T pop_sp = A68_SP;
1215 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1216 if (acot_mp (p, f, x, digs) == NaN_MP) {
1217 errno = EDOM;
1218 A68_SP = pop_sp;
1219 return NaN_MP;
1220 } else {
1221 (void) mp_pi (p, g, MP_180_OVER_PI, digs);
1222 (void) mul_mp (p, z, f, g, digs);
1223 A68_SP = pop_sp;
1224 return z;
1225 }
1226 }
1227
1228 //! @brief PROC (LONG REAL, LONG REAL) LONG REAL atan2dg
1229
1230 MP_T *atan2dg_mp (NODE_T * p, MP_T * z, MP_T * x, MP_T * y, int digs)
1231 {
1232 ADDR_T pop_sp = A68_SP;
1233 MP_T *f = nil_mp (p, digs), *g = nil_mp (p, digs);
1234 if (atan2_mp (p, f, x, y, digs) == NaN_MP) {
1235 errno = EDOM;
1236 A68_SP = pop_sp;
1237 return NaN_MP;
1238 } else {
1239 (void) mp_pi (p, g, MP_180_OVER_PI, digs);
1240 (void) mul_mp (p, z, f, g, digs);
1241 A68_SP = pop_sp;
1242 return z;
1243 }
1244 }
1245
1246 //! @brief PROC (LONG REAL) LONG REAL sinpi
1247
1248 MP_T *sinpi_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1249 {
1250 ADDR_T pop_sp = A68_SP;
1251 MP_T *f = nil_mp (p, digs), *g = lit_mp (p, 1, 0, digs);
1252 (void) mod_mp (p, f, x, g, digs);
1253 if (IS_ZERO_MP (f)) {
1254 SET_MP_ZERO (z, digs);
1255 A68_SP = pop_sp;
1256 return z;
1257 }
1258 (void) mp_pi (p, f, MP_PI, digs);
1259 (void) mul_mp (p, g, x, f, digs);
1260 (void) sin_mp (p, z, g, digs);
1261 A68_SP = pop_sp;
1262 return z;
1263 }
1264
1265 //! @brief PROC (LONG REAL) LONG REAL acospi
1266
1267 MP_T *cospi_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1268 {
1269 ADDR_T pop_sp = A68_SP;
1270 MP_T *f = nil_mp (p, digs), *g = lit_mp (p, 1, 0, digs);
1271 (void) mod_mp (p, f, x, g, digs);
1272 abs_mp (p, f, f, digs);
1273 SET_MP_HALF (g, digs);
1274 (void) sub_mp (p, g, f, g, digs);
1275 if (IS_ZERO_MP (g)) {
1276 SET_MP_ZERO (z, digs);
1277 A68_SP = pop_sp;
1278 return z;
1279 }
1280 (void) mp_pi (p, f, MP_PI, digs);
1281 (void) mul_mp (p, g, x, f, digs);
1282 (void) cos_mp (p, z, g, digs);
1283 A68_SP = pop_sp;
1284 return z;
1285 }
1286
1287 //! @brief PROC (LONG REAL) LONG REAL tanpi
1288
1289 MP_T *tanpi_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1290 {
1291 ADDR_T pop_sp = A68_SP;
1292 MP_T *f = nil_mp (p, digs), *g = lit_mp (p, 1, 0, digs);
1293 MP_T *h = nil_mp (p, digs), *half = nil_mp (p, digs);
1294 SET_MP_ONE (g, digs);
1295 (void) mod_mp (p, f, x, g, digs);
1296 if (IS_ZERO_MP (f)) {
1297 SET_MP_ZERO (z, digs);
1298 A68_SP = pop_sp;
1299 return z;
1300 }
1301 SET_MP_MINUS_HALF (half, digs);
1302 (void) sub_mp (p, h, f, half, digs);
1303 if (MP_DIGIT (h, 1) < 0) {
1304 (void) plus_one_mp (p, f, f, digs);
1305 } else {
1306 SET_MP_HALF (half, digs);
1307 (void) sub_mp (p, h, f, half, digs);
1308 if (MP_DIGIT (h, 1) < 0) {
1309 (void) minus_one_mp (p, f, f, digs);
1310 }
1311 }
1312 BOOL_T neg = MP_DIGIT (f, 1) < 0;
1313 (void) abs_mp (p, f, f, digs);
1314 SET_MP_QUART (g, digs);
1315 (void) sub_mp (p, g, f, g, digs);
1316 if (IS_ZERO_MP (g)) {
1317 if (neg) {
1318 SET_MP_MINUS_ONE (z, digs);
1319 } else {
1320 SET_MP_ONE (z, digs);
1321 }
1322 A68_SP = pop_sp;
1323 return z;
1324 }
1325 (void) mp_pi (p, f, MP_PI, digs);
1326 (void) mul_mp (p, g, x, f, digs);
1327 if (tan_mp (p, z, g, digs) == NaN_MP) {
1328 errno = EDOM;
1329 A68_SP = pop_sp;
1330 return NaN_MP;
1331 } else {
1332 A68_SP = pop_sp;
1333 return z;
1334 }
1335 }
1336
1337 //! @brief PROC (LONG REAL) LONG REAL cotpi
1338
1339 MP_T *cotpi_mp (NODE_T * p, MP_T * z, MP_T * x, int digs)
1340 {
1341 ADDR_T pop_sp = A68_SP;
1342 MP_T *f = nil_mp (p, digs), *g = lit_mp (p, 1, 0, digs);
1343 MP_T *h = nil_mp (p, digs), *half = nil_mp (p, digs);
1344 (void) mod_mp (p, f, x, g, digs);
1345 if (IS_ZERO_MP (f)) {
1346 errno = EDOM;
1347 A68_SP = pop_sp;
1348 return NaN_MP;
1349 }
1350 SET_MP_MINUS_HALF (half, digs);
1351 (void) sub_mp (p, h, f, half, digs);
1352 if (MP_DIGIT (h, 1) < 0) {
1353 (void) plus_one_mp (p, f, f, digs);
1354 } else {
1355 SET_MP_HALF (half, digs);
1356 (void) sub_mp (p, h, f, half, digs);
1357 if (MP_DIGIT (h, 1) < 0) {
1358 (void) minus_one_mp (p, f, f, digs);
1359 }
1360 }
1361 BOOL_T neg = MP_DIGIT (f, 1) < 0;
1362 (void) abs_mp (p, f, f, digs);
1363 SET_MP_QUART (g, digs);
1364 (void) sub_mp (p, g, f, g, digs);
1365 if (IS_ZERO_MP (g)) {
1366 if (neg) {
1367 SET_MP_MINUS_ONE (z, digs);
1368 } else {
1369 SET_MP_ONE (z, digs);
1370 }
1371 A68_SP = pop_sp;
1372 return z;
1373 }
1374 (void) mp_pi (p, f, MP_PI, digs);
1375 (void) mul_mp (p, g, x, f, digs);
1376 if (cot_mp (p, z, g, digs) == NaN_MP) {
1377 errno = EDOM;
1378 A68_SP = pop_sp;
1379 return NaN_MP;
1380 } else {
1381 A68_SP = pop_sp;
1382 return z;
1383 }
1384 }
© 2002-2024 J.M. van der Veer (jmvdveer@xs4all.nl)
|