forked from OSchip/llvm-project
959 lines
23 KiB
C
959 lines
23 KiB
C
|
/*
|
||
|
Name: imrat.c
|
||
|
Purpose: Arbitrary precision rational arithmetic routines.
|
||
|
Author: M. J. Fromberger <http://spinning-yarns.org/michael/>
|
||
|
|
||
|
Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#include "imrat.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#define TEMP(K) (temp + (K))
|
||
|
#define SETUP(E, C) \
|
||
|
do{if((res = (E)) != MP_OK) goto CLEANUP; ++(C);}while(0)
|
||
|
|
||
|
/* Argument checking:
|
||
|
Use CHECK() where a return value is required; NRCHECK() elsewhere */
|
||
|
#define CHECK(TEST) assert(TEST)
|
||
|
#define NRCHECK(TEST) assert(TEST)
|
||
|
|
||
|
/* Reduce the given rational, in place, to lowest terms and canonical form.
|
||
|
Zero is represented as 0/1, one as 1/1. Signs are adjusted so that the sign
|
||
|
of the numerator is definitive. */
|
||
|
static mp_result s_rat_reduce(mp_rat r);
|
||
|
|
||
|
/* Common code for addition and subtraction operations on rationals. */
|
||
|
static mp_result s_rat_combine(mp_rat a, mp_rat b, mp_rat c,
|
||
|
mp_result (*comb_f)(mp_int, mp_int, mp_int));
|
||
|
|
||
|
mp_result mp_rat_init(mp_rat r)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_init(MP_NUMER_P(r))) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_init(MP_DENOM_P(r))) != MP_OK) {
|
||
|
mp_int_clear(MP_NUMER_P(r));
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
return mp_int_set_value(MP_DENOM_P(r), 1);
|
||
|
}
|
||
|
|
||
|
mp_rat mp_rat_alloc(void)
|
||
|
{
|
||
|
mp_rat out = malloc(sizeof(*out));
|
||
|
|
||
|
if (out != NULL) {
|
||
|
if (mp_rat_init(out) != MP_OK) {
|
||
|
free(out);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_reduce(mp_rat r) {
|
||
|
return s_rat_reduce(r);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_init_size(mp_rat r, mp_size n_prec, mp_size d_prec)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_init_size(MP_NUMER_P(r), n_prec)) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_init_size(MP_DENOM_P(r), d_prec)) != MP_OK) {
|
||
|
mp_int_clear(MP_NUMER_P(r));
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
return mp_int_set_value(MP_DENOM_P(r), 1);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_init_copy(mp_rat r, mp_rat old)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_init_copy(MP_NUMER_P(r), MP_NUMER_P(old))) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_init_copy(MP_DENOM_P(r), MP_DENOM_P(old))) != MP_OK)
|
||
|
mp_int_clear(MP_NUMER_P(r));
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_set_value(mp_rat r, mp_small numer, mp_small denom)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if (denom == 0)
|
||
|
return MP_UNDEF;
|
||
|
|
||
|
if ((res = mp_int_set_value(MP_NUMER_P(r), numer)) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_set_value(MP_DENOM_P(r), denom)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
return s_rat_reduce(r);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_set_uvalue(mp_rat r, mp_usmall numer, mp_usmall denom)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if (denom == 0)
|
||
|
return MP_UNDEF;
|
||
|
|
||
|
if ((res = mp_int_set_uvalue(MP_NUMER_P(r), numer)) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_set_uvalue(MP_DENOM_P(r), denom)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
return s_rat_reduce(r);
|
||
|
}
|
||
|
|
||
|
void mp_rat_clear(mp_rat r)
|
||
|
{
|
||
|
mp_int_clear(MP_NUMER_P(r));
|
||
|
mp_int_clear(MP_DENOM_P(r));
|
||
|
|
||
|
}
|
||
|
|
||
|
void mp_rat_free(mp_rat r)
|
||
|
{
|
||
|
NRCHECK(r != NULL);
|
||
|
|
||
|
if (r->num.digits != NULL)
|
||
|
mp_rat_clear(r);
|
||
|
|
||
|
free(r);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_numer(mp_rat r, mp_int z)
|
||
|
{
|
||
|
return mp_int_copy(MP_NUMER_P(r), z);
|
||
|
}
|
||
|
|
||
|
mp_int mp_rat_numer_ref(mp_rat r)
|
||
|
{
|
||
|
return MP_NUMER_P(r);
|
||
|
}
|
||
|
|
||
|
|
||
|
mp_result mp_rat_denom(mp_rat r, mp_int z)
|
||
|
{
|
||
|
return mp_int_copy(MP_DENOM_P(r), z);
|
||
|
}
|
||
|
|
||
|
mp_int mp_rat_denom_ref(mp_rat r)
|
||
|
{
|
||
|
return MP_DENOM_P(r);
|
||
|
}
|
||
|
|
||
|
mp_sign mp_rat_sign(mp_rat r)
|
||
|
{
|
||
|
return MP_SIGN(MP_NUMER_P(r));
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_copy(mp_rat a, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_copy(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c));
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void mp_rat_zero(mp_rat r)
|
||
|
{
|
||
|
mp_int_zero(MP_NUMER_P(r));
|
||
|
mp_int_set_value(MP_DENOM_P(r), 1);
|
||
|
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_abs(mp_rat a, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_abs(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
res = mp_int_abs(MP_DENOM_P(a), MP_DENOM_P(c));
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_neg(mp_rat a, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_neg(MP_NUMER_P(a), MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c));
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_recip(mp_rat a, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if (mp_rat_compare_zero(a) == 0)
|
||
|
return MP_UNDEF;
|
||
|
|
||
|
if ((res = mp_rat_copy(a, c)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
mp_int_swap(MP_NUMER_P(c), MP_DENOM_P(c));
|
||
|
|
||
|
/* Restore the signs of the swapped elements */
|
||
|
{
|
||
|
mp_sign tmp = MP_SIGN(MP_NUMER_P(c));
|
||
|
|
||
|
MP_SIGN(MP_NUMER_P(c)) = MP_SIGN(MP_DENOM_P(c));
|
||
|
MP_SIGN(MP_DENOM_P(c)) = tmp;
|
||
|
}
|
||
|
|
||
|
return MP_OK;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_add(mp_rat a, mp_rat b, mp_rat c)
|
||
|
{
|
||
|
return s_rat_combine(a, b, c, mp_int_add);
|
||
|
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_sub(mp_rat a, mp_rat b, mp_rat c)
|
||
|
{
|
||
|
return s_rat_combine(a, b, c, mp_int_sub);
|
||
|
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_mul(mp_rat a, mp_rat b, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_mul(MP_NUMER_P(a), MP_NUMER_P(b), MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if (mp_int_compare_zero(MP_NUMER_P(c)) != 0) {
|
||
|
if ((res = mp_int_mul(MP_DENOM_P(a), MP_DENOM_P(b), MP_DENOM_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
return s_rat_reduce(c);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_div(mp_rat a, mp_rat b, mp_rat c)
|
||
|
{
|
||
|
mp_result res = MP_OK;
|
||
|
|
||
|
if (mp_rat_compare_zero(b) == 0)
|
||
|
return MP_UNDEF;
|
||
|
|
||
|
if (c == a || c == b) {
|
||
|
mpz_t tmp;
|
||
|
|
||
|
if ((res = mp_int_init(&tmp)) != MP_OK) return res;
|
||
|
if ((res = mp_int_mul(MP_NUMER_P(a), MP_DENOM_P(b), &tmp)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
if ((res = mp_int_mul(MP_DENOM_P(a), MP_NUMER_P(b), MP_DENOM_P(c))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
res = mp_int_copy(&tmp, MP_NUMER_P(c));
|
||
|
|
||
|
CLEANUP:
|
||
|
mp_int_clear(&tmp);
|
||
|
}
|
||
|
else {
|
||
|
if ((res = mp_int_mul(MP_NUMER_P(a), MP_DENOM_P(b), MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_mul(MP_DENOM_P(a), MP_NUMER_P(b), MP_DENOM_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
if (res != MP_OK)
|
||
|
return res;
|
||
|
else
|
||
|
return s_rat_reduce(c);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_add_int(mp_rat a, mp_int b, mp_rat c)
|
||
|
{
|
||
|
mpz_t tmp;
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_init_copy(&tmp, b)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if ((res = mp_int_mul(&tmp, MP_DENOM_P(a), &tmp)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
if ((res = mp_rat_copy(a, c)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
if ((res = mp_int_add(MP_NUMER_P(c), &tmp, MP_NUMER_P(c))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
res = s_rat_reduce(c);
|
||
|
|
||
|
CLEANUP:
|
||
|
mp_int_clear(&tmp);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_sub_int(mp_rat a, mp_int b, mp_rat c)
|
||
|
{
|
||
|
mpz_t tmp;
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_init_copy(&tmp, b)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if ((res = mp_int_mul(&tmp, MP_DENOM_P(a), &tmp)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
if ((res = mp_rat_copy(a, c)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
if ((res = mp_int_sub(MP_NUMER_P(c), &tmp, MP_NUMER_P(c))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
res = s_rat_reduce(c);
|
||
|
|
||
|
CLEANUP:
|
||
|
mp_int_clear(&tmp);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_mul_int(mp_rat a, mp_int b, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_rat_copy(a, c)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if ((res = mp_int_mul(MP_NUMER_P(c), b, MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
return s_rat_reduce(c);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_div_int(mp_rat a, mp_int b, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if (mp_int_compare_zero(b) == 0)
|
||
|
return MP_UNDEF;
|
||
|
|
||
|
if ((res = mp_rat_copy(a, c)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if ((res = mp_int_mul(MP_DENOM_P(c), b, MP_DENOM_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
return s_rat_reduce(c);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_expt(mp_rat a, mp_small b, mp_rat c)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
/* Special cases for easy powers. */
|
||
|
if (b == 0)
|
||
|
return mp_rat_set_value(c, 1, 1);
|
||
|
else if(b == 1)
|
||
|
return mp_rat_copy(a, c);
|
||
|
|
||
|
/* Since rationals are always stored in lowest terms, it is not necessary to
|
||
|
reduce again when raising to an integer power. */
|
||
|
if ((res = mp_int_expt(MP_NUMER_P(a), b, MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
return mp_int_expt(MP_DENOM_P(a), b, MP_DENOM_P(c));
|
||
|
}
|
||
|
|
||
|
int mp_rat_compare(mp_rat a, mp_rat b)
|
||
|
{
|
||
|
/* Quick check for opposite signs. Works because the sign of the numerator
|
||
|
is always definitive. */
|
||
|
if (MP_SIGN(MP_NUMER_P(a)) != MP_SIGN(MP_NUMER_P(b))) {
|
||
|
if (MP_SIGN(MP_NUMER_P(a)) == MP_ZPOS)
|
||
|
return 1;
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
else {
|
||
|
/* Compare absolute magnitudes; if both are positive, the answer stands,
|
||
|
otherwise it needs to be reflected about zero. */
|
||
|
int cmp = mp_rat_compare_unsigned(a, b);
|
||
|
|
||
|
if (MP_SIGN(MP_NUMER_P(a)) == MP_ZPOS)
|
||
|
return cmp;
|
||
|
else
|
||
|
return -cmp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int mp_rat_compare_unsigned(mp_rat a, mp_rat b)
|
||
|
{
|
||
|
/* If the denominators are equal, we can quickly compare numerators without
|
||
|
multiplying. Otherwise, we actually have to do some work. */
|
||
|
if (mp_int_compare_unsigned(MP_DENOM_P(a), MP_DENOM_P(b)) == 0)
|
||
|
return mp_int_compare_unsigned(MP_NUMER_P(a), MP_NUMER_P(b));
|
||
|
|
||
|
else {
|
||
|
mpz_t temp[2];
|
||
|
mp_result res;
|
||
|
int cmp = INT_MAX, last = 0;
|
||
|
|
||
|
/* t0 = num(a) * den(b), t1 = num(b) * den(a) */
|
||
|
SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(a)), last);
|
||
|
SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(b)), last);
|
||
|
|
||
|
if ((res = mp_int_mul(TEMP(0), MP_DENOM_P(b), TEMP(0))) != MP_OK ||
|
||
|
(res = mp_int_mul(TEMP(1), MP_DENOM_P(a), TEMP(1))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
cmp = mp_int_compare_unsigned(TEMP(0), TEMP(1));
|
||
|
|
||
|
CLEANUP:
|
||
|
while (--last >= 0)
|
||
|
mp_int_clear(TEMP(last));
|
||
|
|
||
|
return cmp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int mp_rat_compare_zero(mp_rat r)
|
||
|
{
|
||
|
return mp_int_compare_zero(MP_NUMER_P(r));
|
||
|
}
|
||
|
|
||
|
int mp_rat_compare_value(mp_rat r, mp_small n, mp_small d)
|
||
|
{
|
||
|
mpq_t tmp;
|
||
|
mp_result res;
|
||
|
int out = INT_MAX;
|
||
|
|
||
|
if ((res = mp_rat_init(&tmp)) != MP_OK)
|
||
|
return out;
|
||
|
if ((res = mp_rat_set_value(&tmp, n, d)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
out = mp_rat_compare(r, &tmp);
|
||
|
|
||
|
CLEANUP:
|
||
|
mp_rat_clear(&tmp);
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
int mp_rat_is_integer(mp_rat r)
|
||
|
{
|
||
|
return (mp_int_compare_value(MP_DENOM_P(r), 1) == 0);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_to_ints(mp_rat r, mp_small *num, mp_small *den)
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
if ((res = mp_int_to_int(MP_NUMER_P(r), num)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
res = mp_int_to_int(MP_DENOM_P(r), den);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_to_string(mp_rat r, mp_size radix, char *str, int limit)
|
||
|
{
|
||
|
char *start;
|
||
|
int len;
|
||
|
mp_result res;
|
||
|
|
||
|
/* Write the numerator. The sign of the rational number is written by the
|
||
|
underlying integer implementation. */
|
||
|
if ((res = mp_int_to_string(MP_NUMER_P(r), radix, str, limit)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
/* If the value is zero, don't bother writing any denominator */
|
||
|
if (mp_int_compare_zero(MP_NUMER_P(r)) == 0)
|
||
|
return MP_OK;
|
||
|
|
||
|
/* Locate the end of the numerator, and make sure we are not going to exceed
|
||
|
the limit by writing a slash. */
|
||
|
len = strlen(str);
|
||
|
start = str + len;
|
||
|
limit -= len;
|
||
|
if(limit == 0)
|
||
|
return MP_TRUNC;
|
||
|
|
||
|
*start++ = '/';
|
||
|
limit -= 1;
|
||
|
|
||
|
res = mp_int_to_string(MP_DENOM_P(r), radix, start, limit);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_to_decimal(mp_rat r, mp_size radix, mp_size prec,
|
||
|
mp_round_mode round, char *str, int limit)
|
||
|
{
|
||
|
mpz_t temp[3];
|
||
|
mp_result res;
|
||
|
char *start = str;
|
||
|
int len, lead_0, left = limit, last = 0;
|
||
|
|
||
|
SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(r)), last);
|
||
|
SETUP(mp_int_init(TEMP(last)), last);
|
||
|
SETUP(mp_int_init(TEMP(last)), last);
|
||
|
|
||
|
/* Get the unsigned integer part by dividing denominator into the absolute
|
||
|
value of the numerator. */
|
||
|
mp_int_abs(TEMP(0), TEMP(0));
|
||
|
if ((res = mp_int_div(TEMP(0), MP_DENOM_P(r), TEMP(0), TEMP(1))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
/* Now: T0 = integer portion, unsigned;
|
||
|
T1 = remainder, from which fractional part is computed. */
|
||
|
|
||
|
/* Count up leading zeroes after the radix point. */
|
||
|
for (lead_0 = 0; lead_0 < prec && mp_int_compare(TEMP(1), MP_DENOM_P(r)) < 0;
|
||
|
++lead_0) {
|
||
|
if ((res = mp_int_mul_value(TEMP(1), radix, TEMP(1))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
}
|
||
|
|
||
|
/* Multiply remainder by a power of the radix sufficient to get the right
|
||
|
number of significant figures. */
|
||
|
if (prec > lead_0) {
|
||
|
if ((res = mp_int_expt_value(radix, prec - lead_0, TEMP(2))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
if ((res = mp_int_mul(TEMP(1), TEMP(2), TEMP(1))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
}
|
||
|
if ((res = mp_int_div(TEMP(1), MP_DENOM_P(r), TEMP(1), TEMP(2))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
/* Now: T1 = significant digits of fractional part;
|
||
|
T2 = leftovers, to use for rounding.
|
||
|
|
||
|
At this point, what we do depends on the rounding mode. The default is
|
||
|
MP_ROUND_DOWN, for which everything is as it should be already.
|
||
|
*/
|
||
|
switch (round) {
|
||
|
int cmp;
|
||
|
|
||
|
case MP_ROUND_UP:
|
||
|
if (mp_int_compare_zero(TEMP(2)) != 0) {
|
||
|
if (prec == 0)
|
||
|
res = mp_int_add_value(TEMP(0), 1, TEMP(0));
|
||
|
else
|
||
|
res = mp_int_add_value(TEMP(1), 1, TEMP(1));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MP_ROUND_HALF_UP:
|
||
|
case MP_ROUND_HALF_DOWN:
|
||
|
if ((res = mp_int_mul_pow2(TEMP(2), 1, TEMP(2))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
cmp = mp_int_compare(TEMP(2), MP_DENOM_P(r));
|
||
|
|
||
|
if (round == MP_ROUND_HALF_UP)
|
||
|
cmp += 1;
|
||
|
|
||
|
if (cmp > 0) {
|
||
|
if (prec == 0)
|
||
|
res = mp_int_add_value(TEMP(0), 1, TEMP(0));
|
||
|
else
|
||
|
res = mp_int_add_value(TEMP(1), 1, TEMP(1));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MP_ROUND_DOWN:
|
||
|
break; /* No action required */
|
||
|
|
||
|
default:
|
||
|
return MP_BADARG; /* Invalid rounding specifier */
|
||
|
}
|
||
|
|
||
|
/* The sign of the output should be the sign of the numerator, but if all the
|
||
|
displayed digits will be zero due to the precision, a negative shouldn't
|
||
|
be shown. */
|
||
|
if (MP_SIGN(MP_NUMER_P(r)) == MP_NEG &&
|
||
|
(mp_int_compare_zero(TEMP(0)) != 0 ||
|
||
|
mp_int_compare_zero(TEMP(1)) != 0)) {
|
||
|
*start++ = '-';
|
||
|
left -= 1;
|
||
|
}
|
||
|
|
||
|
if ((res = mp_int_to_string(TEMP(0), radix, start, left)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
len = strlen(start);
|
||
|
start += len;
|
||
|
left -= len;
|
||
|
|
||
|
if (prec == 0)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
*start++ = '.';
|
||
|
left -= 1;
|
||
|
|
||
|
if (left < prec + 1) {
|
||
|
res = MP_TRUNC;
|
||
|
goto CLEANUP;
|
||
|
}
|
||
|
|
||
|
memset(start, '0', lead_0 - 1);
|
||
|
left -= lead_0;
|
||
|
start += lead_0 - 1;
|
||
|
|
||
|
res = mp_int_to_string(TEMP(1), radix, start, left);
|
||
|
|
||
|
CLEANUP:
|
||
|
while (--last >= 0)
|
||
|
mp_int_clear(TEMP(last));
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_string_len(mp_rat r, mp_size radix)
|
||
|
{
|
||
|
mp_result n_len, d_len = 0;
|
||
|
|
||
|
n_len = mp_int_string_len(MP_NUMER_P(r), radix);
|
||
|
|
||
|
if (mp_int_compare_zero(MP_NUMER_P(r)) != 0)
|
||
|
d_len = mp_int_string_len(MP_DENOM_P(r), radix);
|
||
|
|
||
|
/* Though simplistic, this formula is correct. Space for the sign flag is
|
||
|
included in n_len, and the space for the NUL that is counted in n_len
|
||
|
counts for the separator here. The space for the NUL counted in d_len
|
||
|
counts for the final terminator here. */
|
||
|
|
||
|
return n_len + d_len;
|
||
|
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_decimal_len(mp_rat r, mp_size radix, mp_size prec)
|
||
|
{
|
||
|
int z_len, f_len;
|
||
|
|
||
|
z_len = mp_int_string_len(MP_NUMER_P(r), radix);
|
||
|
|
||
|
if (prec == 0)
|
||
|
f_len = 1; /* terminator only */
|
||
|
else
|
||
|
f_len = 1 + prec + 1; /* decimal point, digits, terminator */
|
||
|
|
||
|
return z_len + f_len;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_read_string(mp_rat r, mp_size radix, const char *str)
|
||
|
{
|
||
|
return mp_rat_read_cstring(r, radix, str, NULL);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_read_cstring(mp_rat r, mp_size radix, const char *str,
|
||
|
char **end)
|
||
|
{
|
||
|
mp_result res;
|
||
|
char *endp;
|
||
|
|
||
|
if ((res = mp_int_read_cstring(MP_NUMER_P(r), radix, str, &endp)) != MP_OK &&
|
||
|
(res != MP_TRUNC))
|
||
|
return res;
|
||
|
|
||
|
/* Skip whitespace between numerator and (possible) separator */
|
||
|
while (isspace((unsigned char) *endp))
|
||
|
++endp;
|
||
|
|
||
|
/* If there is no separator, we will stop reading at this point. */
|
||
|
if (*endp != '/') {
|
||
|
mp_int_set_value(MP_DENOM_P(r), 1);
|
||
|
if (end != NULL)
|
||
|
*end = endp;
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
++endp; /* skip separator */
|
||
|
if ((res = mp_int_read_cstring(MP_DENOM_P(r), radix, endp, end)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
/* Make sure the value is well-defined */
|
||
|
if (mp_int_compare_zero(MP_DENOM_P(r)) == 0)
|
||
|
return MP_UNDEF;
|
||
|
|
||
|
/* Reduce to lowest terms */
|
||
|
return s_rat_reduce(r);
|
||
|
}
|
||
|
|
||
|
/* Read a string and figure out what format it's in. The radix may be supplied
|
||
|
as zero to use "default" behaviour.
|
||
|
|
||
|
This function will accept either a/b notation or decimal notation.
|
||
|
*/
|
||
|
mp_result mp_rat_read_ustring(mp_rat r, mp_size radix, const char *str,
|
||
|
char **end)
|
||
|
{
|
||
|
char *endp;
|
||
|
mp_result res;
|
||
|
|
||
|
if (radix == 0)
|
||
|
radix = 10; /* default to decimal input */
|
||
|
|
||
|
if ((res = mp_rat_read_cstring(r, radix, str, &endp)) != MP_OK) {
|
||
|
if (res == MP_TRUNC) {
|
||
|
if (*endp == '.')
|
||
|
res = mp_rat_read_cdecimal(r, radix, str, &endp);
|
||
|
}
|
||
|
else
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
if (end != NULL)
|
||
|
*end = endp;
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_read_decimal(mp_rat r, mp_size radix, const char *str)
|
||
|
{
|
||
|
return mp_rat_read_cdecimal(r, radix, str, NULL);
|
||
|
}
|
||
|
|
||
|
mp_result mp_rat_read_cdecimal(mp_rat r, mp_size radix, const char *str,
|
||
|
char **end)
|
||
|
{
|
||
|
mp_result res;
|
||
|
mp_sign osign;
|
||
|
char *endp;
|
||
|
|
||
|
while (isspace((unsigned char) *str))
|
||
|
++str;
|
||
|
|
||
|
switch (*str) {
|
||
|
case '-':
|
||
|
osign = MP_NEG;
|
||
|
break;
|
||
|
default:
|
||
|
osign = MP_ZPOS;
|
||
|
}
|
||
|
|
||
|
if ((res = mp_int_read_cstring(MP_NUMER_P(r), radix, str, &endp)) != MP_OK &&
|
||
|
(res != MP_TRUNC))
|
||
|
return res;
|
||
|
|
||
|
/* This needs to be here. */
|
||
|
(void) mp_int_set_value(MP_DENOM_P(r), 1);
|
||
|
|
||
|
if (*endp != '.') {
|
||
|
if (end != NULL)
|
||
|
*end = endp;
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/* If the character following the decimal point is whitespace or a sign flag,
|
||
|
we will consider this a truncated value. This special case is because
|
||
|
mp_int_read_string() will consider whitespace or sign flags to be valid
|
||
|
starting characters for a value, and we do not want them following the
|
||
|
decimal point.
|
||
|
|
||
|
Once we have done this check, it is safe to read in the value of the
|
||
|
fractional piece as a regular old integer.
|
||
|
*/
|
||
|
++endp;
|
||
|
if (*endp == '\0') {
|
||
|
if (end != NULL)
|
||
|
*end = endp;
|
||
|
return MP_OK;
|
||
|
}
|
||
|
else if(isspace((unsigned char) *endp) || *endp == '-' || *endp == '+') {
|
||
|
return MP_TRUNC;
|
||
|
}
|
||
|
else {
|
||
|
mpz_t frac;
|
||
|
mp_result save_res;
|
||
|
char *save = endp;
|
||
|
int num_lz = 0;
|
||
|
|
||
|
/* Make a temporary to hold the part after the decimal point. */
|
||
|
if ((res = mp_int_init(&frac)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if ((res = mp_int_read_cstring(&frac, radix, endp, &endp)) != MP_OK &&
|
||
|
(res != MP_TRUNC))
|
||
|
goto CLEANUP;
|
||
|
|
||
|
/* Save this response for later. */
|
||
|
save_res = res;
|
||
|
|
||
|
if (mp_int_compare_zero(&frac) == 0)
|
||
|
goto FINISHED;
|
||
|
|
||
|
/* Discard trailing zeroes (somewhat inefficiently) */
|
||
|
while (mp_int_divisible_value(&frac, radix))
|
||
|
if ((res = mp_int_div_value(&frac, radix, &frac, NULL)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
/* Count leading zeros after the decimal point */
|
||
|
while (save[num_lz] == '0')
|
||
|
++num_lz;
|
||
|
|
||
|
/* Find the least power of the radix that is at least as large as the
|
||
|
significant value of the fractional part, ignoring leading zeroes. */
|
||
|
(void) mp_int_set_value(MP_DENOM_P(r), radix);
|
||
|
|
||
|
while (mp_int_compare(MP_DENOM_P(r), &frac) < 0) {
|
||
|
if ((res = mp_int_mul_value(MP_DENOM_P(r), radix, MP_DENOM_P(r))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
}
|
||
|
|
||
|
/* Also shift by enough to account for leading zeroes */
|
||
|
while (num_lz > 0) {
|
||
|
if ((res = mp_int_mul_value(MP_DENOM_P(r), radix, MP_DENOM_P(r))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
--num_lz;
|
||
|
}
|
||
|
|
||
|
/* Having found this power, shift the numerator leftward that many, digits,
|
||
|
and add the nonzero significant digits of the fractional part to get the
|
||
|
result. */
|
||
|
if ((res = mp_int_mul(MP_NUMER_P(r), MP_DENOM_P(r), MP_NUMER_P(r))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
{ /* This addition needs to be unsigned. */
|
||
|
MP_SIGN(MP_NUMER_P(r)) = MP_ZPOS;
|
||
|
if ((res = mp_int_add(MP_NUMER_P(r), &frac, MP_NUMER_P(r))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
MP_SIGN(MP_NUMER_P(r)) = osign;
|
||
|
}
|
||
|
if ((res = s_rat_reduce(r)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
/* At this point, what we return depends on whether reading the fractional
|
||
|
part was truncated or not. That information is saved from when we
|
||
|
called mp_int_read_string() above. */
|
||
|
FINISHED:
|
||
|
res = save_res;
|
||
|
if (end != NULL)
|
||
|
*end = endp;
|
||
|
|
||
|
CLEANUP:
|
||
|
mp_int_clear(&frac);
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Private functions for internal use. Make unchecked assumptions about format
|
||
|
and validity of inputs. */
|
||
|
|
||
|
static mp_result s_rat_reduce(mp_rat r)
|
||
|
{
|
||
|
mpz_t gcd;
|
||
|
mp_result res = MP_OK;
|
||
|
|
||
|
if (mp_int_compare_zero(MP_NUMER_P(r)) == 0) {
|
||
|
mp_int_set_value(MP_DENOM_P(r), 1);
|
||
|
return MP_OK;
|
||
|
}
|
||
|
|
||
|
/* If the greatest common divisor of the numerator and denominator is greater
|
||
|
than 1, divide it out. */
|
||
|
if ((res = mp_int_init(&gcd)) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
if ((res = mp_int_gcd(MP_NUMER_P(r), MP_DENOM_P(r), &gcd)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
if (mp_int_compare_value(&gcd, 1) != 0) {
|
||
|
if ((res = mp_int_div(MP_NUMER_P(r), &gcd, MP_NUMER_P(r), NULL)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
if ((res = mp_int_div(MP_DENOM_P(r), &gcd, MP_DENOM_P(r), NULL)) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
}
|
||
|
|
||
|
/* Fix up the signs of numerator and denominator */
|
||
|
if (MP_SIGN(MP_NUMER_P(r)) == MP_SIGN(MP_DENOM_P(r)))
|
||
|
MP_SIGN(MP_NUMER_P(r)) = MP_SIGN(MP_DENOM_P(r)) = MP_ZPOS;
|
||
|
else {
|
||
|
MP_SIGN(MP_NUMER_P(r)) = MP_NEG;
|
||
|
MP_SIGN(MP_DENOM_P(r)) = MP_ZPOS;
|
||
|
}
|
||
|
|
||
|
CLEANUP:
|
||
|
mp_int_clear(&gcd);
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static mp_result s_rat_combine(mp_rat a, mp_rat b, mp_rat c,
|
||
|
mp_result (*comb_f)(mp_int, mp_int, mp_int))
|
||
|
{
|
||
|
mp_result res;
|
||
|
|
||
|
/* Shortcut when denominators are already common */
|
||
|
if (mp_int_compare(MP_DENOM_P(a), MP_DENOM_P(b)) == 0) {
|
||
|
if ((res = (comb_f)(MP_NUMER_P(a), MP_NUMER_P(b), MP_NUMER_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
if ((res = mp_int_copy(MP_DENOM_P(a), MP_DENOM_P(c))) != MP_OK)
|
||
|
return res;
|
||
|
|
||
|
return s_rat_reduce(c);
|
||
|
}
|
||
|
else {
|
||
|
mpz_t temp[2];
|
||
|
int last = 0;
|
||
|
|
||
|
SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(a)), last);
|
||
|
SETUP(mp_int_init_copy(TEMP(last), MP_NUMER_P(b)), last);
|
||
|
|
||
|
if ((res = mp_int_mul(TEMP(0), MP_DENOM_P(b), TEMP(0))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
if ((res = mp_int_mul(TEMP(1), MP_DENOM_P(a), TEMP(1))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
if ((res = (comb_f)(TEMP(0), TEMP(1), MP_NUMER_P(c))) != MP_OK)
|
||
|
goto CLEANUP;
|
||
|
|
||
|
res = mp_int_mul(MP_DENOM_P(a), MP_DENOM_P(b), MP_DENOM_P(c));
|
||
|
|
||
|
CLEANUP:
|
||
|
while (--last >= 0)
|
||
|
mp_int_clear(TEMP(last));
|
||
|
|
||
|
if (res == MP_OK)
|
||
|
return s_rat_reduce(c);
|
||
|
else
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Here there be dragons */
|