/* * interface dc to the bc numeric routines * * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc. * * 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, you can either send email to this * program's author (see below) or write to: The Free Software Foundation, * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA. */ /* This should be the only module that knows the internals of type dc_num */ /* In this particular implementation we just slather out some glue and * make use of bc's numeric routines. */ #include "config.h" #include #include #ifdef HAVE_LIMITS_H # include #else # define UCHAR_MAX ((unsigned char)~0) #endif #include "bcdefs.h" #include "proto.h" #include "global.h" #include "dc.h" #include "dc-proto.h" #ifdef __GNUC__ # define ATTRIB(x) __attribute__(x) #else # define ATTRIB(x) #endif /* there is no POSIX standard for dc, so we'll take the GNU definitions */ int std_only = FALSE; /* convert an opaque dc_num into a real bc_num */ #define CastNum(x) ((bc_num)(x)) /* add two dc_nums, place into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_add DC_DECLARG((a, b, kscale, result)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale ATTRIB((unused)) DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); bc_add(CastNum(a), CastNum(b), (bc_num *)result, 0); return DC_SUCCESS; } /* subtract two dc_nums, place into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_sub DC_DECLARG((a, b, kscale, result)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale ATTRIB((unused)) DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); bc_sub(CastNum(a), CastNum(b), (bc_num *)result, 0); return DC_SUCCESS; } /* multiply two dc_nums, place into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_mul DC_DECLARG((a, b, kscale, result)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); bc_multiply(CastNum(a), CastNum(b), (bc_num *)result, kscale); return DC_SUCCESS; } /* divide two dc_nums, place into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_div DC_DECLARG((a, b, kscale, result)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); if (bc_divide(CastNum(a), CastNum(b), (bc_num *)result, kscale)){ fprintf(stderr, "%s: divide by zero\n", progname); return DC_DOMAIN_ERROR; } return DC_SUCCESS; } /* divide two dc_nums, place quotient into *quotient and remainder * into *remainder; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_divrem DC_DECLARG((a, b, kscale, quotient, remainder)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale DC_DECLSEP dc_num *quotient DC_DECLSEP dc_num *remainder DC_DECLEND { init_num((bc_num *)quotient); init_num((bc_num *)remainder); if (bc_divmod(CastNum(a), CastNum(b), (bc_num *)quotient, (bc_num *)remainder, kscale)){ fprintf(stderr, "%s: divide by zero\n", progname); return DC_DOMAIN_ERROR; } return DC_SUCCESS; } /* place the reminder of dividing a by b into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_rem DC_DECLARG((a, b, kscale, result)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); if (bc_modulo(CastNum(a), CastNum(b), (bc_num *)result, kscale)){ fprintf(stderr, "%s: remainder by zero\n", progname); return DC_DOMAIN_ERROR; } return DC_SUCCESS; } int dc_modexp DC_DECLARG((base, expo, mod, kscale, result)) dc_num base DC_DECLSEP dc_num expo DC_DECLSEP dc_num mod DC_DECLSEP int kscale DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); if (bc_raisemod(CastNum(base), CastNum(expo), CastNum(mod), (bc_num *)result, kscale)){ if (is_zero(CastNum(mod))) fprintf(stderr, "%s: remainder by zero\n", progname); return DC_DOMAIN_ERROR; } return DC_SUCCESS; } /* place the result of exponentiationg a by b into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_exp DC_DECLARG((a, b, kscale, result)) dc_num a DC_DECLSEP dc_num b DC_DECLSEP int kscale DC_DECLSEP dc_num *result DC_DECLEND { init_num((bc_num *)result); bc_raise(CastNum(a), CastNum(b), (bc_num *)result, kscale); return DC_SUCCESS; } /* take the square root of the value, place into *result; * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error */ int dc_sqrt DC_DECLARG((value, kscale, result)) dc_num value DC_DECLSEP int kscale DC_DECLSEP dc_num *result DC_DECLEND { bc_num tmp; tmp = copy_num(CastNum(value)); if (!bc_sqrt(&tmp, kscale)){ fprintf(stderr, "%s: square root of negative number\n", progname); free_num(&tmp); return DC_DOMAIN_ERROR; } *((bc_num *)result) = tmp; return DC_SUCCESS; } /* compare dc_nums a and b; * return a negative value if a < b; * return a positive value if a > b; * return zero value if a == b */ int dc_compare DC_DECLARG((a, b)) dc_num a DC_DECLSEP dc_num b DC_DECLEND { return bc_compare(CastNum(a), CastNum(b)); } /* attempt to convert a dc_num to its corresponding int value * If discard_p is DC_TOSS then deallocate the value after use. */ int dc_num2int DC_DECLARG((value, discard_p)) dc_num value DC_DECLSEP dc_discard discard_p DC_DECLEND { long result; result = num2long(CastNum(value)); if (discard_p == DC_TOSS) dc_free_num(&value); return (int)result; } /* convert a C integer value into a dc_num */ /* For convenience of the caller, package the dc_num * into a dc_data result. */ dc_data dc_int2data DC_DECLARG((value)) int value DC_DECLEND { dc_data result; init_num((bc_num *)&result.v.number); int2num((bc_num *)&result.v.number, value); result.dc_type = DC_NUMBER; return result; } /* get a dc_num from some input stream; * input is a function which knows how to read the desired input stream * ibase is the input base (2<=ibase<=DC_IBASE_MAX) * *readahead will be set to the readahead character consumed while * looking for the end-of-number */ /* For convenience of the caller, package the dc_num * into a dc_data result. */ dc_data dc_getnum DC_DECLARG((input, ibase, readahead)) int (*input) DC_PROTO((void)) DC_DECLSEP int ibase DC_DECLSEP int *readahead DC_DECLEND { bc_num base; bc_num result; bc_num build; bc_num tmp; bc_num divisor; dc_data full_result; int negative = 0; int digit; int decimal; int c; init_num(&tmp); init_num(&build); init_num(&base); result = copy_num(_zero_); int2num(&base, ibase); c = (*input)(); while (isspace(c)) c = (*input)(); if (c == '_' || c == '-'){ negative = c; c = (*input)(); }else if (c == '+'){ c = (*input)(); } while (isspace(c)) c = (*input)(); for (;;){ if (isdigit(c)) digit = c - '0'; else if ('A' <= c && c <= 'F') digit = 10 + c - 'A'; else break; c = (*input)(); int2num(&tmp, digit); bc_multiply(result, base, &result, 0); bc_add(result, tmp, &result, 0); } if (c == '.'){ free_num(&build); free_num(&tmp); divisor = copy_num(_one_); build = copy_num(_zero_); decimal = 0; for (;;){ c = (*input)(); if (isdigit(c)) digit = c - '0'; else if ('A' <= c && c <= 'F') digit = 10 + c - 'A'; else break; int2num(&tmp, digit); bc_multiply(build, base, &build, 0); bc_add(build, tmp, &build, 0); bc_multiply(divisor, base, &divisor, 0); ++decimal; } bc_divide(build, divisor, &build, decimal); bc_add(result, build, &result, 0); } /* Final work. */ if (negative) bc_sub(_zero_, result, &result, 0); free_num(&tmp); free_num(&build); free_num(&base); if (readahead) *readahead = c; full_result.v.number = (dc_num)result; full_result.dc_type = DC_NUMBER; return full_result; } /* return the "length" of the number */ int dc_numlen DC_DECLARG((value)) dc_num value DC_DECLEND { bc_num num = CastNum(value); /* is this right??? */ return num->n_len + num->n_scale; } /* return the scale factor of the passed dc_num * If discard_p is DC_TOSS then deallocate the value after use. */ int dc_tell_scale DC_DECLARG((value, discard_p)) dc_num value DC_DECLSEP dc_discard discard_p DC_DECLEND { int kscale; kscale = CastNum(value)->n_scale; if (discard_p == DC_TOSS) dc_free_num(&value); return kscale; } /* initialize the math subsystem */ void dc_math_init DC_DECLVOID() { init_numbers(); } /* print out a dc_num in output base obase to stdout; * if newline_p is DC_WITHNL, terminate output with a '\n'; * if discard_p is DC_TOSS then deallocate the value after use */ void dc_out_num DC_DECLARG((value, obase, newline_p, discard_p)) dc_num value DC_DECLSEP int obase DC_DECLSEP dc_newline newline_p DC_DECLSEP dc_discard discard_p DC_DECLEND { out_num(CastNum(value), obase, out_char); if (newline_p == DC_WITHNL) out_char('\n'); if (discard_p == DC_TOSS) dc_free_num(&value); } /* dump out the absolute value of the integer part of a * dc_num as a byte stream, without any line wrapping; * if discard_p is DC_TOSS then deallocate the value after use */ void dc_dump_num DC_DECLARG((value, discard_p)) dc_num dcvalue DC_DECLSEP dc_discard discard_p DC_DECLEND { struct digit_stack { int digit; struct digit_stack *link;}; struct digit_stack *top_of_stack = NULL; struct digit_stack *cur; struct digit_stack *next; bc_num value; bc_num obase; bc_num digit; init_num(&value); init_num(&obase); init_num(&digit); /* we only handle the integer portion: */ bc_divide(CastNum(dcvalue), _one_, &value, 0); /* we only handle the absolute value: */ value->n_sign = PLUS; /* we're done with the dcvalue parameter: */ if (discard_p == DC_TOSS) dc_free_num(&dcvalue); int2num(&obase, 1+UCHAR_MAX); do { (void) bc_divmod(value, obase, &value, &digit, 0); cur = dc_malloc(sizeof *cur); cur->digit = (int)num2long(digit); cur->link = top_of_stack; top_of_stack = cur; } while (!is_zero(value)); for (cur=top_of_stack; cur; cur=next) { putchar(cur->digit); next = cur->link; free(cur); } free_num(&digit); free_num(&obase); free_num(&value); } /* deallocate an instance of a dc_num */ void dc_free_num DC_DECLARG((value)) dc_num *value DC_DECLEND { free_num((bc_num *)value); } /* return a duplicate of the number in the passed value */ /* The mismatched data types forces the caller to deal with * bad dc_type'd dc_data values, and makes it more convenient * for the caller to not have to do the grunge work of setting * up a dc_type result. */ dc_data dc_dup_num DC_DECLARG((value)) dc_num value DC_DECLEND { dc_data result; ++CastNum(value)->n_refs; result.v.number = value; result.dc_type = DC_NUMBER; return result; } /*---------------------------------------------------------------------------\ | The rest of this file consists of stubs for bc routines called by numeric.c| | so as to minimize the amount of bc code needed to build dc. | | The bulk of the code was just lifted straight out of the bc source. | \---------------------------------------------------------------------------*/ #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STDARG_H # include #else # include #endif int out_col = 0; /* Output routines: Write a character CH to the standard output. It keeps track of the number of characters output and may break the output with a "\". */ void out_char (ch) char ch; { if (ch == '\n') { out_col = 0; putchar ('\n'); } else { out_col++; if (out_col == 70) { putchar ('\\'); putchar ('\n'); out_col = 1; } putchar (ch); } } /* Malloc could not get enough memory. */ void out_of_memory() { dc_memfail(); } /* Runtime error will print a message and stop the machine. */ #ifdef HAVE_STDARG_H #ifdef __STDC__ void rt_error (char *mesg, ...) #else void rt_error (mesg) char *mesg; #endif #else void rt_error (mesg, va_alist) char *mesg; #endif { va_list args; char error_mesg [255]; #ifdef HAVE_STDARG_H va_start (args, mesg); #else va_start (args); #endif vsnprintf (error_mesg, sizeof error_mesg, mesg, args); va_end (args); fprintf (stderr, "Runtime error: %s\n", error_mesg); } /* A runtime warning tells of some action taken by the processor that may change the program execution but was not enough of a problem to stop the execution. */ #ifdef HAVE_STDARG_H #ifdef __STDC__ void rt_warn (char *mesg, ...) #else void rt_warn (mesg) char *mesg; #endif #else void rt_warn (mesg, va_alist) char *mesg; #endif { va_list args; char error_mesg [255]; #ifdef HAVE_STDARG_H va_start (args, mesg); #else va_start (args); #endif vsnprintf (error_mesg, sizeof error_mesg, mesg, args); va_end (args); fprintf (stderr, "Runtime warning: %s\n", error_mesg); }