1 /* 2 * interface dc to the bc numeric routines 3 * 4 * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, you can either send email to this 18 * program's author (see below) or write to: The Free Software Foundation, 19 * Inc.; 675 Mass Ave. Cambridge, MA 02139, USA. 20 */ 21 22 /* This should be the only module that knows the internals of type dc_num */ 23 /* In this particular implementation we just slather out some glue and 24 * make use of bc's numeric routines. 25 */ 26 27 #include "config.h" 28 29 #include <stdio.h> 30 #include <ctype.h> 31 #ifdef HAVE_LIMITS_H 32 # include <limits.h> 33 #else 34 # define UCHAR_MAX ((unsigned char)~0) 35 #endif 36 #include "bcdefs.h" 37 #include "proto.h" 38 #include "global.h" 39 #include "dc.h" 40 #include "dc-proto.h" 41 42 #ifdef __GNUC__ 43 # define ATTRIB(x) __attribute__(x) 44 #else 45 # define ATTRIB(x) 46 #endif 47 48 /* there is no POSIX standard for dc, so we'll take the GNU definitions */ 49 int std_only = FALSE; 50 51 /* convert an opaque dc_num into a real bc_num */ 52 #define CastNum(x) ((bc_num)(x)) 53 54 /* add two dc_nums, place into *result; 55 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 56 */ 57 int 58 dc_add DC_DECLARG((a, b, kscale, result)) 59 dc_num a DC_DECLSEP 60 dc_num b DC_DECLSEP 61 int kscale ATTRIB((unused)) DC_DECLSEP 62 dc_num *result DC_DECLEND 63 { 64 init_num((bc_num *)result); 65 bc_add(CastNum(a), CastNum(b), (bc_num *)result, 0); 66 return DC_SUCCESS; 67 } 68 69 /* subtract two dc_nums, place into *result; 70 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 71 */ 72 int 73 dc_sub DC_DECLARG((a, b, kscale, result)) 74 dc_num a DC_DECLSEP 75 dc_num b DC_DECLSEP 76 int kscale ATTRIB((unused)) DC_DECLSEP 77 dc_num *result DC_DECLEND 78 { 79 init_num((bc_num *)result); 80 bc_sub(CastNum(a), CastNum(b), (bc_num *)result, 0); 81 return DC_SUCCESS; 82 } 83 84 /* multiply two dc_nums, place into *result; 85 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 86 */ 87 int 88 dc_mul DC_DECLARG((a, b, kscale, result)) 89 dc_num a DC_DECLSEP 90 dc_num b DC_DECLSEP 91 int kscale DC_DECLSEP 92 dc_num *result DC_DECLEND 93 { 94 init_num((bc_num *)result); 95 bc_multiply(CastNum(a), CastNum(b), (bc_num *)result, kscale); 96 return DC_SUCCESS; 97 } 98 99 /* divide two dc_nums, place into *result; 100 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 101 */ 102 int 103 dc_div DC_DECLARG((a, b, kscale, result)) 104 dc_num a DC_DECLSEP 105 dc_num b DC_DECLSEP 106 int kscale DC_DECLSEP 107 dc_num *result DC_DECLEND 108 { 109 init_num((bc_num *)result); 110 if (bc_divide(CastNum(a), CastNum(b), (bc_num *)result, kscale)){ 111 fprintf(stderr, "%s: divide by zero\n", progname); 112 return DC_DOMAIN_ERROR; 113 } 114 return DC_SUCCESS; 115 } 116 117 /* divide two dc_nums, place quotient into *quotient and remainder 118 * into *remainder; 119 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 120 */ 121 int 122 dc_divrem DC_DECLARG((a, b, kscale, quotient, remainder)) 123 dc_num a DC_DECLSEP 124 dc_num b DC_DECLSEP 125 int kscale DC_DECLSEP 126 dc_num *quotient DC_DECLSEP 127 dc_num *remainder DC_DECLEND 128 { 129 init_num((bc_num *)quotient); 130 init_num((bc_num *)remainder); 131 if (bc_divmod(CastNum(a), CastNum(b), 132 (bc_num *)quotient, (bc_num *)remainder, kscale)){ 133 fprintf(stderr, "%s: divide by zero\n", progname); 134 return DC_DOMAIN_ERROR; 135 } 136 return DC_SUCCESS; 137 } 138 139 /* place the reminder of dividing a by b into *result; 140 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 141 */ 142 int 143 dc_rem DC_DECLARG((a, b, kscale, result)) 144 dc_num a DC_DECLSEP 145 dc_num b DC_DECLSEP 146 int kscale DC_DECLSEP 147 dc_num *result DC_DECLEND 148 { 149 init_num((bc_num *)result); 150 if (bc_modulo(CastNum(a), CastNum(b), (bc_num *)result, kscale)){ 151 fprintf(stderr, "%s: remainder by zero\n", progname); 152 return DC_DOMAIN_ERROR; 153 } 154 return DC_SUCCESS; 155 } 156 157 int 158 dc_modexp DC_DECLARG((base, expo, mod, kscale, result)) 159 dc_num base DC_DECLSEP 160 dc_num expo DC_DECLSEP 161 dc_num mod DC_DECLSEP 162 int kscale DC_DECLSEP 163 dc_num *result DC_DECLEND 164 { 165 init_num((bc_num *)result); 166 if (bc_raisemod(CastNum(base), CastNum(expo), CastNum(mod), 167 (bc_num *)result, kscale)){ 168 if (is_zero(CastNum(mod))) 169 fprintf(stderr, "%s: remainder by zero\n", progname); 170 return DC_DOMAIN_ERROR; 171 } 172 return DC_SUCCESS; 173 } 174 175 /* place the result of exponentiationg a by b into *result; 176 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 177 */ 178 int 179 dc_exp DC_DECLARG((a, b, kscale, result)) 180 dc_num a DC_DECLSEP 181 dc_num b DC_DECLSEP 182 int kscale DC_DECLSEP 183 dc_num *result DC_DECLEND 184 { 185 init_num((bc_num *)result); 186 bc_raise(CastNum(a), CastNum(b), (bc_num *)result, kscale); 187 return DC_SUCCESS; 188 } 189 190 /* take the square root of the value, place into *result; 191 * return DC_SUCCESS on success, DC_DOMAIN_ERROR on domain error 192 */ 193 int 194 dc_sqrt DC_DECLARG((value, kscale, result)) 195 dc_num value DC_DECLSEP 196 int kscale DC_DECLSEP 197 dc_num *result DC_DECLEND 198 { 199 bc_num tmp; 200 201 tmp = copy_num(CastNum(value)); 202 if (!bc_sqrt(&tmp, kscale)){ 203 fprintf(stderr, "%s: square root of negative number\n", progname); 204 free_num(&tmp); 205 return DC_DOMAIN_ERROR; 206 } 207 *((bc_num *)result) = tmp; 208 return DC_SUCCESS; 209 } 210 211 /* compare dc_nums a and b; 212 * return a negative value if a < b; 213 * return a positive value if a > b; 214 * return zero value if a == b 215 */ 216 int 217 dc_compare DC_DECLARG((a, b)) 218 dc_num a DC_DECLSEP 219 dc_num b DC_DECLEND 220 { 221 return bc_compare(CastNum(a), CastNum(b)); 222 } 223 224 /* attempt to convert a dc_num to its corresponding int value 225 * If discard_p is DC_TOSS then deallocate the value after use. 226 */ 227 int 228 dc_num2int DC_DECLARG((value, discard_p)) 229 dc_num value DC_DECLSEP 230 dc_discard discard_p DC_DECLEND 231 { 232 long result; 233 234 result = num2long(CastNum(value)); 235 if (discard_p == DC_TOSS) 236 dc_free_num(&value); 237 return (int)result; 238 } 239 240 /* convert a C integer value into a dc_num */ 241 /* For convenience of the caller, package the dc_num 242 * into a dc_data result. 243 */ 244 dc_data 245 dc_int2data DC_DECLARG((value)) 246 int value DC_DECLEND 247 { 248 dc_data result; 249 250 init_num((bc_num *)&result.v.number); 251 int2num((bc_num *)&result.v.number, value); 252 result.dc_type = DC_NUMBER; 253 return result; 254 } 255 256 /* get a dc_num from some input stream; 257 * input is a function which knows how to read the desired input stream 258 * ibase is the input base (2<=ibase<=DC_IBASE_MAX) 259 * *readahead will be set to the readahead character consumed while 260 * looking for the end-of-number 261 */ 262 /* For convenience of the caller, package the dc_num 263 * into a dc_data result. 264 */ 265 dc_data 266 dc_getnum DC_DECLARG((input, ibase, readahead)) 267 int (*input) DC_PROTO((void)) DC_DECLSEP 268 int ibase DC_DECLSEP 269 int *readahead DC_DECLEND 270 { 271 bc_num base; 272 bc_num result; 273 bc_num build; 274 bc_num tmp; 275 bc_num divisor; 276 dc_data full_result; 277 int negative = 0; 278 int digit; 279 int decimal; 280 int c; 281 282 init_num(&tmp); 283 init_num(&build); 284 init_num(&base); 285 result = copy_num(_zero_); 286 int2num(&base, ibase); 287 c = (*input)(); 288 while (isspace(c)) 289 c = (*input)(); 290 if (c == '_' || c == '-'){ 291 negative = c; 292 c = (*input)(); 293 }else if (c == '+'){ 294 c = (*input)(); 295 } 296 while (isspace(c)) 297 c = (*input)(); 298 for (;;){ 299 if (isdigit(c)) 300 digit = c - '0'; 301 else if ('A' <= c && c <= 'F') 302 digit = 10 + c - 'A'; 303 else 304 break; 305 c = (*input)(); 306 int2num(&tmp, digit); 307 bc_multiply(result, base, &result, 0); 308 bc_add(result, tmp, &result, 0); 309 } 310 if (c == '.'){ 311 free_num(&build); 312 free_num(&tmp); 313 divisor = copy_num(_one_); 314 build = copy_num(_zero_); 315 decimal = 0; 316 for (;;){ 317 c = (*input)(); 318 if (isdigit(c)) 319 digit = c - '0'; 320 else if ('A' <= c && c <= 'F') 321 digit = 10 + c - 'A'; 322 else 323 break; 324 int2num(&tmp, digit); 325 bc_multiply(build, base, &build, 0); 326 bc_add(build, tmp, &build, 0); 327 bc_multiply(divisor, base, &divisor, 0); 328 ++decimal; 329 } 330 bc_divide(build, divisor, &build, decimal); 331 bc_add(result, build, &result, 0); 332 } 333 /* Final work. */ 334 if (negative) 335 bc_sub(_zero_, result, &result, 0); 336 337 free_num(&tmp); 338 free_num(&build); 339 free_num(&base); 340 if (readahead) 341 *readahead = c; 342 full_result.v.number = (dc_num)result; 343 full_result.dc_type = DC_NUMBER; 344 return full_result; 345 } 346 347 348 /* return the "length" of the number */ 349 int 350 dc_numlen DC_DECLARG((value)) 351 dc_num value DC_DECLEND 352 { 353 bc_num num = CastNum(value); 354 355 /* is this right??? */ 356 return num->n_len + num->n_scale; 357 } 358 359 /* return the scale factor of the passed dc_num 360 * If discard_p is DC_TOSS then deallocate the value after use. 361 */ 362 int 363 dc_tell_scale DC_DECLARG((value, discard_p)) 364 dc_num value DC_DECLSEP 365 dc_discard discard_p DC_DECLEND 366 { 367 int kscale; 368 369 kscale = CastNum(value)->n_scale; 370 if (discard_p == DC_TOSS) 371 dc_free_num(&value); 372 return kscale; 373 } 374 375 376 /* initialize the math subsystem */ 377 void 378 dc_math_init DC_DECLVOID() 379 { 380 init_numbers(); 381 } 382 383 /* print out a dc_num in output base obase to stdout; 384 * if newline_p is DC_WITHNL, terminate output with a '\n'; 385 * if discard_p is DC_TOSS then deallocate the value after use 386 */ 387 void 388 dc_out_num DC_DECLARG((value, obase, newline_p, discard_p)) 389 dc_num value DC_DECLSEP 390 int obase DC_DECLSEP 391 dc_newline newline_p DC_DECLSEP 392 dc_discard discard_p DC_DECLEND 393 { 394 out_num(CastNum(value), obase, out_char); 395 if (newline_p == DC_WITHNL) 396 out_char('\n'); 397 if (discard_p == DC_TOSS) 398 dc_free_num(&value); 399 } 400 401 /* dump out the absolute value of the integer part of a 402 * dc_num as a byte stream, without any line wrapping; 403 * if discard_p is DC_TOSS then deallocate the value after use 404 */ 405 void 406 dc_dump_num DC_DECLARG((value, discard_p)) 407 dc_num dcvalue DC_DECLSEP 408 dc_discard discard_p DC_DECLEND 409 { 410 struct digit_stack { int digit; struct digit_stack *link;}; 411 struct digit_stack *top_of_stack = NULL; 412 struct digit_stack *cur; 413 struct digit_stack *next; 414 bc_num value; 415 bc_num obase; 416 bc_num digit; 417 418 init_num(&value); 419 init_num(&obase); 420 init_num(&digit); 421 422 /* we only handle the integer portion: */ 423 bc_divide(CastNum(dcvalue), _one_, &value, 0); 424 /* we only handle the absolute value: */ 425 value->n_sign = PLUS; 426 /* we're done with the dcvalue parameter: */ 427 if (discard_p == DC_TOSS) 428 dc_free_num(&dcvalue); 429 430 int2num(&obase, 1+UCHAR_MAX); 431 do { 432 (void) bc_divmod(value, obase, &value, &digit, 0); 433 cur = dc_malloc(sizeof *cur); 434 cur->digit = (int)num2long(digit); 435 cur->link = top_of_stack; 436 top_of_stack = cur; 437 } while (!is_zero(value)); 438 439 for (cur=top_of_stack; cur; cur=next) { 440 putchar(cur->digit); 441 next = cur->link; 442 free(cur); 443 } 444 445 free_num(&digit); 446 free_num(&obase); 447 free_num(&value); 448 } 449 450 /* deallocate an instance of a dc_num */ 451 void 452 dc_free_num DC_DECLARG((value)) 453 dc_num *value DC_DECLEND 454 { 455 free_num((bc_num *)value); 456 } 457 458 /* return a duplicate of the number in the passed value */ 459 /* The mismatched data types forces the caller to deal with 460 * bad dc_type'd dc_data values, and makes it more convenient 461 * for the caller to not have to do the grunge work of setting 462 * up a dc_type result. 463 */ 464 dc_data 465 dc_dup_num DC_DECLARG((value)) 466 dc_num value DC_DECLEND 467 { 468 dc_data result; 469 470 ++CastNum(value)->n_refs; 471 result.v.number = value; 472 result.dc_type = DC_NUMBER; 473 return result; 474 } 475 476 477 478 /*---------------------------------------------------------------------------\ 479 | The rest of this file consists of stubs for bc routines called by numeric.c| 480 | so as to minimize the amount of bc code needed to build dc. | 481 | The bulk of the code was just lifted straight out of the bc source. | 482 \---------------------------------------------------------------------------*/ 483 484 #ifdef HAVE_STDLIB_H 485 # include <stdlib.h> 486 #endif 487 488 #ifdef HAVE_STDARG_H 489 # include <stdarg.h> 490 #else 491 # include <varargs.h> 492 #endif 493 494 495 int out_col = 0; 496 497 /* Output routines: Write a character CH to the standard output. 498 It keeps track of the number of characters output and may 499 break the output with a "\<cr>". */ 500 501 void 502 out_char (ch) 503 char ch; 504 { 505 506 if (ch == '\n') 507 { 508 out_col = 0; 509 putchar ('\n'); 510 } 511 else 512 { 513 out_col++; 514 if (out_col == 70) 515 { 516 putchar ('\\'); 517 putchar ('\n'); 518 out_col = 1; 519 } 520 putchar (ch); 521 } 522 } 523 524 /* Malloc could not get enough memory. */ 525 526 void 527 out_of_memory() 528 { 529 dc_memfail(); 530 } 531 532 /* Runtime error will print a message and stop the machine. */ 533 534 #ifdef HAVE_STDARG_H 535 #ifdef __STDC__ 536 void 537 rt_error (char *mesg, ...) 538 #else 539 void 540 rt_error (mesg) 541 char *mesg; 542 #endif 543 #else 544 void 545 rt_error (mesg, va_alist) 546 char *mesg; 547 #endif 548 { 549 va_list args; 550 char error_mesg [255]; 551 552 #ifdef HAVE_STDARG_H 553 va_start (args, mesg); 554 #else 555 va_start (args); 556 #endif 557 vsprintf (error_mesg, mesg, args); 558 va_end (args); 559 560 fprintf (stderr, "Runtime error: %s\n", error_mesg); 561 } 562 563 564 /* A runtime warning tells of some action taken by the processor that 565 may change the program execution but was not enough of a problem 566 to stop the execution. */ 567 568 #ifdef HAVE_STDARG_H 569 #ifdef __STDC__ 570 void 571 rt_warn (char *mesg, ...) 572 #else 573 void 574 rt_warn (mesg) 575 char *mesg; 576 #endif 577 #else 578 void 579 rt_warn (mesg, va_alist) 580 char *mesg; 581 #endif 582 { 583 va_list args; 584 char error_mesg [255]; 585 586 #ifdef HAVE_STDARG_H 587 va_start (args, mesg); 588 #else 589 va_start (args); 590 #endif 591 vsprintf (error_mesg, mesg, args); 592 va_end (args); 593 594 fprintf (stderr, "Runtime warning: %s\n", error_mesg); 595 } |