1 /* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2023 Gavin D. Howard and contributors. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * * Redistributions of source code must retain the above copyright notice, this 12 * list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 * 30 * ***************************************************************************** 31 * 32 * The public functions for libbc. 33 * 34 */ 35 36 #if BC_ENABLE_LIBRARY 37 38 #include <setjmp.h> 39 #include <string.h> 40 #include <time.h> 41 42 #include <bcl.h> 43 44 #include <library.h> 45 #include <num.h> 46 #include <vm.h> 47 48 #ifndef _WIN32 49 #include <pthread.h> 50 #endif // _WIN32 51 52 // The asserts in this file are important to testing; in many cases, the test 53 // would not work without the asserts, so don't remove them without reason. 54 // 55 // Also, there are many uses of bc_num_clear() here; that is because numbers are 56 // being reused, and a clean slate is required. 57 // 58 // Also, there are a bunch of BC_UNSETJMP between calls to bc_num_init(). That 59 // is because locals are being initialized, and unlike bc proper, this code 60 // cannot assume that allocation failures are fatal. So we have to reset the 61 // jumps every time to ensure that the locals will be correct after jumping. 62 63 static BclTls* tls = NULL; 64 static BclTls tls_real; 65 66 BclError 67 bcl_start(void) 68 { 69 #ifndef _WIN32 70 71 int r; 72 73 if (tls != NULL) return BCL_ERROR_NONE; 74 75 r = pthread_key_create(&tls_real, NULL); 76 if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR; 77 78 #else // _WIN32 79 80 if (tls != NULL) return BCL_ERROR_NONE; 81 82 tls_real = TlsAlloc(); 83 if (BC_ERR(tls_real == TLS_OUT_OF_INDEXES)) 84 { 85 return BCL_ERROR_FATAL_ALLOC_ERR; 86 } 87 88 #endif // _WIN32 89 90 tls = &tls_real; 91 92 return BCL_ERROR_NONE; 93 } 94 95 /** 96 * Sets the thread-specific data for the thread. 97 * @param vm The @a BcVm to set as the thread data. 98 * @return An error code, if any. 99 */ 100 static BclError 101 bcl_setspecific(BcVm* vm) 102 { 103 #ifndef _WIN32 104 105 int r; 106 107 assert(tls != NULL); 108 109 r = pthread_setspecific(tls_real, vm); 110 if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR; 111 112 #else // _WIN32 113 114 bool r; 115 116 assert(tls != NULL); 117 118 r = TlsSetValue(tls_real, vm); 119 if (BC_ERR(!r)) return BCL_ERROR_FATAL_ALLOC_ERR; 120 121 #endif // _WIN32 122 123 return BCL_ERROR_NONE; 124 } 125 126 BcVm* 127 bcl_getspecific(void) 128 { 129 BcVm* vm; 130 131 #ifndef _WIN32 132 133 vm = pthread_getspecific(tls_real); 134 135 #else // _WIN32 136 137 vm = TlsGetValue(tls_real); 138 139 #endif // _WIN32 140 141 return vm; 142 } 143 144 BclError 145 bcl_init(void) 146 { 147 BclError e = BCL_ERROR_NONE; 148 BcVm* vm; 149 150 assert(tls != NULL); 151 152 vm = bcl_getspecific(); 153 if (vm != NULL) 154 { 155 assert(vm->refs >= 1); 156 157 vm->refs += 1; 158 159 return e; 160 } 161 162 vm = bc_vm_malloc(sizeof(BcVm)); 163 if (BC_ERR(vm == NULL)) return BCL_ERROR_FATAL_ALLOC_ERR; 164 165 e = bcl_setspecific(vm); 166 if (BC_ERR(e != BCL_ERROR_NONE)) 167 { 168 free(vm); 169 return e; 170 } 171 172 memset(vm, 0, sizeof(BcVm)); 173 174 vm->refs += 1; 175 176 assert(vm->refs == 1); 177 178 // Setting these to NULL ensures that if an error occurs, we only free what 179 // is necessary. 180 vm->ctxts.v = NULL; 181 vm->jmp_bufs.v = NULL; 182 vm->out.v = NULL; 183 184 vm->abrt = false; 185 vm->leading_zeroes = false; 186 vm->digit_clamp = true; 187 188 // The jmp_bufs always has to be initialized first. 189 bc_vec_init(&vm->jmp_bufs, sizeof(sigjmp_buf), BC_DTOR_NONE); 190 191 BC_FUNC_HEADER(vm, err); 192 193 bc_vm_init(); 194 195 bc_vec_init(&vm->ctxts, sizeof(BclContext), BC_DTOR_NONE); 196 bc_vec_init(&vm->out, sizeof(uchar), BC_DTOR_NONE); 197 198 // We need to seed this in case /dev/random and /dev/urandm don't work. 199 srand((unsigned int) time(NULL)); 200 bc_rand_init(&vm->rng); 201 202 err: 203 204 // This is why we had to set them to NULL. 205 if (BC_ERR(vm != NULL && vm->err)) 206 { 207 if (vm->out.v != NULL) bc_vec_free(&vm->out); 208 if (vm->jmp_bufs.v != NULL) bc_vec_free(&vm->jmp_bufs); 209 if (vm->ctxts.v != NULL) bc_vec_free(&vm->ctxts); 210 bcl_setspecific(NULL); 211 free(vm); 212 } 213 214 BC_FUNC_FOOTER(vm, e); 215 216 return e; 217 } 218 219 BclError 220 bcl_pushContext(BclContext ctxt) 221 { 222 BclError e = BCL_ERROR_NONE; 223 BcVm* vm = bcl_getspecific(); 224 225 BC_FUNC_HEADER(vm, err); 226 227 bc_vec_push(&vm->ctxts, &ctxt); 228 229 err: 230 BC_FUNC_FOOTER(vm, e); 231 return e; 232 } 233 234 void 235 bcl_popContext(void) 236 { 237 BcVm* vm = bcl_getspecific(); 238 239 if (vm->ctxts.len) bc_vec_pop(&vm->ctxts); 240 } 241 242 static BclContext 243 bcl_contextHelper(BcVm* vm) 244 { 245 if (!vm->ctxts.len) return NULL; 246 return *((BclContext*) bc_vec_top(&vm->ctxts)); 247 } 248 249 BclContext 250 bcl_context(void) 251 { 252 BcVm* vm = bcl_getspecific(); 253 return bcl_contextHelper(vm); 254 } 255 256 void 257 bcl_free(void) 258 { 259 size_t i; 260 BcVm* vm = bcl_getspecific(); 261 262 vm->refs -= 1; 263 if (vm->refs) return; 264 265 bc_rand_free(&vm->rng); 266 bc_vec_free(&vm->out); 267 268 for (i = 0; i < vm->ctxts.len; ++i) 269 { 270 BclContext ctxt = *((BclContext*) bc_vec_item(&vm->ctxts, i)); 271 bcl_ctxt_free(ctxt); 272 } 273 274 bc_vec_free(&vm->ctxts); 275 276 bc_vm_atexit(); 277 278 free(vm); 279 bcl_setspecific(NULL); 280 } 281 282 void 283 bcl_end(void) 284 { 285 #ifndef _WIN32 286 287 // We ignore the return value. 288 pthread_key_delete(tls_real); 289 290 #else // _WIN32 291 292 // We ignore the return value. 293 TlsFree(tls_real); 294 295 #endif // _WIN32 296 297 tls = NULL; 298 } 299 300 void 301 bcl_gc(void) 302 { 303 bc_vm_freeTemps(); 304 } 305 306 bool 307 bcl_abortOnFatalError(void) 308 { 309 BcVm* vm = bcl_getspecific(); 310 311 return vm->abrt; 312 } 313 314 void 315 bcl_setAbortOnFatalError(bool abrt) 316 { 317 BcVm* vm = bcl_getspecific(); 318 319 vm->abrt = abrt; 320 } 321 322 bool 323 bcl_leadingZeroes(void) 324 { 325 BcVm* vm = bcl_getspecific(); 326 327 return vm->leading_zeroes; 328 } 329 330 void 331 bcl_setLeadingZeroes(bool leadingZeroes) 332 { 333 BcVm* vm = bcl_getspecific(); 334 335 vm->leading_zeroes = leadingZeroes; 336 } 337 338 bool 339 bcl_digitClamp(void) 340 { 341 BcVm* vm = bcl_getspecific(); 342 343 return vm->digit_clamp; 344 } 345 346 void 347 bcl_setDigitClamp(bool digitClamp) 348 { 349 BcVm* vm = bcl_getspecific(); 350 351 vm->digit_clamp = digitClamp; 352 } 353 354 BclContext 355 bcl_ctxt_create(void) 356 { 357 BcVm* vm = bcl_getspecific(); 358 BclContext ctxt = NULL; 359 360 BC_FUNC_HEADER(vm, err); 361 362 // We want the context to be free of any interference of other parties, so 363 // malloc() is appropriate here. 364 ctxt = bc_vm_malloc(sizeof(BclCtxt)); 365 366 bc_vec_init(&ctxt->nums, sizeof(BcNum), BC_DTOR_BCL_NUM); 367 bc_vec_init(&ctxt->free_nums, sizeof(BclNumber), BC_DTOR_NONE); 368 369 ctxt->scale = 0; 370 ctxt->ibase = 10; 371 ctxt->obase = 10; 372 373 err: 374 375 if (BC_ERR(vm->err && ctxt != NULL)) 376 { 377 if (ctxt->nums.v != NULL) bc_vec_free(&ctxt->nums); 378 free(ctxt); 379 ctxt = NULL; 380 } 381 382 BC_FUNC_FOOTER_NO_ERR(vm); 383 384 return ctxt; 385 } 386 387 void 388 bcl_ctxt_free(BclContext ctxt) 389 { 390 bc_vec_free(&ctxt->free_nums); 391 bc_vec_free(&ctxt->nums); 392 free(ctxt); 393 } 394 395 void 396 bcl_ctxt_freeNums(BclContext ctxt) 397 { 398 bc_vec_popAll(&ctxt->nums); 399 bc_vec_popAll(&ctxt->free_nums); 400 } 401 402 size_t 403 bcl_ctxt_scale(BclContext ctxt) 404 { 405 return ctxt->scale; 406 } 407 408 void 409 bcl_ctxt_setScale(BclContext ctxt, size_t scale) 410 { 411 ctxt->scale = scale; 412 } 413 414 size_t 415 bcl_ctxt_ibase(BclContext ctxt) 416 { 417 return ctxt->ibase; 418 } 419 420 void 421 bcl_ctxt_setIbase(BclContext ctxt, size_t ibase) 422 { 423 if (ibase < BC_NUM_MIN_BASE) ibase = BC_NUM_MIN_BASE; 424 else if (ibase > BC_NUM_MAX_IBASE) ibase = BC_NUM_MAX_IBASE; 425 ctxt->ibase = ibase; 426 } 427 428 size_t 429 bcl_ctxt_obase(BclContext ctxt) 430 { 431 return ctxt->obase; 432 } 433 434 void 435 bcl_ctxt_setObase(BclContext ctxt, size_t obase) 436 { 437 ctxt->obase = obase; 438 } 439 440 BclError 441 bcl_err(BclNumber n) 442 { 443 BclContext ctxt; 444 BcVm* vm = bcl_getspecific(); 445 446 BC_CHECK_CTXT_ERR(vm, ctxt); 447 448 // Errors are encoded as (0 - error_code). If the index is in that range, it 449 // is an encoded error. 450 if (n.i >= ctxt->nums.len) 451 { 452 if (n.i > 0 - (size_t) BCL_ERROR_NELEMS) return (BclError) (0 - n.i); 453 else return BCL_ERROR_INVALID_NUM; 454 } 455 else return BCL_ERROR_NONE; 456 } 457 458 /** 459 * Inserts a BcNum into a context's list of numbers. 460 * @param ctxt The context to insert into. 461 * @param n The BcNum to insert. 462 * @return The resulting BclNumber from the insert. 463 */ 464 static BclNumber 465 bcl_num_insert(BclContext ctxt, BcNum* restrict n) 466 { 467 BclNumber idx; 468 469 // If there is a free spot... 470 if (ctxt->free_nums.len) 471 { 472 BcNum* ptr; 473 474 // Get the index of the free spot and remove it. 475 idx = *((BclNumber*) bc_vec_top(&ctxt->free_nums)); 476 bc_vec_pop(&ctxt->free_nums); 477 478 // Copy the number into the spot. 479 ptr = bc_vec_item(&ctxt->nums, idx.i); 480 memcpy(ptr, n, sizeof(BcNum)); 481 } 482 else 483 { 484 // Just push the number onto the vector. 485 idx.i = ctxt->nums.len; 486 bc_vec_push(&ctxt->nums, n); 487 } 488 489 return idx; 490 } 491 492 BclNumber 493 bcl_num_create(void) 494 { 495 BclError e = BCL_ERROR_NONE; 496 BcNum n; 497 BclNumber idx; 498 BclContext ctxt; 499 BcVm* vm = bcl_getspecific(); 500 501 BC_CHECK_CTXT(vm, ctxt); 502 503 BC_FUNC_HEADER(vm, err); 504 505 bc_vec_grow(&ctxt->nums, 1); 506 507 bc_num_init(&n, BC_NUM_DEF_SIZE); 508 509 err: 510 BC_FUNC_FOOTER(vm, e); 511 BC_MAYBE_SETUP(ctxt, e, n, idx); 512 513 return idx; 514 } 515 516 /** 517 * Destructs a number and marks its spot as free. 518 * @param ctxt The context. 519 * @param n The index of the number. 520 * @param num The number to destroy. 521 */ 522 static void 523 bcl_num_dtor(BclContext ctxt, BclNumber n, BcNum* restrict num) 524 { 525 assert(num != NULL && num->num != NULL); 526 527 bcl_num_destruct(num); 528 bc_vec_push(&ctxt->free_nums, &n); 529 } 530 531 void 532 bcl_num_free(BclNumber n) 533 { 534 BcNum* num; 535 BclContext ctxt; 536 BcVm* vm = bcl_getspecific(); 537 538 BC_CHECK_CTXT_ASSERT(vm, ctxt); 539 540 assert(n.i < ctxt->nums.len); 541 542 num = BC_NUM(ctxt, n); 543 544 bcl_num_dtor(ctxt, n, num); 545 } 546 547 BclError 548 bcl_copy(BclNumber d, BclNumber s) 549 { 550 BclError e = BCL_ERROR_NONE; 551 BcNum* dest; 552 BcNum* src; 553 BclContext ctxt; 554 BcVm* vm = bcl_getspecific(); 555 556 BC_CHECK_CTXT_ERR(vm, ctxt); 557 558 BC_FUNC_HEADER(vm, err); 559 560 assert(d.i < ctxt->nums.len && s.i < ctxt->nums.len); 561 562 dest = BC_NUM(ctxt, d); 563 src = BC_NUM(ctxt, s); 564 565 assert(dest != NULL && src != NULL); 566 assert(dest->num != NULL && src->num != NULL); 567 568 bc_num_copy(dest, src); 569 570 err: 571 BC_FUNC_FOOTER(vm, e); 572 573 return e; 574 } 575 576 BclNumber 577 bcl_dup(BclNumber s) 578 { 579 BclError e = BCL_ERROR_NONE; 580 BcNum *src, dest; 581 BclNumber idx; 582 BclContext ctxt; 583 BcVm* vm = bcl_getspecific(); 584 585 BC_CHECK_CTXT(vm, ctxt); 586 587 BC_FUNC_HEADER(vm, err); 588 589 bc_vec_grow(&ctxt->nums, 1); 590 591 assert(s.i < ctxt->nums.len); 592 593 src = BC_NUM(ctxt, s); 594 595 assert(src != NULL && src->num != NULL); 596 597 // Copy the number. 598 bc_num_clear(&dest); 599 bc_num_createCopy(&dest, src); 600 601 err: 602 BC_FUNC_FOOTER(vm, e); 603 BC_MAYBE_SETUP(ctxt, e, dest, idx); 604 605 return idx; 606 } 607 608 void 609 bcl_num_destruct(void* num) 610 { 611 BcNum* n = (BcNum*) num; 612 613 assert(n != NULL); 614 615 if (n->num == NULL) return; 616 617 bc_num_free(num); 618 bc_num_clear(num); 619 } 620 621 bool 622 bcl_num_neg(BclNumber n) 623 { 624 BcNum* num; 625 BclContext ctxt; 626 BcVm* vm = bcl_getspecific(); 627 628 BC_CHECK_CTXT_ASSERT(vm, ctxt); 629 630 assert(n.i < ctxt->nums.len); 631 632 num = BC_NUM(ctxt, n); 633 634 assert(num != NULL && num->num != NULL); 635 636 return BC_NUM_NEG(num) != 0; 637 } 638 639 void 640 bcl_num_setNeg(BclNumber n, bool neg) 641 { 642 BcNum* num; 643 BclContext ctxt; 644 BcVm* vm = bcl_getspecific(); 645 646 BC_CHECK_CTXT_ASSERT(vm, ctxt); 647 648 assert(n.i < ctxt->nums.len); 649 650 num = BC_NUM(ctxt, n); 651 652 assert(num != NULL && num->num != NULL); 653 654 num->rdx = BC_NUM_NEG_VAL(num, neg); 655 } 656 657 size_t 658 bcl_num_scale(BclNumber n) 659 { 660 BcNum* num; 661 BclContext ctxt; 662 BcVm* vm = bcl_getspecific(); 663 664 BC_CHECK_CTXT_ASSERT(vm, ctxt); 665 666 assert(n.i < ctxt->nums.len); 667 668 num = BC_NUM(ctxt, n); 669 670 assert(num != NULL && num->num != NULL); 671 672 return bc_num_scale(num); 673 } 674 675 BclError 676 bcl_num_setScale(BclNumber n, size_t scale) 677 { 678 BclError e = BCL_ERROR_NONE; 679 BcNum* nptr; 680 BclContext ctxt; 681 BcVm* vm = bcl_getspecific(); 682 683 BC_CHECK_CTXT_ERR(vm, ctxt); 684 685 BC_CHECK_NUM_ERR(ctxt, n); 686 687 BC_FUNC_HEADER(vm, err); 688 689 assert(n.i < ctxt->nums.len); 690 691 nptr = BC_NUM(ctxt, n); 692 693 assert(nptr != NULL && nptr->num != NULL); 694 695 if (scale > nptr->scale) bc_num_extend(nptr, scale - nptr->scale); 696 else if (scale < nptr->scale) bc_num_truncate(nptr, nptr->scale - scale); 697 698 err: 699 BC_FUNC_FOOTER(vm, e); 700 701 return e; 702 } 703 704 size_t 705 bcl_num_len(BclNumber n) 706 { 707 BcNum* num; 708 BclContext ctxt; 709 BcVm* vm = bcl_getspecific(); 710 711 BC_CHECK_CTXT_ASSERT(vm, ctxt); 712 713 assert(n.i < ctxt->nums.len); 714 715 num = BC_NUM(ctxt, n); 716 717 assert(num != NULL && num->num != NULL); 718 719 return bc_num_len(num); 720 } 721 722 BclError 723 bcl_bigdig(BclNumber n, BclBigDig* result) 724 { 725 BclError e = BCL_ERROR_NONE; 726 BcNum* num; 727 BclContext ctxt; 728 BcVm* vm = bcl_getspecific(); 729 730 BC_CHECK_CTXT_ERR(vm, ctxt); 731 732 BC_FUNC_HEADER(vm, err); 733 734 assert(n.i < ctxt->nums.len); 735 assert(result != NULL); 736 737 num = BC_NUM(ctxt, n); 738 739 assert(num != NULL && num->num != NULL); 740 741 *result = bc_num_bigdig(num); 742 743 err: 744 bcl_num_dtor(ctxt, n, num); 745 BC_FUNC_FOOTER(vm, e); 746 747 return e; 748 } 749 750 BclNumber 751 bcl_bigdig2num(BclBigDig val) 752 { 753 BclError e = BCL_ERROR_NONE; 754 BcNum n; 755 BclNumber idx; 756 BclContext ctxt; 757 BcVm* vm = bcl_getspecific(); 758 759 BC_CHECK_CTXT(vm, ctxt); 760 761 BC_FUNC_HEADER(vm, err); 762 763 bc_vec_grow(&ctxt->nums, 1); 764 765 bc_num_createFromBigdig(&n, val); 766 767 err: 768 BC_FUNC_FOOTER(vm, e); 769 BC_MAYBE_SETUP(ctxt, e, n, idx); 770 771 return idx; 772 } 773 774 /** 775 * Sets up and executes a binary operator operation. 776 * @param a The first operand. 777 * @param b The second operand. 778 * @param op The operation. 779 * @param req The function to get the size of the result for preallocation. 780 * @return The result of the operation. 781 */ 782 static BclNumber 783 bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op, 784 const BcNumBinaryOpReq req) 785 { 786 BclError e = BCL_ERROR_NONE; 787 BcNum* aptr; 788 BcNum* bptr; 789 BcNum c; 790 BclNumber idx; 791 BclContext ctxt; 792 BcVm* vm = bcl_getspecific(); 793 794 BC_CHECK_CTXT(vm, ctxt); 795 796 BC_CHECK_NUM(ctxt, a); 797 BC_CHECK_NUM(ctxt, b); 798 799 BC_FUNC_HEADER(vm, err); 800 801 bc_vec_grow(&ctxt->nums, 1); 802 803 assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); 804 805 aptr = BC_NUM(ctxt, a); 806 bptr = BC_NUM(ctxt, b); 807 808 assert(aptr != NULL && bptr != NULL); 809 assert(aptr->num != NULL && bptr->num != NULL); 810 811 // Clear and initialize the result. 812 bc_num_clear(&c); 813 bc_num_init(&c, req(aptr, bptr, ctxt->scale)); 814 815 op(aptr, bptr, &c, ctxt->scale); 816 817 err: 818 819 // Eat the operands. 820 bcl_num_dtor(ctxt, a, aptr); 821 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr); 822 823 BC_FUNC_FOOTER(vm, e); 824 BC_MAYBE_SETUP(ctxt, e, c, idx); 825 826 return idx; 827 } 828 829 BclNumber 830 bcl_add(BclNumber a, BclNumber b) 831 { 832 return bcl_binary(a, b, bc_num_add, bc_num_addReq); 833 } 834 835 BclNumber 836 bcl_sub(BclNumber a, BclNumber b) 837 { 838 return bcl_binary(a, b, bc_num_sub, bc_num_addReq); 839 } 840 841 BclNumber 842 bcl_mul(BclNumber a, BclNumber b) 843 { 844 return bcl_binary(a, b, bc_num_mul, bc_num_mulReq); 845 } 846 847 BclNumber 848 bcl_div(BclNumber a, BclNumber b) 849 { 850 return bcl_binary(a, b, bc_num_div, bc_num_divReq); 851 } 852 853 BclNumber 854 bcl_mod(BclNumber a, BclNumber b) 855 { 856 return bcl_binary(a, b, bc_num_mod, bc_num_divReq); 857 } 858 859 BclNumber 860 bcl_pow(BclNumber a, BclNumber b) 861 { 862 return bcl_binary(a, b, bc_num_pow, bc_num_powReq); 863 } 864 865 BclNumber 866 bcl_lshift(BclNumber a, BclNumber b) 867 { 868 return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq); 869 } 870 871 BclNumber 872 bcl_rshift(BclNumber a, BclNumber b) 873 { 874 return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq); 875 } 876 877 BclNumber 878 bcl_sqrt(BclNumber a) 879 { 880 BclError e = BCL_ERROR_NONE; 881 BcNum* aptr; 882 BcNum b; 883 BclNumber idx; 884 BclContext ctxt; 885 BcVm* vm = bcl_getspecific(); 886 887 BC_CHECK_CTXT(vm, ctxt); 888 889 BC_CHECK_NUM(ctxt, a); 890 891 BC_FUNC_HEADER(vm, err); 892 893 bc_vec_grow(&ctxt->nums, 1); 894 895 assert(a.i < ctxt->nums.len); 896 897 aptr = BC_NUM(ctxt, a); 898 899 bc_num_sqrt(aptr, &b, ctxt->scale); 900 901 err: 902 bcl_num_dtor(ctxt, a, aptr); 903 BC_FUNC_FOOTER(vm, e); 904 BC_MAYBE_SETUP(ctxt, e, b, idx); 905 906 return idx; 907 } 908 909 BclError 910 bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d) 911 { 912 BclError e = BCL_ERROR_NONE; 913 size_t req; 914 BcNum* aptr; 915 BcNum* bptr; 916 BcNum cnum, dnum; 917 BclContext ctxt; 918 BcVm* vm = bcl_getspecific(); 919 920 BC_CHECK_CTXT_ERR(vm, ctxt); 921 922 BC_CHECK_NUM_ERR(ctxt, a); 923 BC_CHECK_NUM_ERR(ctxt, b); 924 925 BC_FUNC_HEADER(vm, err); 926 927 bc_vec_grow(&ctxt->nums, 2); 928 929 assert(c != NULL && d != NULL); 930 931 aptr = BC_NUM(ctxt, a); 932 bptr = BC_NUM(ctxt, b); 933 934 assert(aptr != NULL && bptr != NULL); 935 assert(aptr->num != NULL && bptr->num != NULL); 936 937 bc_num_clear(&cnum); 938 bc_num_clear(&dnum); 939 940 req = bc_num_divReq(aptr, bptr, ctxt->scale); 941 942 // Initialize the numbers. 943 bc_num_init(&cnum, req); 944 BC_UNSETJMP(vm); 945 BC_SETJMP(vm, err); 946 bc_num_init(&dnum, req); 947 948 bc_num_divmod(aptr, bptr, &cnum, &dnum, ctxt->scale); 949 950 err: 951 952 // Eat the operands. 953 bcl_num_dtor(ctxt, a, aptr); 954 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr); 955 956 // If there was an error... 957 if (BC_ERR(vm->err)) 958 { 959 // Free the results. 960 if (cnum.num != NULL) bc_num_free(&cnum); 961 if (dnum.num != NULL) bc_num_free(&dnum); 962 963 // Make sure the return values are invalid. 964 c->i = 0 - (size_t) BCL_ERROR_INVALID_NUM; 965 d->i = c->i; 966 967 BC_FUNC_FOOTER(vm, e); 968 } 969 else 970 { 971 BC_FUNC_FOOTER(vm, e); 972 973 // Insert the results into the context. 974 *c = bcl_num_insert(ctxt, &cnum); 975 *d = bcl_num_insert(ctxt, &dnum); 976 } 977 978 return e; 979 } 980 981 BclNumber 982 bcl_modexp(BclNumber a, BclNumber b, BclNumber c) 983 { 984 BclError e = BCL_ERROR_NONE; 985 size_t req; 986 BcNum* aptr; 987 BcNum* bptr; 988 BcNum* cptr; 989 BcNum d; 990 BclNumber idx; 991 BclContext ctxt; 992 BcVm* vm = bcl_getspecific(); 993 994 BC_CHECK_CTXT(vm, ctxt); 995 996 BC_CHECK_NUM(ctxt, a); 997 BC_CHECK_NUM(ctxt, b); 998 BC_CHECK_NUM(ctxt, c); 999 1000 BC_FUNC_HEADER(vm, err); 1001 1002 bc_vec_grow(&ctxt->nums, 1); 1003 1004 assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); 1005 assert(c.i < ctxt->nums.len); 1006 1007 aptr = BC_NUM(ctxt, a); 1008 bptr = BC_NUM(ctxt, b); 1009 cptr = BC_NUM(ctxt, c); 1010 1011 assert(aptr != NULL && bptr != NULL && cptr != NULL); 1012 assert(aptr->num != NULL && bptr->num != NULL && cptr->num != NULL); 1013 1014 // Prepare the result. 1015 bc_num_clear(&d); 1016 1017 req = bc_num_divReq(aptr, cptr, 0); 1018 1019 // Initialize the result. 1020 bc_num_init(&d, req); 1021 1022 bc_num_modexp(aptr, bptr, cptr, &d); 1023 1024 err: 1025 1026 // Eat the operands. 1027 bcl_num_dtor(ctxt, a, aptr); 1028 if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr); 1029 if (c.i != a.i && c.i != b.i) bcl_num_dtor(ctxt, c, cptr); 1030 1031 BC_FUNC_FOOTER(vm, e); 1032 BC_MAYBE_SETUP(ctxt, e, d, idx); 1033 1034 return idx; 1035 } 1036 1037 ssize_t 1038 bcl_cmp(BclNumber a, BclNumber b) 1039 { 1040 BcNum* aptr; 1041 BcNum* bptr; 1042 BclContext ctxt; 1043 BcVm* vm = bcl_getspecific(); 1044 1045 BC_CHECK_CTXT_ASSERT(vm, ctxt); 1046 1047 assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); 1048 1049 aptr = BC_NUM(ctxt, a); 1050 bptr = BC_NUM(ctxt, b); 1051 1052 assert(aptr != NULL && bptr != NULL); 1053 assert(aptr->num != NULL && bptr->num != NULL); 1054 1055 return bc_num_cmp(aptr, bptr); 1056 } 1057 1058 void 1059 bcl_zero(BclNumber n) 1060 { 1061 BcNum* nptr; 1062 BclContext ctxt; 1063 BcVm* vm = bcl_getspecific(); 1064 1065 BC_CHECK_CTXT_ASSERT(vm, ctxt); 1066 1067 assert(n.i < ctxt->nums.len); 1068 1069 nptr = BC_NUM(ctxt, n); 1070 1071 assert(nptr != NULL && nptr->num != NULL); 1072 1073 bc_num_zero(nptr); 1074 } 1075 1076 void 1077 bcl_one(BclNumber n) 1078 { 1079 BcNum* nptr; 1080 BclContext ctxt; 1081 BcVm* vm = bcl_getspecific(); 1082 1083 BC_CHECK_CTXT_ASSERT(vm, ctxt); 1084 1085 assert(n.i < ctxt->nums.len); 1086 1087 nptr = BC_NUM(ctxt, n); 1088 1089 assert(nptr != NULL && nptr->num != NULL); 1090 1091 bc_num_one(nptr); 1092 } 1093 1094 BclNumber 1095 bcl_parse(const char* restrict val) 1096 { 1097 BclError e = BCL_ERROR_NONE; 1098 BcNum n; 1099 BclNumber idx; 1100 BclContext ctxt; 1101 BcVm* vm = bcl_getspecific(); 1102 bool neg; 1103 1104 BC_CHECK_CTXT(vm, ctxt); 1105 1106 BC_FUNC_HEADER(vm, err); 1107 1108 bc_vec_grow(&ctxt->nums, 1); 1109 1110 assert(val != NULL); 1111 1112 // We have to take care of negative here because bc's number parsing does 1113 // not. 1114 neg = (val[0] == '-'); 1115 1116 if (neg) val += 1; 1117 1118 if (!bc_num_strValid(val)) 1119 { 1120 vm->err = BCL_ERROR_PARSE_INVALID_STR; 1121 goto err; 1122 } 1123 1124 // Clear and initialize the number. 1125 bc_num_clear(&n); 1126 bc_num_init(&n, BC_NUM_DEF_SIZE); 1127 1128 bc_num_parse(&n, val, (BcBigDig) ctxt->ibase); 1129 1130 // Set the negative. 1131 n.rdx = BC_NUM_NEG_VAL_NP(n, neg); 1132 1133 err: 1134 BC_FUNC_FOOTER(vm, e); 1135 BC_MAYBE_SETUP(ctxt, e, n, idx); 1136 1137 return idx; 1138 } 1139 1140 char* 1141 bcl_string(BclNumber n) 1142 { 1143 BcNum* nptr; 1144 char* str = NULL; 1145 BclContext ctxt; 1146 BcVm* vm = bcl_getspecific(); 1147 1148 BC_CHECK_CTXT_ASSERT(vm, ctxt); 1149 1150 if (BC_ERR(n.i >= ctxt->nums.len)) return str; 1151 1152 BC_FUNC_HEADER(vm, err); 1153 1154 assert(n.i < ctxt->nums.len); 1155 1156 nptr = BC_NUM(ctxt, n); 1157 1158 assert(nptr != NULL && nptr->num != NULL); 1159 1160 // Clear the buffer. 1161 bc_vec_popAll(&vm->out); 1162 1163 // Print to the buffer. 1164 bc_num_print(nptr, (BcBigDig) ctxt->obase, false); 1165 bc_vec_pushByte(&vm->out, '\0'); 1166 1167 // Just dup the string; the caller is responsible for it. 1168 str = bc_vm_strdup(vm->out.v); 1169 1170 err: 1171 1172 // Eat the operand. 1173 bcl_num_dtor(ctxt, n, nptr); 1174 1175 BC_FUNC_FOOTER_NO_ERR(vm); 1176 1177 return str; 1178 } 1179 1180 BclNumber 1181 bcl_irand(BclNumber a) 1182 { 1183 BclError e = BCL_ERROR_NONE; 1184 BcNum* aptr; 1185 BcNum b; 1186 BclNumber idx; 1187 BclContext ctxt; 1188 BcVm* vm = bcl_getspecific(); 1189 1190 BC_CHECK_CTXT(vm, ctxt); 1191 1192 BC_CHECK_NUM(ctxt, a); 1193 1194 BC_FUNC_HEADER(vm, err); 1195 1196 bc_vec_grow(&ctxt->nums, 1); 1197 1198 assert(a.i < ctxt->nums.len); 1199 1200 aptr = BC_NUM(ctxt, a); 1201 1202 assert(aptr != NULL && aptr->num != NULL); 1203 1204 // Clear and initialize the result. 1205 bc_num_clear(&b); 1206 bc_num_init(&b, BC_NUM_DEF_SIZE); 1207 1208 bc_num_irand(aptr, &b, &vm->rng); 1209 1210 err: 1211 1212 // Eat the operand. 1213 bcl_num_dtor(ctxt, a, aptr); 1214 1215 BC_FUNC_FOOTER(vm, e); 1216 BC_MAYBE_SETUP(ctxt, e, b, idx); 1217 1218 return idx; 1219 } 1220 1221 /** 1222 * Helps bcl_frand(). This is separate because the error handling is easier that 1223 * way. It is also easier to do ifrand that way. 1224 * @param b The return parameter. 1225 * @param places The number of decimal places to generate. 1226 */ 1227 static void 1228 bcl_frandHelper(BcNum* restrict b, size_t places) 1229 { 1230 BcNum exp, pow, ten; 1231 BcDig exp_digs[BC_NUM_BIGDIG_LOG10]; 1232 BcDig ten_digs[BC_NUM_BIGDIG_LOG10]; 1233 BcVm* vm = bcl_getspecific(); 1234 1235 // Set up temporaries. 1236 bc_num_setup(&exp, exp_digs, BC_NUM_BIGDIG_LOG10); 1237 bc_num_setup(&ten, ten_digs, BC_NUM_BIGDIG_LOG10); 1238 1239 ten.num[0] = 10; 1240 ten.len = 1; 1241 1242 bc_num_bigdig2num(&exp, (BcBigDig) places); 1243 1244 // Clear the temporary that might need to grow. 1245 bc_num_clear(&pow); 1246 1247 // Initialize the temporary that might need to grow. 1248 bc_num_init(&pow, bc_num_powReq(&ten, &exp, 0)); 1249 1250 BC_SETJMP(vm, err); 1251 1252 // Generate the number. 1253 bc_num_pow(&ten, &exp, &pow, 0); 1254 bc_num_irand(&pow, b, &vm->rng); 1255 1256 // Make the number entirely fraction. 1257 bc_num_shiftRight(b, places); 1258 1259 err: 1260 bc_num_free(&pow); 1261 BC_LONGJMP_CONT(vm); 1262 } 1263 1264 BclNumber 1265 bcl_frand(size_t places) 1266 { 1267 BclError e = BCL_ERROR_NONE; 1268 BcNum n; 1269 BclNumber idx; 1270 BclContext ctxt; 1271 BcVm* vm = bcl_getspecific(); 1272 1273 BC_CHECK_CTXT(vm, ctxt); 1274 1275 BC_FUNC_HEADER(vm, err); 1276 1277 bc_vec_grow(&ctxt->nums, 1); 1278 1279 // Clear and initialize the number. 1280 bc_num_clear(&n); 1281 bc_num_init(&n, BC_NUM_DEF_SIZE); 1282 1283 bcl_frandHelper(&n, places); 1284 1285 err: 1286 1287 BC_FUNC_FOOTER(vm, e); 1288 BC_MAYBE_SETUP(ctxt, e, n, idx); 1289 1290 return idx; 1291 } 1292 1293 /** 1294 * Helps bc_ifrand(). This is separate because error handling is easier that 1295 * way. 1296 * @param a The limit for bc_num_irand(). 1297 * @param b The return parameter. 1298 * @param places The number of decimal places to generate. 1299 */ 1300 static void 1301 bcl_ifrandHelper(BcNum* restrict a, BcNum* restrict b, size_t places) 1302 { 1303 BcNum ir, fr; 1304 BcVm* vm = bcl_getspecific(); 1305 1306 // Clear the integer and fractional numbers. 1307 bc_num_clear(&ir); 1308 bc_num_clear(&fr); 1309 1310 // Initialize the integer and fractional numbers. 1311 bc_num_init(&ir, BC_NUM_DEF_SIZE); 1312 bc_num_init(&fr, BC_NUM_DEF_SIZE); 1313 1314 BC_SETJMP(vm, err); 1315 1316 bc_num_irand(a, &ir, &vm->rng); 1317 bcl_frandHelper(&fr, places); 1318 1319 bc_num_add(&ir, &fr, b, 0); 1320 1321 err: 1322 bc_num_free(&fr); 1323 bc_num_free(&ir); 1324 BC_LONGJMP_CONT(vm); 1325 } 1326 1327 BclNumber 1328 bcl_ifrand(BclNumber a, size_t places) 1329 { 1330 BclError e = BCL_ERROR_NONE; 1331 BcNum* aptr; 1332 BcNum b; 1333 BclNumber idx; 1334 BclContext ctxt; 1335 BcVm* vm = bcl_getspecific(); 1336 1337 BC_CHECK_CTXT(vm, ctxt); 1338 BC_CHECK_NUM(ctxt, a); 1339 1340 BC_FUNC_HEADER(vm, err); 1341 1342 bc_vec_grow(&ctxt->nums, 1); 1343 1344 assert(a.i < ctxt->nums.len); 1345 1346 aptr = BC_NUM(ctxt, a); 1347 1348 assert(aptr != NULL && aptr->num != NULL); 1349 1350 // Clear and initialize the number. 1351 bc_num_clear(&b); 1352 bc_num_init(&b, BC_NUM_DEF_SIZE); 1353 1354 bcl_ifrandHelper(aptr, &b, places); 1355 1356 err: 1357 1358 // Eat the oprand. 1359 bcl_num_dtor(ctxt, a, aptr); 1360 1361 BC_FUNC_FOOTER(vm, e); 1362 BC_MAYBE_SETUP(ctxt, e, b, idx); 1363 1364 return idx; 1365 } 1366 1367 BclError 1368 bcl_rand_seedWithNum(BclNumber n) 1369 { 1370 BclError e = BCL_ERROR_NONE; 1371 BcNum* nptr; 1372 BclContext ctxt; 1373 BcVm* vm = bcl_getspecific(); 1374 1375 BC_CHECK_CTXT_ERR(vm, ctxt); 1376 BC_CHECK_NUM_ERR(ctxt, n); 1377 1378 BC_FUNC_HEADER(vm, err); 1379 1380 assert(n.i < ctxt->nums.len); 1381 1382 nptr = BC_NUM(ctxt, n); 1383 1384 assert(nptr != NULL && nptr->num != NULL); 1385 1386 bc_num_rng(nptr, &vm->rng); 1387 1388 err: 1389 BC_FUNC_FOOTER(vm, e); 1390 return e; 1391 } 1392 1393 BclError 1394 bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE]) 1395 { 1396 BclError e = BCL_ERROR_NONE; 1397 size_t i; 1398 ulong vals[BCL_SEED_ULONGS]; 1399 BcVm* vm = bcl_getspecific(); 1400 1401 BC_FUNC_HEADER(vm, err); 1402 1403 // Fill the array. 1404 for (i = 0; i < BCL_SEED_SIZE; ++i) 1405 { 1406 ulong val = ((ulong) seed[i]) 1407 << (((ulong) CHAR_BIT) * (i % sizeof(ulong))); 1408 vals[i / sizeof(long)] |= val; 1409 } 1410 1411 bc_rand_seed(&vm->rng, vals[0], vals[1], vals[2], vals[3]); 1412 1413 err: 1414 BC_FUNC_FOOTER(vm, e); 1415 return e; 1416 } 1417 1418 void 1419 bcl_rand_reseed(void) 1420 { 1421 BcVm* vm = bcl_getspecific(); 1422 1423 bc_rand_srand(bc_vec_top(&vm->rng.v)); 1424 } 1425 1426 BclNumber 1427 bcl_rand_seed2num(void) 1428 { 1429 BclError e = BCL_ERROR_NONE; 1430 BcNum n; 1431 BclNumber idx; 1432 BclContext ctxt; 1433 BcVm* vm = bcl_getspecific(); 1434 1435 BC_CHECK_CTXT(vm, ctxt); 1436 1437 BC_FUNC_HEADER(vm, err); 1438 1439 // Clear and initialize the number. 1440 bc_num_clear(&n); 1441 bc_num_init(&n, BC_NUM_DEF_SIZE); 1442 1443 bc_num_createFromRNG(&n, &vm->rng); 1444 1445 err: 1446 BC_FUNC_FOOTER(vm, e); 1447 BC_MAYBE_SETUP(ctxt, e, n, idx); 1448 1449 return idx; 1450 } 1451 1452 BclRandInt 1453 bcl_rand_int(void) 1454 { 1455 BcVm* vm = bcl_getspecific(); 1456 1457 return (BclRandInt) bc_rand_int(&vm->rng); 1458 } 1459 1460 BclRandInt 1461 bcl_rand_bounded(BclRandInt bound) 1462 { 1463 BcVm* vm = bcl_getspecific(); 1464 1465 if (bound <= 1) return 0; 1466 return (BclRandInt) bc_rand_bounded(&vm->rng, (BcRand) bound); 1467 } 1468 1469 #endif // BC_ENABLE_LIBRARY 1470