diff options
Diffstat (limited to 'src/mp.c')
-rw-r--r-- | src/mp.c | 2092 |
1 files changed, 2092 insertions, 0 deletions
diff --git a/src/mp.c b/src/mp.c new file mode 100644 index 0000000..0e46b35 --- /dev/null +++ b/src/mp.c @@ -0,0 +1,2092 @@ +/* Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2008-2009 Robert Ancell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <errno.h> + +#include "mp.h" +#include "mp-private.h" + +// FIXME: Re-add overflow and underflow detection + +char *mp_error = NULL; + +/* THIS ROUTINE IS CALLED WHEN AN ERROR CONDITION IS ENCOUNTERED, AND + * AFTER A MESSAGE HAS BEEN WRITTEN TO STDERR. + */ +void +mperr(const char *format, ...) +{ + char text[1024]; + va_list args; + + va_start(args, format); + vsnprintf(text, 1024, format, args); + va_end(args); + + if (mp_error) + free(mp_error); + mp_error = strdup(text); +} + + +const char * +mp_get_error() +{ + return mp_error; +} + + +void mp_clear_error() +{ + if (mp_error) + free(mp_error); + mp_error = NULL; +} + + +/* ROUTINE CALLED BY MP_DIVIDE AND MP_SQRT TO ENSURE THAT + * RESULTS ARE REPRESENTED EXACTLY IN T-2 DIGITS IF THEY + * CAN BE. X IS AN MP NUMBER, I AND J ARE INTEGERS. + */ +static void +mp_ext(int i, int j, MPNumber *x) +{ + int q, s; + + if (mp_is_zero(x) || MP_T <= 2 || i == 0) + return; + + /* COMPUTE MAXIMUM POSSIBLE ERROR IN THE LAST PLACE */ + q = (j + 1) / i + 1; + s = MP_BASE * x->fraction[MP_T - 2] + x->fraction[MP_T - 1]; + + /* SET LAST TWO DIGITS TO ZERO */ + if (s <= q) { + x->fraction[MP_T - 2] = 0; + x->fraction[MP_T - 1] = 0; + return; + } + + if (s + q < MP_BASE * MP_BASE) + return; + + /* ROUND UP HERE */ + x->fraction[MP_T - 2] = MP_BASE - 1; + x->fraction[MP_T - 1] = MP_BASE; + + /* NORMALIZE X (LAST DIGIT B IS OK IN MP_MULTIPLY_INTEGER) */ + mp_multiply_integer(x, 1, x); +} + + +void +mp_get_eulers(MPNumber *z) +{ + MPNumber t; + mp_set_from_integer(1, &t); + mp_epowy(&t, z); +} + + +void +mp_get_i(MPNumber *z) +{ + mp_set_from_integer(0, z); + z->im_sign = 1; + z->im_exponent = 1; + z->im_fraction[0] = 1; +} + + +void +mp_abs(const MPNumber *x, MPNumber *z) +{ + if (mp_is_complex(x)){ + MPNumber x_real, x_im; + + mp_real_component(x, &x_real); + mp_imaginary_component(x, &x_im); + + mp_multiply(&x_real, &x_real, &x_real); + mp_multiply(&x_im, &x_im, &x_im); + mp_add(&x_real, &x_im, z); + mp_root(z, 2, z); + } + else { + mp_set_from_mp(x, z); + if (z->sign < 0) + z->sign = -z->sign; + } +} + + +void +mp_arg(const MPNumber *x, MPAngleUnit unit, MPNumber *z) +{ + MPNumber x_real, x_im, pi; + + if (mp_is_zero(x)) { + /* Translators: Error display when attempting to take argument of zero */ + mperr(_("Argument not defined for zero")); + mp_set_from_integer(0, z); + return; + } + + mp_real_component(x, &x_real); + mp_imaginary_component(x, &x_im); + mp_get_pi(&pi); + + if (mp_is_zero(&x_im)) { + if (mp_is_negative(&x_real)) + convert_from_radians(&pi, MP_RADIANS, z); + else + mp_set_from_integer(0, z); + } + else if (mp_is_zero(&x_real)) { + mp_set_from_mp(&pi, z); + if (mp_is_negative(&x_im)) + mp_divide_integer(z, -2, z); + else + mp_divide_integer(z, 2, z); + } + else if (mp_is_negative(&x_real)) { + mp_divide(&x_im, &x_real, z); + mp_atan(z, MP_RADIANS, z); + if (mp_is_negative(&x_im)) + mp_subtract(z, &pi, z); + else + mp_add(z, &pi, z); + } + else { + mp_divide(&x_im, &x_real, z); + mp_atan(z, MP_RADIANS, z); + } + + convert_from_radians(z, unit, z); +} + + +void +mp_conjugate(const MPNumber *x, MPNumber *z) +{ + mp_set_from_mp(x, z); + z->im_sign = -z->im_sign; +} + + +void +mp_real_component(const MPNumber *x, MPNumber *z) +{ + mp_set_from_mp(x, z); + + /* Clear imaginary component */ + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); +} + + +void +mp_imaginary_component(const MPNumber *x, MPNumber *z) +{ + /* Copy imaginary component to real component */ + z->sign = x->im_sign; + z->exponent = x->im_exponent; + memcpy(z->fraction, x->im_fraction, sizeof(int) * MP_SIZE); + + /* Clear (old) imaginary component */ + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); +} + + +static void +mp_add_real(const MPNumber *x, int y_sign, const MPNumber *y, MPNumber *z) +{ + int sign_prod, i, c; + int exp_diff, med; + bool x_largest = false; + const int *big_fraction, *small_fraction; + MPNumber x_copy, y_copy; + + /* 0 + y = y */ + if (mp_is_zero(x)) { + mp_set_from_mp(y, z); + z->sign = y_sign; + return; + } + /* x + 0 = x */ + else if (mp_is_zero(y)) { + mp_set_from_mp(x, z); + return; + } + + sign_prod = y_sign * x->sign; + exp_diff = x->exponent - y->exponent; + med = abs(exp_diff); + if (exp_diff < 0) { + x_largest = false; + } else if (exp_diff > 0) { + x_largest = true; + } else { + /* EXPONENTS EQUAL SO COMPARE SIGNS, THEN FRACTIONS IF NEC. */ + if (sign_prod < 0) { + /* Signs are not equal. find out which mantissa is larger. */ + int j; + for (j = 0; j < MP_T; j++) { + int i = x->fraction[j] - y->fraction[j]; + if (i == 0) + continue; + if (i < 0) + x_largest = false; + else if (i > 0) + x_largest = true; + break; + } + + /* Both mantissas equal, so result is zero. */ + if (j >= MP_T) { + mp_set_from_integer(0, z); + return; + } + } + } + + mp_set_from_mp(x, &x_copy); + mp_set_from_mp(y, &y_copy); + mp_set_from_integer(0, z); + + if (x_largest) { + z->sign = x_copy.sign; + z->exponent = x_copy.exponent; + big_fraction = x_copy.fraction; + small_fraction = y_copy.fraction; + } else { + z->sign = y_sign; + z->exponent = y_copy.exponent; + big_fraction = y_copy.fraction; + small_fraction = x_copy.fraction; + } + + /* CLEAR GUARD DIGITS TO RIGHT OF X DIGITS */ + for(i = 3; i >= med; i--) + z->fraction[MP_T + i] = 0; + + if (sign_prod >= 0) { + /* HERE DO ADDITION, EXPONENT(Y) >= EXPONENT(X) */ + for (i = MP_T + 3; i >= MP_T; i--) + z->fraction[i] = small_fraction[i - med]; + + c = 0; + for (; i >= med; i--) { + c = big_fraction[i] + small_fraction[i - med] + c; + + if (c < MP_BASE) { + /* NO CARRY GENERATED HERE */ + z->fraction[i] = c; + c = 0; + } else { + /* CARRY GENERATED HERE */ + z->fraction[i] = c - MP_BASE; + c = 1; + } + } + + for (; i >= 0; i--) + { + c = big_fraction[i] + c; + if (c < MP_BASE) { + z->fraction[i] = c; + i--; + + /* NO CARRY POSSIBLE HERE */ + for (; i >= 0; i--) + z->fraction[i] = big_fraction[i]; + + c = 0; + break; + } + + z->fraction[i] = 0; + c = 1; + } + + /* MUST SHIFT RIGHT HERE AS CARRY OFF END */ + if (c != 0) { + for (i = MP_T + 3; i > 0; i--) + z->fraction[i] = z->fraction[i - 1]; + z->fraction[0] = 1; + z->exponent++; + } + } + else { + c = 0; + for (i = MP_T + med - 1; i >= MP_T; i--) { + /* HERE DO SUBTRACTION, ABS(Y) > ABS(X) */ + z->fraction[i] = c - small_fraction[i - med]; + c = 0; + + /* BORROW GENERATED HERE */ + if (z->fraction[i] < 0) { + c = -1; + z->fraction[i] += MP_BASE; + } + } + + for(; i >= med; i--) { + c = big_fraction[i] + c - small_fraction[i - med]; + if (c >= 0) { + /* NO BORROW GENERATED HERE */ + z->fraction[i] = c; + c = 0; + } else { + /* BORROW GENERATED HERE */ + z->fraction[i] = c + MP_BASE; + c = -1; + } + } + + for (; i >= 0; i--) { + c = big_fraction[i] + c; + + if (c >= 0) { + z->fraction[i] = c; + i--; + + /* NO CARRY POSSIBLE HERE */ + for (; i >= 0; i--) + z->fraction[i] = big_fraction[i]; + + break; + } + + z->fraction[i] = c + MP_BASE; + c = -1; + } + } + + mp_normalize(z); +} + + +static void +mp_add_with_sign(const MPNumber *x, int y_sign, const MPNumber *y, MPNumber *z) +{ + if (mp_is_complex(x) || mp_is_complex(y)) { + MPNumber real_x, real_y, im_x, im_y, real_z, im_z; + + mp_real_component(x, &real_x); + mp_imaginary_component(x, &im_x); + mp_real_component(y, &real_y); + mp_imaginary_component(y, &im_y); + + mp_add_real(&real_x, y_sign * y->sign, &real_y, &real_z); + mp_add_real(&im_x, y_sign * y->im_sign, &im_y, &im_z); + + mp_set_from_complex(&real_z, &im_z, z); + } + else + mp_add_real(x, y_sign * y->sign, y, z); +} + + +void +mp_add(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + mp_add_with_sign(x, 1, y, z); +} + + +void +mp_add_integer(const MPNumber *x, int64_t y, MPNumber *z) +{ + MPNumber t; + mp_set_from_integer(y, &t); + mp_add(x, &t, z); +} + + +void +mp_add_fraction(const MPNumber *x, int64_t i, int64_t j, MPNumber *y) +{ + MPNumber t; + mp_set_from_fraction(i, j, &t); + mp_add(x, &t, y); +} + + +void +mp_subtract(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + mp_add_with_sign(x, -1, y, z); +} + + +void +mp_sgn(const MPNumber *x, MPNumber *z) +{ + if (mp_is_zero(x)) + mp_set_from_integer(0, z); + else if (mp_is_negative(x)) + mp_set_from_integer(-1, z); + else + mp_set_from_integer(1, z); +} + +void +mp_integer_component(const MPNumber *x, MPNumber *z) +{ + int i; + + /* Clear fraction */ + mp_set_from_mp(x, z); + for (i = z->exponent; i < MP_SIZE; i++) + z->fraction[i] = 0; + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); +} + +void +mp_fractional_component(const MPNumber *x, MPNumber *z) +{ + int i, shift; + + /* Fractional component of zero is 0 */ + if (mp_is_zero(x)) { + mp_set_from_integer(0, z); + return; + } + + /* All fractional */ + if (x->exponent <= 0) { + mp_set_from_mp(x, z); + return; + } + + /* Shift fractional component */ + shift = x->exponent; + for (i = shift; i < MP_SIZE && x->fraction[i] == 0; i++) + shift++; + z->sign = x->sign; + z->exponent = x->exponent - shift; + for (i = 0; i < MP_SIZE; i++) { + if (i + shift >= MP_SIZE) + z->fraction[i] = 0; + else + z->fraction[i] = x->fraction[i + shift]; + } + if (z->fraction[0] == 0) + z->sign = 0; + + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); +} + + +void +mp_fractional_part(const MPNumber *x, MPNumber *z) +{ + MPNumber f; + mp_floor(x, &f); + mp_subtract(x, &f, z); +} + + +void +mp_floor(const MPNumber *x, MPNumber *z) +{ + int i; + bool have_fraction = false, is_negative; + + /* Integer component of zero = 0 */ + if (mp_is_zero(x)) { + mp_set_from_mp(x, z); + return; + } + + /* If all fractional then no integer component */ + if (x->exponent <= 0) { + mp_set_from_integer(0, z); + return; + } + + is_negative = mp_is_negative(x); + + /* Clear fraction */ + mp_set_from_mp(x, z); + for (i = z->exponent; i < MP_SIZE; i++) { + if (z->fraction[i]) + have_fraction = true; + z->fraction[i] = 0; + } + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); + + if (have_fraction && is_negative) + mp_add_integer(z, -1, z); +} + + +void +mp_ceiling(const MPNumber *x, MPNumber *z) +{ + MPNumber f; + + mp_floor(x, z); + mp_fractional_component(x, &f); + if (mp_is_zero(&f)) + return; + mp_add_integer(z, 1, z); +} + + +void +mp_round(const MPNumber *x, MPNumber *z) +{ + MPNumber f, one; + bool do_floor; + + do_floor = !mp_is_negative(x); + + mp_fractional_component(x, &f); + mp_multiply_integer(&f, 2, &f); + mp_abs(&f, &f); + mp_set_from_integer(1, &one); + if (mp_is_greater_equal(&f, &one)) + do_floor = !do_floor; + + if (do_floor) + mp_floor(x, z); + else + mp_ceiling(x, z); +} + +int +mp_compare_mp_to_mp(const MPNumber *x, const MPNumber *y) +{ + int i; + + if (x->sign != y->sign) { + if (x->sign > y->sign) + return 1; + else + return -1; + } + + /* x = y = 0 */ + if (mp_is_zero(x)) + return 0; + + /* See if numbers are of different magnitude */ + if (x->exponent != y->exponent) { + if (x->exponent > y->exponent) + return x->sign; + else + return -x->sign; + } + + /* Compare fractions */ + for (i = 0; i < MP_SIZE; i++) { + if (x->fraction[i] == y->fraction[i]) + continue; + + if (x->fraction[i] > y->fraction[i]) + return x->sign; + else + return -x->sign; + } + + /* x = y */ + return 0; +} + + +void +mp_divide(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + int i, ie; + MPNumber t; + + /* x/0 */ + if (mp_is_zero(y)) { + /* Translators: Error displayed attempted to divide by zero */ + mperr(_("Division by zero is undefined")); + mp_set_from_integer(0, z); + return; + } + + /* 0/y = 0 */ + if (mp_is_zero(x)) { + mp_set_from_integer(0, z); + return; + } + + /* z = x × y⁻¹ */ + /* FIXME: Set exponent to zero to avoid overflow in mp_multiply??? */ + mp_reciprocal(y, &t); + ie = t.exponent; + t.exponent = 0; + i = t.fraction[0]; + mp_multiply(x, &t, z); + mp_ext(i, z->fraction[0], z); + z->exponent += ie; +} + + +static void +mp_divide_integer_real(const MPNumber *x, int64_t y, MPNumber *z) +{ + int c, i, k, b2, c2, j1, j2; + MPNumber x_copy; + + /* x/0 */ + if (y == 0) { + /* Translators: Error displayed attempted to divide by zero */ + mperr(_("Division by zero is undefined")); + mp_set_from_integer(0, z); + return; + } + + /* 0/y = 0 */ + if (mp_is_zero(x)) { + mp_set_from_integer(0, z); + return; + } + + /* Division by -1 or 1 just changes sign */ + if (y == 1 || y == -1) { + if (y < 0) + mp_invert_sign(x, z); + else + mp_set_from_mp(x, z); + return; + } + + /* Copy x as z may also refer to x */ + mp_set_from_mp(x, &x_copy); + mp_set_from_integer(0, z); + + if (y < 0) { + y = -y; + z->sign = -x_copy.sign; + } + else + z->sign = x_copy.sign; + z->exponent = x_copy.exponent; + + c = 0; + i = 0; + + /* IF y*B NOT REPRESENTABLE AS AN INTEGER HAVE TO SIMULATE + * LONG DIVISION. ASSUME AT LEAST 16-BIT WORD. + */ + + /* Computing MAX */ + b2 = max(MP_BASE << 3, 32767 / MP_BASE); + if (y < b2) { + int kh, r1; + + /* LOOK FOR FIRST NONZERO DIGIT IN QUOTIENT */ + do { + c = MP_BASE * c; + if (i < MP_T) + c += x_copy.fraction[i]; + i++; + r1 = c / y; + if (r1 < 0) + goto L210; + } while (r1 == 0); + + /* ADJUST EXPONENT AND GET T+4 DIGITS IN QUOTIENT */ + z->exponent += 1 - i; + z->fraction[0] = r1; + c = MP_BASE * (c - y * r1); + kh = 1; + if (i < MP_T) { + kh = MP_T + 1 - i; + for (k = 1; k < kh; k++) { + c += x_copy.fraction[i]; + z->fraction[k] = c / y; + c = MP_BASE * (c - y * z->fraction[k]); + i++; + } + if (c < 0) + goto L210; + } + + for (k = kh; k < MP_T + 4; k++) { + z->fraction[k] = c / y; + c = MP_BASE * (c - y * z->fraction[k]); + } + if (c < 0) + goto L210; + + mp_normalize(z); + return; + } + + /* HERE NEED SIMULATED DOUBLE-PRECISION DIVISION */ + j1 = y / MP_BASE; + j2 = y - j1 * MP_BASE; + + /* LOOK FOR FIRST NONZERO DIGIT */ + c2 = 0; + do { + c = MP_BASE * c + c2; + c2 = i < MP_T ? x_copy.fraction[i] : 0; + i++; + } while (c < j1 || (c == j1 && c2 < j2)); + + /* COMPUTE T+4 QUOTIENT DIGITS */ + z->exponent += 1 - i; + i--; + + /* MAIN LOOP FOR LARGE ABS(y) CASE */ + for (k = 1; k <= MP_T + 4; k++) { + int ir, iq, iqj; + + /* GET APPROXIMATE QUOTIENT FIRST */ + ir = c / (j1 + 1); + + /* NOW REDUCE SO OVERFLOW DOES NOT OCCUR */ + iq = c - ir * j1; + if (iq >= b2) { + /* HERE IQ*B WOULD POSSIBLY OVERFLOW SO INCREASE IR */ + ++ir; + iq -= j1; + } + + iq = iq * MP_BASE - ir * j2; + if (iq < 0) { + /* HERE IQ NEGATIVE SO IR WAS TOO LARGE */ + ir--; + iq += y; + } + + if (i < MP_T) + iq += x_copy.fraction[i]; + i++; + iqj = iq / y; + + /* R(K) = QUOTIENT, C = REMAINDER */ + z->fraction[k - 1] = iqj + ir; + c = iq - y * iqj; + + if (c < 0) + goto L210; + } + + mp_normalize(z); + +L210: + /* CARRY NEGATIVE SO OVERFLOW MUST HAVE OCCURRED */ + mperr("*** INTEGER OVERFLOW IN MP_DIVIDE_INTEGER, B TOO LARGE ***"); + mp_set_from_integer(0, z); +} + + +void +mp_divide_integer(const MPNumber *x, int64_t y, MPNumber *z) +{ + if (mp_is_complex(x)) { + MPNumber re_z, im_z; + + mp_real_component(x, &re_z); + mp_imaginary_component(x, &im_z); + mp_divide_integer_real(&re_z, y, &re_z); + mp_divide_integer_real(&im_z, y, &im_z); + mp_set_from_complex(&re_z, &im_z, z); + } + else + mp_divide_integer_real(x, y, z); +} + + +bool +mp_is_integer(const MPNumber *x) +{ + MPNumber t1, t2, t3; + + if (mp_is_complex(x)) + return false; + + /* This fix is required for 1/3 repiprocal not being detected as an integer */ + /* Multiplication and division by 10000 is used to get around a + * limitation to the "fix" for Sun bugtraq bug #4006391 in the + * mp_floor() routine in mp.c, when the exponent is less than 1. + */ + mp_set_from_integer(10000, &t3); + mp_multiply(x, &t3, &t1); + mp_divide(&t1, &t3, &t1); + mp_floor(&t1, &t2); + return mp_is_equal(&t1, &t2); + + /* Correct way to check for integer */ + /*int i; + + // Zero is an integer + if (mp_is_zero(x)) + return true; + + // Fractional + if (x->exponent <= 0) + return false; + + // Look for fractional components + for (i = x->exponent; i < MP_SIZE; i++) { + if (x->fraction[i] != 0) + return false; + } + + return true;*/ +} + + +bool +mp_is_positive_integer(const MPNumber *x) +{ + if (mp_is_complex(x)) + return false; + else + return x->sign >= 0 && mp_is_integer(x); +} + + +bool +mp_is_natural(const MPNumber *x) +{ + if (mp_is_complex(x)) + return false; + else + return x->sign > 0 && mp_is_integer(x); +} + + +bool +mp_is_complex(const MPNumber *x) +{ + return x->im_sign != 0; +} + + +bool +mp_is_equal(const MPNumber *x, const MPNumber *y) +{ + return mp_compare_mp_to_mp(x, y) == 0; +} + + +/* Return e^x for |x| < 1 USING AN O(SQRT(T).M(T)) ALGORITHM + * DESCRIBED IN - R. P. BRENT, THE COMPLEXITY OF MULTIPLE- + * PRECISION ARITHMETIC (IN COMPLEXITY OF COMPUTATIONAL PROBLEM + * SOLVING, UNIV. OF QUEENSLAND PRESS, BRISBANE, 1976, 126-165). + * ASYMPTOTICALLY FASTER METHODS EXIST, BUT ARE NOT USEFUL + * UNLESS T IS VERY LARGE. SEE COMMENTS TO MP_ATAN AND MPPIGL. + */ +static void +mp_exp(const MPNumber *x, MPNumber *z) +{ + int i, q; + float rlb; + MPNumber t1, t2; + + /* e^0 = 1 */ + if (mp_is_zero(x)) { + mp_set_from_integer(1, z); + return; + } + + /* Only defined for |x| < 1 */ + if (x->exponent > 0) { + mperr("*** ABS(X) NOT LESS THAN 1 IN CALL TO MP_EXP ***"); + mp_set_from_integer(0, z); + return; + } + + mp_set_from_mp(x, &t1); + rlb = log((float)MP_BASE); + + /* Compute approximately optimal q (and divide x by 2^q) */ + q = (int)(sqrt((float)MP_T * 0.48f * rlb) + (float) x->exponent * 1.44f * rlb); + + /* HALVE Q TIMES */ + if (q > 0) { + int ib, ic; + + ib = MP_BASE << 2; + ic = 1; + for (i = 1; i <= q; ++i) { + ic *= 2; + if (ic < ib && ic != MP_BASE && i < q) + continue; + mp_divide_integer(&t1, ic, &t1); + ic = 1; + } + } + + if (mp_is_zero(&t1)) { + mp_set_from_integer(0, z); + return; + } + + /* Sum series, reducing t where possible */ + mp_set_from_mp(&t1, z); + mp_set_from_mp(&t1, &t2); + for (i = 2; MP_T + t2.exponent - z->exponent > 0; i++) { + mp_multiply(&t1, &t2, &t2); + mp_divide_integer(&t2, i, &t2); + mp_add(&t2, z, z); + if (mp_is_zero(&t2)) + break; + } + + /* Apply (x+1)^2 - 1 = x(2 + x) for q iterations */ + for (i = 1; i <= q; ++i) { + mp_add_integer(z, 2, &t1); + mp_multiply(&t1, z, z); + } + + mp_add_integer(z, 1, z); +} + + +static void +mp_epowy_real(const MPNumber *x, MPNumber *z) +{ + float r__1; + int i, ix, xs, tss; + float rx, rz, rlb; + MPNumber t1, t2; + + /* e^0 = 1 */ + if (mp_is_zero(x)) { + mp_set_from_integer(1, z); + return; + } + + /* If |x| < 1 use mp_exp */ + if (x->exponent <= 0) { + mp_exp(x, z); + return; + } + + /* SEE IF ABS(X) SO LARGE THAT EXP(X) WILL CERTAINLY OVERFLOW + * OR UNDERFLOW. 1.01 IS TO ALLOW FOR ERRORS IN ALOG. + */ + rlb = log((float)MP_BASE) * 1.01f; + + /* NOW SAFE TO CONVERT X TO REAL */ + rx = mp_cast_to_float(x); + + /* SAVE SIGN AND WORK WITH ABS(X) */ + xs = x->sign; + mp_abs(x, &t2); + + /* GET FRACTIONAL AND INTEGER PARTS OF ABS(X) */ + ix = mp_cast_to_int(&t2); + mp_fractional_component(&t2, &t2); + + /* ATTACH SIGN TO FRACTIONAL PART AND COMPUTE EXP OF IT */ + t2.sign *= xs; + mp_exp(&t2, z); + + /* COMPUTE E-2 OR 1/E USING TWO EXTRA DIGITS IN CASE ABS(X) LARGE + * (BUT ONLY ONE EXTRA DIGIT IF T < 4) + */ + if (MP_T < 4) + tss = MP_T + 1; + else + tss = MP_T + 2; + + /* LOOP FOR E COMPUTATION. DECREASE T IF POSSIBLE. */ + /* Computing MIN */ + mp_set_from_integer(xs, &t1); + + t2.sign = 0; + for (i = 2 ; ; i++) { + if (min(tss, tss + 2 + t1.exponent) <= 2) + break; + + mp_divide_integer(&t1, i * xs, &t1); + mp_add(&t2, &t1, &t2); + if (mp_is_zero(&t1)) + break; + } + + /* RAISE E OR 1/E TO POWER IX */ + if (xs > 0) + mp_add_integer(&t2, 2, &t2); + mp_xpowy_integer(&t2, ix, &t2); + + /* MULTIPLY EXPS OF INTEGER AND FRACTIONAL PARTS */ + mp_multiply(z, &t2, z); + + /* CHECK THAT RELATIVE ERROR LESS THAN 0.01 UNLESS ABS(X) LARGE + * (WHEN EXP MIGHT OVERFLOW OR UNDERFLOW) + */ + if (fabs(rx) > 10.0f) + return; + + rz = mp_cast_to_float(z); + r__1 = rz - exp(rx); + if (fabs(r__1) < rz * 0.01f) + return; + + /* THE FOLLOWING MESSAGE MAY INDICATE THAT + * B**(T-1) IS TOO SMALL, OR THAT M IS TOO SMALL SO THE + * RESULT UNDERFLOWED. + */ + mperr("*** ERROR OCCURRED IN MP_EPOWY, RESULT INCORRECT ***"); +} + + +void +mp_epowy(const MPNumber *x, MPNumber *z) +{ + /* e^0 = 1 */ + if (mp_is_zero(x)) { + mp_set_from_integer(1, z); + return; + } + + if (mp_is_complex(x)) { + MPNumber x_real, r, theta; + + mp_real_component(x, &x_real); + mp_imaginary_component(x, &theta); + + mp_epowy_real(&x_real, &r); + mp_set_from_polar(&r, MP_RADIANS, &theta, z); + } + else + mp_epowy_real(x, z); +} + + +/* RETURNS K = K/GCD AND L = L/GCD, WHERE GCD IS THE + * GREATEST COMMON DIVISOR OF K AND L. + * SAVE INPUT PARAMETERS IN LOCAL VARIABLES + */ +void +mp_gcd(int64_t *k, int64_t *l) +{ + int64_t i, j; + + i = abs(*k); + j = abs(*l); + if (j == 0) { + /* IF J = 0 RETURN (1, 0) UNLESS I = 0, THEN (0, 0) */ + *k = 1; + *l = 0; + if (i == 0) + *k = 0; + return; + } + + /* EUCLIDEAN ALGORITHM LOOP */ + do { + i %= j; + if (i == 0) { + *k = *k / j; + *l = *l / j; + return; + } + j %= i; + } while (j != 0); + + /* HERE J IS THE GCD OF K AND L */ + *k = *k / i; + *l = *l / i; +} + + +bool +mp_is_zero(const MPNumber *x) +{ + return x->sign == 0 && x->im_sign == 0; +} + + +bool +mp_is_negative(const MPNumber *x) +{ + return x->sign < 0; +} + + +bool +mp_is_greater_equal(const MPNumber *x, const MPNumber *y) +{ + return mp_compare_mp_to_mp(x, y) >= 0; +} + + +bool +mp_is_greater_than(const MPNumber *x, const MPNumber *y) +{ + return mp_compare_mp_to_mp(x, y) > 0; +} + + +bool +mp_is_less_equal(const MPNumber *x, const MPNumber *y) +{ + return mp_compare_mp_to_mp(x, y) <= 0; +} + + +/* RETURNS MP Y = LN(1+X) IF X IS AN MP NUMBER SATISFYING THE + * CONDITION ABS(X) < 1/B, ERROR OTHERWISE. + * USES NEWTONS METHOD TO SOLVE THE EQUATION + * EXP1(-Y) = X, THEN REVERSES SIGN OF Y. + */ +static void +mp_lns(const MPNumber *x, MPNumber *z) +{ + int t, it0; + MPNumber t1, t2, t3; + + /* ln(1+0) = 0 */ + if (mp_is_zero(x)) { + mp_set_from_integer(0, z); + return; + } + + /* Get starting approximation -ln(1+x) ~= -x + x^2/2 - x^3/3 + x^4/4 */ + mp_set_from_mp(x, &t2); + mp_divide_integer(x, 4, &t1); + mp_add_fraction(&t1, -1, 3, &t1); + mp_multiply(x, &t1, &t1); + mp_add_fraction(&t1, 1, 2, &t1); + mp_multiply(x, &t1, &t1); + mp_add_integer(&t1, -1, &t1); + mp_multiply(x, &t1, z); + + /* Solve using Newtons method */ + it0 = t = 5; + while(1) + { + int ts2, ts3; + + /* t3 = (e^t3 - 1) */ + /* z = z - (t2 + t3 + (t2 * t3)) */ + mp_epowy(z, &t3); + mp_add_integer(&t3, -1, &t3); + mp_multiply(&t2, &t3, &t1); + mp_add(&t3, &t1, &t3); + mp_add(&t2, &t3, &t3); + mp_subtract(z, &t3, z); + if (t >= MP_T) + break; + + /* FOLLOWING LOOP COMPUTES NEXT VALUE OF T TO USE. + * BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE, + * WE CAN ALMOST DOUBLE T EACH TIME. + */ + ts3 = t; + t = MP_T; + do { + ts2 = t; + t = (t + it0) / 2; + } while (t > ts3); + t = ts2; + } + + /* CHECK THAT NEWTON ITERATION WAS CONVERGING AS EXPECTED */ + if (t3.sign != 0 && t3.exponent << 1 > it0 - MP_T) { + mperr("*** ERROR OCCURRED IN MP_LNS, NEWTON ITERATION NOT CONVERGING PROPERLY ***"); + } + + z->sign = -z->sign; +} + + +static void +mp_ln_real(const MPNumber *x, MPNumber *z) +{ + int e, k; + float rx, rlx; + MPNumber t1, t2; + + /* LOOP TO GET APPROXIMATE LN(X) USING SINGLE-PRECISION */ + mp_set_from_mp(x, &t1); + mp_set_from_integer(0, z); + for(k = 0; k < 10; k++) + { + /* COMPUTE FINAL CORRECTION ACCURATELY USING MP_LNS */ + mp_add_integer(&t1, -1, &t2); + if (mp_is_zero(&t2) || t2.exponent + 1 <= 0) { + mp_lns(&t2, &t2); + mp_add(z, &t2, z); + return; + } + + /* REMOVE EXPONENT TO AVOID FLOATING-POINT OVERFLOW */ + e = t1.exponent; + t1.exponent = 0; + rx = mp_cast_to_float(&t1); + t1.exponent = e; + rlx = log(rx) + (float)e * log((float)MP_BASE); + mp_set_from_float(-(double)rlx, &t2); + + /* UPDATE Z AND COMPUTE ACCURATE EXP OF APPROXIMATE LOG */ + mp_subtract(z, &t2, z); + mp_epowy(&t2, &t2); + + /* COMPUTE RESIDUAL WHOSE LOG IS STILL TO BE FOUND */ + mp_multiply(&t1, &t2, &t1); + } + + mperr("*** ERROR IN MP_LN, ITERATION NOT CONVERGING ***"); +} + + +void +mp_ln(const MPNumber *x, MPNumber *z) +{ + /* ln(0) undefined */ + if (mp_is_zero(x)) { + /* Translators: Error displayed when attempting to take logarithm of zero */ + mperr(_("Logarithm of zero is undefined")); + mp_set_from_integer(0, z); + return; + } + + /* ln(-x) complex */ + /* FIXME: Make complex numbers optional */ + /*if (mp_is_negative(x)) { + // Translators: Error displayed attempted to take logarithm of negative value + mperr(_("Logarithm of negative values is undefined")); + mp_set_from_integer(0, z); + return; + }*/ + + if (mp_is_complex(x) || mp_is_negative(x)) { + MPNumber r, theta, z_real; + + /* ln(re^iθ) = e^(ln(r)+iθ) */ + mp_abs(x, &r); + mp_arg(x, MP_RADIANS, &theta); + + mp_ln_real(&r, &z_real); + mp_set_from_complex(&z_real, &theta, z); + } + else + mp_ln_real(x, z); +} + + +void +mp_logarithm(int64_t n, const MPNumber *x, MPNumber *z) +{ + MPNumber t1, t2; + + /* log(0) undefined */ + if (mp_is_zero(x)) { + /* Translators: Error displayed when attempting to take logarithm of zero */ + mperr(_("Logarithm of zero is undefined")); + mp_set_from_integer(0, z); + return; + } + + /* logn(x) = ln(x) / ln(n) */ + mp_set_from_integer(n, &t1); + mp_ln(&t1, &t1); + mp_ln(x, &t2); + mp_divide(&t2, &t1, z); +} + + +bool +mp_is_less_than(const MPNumber *x, const MPNumber *y) +{ + return mp_compare_mp_to_mp(x, y) < 0; +} + + +static void +mp_multiply_real(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + int c, i, xi; + MPNumber r; + + /* x*0 = 0*y = 0 */ + if (x->sign == 0 || y->sign == 0) { + mp_set_from_integer(0, z); + return; + } + + z->sign = x->sign * y->sign; + z->exponent = x->exponent + y->exponent; + memset(&r, 0, sizeof(MPNumber)); + + /* PERFORM MULTIPLICATION */ + c = 8; + for (i = 0; i < MP_T; i++) { + int j; + + xi = x->fraction[i]; + + /* FOR SPEED, PUT THE NUMBER WITH MANY ZEROS FIRST */ + if (xi == 0) + continue; + + /* Computing MIN */ + for (j = 0; j < min(MP_T, MP_T + 3 - i); j++) + r.fraction[i+j+1] += xi * y->fraction[j]; + c--; + if (c > 0) + continue; + + /* CHECK FOR LEGAL BASE B DIGIT */ + if (xi < 0 || xi >= MP_BASE) { + mperr("*** ILLEGAL BASE B DIGIT IN CALL TO MP_MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***"); + mp_set_from_integer(0, z); + return; + } + + /* PROPAGATE CARRIES AT END AND EVERY EIGHTH TIME, + * FASTER THAN DOING IT EVERY TIME. + */ + for (j = MP_T + 3; j >= 0; j--) { + int ri = r.fraction[j] + c; + if (ri < 0) { + mperr("*** INTEGER OVERFLOW IN MP_MULTIPLY, B TOO LARGE ***"); + mp_set_from_integer(0, z); + return; + } + c = ri / MP_BASE; + r.fraction[j] = ri - MP_BASE * c; + } + if (c != 0) { + mperr("*** ILLEGAL BASE B DIGIT IN CALL TO MP_MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***"); + mp_set_from_integer(0, z); + return; + } + c = 8; + } + + if (c != 8) { + if (xi < 0 || xi >= MP_BASE) { + mperr("*** ILLEGAL BASE B DIGIT IN CALL TO MP_MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***"); + mp_set_from_integer(0, z); + return; + } + + c = 0; + for (i = MP_T + 3; i >= 0; i--) { + int ri = r.fraction[i] + c; + if (ri < 0) { + mperr("*** INTEGER OVERFLOW IN MP_MULTIPLY, B TOO LARGE ***"); + mp_set_from_integer(0, z); + return; + } + c = ri / MP_BASE; + r.fraction[i] = ri - MP_BASE * c; + } + + if (c != 0) { + mperr("*** ILLEGAL BASE B DIGIT IN CALL TO MP_MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***"); + mp_set_from_integer(0, z); + return; + } + } + + /* Clear complex part */ + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); + + /* NORMALIZE AND ROUND RESULT */ + // FIXME: Use stack variable because of mp_normalize brokeness + for (i = 0; i < MP_SIZE; i++) + z->fraction[i] = r.fraction[i]; + mp_normalize(z); +} + + +void +mp_multiply(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + /* x*0 = 0*y = 0 */ + if (mp_is_zero(x) || mp_is_zero(y)) { + mp_set_from_integer(0, z); + return; + } + + /* (a+bi)(c+di) = (ac-bd)+(ad+bc)i */ + if (mp_is_complex(x) || mp_is_complex(y)) { + MPNumber real_x, real_y, im_x, im_y, t1, t2, real_z, im_z; + + mp_real_component(x, &real_x); + mp_imaginary_component(x, &im_x); + mp_real_component(y, &real_y); + mp_imaginary_component(y, &im_y); + + mp_multiply_real(&real_x, &real_y, &t1); + mp_multiply_real(&im_x, &im_y, &t2); + mp_subtract(&t1, &t2, &real_z); + + mp_multiply_real(&real_x, &im_y, &t1); + mp_multiply_real(&im_x, &real_y, &t2); + mp_add(&t1, &t2, &im_z); + + mp_set_from_complex(&real_z, &im_z, z); + } + else { + mp_multiply_real(x, y, z); + } +} + + +static void +mp_multiply_integer_real(const MPNumber *x, int64_t y, MPNumber *z) +{ + int c, i; + MPNumber x_copy; + + /* x*0 = 0*y = 0 */ + if (mp_is_zero(x) || y == 0) { + mp_set_from_integer(0, z); + return; + } + + /* x*1 = x, x*-1 = -x */ + // FIXME: Why is this not working? mp_ext is using this function to do a normalization + /*if (y == 1 || y == -1) { + if (y < 0) + mp_invert_sign(x, z); + else + mp_set_from_mp(x, z); + return; + }*/ + + /* Copy x as z may also refer to x */ + mp_set_from_mp(x, &x_copy); + mp_set_from_integer(0, z); + + if (y < 0) { + y = -y; + z->sign = -x_copy.sign; + } + else + z->sign = x_copy.sign; + z->exponent = x_copy.exponent + 4; + + /* FORM PRODUCT IN ACCUMULATOR */ + c = 0; + + /* IF y*B NOT REPRESENTABLE AS AN INTEGER WE HAVE TO SIMULATE + * DOUBLE-PRECISION MULTIPLICATION. + */ + + /* Computing MAX */ + if (y >= max(MP_BASE << 3, 32767 / MP_BASE)) { + int64_t j1, j2; + + /* HERE J IS TOO LARGE FOR SINGLE-PRECISION MULTIPLICATION */ + j1 = y / MP_BASE; + j2 = y - j1 * MP_BASE; + + /* FORM PRODUCT */ + for (i = MP_T + 3; i >= 0; i--) { + int64_t c1, c2, is, ix, t; + + c1 = c / MP_BASE; + c2 = c - MP_BASE * c1; + ix = 0; + if (i > 3) + ix = x_copy.fraction[i - 4]; + + t = j2 * ix + c2; + is = t / MP_BASE; + c = j1 * ix + c1 + is; + z->fraction[i] = t - MP_BASE * is; + } + } + else + { + int64_t ri = 0; + + for (i = MP_T + 3; i >= 4; i--) { + ri = y * x_copy.fraction[i - 4] + c; + c = ri / MP_BASE; + z->fraction[i] = ri - MP_BASE * c; + } + + /* CHECK FOR INTEGER OVERFLOW */ + if (ri < 0) { + mperr("*** INTEGER OVERFLOW IN mp_multiply_integer, B TOO LARGE ***"); + mp_set_from_integer(0, z); + return; + } + + /* HAVE TO TREAT FIRST FOUR WORDS OF R SEPARATELY */ + for (i = 3; i >= 0; i--) { + int t; + + t = c; + c = t / MP_BASE; + z->fraction[i] = t - MP_BASE * c; + } + } + + /* HAVE TO SHIFT RIGHT HERE AS CARRY OFF END */ + while (c != 0) { + int64_t t; + + for (i = MP_T + 3; i >= 1; i--) + z->fraction[i] = z->fraction[i - 1]; + t = c; + c = t / MP_BASE; + z->fraction[0] = t - MP_BASE * c; + z->exponent++; + } + + if (c < 0) { + mperr("*** INTEGER OVERFLOW IN mp_multiply_integer, B TOO LARGE ***"); + mp_set_from_integer(0, z); + return; + } + + z->im_sign = 0; + z->im_exponent = 0; + memset(z->im_fraction, 0, sizeof(int) * MP_SIZE); + mp_normalize(z); +} + + +void +mp_multiply_integer(const MPNumber *x, int64_t y, MPNumber *z) +{ + if (mp_is_complex(x)) { + MPNumber re_z, im_z; + mp_real_component(x, &re_z); + mp_imaginary_component(x, &im_z); + mp_multiply_integer_real(&re_z, y, &re_z); + mp_multiply_integer_real(&im_z, y, &im_z); + mp_set_from_complex(&re_z, &im_z, z); + } + else + mp_multiply_integer_real(x, y, z); +} + + +void +mp_multiply_fraction(const MPNumber *x, int64_t numerator, int64_t denominator, MPNumber *z) +{ + if (denominator == 0) { + mperr(_("Division by zero is undefined")); + mp_set_from_integer(0, z); + return; + } + + if (numerator == 0) { + mp_set_from_integer(0, z); + return; + } + + /* Reduce to lowest terms */ + mp_gcd(&numerator, &denominator); + mp_divide_integer(x, denominator, z); + mp_multiply_integer(z, numerator, z); +} + + +void +mp_invert_sign(const MPNumber *x, MPNumber *z) +{ + mp_set_from_mp(x, z); + z->sign = -z->sign; + z->im_sign = -z->im_sign; +} + + +// FIXME: Is r->fraction large enough? It seems to be in practise but it may be MP_T+4 instead of MP_T +// FIXME: There is some sort of stack corruption/use of unitialised variables here. Some functions are +// using stack variables as x otherwise there are corruption errors. e.g. "Cos(45) - 1/Sqrt(2) = -0" +// (try in scientific mode) +void +mp_normalize(MPNumber *x) +{ + int start_index; + + /* Find first non-zero digit */ + for (start_index = 0; start_index < MP_SIZE && x->fraction[start_index] == 0; start_index++); + + /* Mark as zero */ + if (start_index >= MP_SIZE) { + x->sign = 0; + x->exponent = 0; + return; + } + + /* Shift left so first digit is non-zero */ + if (start_index > 0) { + int i; + + x->exponent -= start_index; + for (i = 0; (i + start_index) < MP_SIZE; i++) + x->fraction[i] = x->fraction[i + start_index]; + for (; i < MP_SIZE; i++) + x->fraction[i] = 0; + } +} + + +static void +mp_pwr(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + MPNumber t; + + /* (-x)^y imaginary */ + /* FIXME: Make complex numbers optional */ + /*if (x->sign < 0) { + mperr(_("The power of negative numbers is only defined for integer exponents")); + mp_set_from_integer(0, z); + return; + }*/ + + /* 0^-y illegal */ + if (mp_is_zero(x) && y->sign < 0) { + mperr(_("The power of zero is undefined for a negative exponent")); + mp_set_from_integer(0, z); + return; + } + + /* x^0 = 1 */ + if (mp_is_zero(y)) { + mp_set_from_integer(1, z); + return; + } + + mp_ln(x, &t); + mp_multiply(y, &t, z); + mp_epowy(z, z); +} + + +static void +mp_reciprocal_real(const MPNumber *x, MPNumber *z) +{ + MPNumber t1, t2; + int it0, t; + + /* 1/0 invalid */ + if (mp_is_zero(x)) { + mperr(_("Reciprocal of zero is undefined")); + mp_set_from_integer(0, z); + return; + } + + /* Start by approximating value using floating point */ + mp_set_from_mp(x, &t1); + t1.exponent = 0; + mp_set_from_float(1.0f / mp_cast_to_float(&t1), &t1); + t1.exponent -= x->exponent; + + it0 = t = 3; + while(1) { + int ts2, ts3; + + /* t1 = t1 - (t1 * ((x * t1) - 1)) (2*t1 - t1^2*x) */ + mp_multiply(x, &t1, &t2); + mp_add_integer(&t2, -1, &t2); + mp_multiply(&t1, &t2, &t2); + mp_subtract(&t1, &t2, &t1); + if (t >= MP_T) + break; + + /* FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE + * BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE). + */ + ts3 = t; + t = MP_T; + do { + ts2 = t; + t = (t + it0) / 2; + } while (t > ts3); + t = min(ts2, MP_T); + } + + /* RETURN IF NEWTON ITERATION WAS CONVERGING */ + if (t2.sign != 0 && (t1.exponent - t2.exponent) << 1 < MP_T - it0) { + /* THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL, + * OR THAT THE STARTING APPROXIMATION IS NOT ACCURATE ENOUGH. + */ + mperr("*** ERROR OCCURRED IN MP_RECIPROCAL, NEWTON ITERATION NOT CONVERGING PROPERLY ***"); + } + + mp_set_from_mp(&t1, z); +} + + +void +mp_reciprocal(const MPNumber *x, MPNumber *z) +{ + if (mp_is_complex(x)) { + MPNumber t1, t2; + MPNumber real_x, im_x; + + mp_real_component(x, &real_x); + mp_imaginary_component(x, &im_x); + + /* 1/(a+bi) = (a-bi)/(a+bi)(a-bi) = (a-bi)/(a²+b²) */ + mp_multiply(&real_x, &real_x, &t1); + mp_multiply(&im_x, &im_x, &t2); + mp_add(&t1, &t2, &t1); + mp_reciprocal_real(&t1, z); + mp_conjugate(x, &t1); + mp_multiply(&t1, z, z); + } + else + mp_reciprocal_real(x, z); +} + + +static void +mp_root_real(const MPNumber *x, int64_t n, MPNumber *z) +{ + float approximation; + int ex, np, it0, t; + MPNumber t1, t2; + + /* x^(1/1) = x */ + if (n == 1) { + mp_set_from_mp(x, z); + return; + } + + /* x^(1/0) invalid */ + if (n == 0) { + mperr(_("Root must be non-zero")); + mp_set_from_integer(0, z); + return; + } + + np = abs(n); + + /* LOSS OF ACCURACY IF NP LARGE, SO ONLY ALLOW NP <= MAX (B, 64) */ + if (np > max(MP_BASE, 64)) { + mperr("*** ABS(N) TOO LARGE IN CALL TO MP_ROOT ***"); + mp_set_from_integer(0, z); + return; + } + + /* 0^(1/n) = 0 for positive n */ + if (mp_is_zero(x)) { + mp_set_from_integer(0, z); + if (n <= 0) + mperr(_("Negative root of zero is undefined")); + return; + } + + // FIXME: Imaginary root + if (x->sign < 0 && np % 2 == 0) { + mperr(_("nth root of negative number is undefined for even n")); + mp_set_from_integer(0, z); + return; + } + + /* DIVIDE EXPONENT BY NP */ + ex = x->exponent / np; + + /* Get initial approximation */ + mp_set_from_mp(x, &t1); + t1.exponent = 0; + approximation = exp(((float)(np * ex - x->exponent) * log((float)MP_BASE) - + log((fabs(mp_cast_to_float(&t1))))) / (float)np); + mp_set_from_float(approximation, &t1); + t1.sign = x->sign; + t1.exponent -= ex; + + /* MAIN ITERATION LOOP */ + it0 = t = 3; + while(1) { + int ts2, ts3; + + /* t1 = t1 - ((t1 * ((x * t1^np) - 1)) / np) */ + mp_xpowy_integer(&t1, np, &t2); + mp_multiply(x, &t2, &t2); + mp_add_integer(&t2, -1, &t2); + mp_multiply(&t1, &t2, &t2); + mp_divide_integer(&t2, np, &t2); + mp_subtract(&t1, &t2, &t1); + + /* FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE BECAUSE + * NEWTONS METHOD HAS 2ND ORDER CONVERGENCE). + */ + if (t >= MP_T) + break; + + ts3 = t; + t = MP_T; + do { + ts2 = t; + t = (t + it0) / 2; + } while (t > ts3); + t = min(ts2, MP_T); + } + + /* NOW R(I2) IS X**(-1/NP) + * CHECK THAT NEWTON ITERATION WAS CONVERGING + */ + if (t2.sign != 0 && (t1.exponent - t2.exponent) << 1 < MP_T - it0) { + /* THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL, + * OR THAT THE INITIAL APPROXIMATION OBTAINED USING ALOG AND EXP + * IS NOT ACCURATE ENOUGH. + */ + mperr("*** ERROR OCCURRED IN MP_ROOT, NEWTON ITERATION NOT CONVERGING PROPERLY ***"); + } + + if (n >= 0) { + mp_xpowy_integer(&t1, n - 1, &t1); + mp_multiply(x, &t1, z); + return; + } + + mp_set_from_mp(&t1, z); +} + + +void +mp_root(const MPNumber *x, int64_t n, MPNumber *z) +{ + if (!mp_is_complex(x) && mp_is_negative(x) && n % 2 == 1) { + mp_abs(x, z); + mp_root_real(z, n, z); + mp_invert_sign(z, z); + } + else if (mp_is_complex(x) || mp_is_negative(x)) { + MPNumber r, theta; + + mp_abs(x, &r); + mp_arg(x, MP_RADIANS, &theta); + + mp_root_real(&r, n, &r); + mp_divide_integer(&theta, n, &theta); + mp_set_from_polar(&r, MP_RADIANS, &theta, z); + } + else + mp_root_real(x, n, z); +} + + +void +mp_sqrt(const MPNumber *x, MPNumber *z) +{ + if (mp_is_zero(x)) + mp_set_from_integer(0, z); + /* FIXME: Make complex numbers optional */ + /*else if (x->sign < 0) { + mperr(_("Square root is undefined for negative values")); + mp_set_from_integer(0, z); + }*/ + else { + MPNumber t; + + mp_root(x, -2, &t); + mp_multiply(x, &t, z); + mp_ext(t.fraction[0], z->fraction[0], z); + } +} + + +void +mp_factorial(const MPNumber *x, MPNumber *z) +{ + int i, value; + + /* 0! == 1 */ + if (mp_is_zero(x)) { + mp_set_from_integer(1, z); + return; + } + if (!mp_is_natural(x)) { + /* Translators: Error displayed when attempted take the factorial of a fractional number */ + mperr(_("Factorial is only defined for natural numbers")); + mp_set_from_integer(0, z); + return; + } + + /* Convert to integer - if couldn't be converted then the factorial would be too big anyway */ + value = mp_cast_to_int(x); + mp_set_from_mp(x, z); + for (i = 2; i < value; i++) + mp_multiply_integer(z, i, z); +} + + +void +mp_modulus_divide(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + MPNumber t1, t2; + + if (!mp_is_integer(x) || !mp_is_integer(y)) { + /* Translators: Error displayed when attemping to do a modulus division on non-integer numbers */ + mperr(_("Modulus division is only defined for integers")); + mp_set_from_integer(0, z); + } + + mp_divide(x, y, &t1); + mp_floor(&t1, &t1); + mp_multiply(&t1, y, &t2); + mp_subtract(x, &t2, z); + + mp_set_from_integer(0, &t1); + if ((mp_is_less_than(y, &t1) && mp_is_greater_than(z, &t1)) || mp_is_less_than(z, &t1)) + mp_add(z, y, z); +} + + +void +mp_xpowy(const MPNumber *x, const MPNumber *y, MPNumber *z) +{ + if (mp_is_integer(y)) { + mp_xpowy_integer(x, mp_cast_to_int(y), z); + } else { + MPNumber reciprocal; + mp_reciprocal(y, &reciprocal); + if (mp_is_integer(&reciprocal)) + mp_root(x, mp_cast_to_int(&reciprocal), z); + else + mp_pwr(x, y, z); + } +} + + +void +mp_xpowy_integer(const MPNumber *x, int64_t n, MPNumber *z) +{ + int i; + MPNumber t; + + /* 0^-n invalid */ + if (mp_is_zero(x) && n < 0) { + /* Translators: Error displayed when attempted to raise 0 to a negative exponent */ + mperr(_("The power of zero is undefined for a negative exponent")); + mp_set_from_integer(0, z); + return; + } + + /* x^0 = 1 */ + if (n == 0) { + mp_set_from_integer(1, z); + return; + } + + /* 0^n = 0 */ + if (mp_is_zero(x)) { + mp_set_from_integer(0, z); + return; + } + + /* x^1 = x */ + if (n == 1) { + mp_set_from_mp(x, z); + return; + } + + if (n < 0) { + mp_reciprocal(x, &t); + n = -n; + } + else + mp_set_from_mp(x, &t); + + /* Multply x n times */ + // FIXME: Can do mp_multiply(z, z, z) until close to answer (each call doubles number of multiples) */ + mp_set_from_integer(1, z); + for (i = 0; i < n; i++) + mp_multiply(z, &t, z); +} + + +GList* +mp_factorize(const MPNumber *x) +{ + GList *list = NULL; + MPNumber *factor = g_slice_alloc0(sizeof(MPNumber)); + MPNumber value, tmp, divisor, root; + + mp_abs(x, &value); + + if (mp_is_zero(&value)) { + mp_set_from_mp(&value, factor); + list = g_list_append(list, factor); + return list; + } + + mp_set_from_integer(1, &tmp); + if (mp_is_equal(&value, &tmp)) { + mp_set_from_mp(x, factor); + list = g_list_append(list, factor); + return list; + } + + mp_set_from_integer(2, &divisor); + while (TRUE) { + mp_divide(&value, &divisor, &tmp); + if (mp_is_integer(&tmp)) { + value = tmp; + mp_set_from_mp(&divisor, factor); + list = g_list_append(list, factor); + factor = g_slice_alloc0(sizeof(MPNumber)); + } else { + break; + } + } + + mp_set_from_integer(3, &divisor); + mp_sqrt(&value, &root); + while (mp_is_less_equal(&divisor, &root)) { + mp_divide(&value, &divisor, &tmp); + if (mp_is_integer(&tmp)) { + value = tmp; + mp_sqrt(&value, &root); + mp_set_from_mp(&divisor, factor); + list = g_list_append(list, factor); + factor = g_slice_alloc0(sizeof(MPNumber)); + } else { + mp_add_integer(&divisor, 2, &tmp); + divisor = tmp; + } + } + + mp_set_from_integer(1, &tmp); + if (mp_is_greater_than(&value, &tmp)) { + mp_set_from_mp(&value, factor); + list = g_list_append(list, factor); + } else { + g_slice_free(MPNumber, factor); + } + + if (mp_is_negative(x)) { + mp_invert_sign(list->data, list->data); + } + + return list; +} |