| /* |
| * ***************************************************************************** |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| * |
| * Copyright (c) 2018-2023 Gavin D. Howard and contributors. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| * ***************************************************************************** |
| * |
| * The public functions for libbc. |
| * |
| */ |
| |
| #if BC_ENABLE_LIBRARY |
| |
| #include <setjmp.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #include <bcl.h> |
| |
| #include <library.h> |
| #include <num.h> |
| #include <vm.h> |
| |
| #ifndef _WIN32 |
| #include <pthread.h> |
| #endif // _WIN32 |
| |
| // The asserts in this file are important to testing; in many cases, the test |
| // would not work without the asserts, so don't remove them without reason. |
| // |
| // Also, there are many uses of bc_num_clear() here; that is because numbers are |
| // being reused, and a clean slate is required. |
| // |
| // Also, there are a bunch of BC_UNSETJMP between calls to bc_num_init(). That |
| // is because locals are being initialized, and unlike bc proper, this code |
| // cannot assume that allocation failures are fatal. So we have to reset the |
| // jumps every time to ensure that the locals will be correct after jumping. |
| |
| #if BC_ENABLE_MEMCHECK |
| |
| BC_NORETURN void |
| bcl_invalidGeneration(void) |
| { |
| abort(); |
| } |
| |
| BC_NORETURN void |
| bcl_nonexistentNum(void) |
| { |
| abort(); |
| } |
| |
| BC_NORETURN void |
| bcl_numIdxOutOfRange(void) |
| { |
| abort(); |
| } |
| |
| #endif // BC_ENABLE_MEMCHECK |
| |
| static BclTls* tls = NULL; |
| static BclTls tls_real; |
| |
| BclError |
| bcl_start(void) |
| { |
| #ifndef _WIN32 |
| |
| int r; |
| |
| if (tls != NULL) return BCL_ERROR_NONE; |
| |
| r = pthread_key_create(&tls_real, NULL); |
| if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR; |
| |
| #else // _WIN32 |
| |
| if (tls != NULL) return BCL_ERROR_NONE; |
| |
| tls_real = TlsAlloc(); |
| if (BC_ERR(tls_real == TLS_OUT_OF_INDEXES)) |
| { |
| return BCL_ERROR_FATAL_ALLOC_ERR; |
| } |
| |
| #endif // _WIN32 |
| |
| tls = &tls_real; |
| |
| return BCL_ERROR_NONE; |
| } |
| |
| /** |
| * Sets the thread-specific data for the thread. |
| * @param vm The @a BcVm to set as the thread data. |
| * @return An error code, if any. |
| */ |
| static BclError |
| bcl_setspecific(BcVm* vm) |
| { |
| #ifndef _WIN32 |
| |
| int r; |
| |
| assert(tls != NULL); |
| |
| r = pthread_setspecific(tls_real, vm); |
| if (BC_ERR(r != 0)) return BCL_ERROR_FATAL_ALLOC_ERR; |
| |
| #else // _WIN32 |
| |
| bool r; |
| |
| assert(tls != NULL); |
| |
| r = TlsSetValue(tls_real, vm); |
| if (BC_ERR(!r)) return BCL_ERROR_FATAL_ALLOC_ERR; |
| |
| #endif // _WIN32 |
| |
| return BCL_ERROR_NONE; |
| } |
| |
| BcVm* |
| bcl_getspecific(void) |
| { |
| BcVm* vm; |
| |
| #ifndef _WIN32 |
| |
| vm = pthread_getspecific(tls_real); |
| |
| #else // _WIN32 |
| |
| vm = TlsGetValue(tls_real); |
| |
| #endif // _WIN32 |
| |
| return vm; |
| } |
| |
| BclError |
| bcl_init(void) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BcVm* vm; |
| |
| assert(tls != NULL); |
| |
| vm = bcl_getspecific(); |
| if (vm != NULL) |
| { |
| assert(vm->refs >= 1); |
| |
| vm->refs += 1; |
| |
| return e; |
| } |
| |
| vm = bc_vm_malloc(sizeof(BcVm)); |
| if (BC_ERR(vm == NULL)) return BCL_ERROR_FATAL_ALLOC_ERR; |
| |
| e = bcl_setspecific(vm); |
| if (BC_ERR(e != BCL_ERROR_NONE)) |
| { |
| free(vm); |
| return e; |
| } |
| |
| memset(vm, 0, sizeof(BcVm)); |
| |
| vm->refs += 1; |
| |
| assert(vm->refs == 1); |
| |
| // Setting these to NULL ensures that if an error occurs, we only free what |
| // is necessary. |
| vm->ctxts.v = NULL; |
| vm->jmp_bufs.v = NULL; |
| vm->out.v = NULL; |
| |
| vm->abrt = false; |
| vm->leading_zeroes = false; |
| vm->digit_clamp = true; |
| |
| // The jmp_bufs always has to be initialized first. |
| bc_vec_init(&vm->jmp_bufs, sizeof(sigjmp_buf), BC_DTOR_NONE); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| bc_vm_init(); |
| |
| bc_vec_init(&vm->ctxts, sizeof(BclContext), BC_DTOR_NONE); |
| bc_vec_init(&vm->out, sizeof(uchar), BC_DTOR_NONE); |
| |
| // We need to seed this in case /dev/random and /dev/urandom don't work. |
| srand((unsigned int) time(NULL)); |
| bc_rand_init(&vm->rng); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| |
| // This is why we had to set them to NULL. |
| if (BC_ERR(vm != NULL && vm->err)) |
| { |
| if (vm->out.v != NULL) bc_vec_free(&vm->out); |
| if (vm->jmp_bufs.v != NULL) bc_vec_free(&vm->jmp_bufs); |
| if (vm->ctxts.v != NULL) bc_vec_free(&vm->ctxts); |
| bcl_setspecific(NULL); |
| free(vm); |
| } |
| |
| return e; |
| } |
| |
| BclError |
| bcl_pushContext(BclContext ctxt) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| bc_vec_push(&vm->ctxts, &ctxt); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| return e; |
| } |
| |
| void |
| bcl_popContext(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| if (vm->ctxts.len) bc_vec_pop(&vm->ctxts); |
| } |
| |
| static BclContext |
| bcl_contextHelper(BcVm* vm) |
| { |
| if (!vm->ctxts.len) return NULL; |
| return *((BclContext*) bc_vec_top(&vm->ctxts)); |
| } |
| |
| BclContext |
| bcl_context(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| return bcl_contextHelper(vm); |
| } |
| |
| void |
| bcl_free(void) |
| { |
| size_t i; |
| BcVm* vm = bcl_getspecific(); |
| |
| vm->refs -= 1; |
| if (vm->refs) return; |
| |
| bc_rand_free(&vm->rng); |
| bc_vec_free(&vm->out); |
| |
| for (i = 0; i < vm->ctxts.len; ++i) |
| { |
| BclContext ctxt = *((BclContext*) bc_vec_item(&vm->ctxts, i)); |
| bcl_ctxt_free(ctxt); |
| } |
| |
| bc_vec_free(&vm->ctxts); |
| |
| bc_vm_atexit(); |
| |
| free(vm); |
| bcl_setspecific(NULL); |
| } |
| |
| void |
| bcl_end(void) |
| { |
| #ifndef _WIN32 |
| |
| // We ignore the return value. |
| pthread_key_delete(tls_real); |
| |
| #else // _WIN32 |
| |
| // We ignore the return value. |
| TlsFree(tls_real); |
| |
| #endif // _WIN32 |
| |
| tls = NULL; |
| } |
| |
| void |
| bcl_gc(void) |
| { |
| bc_vm_freeTemps(); |
| } |
| |
| bool |
| bcl_abortOnFatalError(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| return vm->abrt; |
| } |
| |
| void |
| bcl_setAbortOnFatalError(bool abrt) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| vm->abrt = abrt; |
| } |
| |
| bool |
| bcl_leadingZeroes(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| return vm->leading_zeroes; |
| } |
| |
| void |
| bcl_setLeadingZeroes(bool leadingZeroes) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| vm->leading_zeroes = leadingZeroes; |
| } |
| |
| bool |
| bcl_digitClamp(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| return vm->digit_clamp; |
| } |
| |
| void |
| bcl_setDigitClamp(bool digitClamp) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| vm->digit_clamp = digitClamp; |
| } |
| |
| BclContext |
| bcl_ctxt_create(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| BclContext ctxt = NULL; |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| // We want the context to be free of any interference of other parties, so |
| // malloc() is appropriate here. |
| ctxt = bc_vm_malloc(sizeof(BclCtxt)); |
| |
| bc_vec_init(&ctxt->nums, sizeof(BclNum), BC_DTOR_BCL_NUM); |
| bc_vec_init(&ctxt->free_nums, sizeof(BclNumber), BC_DTOR_NONE); |
| |
| ctxt->scale = 0; |
| ctxt->ibase = 10; |
| ctxt->obase = 10; |
| |
| err: |
| |
| if (BC_ERR(vm->err && ctxt != NULL)) |
| { |
| if (ctxt->nums.v != NULL) bc_vec_free(&ctxt->nums); |
| free(ctxt); |
| ctxt = NULL; |
| } |
| |
| BC_FUNC_FOOTER_NO_ERR(vm); |
| |
| return ctxt; |
| } |
| |
| void |
| bcl_ctxt_free(BclContext ctxt) |
| { |
| bc_vec_free(&ctxt->free_nums); |
| bc_vec_free(&ctxt->nums); |
| free(ctxt); |
| } |
| |
| void |
| bcl_ctxt_freeNums(BclContext ctxt) |
| { |
| bc_vec_popAll(&ctxt->nums); |
| bc_vec_popAll(&ctxt->free_nums); |
| } |
| |
| size_t |
| bcl_ctxt_scale(BclContext ctxt) |
| { |
| return ctxt->scale; |
| } |
| |
| void |
| bcl_ctxt_setScale(BclContext ctxt, size_t scale) |
| { |
| ctxt->scale = scale; |
| } |
| |
| size_t |
| bcl_ctxt_ibase(BclContext ctxt) |
| { |
| return ctxt->ibase; |
| } |
| |
| void |
| bcl_ctxt_setIbase(BclContext ctxt, size_t ibase) |
| { |
| if (ibase < BC_NUM_MIN_BASE) ibase = BC_NUM_MIN_BASE; |
| else if (ibase > BC_NUM_MAX_IBASE) ibase = BC_NUM_MAX_IBASE; |
| ctxt->ibase = ibase; |
| } |
| |
| size_t |
| bcl_ctxt_obase(BclContext ctxt) |
| { |
| return ctxt->obase; |
| } |
| |
| void |
| bcl_ctxt_setObase(BclContext ctxt, size_t obase) |
| { |
| ctxt->obase = obase; |
| } |
| |
| BclError |
| bcl_err(BclNumber n) |
| { |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ERR(vm, ctxt); |
| |
| // We need to clear the top byte in memcheck mode. We can do this because |
| // the parameter is a copy. |
| BCL_CLEAR_GEN(n); |
| |
| // Errors are encoded as (0 - error_code). If the index is in that range, it |
| // is an encoded error. |
| if (n.i >= ctxt->nums.len) |
| { |
| if (n.i > 0 - (size_t) BCL_ERROR_NELEMS) return (BclError) (0 - n.i); |
| else return BCL_ERROR_INVALID_NUM; |
| } |
| else return BCL_ERROR_NONE; |
| } |
| |
| /** |
| * Inserts a BcNum into a context's list of numbers. |
| * @param ctxt The context to insert into. |
| * @param n The BcNum to insert. |
| * @return The resulting BclNumber from the insert. |
| */ |
| static BclNumber |
| bcl_num_insert(BclContext ctxt, BclNum* restrict n) |
| { |
| BclNumber idx; |
| |
| // If there is a free spot... |
| if (ctxt->free_nums.len) |
| { |
| BclNum* ptr; |
| |
| // Get the index of the free spot and remove it. |
| idx = *((BclNumber*) bc_vec_top(&ctxt->free_nums)); |
| bc_vec_pop(&ctxt->free_nums); |
| |
| // Copy the number into the spot. |
| ptr = bc_vec_item(&ctxt->nums, idx.i); |
| |
| memcpy(BCL_NUM_NUM(ptr), n, sizeof(BcNum)); |
| |
| #if BC_ENABLE_MEMCHECK |
| |
| ptr->gen_idx += 1; |
| |
| if (ptr->gen_idx == UCHAR_MAX) |
| { |
| ptr->gen_idx = 0; |
| } |
| |
| idx.i |= (ptr->gen_idx << ((sizeof(size_t) - 1) * CHAR_BIT)); |
| |
| #endif // BC_ENABLE_MEMCHECK |
| } |
| else |
| { |
| #if BC_ENABLE_MEMCHECK |
| n->gen_idx = 0; |
| #endif // BC_ENABLE_MEMCHECK |
| |
| // Just push the number onto the vector. |
| idx.i = ctxt->nums.len; |
| bc_vec_push(&ctxt->nums, n); |
| } |
| |
| return idx; |
| } |
| |
| BclNumber |
| bcl_num_create(void) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum n; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, n, idx); |
| |
| return idx; |
| } |
| |
| /** |
| * Destructs a number and marks its spot as free. |
| * @param ctxt The context. |
| * @param n The index of the number. |
| * @param num The number to destroy. |
| */ |
| static void |
| bcl_num_dtor(BclContext ctxt, BclNumber n, BclNum* restrict num) |
| { |
| assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); |
| |
| BCL_CLEAR_GEN(n); |
| |
| bcl_num_destruct(num); |
| bc_vec_push(&ctxt->free_nums, &n); |
| |
| #if BC_ENABLE_MEMCHECK |
| num->n.num = NULL; |
| #endif // BC_ENABLE_MEMCHECK |
| } |
| |
| void |
| bcl_num_free(BclNumber n) |
| { |
| BclNum* num; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(BCL_NO_GEN(n) < ctxt->nums.len); |
| |
| num = BCL_NUM(ctxt, n); |
| |
| bcl_num_dtor(ctxt, n, num); |
| } |
| |
| BclError |
| bcl_copy(BclNumber d, BclNumber s) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* dest; |
| BclNum* src; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ERR(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, d); |
| BCL_CHECK_NUM_VALID(ctxt, s); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| assert(BCL_NO_GEN(d) < ctxt->nums.len); |
| assert(BCL_NO_GEN(s) < ctxt->nums.len); |
| |
| dest = BCL_NUM(ctxt, d); |
| src = BCL_NUM(ctxt, s); |
| |
| assert(dest != NULL && src != NULL); |
| assert(BCL_NUM_ARRAY(dest) != NULL && BCL_NUM_ARRAY(src) != NULL); |
| |
| bc_num_copy(BCL_NUM_NUM(dest), BCL_NUM_NUM(src)); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| |
| return e; |
| } |
| |
| BclNumber |
| bcl_dup(BclNumber s) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum *src, dest; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, s); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(BCL_NO_GEN(s) < ctxt->nums.len); |
| |
| src = BCL_NUM(ctxt, s); |
| |
| assert(src != NULL && BCL_NUM_NUM(src) != NULL); |
| |
| // Copy the number. |
| bc_num_clear(BCL_NUM_NUM(&dest)); |
| bc_num_createCopy(BCL_NUM_NUM(&dest), BCL_NUM_NUM(src)); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, dest, idx); |
| |
| return idx; |
| } |
| |
| void |
| bcl_num_destruct(void* num) |
| { |
| BclNum* n = (BclNum*) num; |
| |
| assert(n != NULL); |
| |
| if (BCL_NUM_ARRAY(n) == NULL) return; |
| |
| bc_num_free(BCL_NUM_NUM(n)); |
| bc_num_clear(BCL_NUM_NUM(n)); |
| } |
| |
| bool |
| bcl_num_neg(BclNumber n) |
| { |
| BclNum* num; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| num = BCL_NUM(ctxt, n); |
| |
| assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); |
| |
| return BC_NUM_NEG(BCL_NUM_NUM(num)) != 0; |
| } |
| |
| void |
| bcl_num_setNeg(BclNumber n, bool neg) |
| { |
| BclNum* num; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| num = BCL_NUM(ctxt, n); |
| |
| assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); |
| |
| BCL_NUM_NUM(num)->rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM(num), neg); |
| } |
| |
| size_t |
| bcl_num_scale(BclNumber n) |
| { |
| BclNum* num; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| num = BCL_NUM(ctxt, n); |
| |
| assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); |
| |
| return bc_num_scale(BCL_NUM_NUM(num)); |
| } |
| |
| BclError |
| bcl_num_setScale(BclNumber n, size_t scale) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* nptr; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ERR(vm, ctxt); |
| |
| BC_CHECK_NUM_ERR(ctxt, n); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| nptr = BCL_NUM(ctxt, n); |
| |
| assert(nptr != NULL && BCL_NUM_ARRAY(nptr) != NULL); |
| |
| if (scale > BCL_NUM_NUM(nptr)->scale) |
| { |
| bc_num_extend(BCL_NUM_NUM(nptr), scale - BCL_NUM_NUM(nptr)->scale); |
| } |
| else if (scale < BCL_NUM_NUM(nptr)->scale) |
| { |
| bc_num_truncate(BCL_NUM_NUM(nptr), BCL_NUM_NUM(nptr)->scale - scale); |
| } |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| |
| return e; |
| } |
| |
| size_t |
| bcl_num_len(BclNumber n) |
| { |
| BclNum* num; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| num = BCL_NUM(ctxt, n); |
| |
| assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); |
| |
| return bc_num_len(BCL_NUM_NUM(num)); |
| } |
| |
| BclError |
| bcl_bigdig(BclNumber n, BclBigDig* result) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* num; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ERR(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| assert(n.i < ctxt->nums.len); |
| assert(result != NULL); |
| |
| num = BCL_NUM(ctxt, n); |
| |
| assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); |
| |
| *result = bc_num_bigdig(BCL_NUM_NUM(num)); |
| |
| err: |
| |
| bcl_num_dtor(ctxt, n, num); |
| BC_FUNC_FOOTER(vm, e); |
| |
| return e; |
| } |
| |
| BclNumber |
| bcl_bigdig2num(BclBigDig val) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum n; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| bc_num_createFromBigdig(BCL_NUM_NUM_NP(n), val); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, n, idx); |
| |
| return idx; |
| } |
| |
| /** |
| * Sets up and executes a binary operator operation. |
| * @param a The first operand. |
| * @param b The second operand. |
| * @param op The operation. |
| * @param req The function to get the size of the result for preallocation. |
| * @return The result of the operation. |
| */ |
| static BclNumber |
| bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op, |
| const BcNumBinaryOpReq req) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* aptr; |
| BclNum* bptr; |
| BclNum c; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_CHECK_NUM(ctxt, a); |
| BC_CHECK_NUM(ctxt, b); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); |
| |
| aptr = BCL_NUM(ctxt, a); |
| bptr = BCL_NUM(ctxt, b); |
| |
| assert(aptr != NULL && bptr != NULL); |
| assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL); |
| |
| // Clear and initialize the result. |
| bc_num_clear(BCL_NUM_NUM_NP(c)); |
| bc_num_init(BCL_NUM_NUM_NP(c), |
| req(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale)); |
| |
| op(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM_NP(c), ctxt->scale); |
| |
| err: |
| |
| // Eat the operands. |
| bcl_num_dtor(ctxt, a, aptr); |
| if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr); |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, c, idx); |
| |
| return idx; |
| } |
| |
| BclNumber |
| bcl_add(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_add, bc_num_addReq); |
| } |
| |
| BclNumber |
| bcl_sub(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_sub, bc_num_addReq); |
| } |
| |
| BclNumber |
| bcl_mul(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_mul, bc_num_mulReq); |
| } |
| |
| BclNumber |
| bcl_div(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_div, bc_num_divReq); |
| } |
| |
| BclNumber |
| bcl_mod(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_mod, bc_num_divReq); |
| } |
| |
| BclNumber |
| bcl_pow(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_pow, bc_num_powReq); |
| } |
| |
| BclNumber |
| bcl_lshift(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq); |
| } |
| |
| BclNumber |
| bcl_rshift(BclNumber a, BclNumber b) |
| { |
| return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq); |
| } |
| |
| BclNumber |
| bcl_sqrt(BclNumber a) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* aptr; |
| BclNum b; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_CHECK_NUM(ctxt, a); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(a.i < ctxt->nums.len); |
| |
| aptr = BCL_NUM(ctxt, a); |
| |
| bc_num_sqrt(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), ctxt->scale); |
| |
| err: |
| |
| bcl_num_dtor(ctxt, a, aptr); |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, b, idx); |
| |
| return idx; |
| } |
| |
| BclError |
| bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d) |
| { |
| BclError e = BCL_ERROR_NONE; |
| size_t req; |
| BclNum* aptr; |
| BclNum* bptr; |
| BclNum cnum, dnum; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ERR(vm, ctxt); |
| |
| BC_CHECK_NUM_ERR(ctxt, a); |
| BC_CHECK_NUM_ERR(ctxt, b); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(c != NULL && d != NULL); |
| |
| aptr = BCL_NUM(ctxt, a); |
| bptr = BCL_NUM(ctxt, b); |
| |
| assert(aptr != NULL && bptr != NULL); |
| assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL); |
| |
| bc_num_clear(BCL_NUM_NUM_NP(cnum)); |
| bc_num_clear(BCL_NUM_NUM_NP(dnum)); |
| |
| req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale); |
| |
| // Initialize the numbers. |
| bc_num_init(BCL_NUM_NUM_NP(cnum), req); |
| BC_UNSETJMP(vm); |
| BC_SETJMP(vm, err); |
| bc_num_init(BCL_NUM_NUM_NP(dnum), req); |
| |
| bc_num_divmod(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM_NP(cnum), |
| BCL_NUM_NUM_NP(dnum), ctxt->scale); |
| |
| err: |
| |
| // Eat the operands. |
| bcl_num_dtor(ctxt, a, aptr); |
| if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr); |
| |
| // If there was an error... |
| if (BC_ERR(vm->err)) |
| { |
| // Free the results. |
| if (BCL_NUM_ARRAY_NP(cnum) != NULL) bc_num_free(&cnum); |
| if (BCL_NUM_ARRAY_NP(cnum) != NULL) bc_num_free(&dnum); |
| |
| // Make sure the return values are invalid. |
| c->i = 0 - (size_t) BCL_ERROR_INVALID_NUM; |
| d->i = c->i; |
| |
| BC_FUNC_FOOTER(vm, e); |
| } |
| else |
| { |
| BC_FUNC_FOOTER(vm, e); |
| |
| // Insert the results into the context. |
| *c = bcl_num_insert(ctxt, &cnum); |
| *d = bcl_num_insert(ctxt, &dnum); |
| } |
| |
| return e; |
| } |
| |
| BclNumber |
| bcl_modexp(BclNumber a, BclNumber b, BclNumber c) |
| { |
| BclError e = BCL_ERROR_NONE; |
| size_t req; |
| BclNum* aptr; |
| BclNum* bptr; |
| BclNum* cptr; |
| BclNum d; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_CHECK_NUM(ctxt, a); |
| BC_CHECK_NUM(ctxt, b); |
| BC_CHECK_NUM(ctxt, c); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); |
| assert(c.i < ctxt->nums.len); |
| |
| aptr = BCL_NUM(ctxt, a); |
| bptr = BCL_NUM(ctxt, b); |
| cptr = BCL_NUM(ctxt, c); |
| |
| assert(aptr != NULL && bptr != NULL && cptr != NULL); |
| assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr) != NULL && |
| BCL_NUM_NUM(cptr) != NULL); |
| |
| // Prepare the result. |
| bc_num_clear(BCL_NUM_NUM_NP(d)); |
| |
| req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(cptr), 0); |
| |
| // Initialize the result. |
| bc_num_init(BCL_NUM_NUM_NP(d), req); |
| |
| bc_num_modexp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), BCL_NUM_NUM(cptr), |
| BCL_NUM_NUM_NP(d)); |
| |
| err: |
| |
| // Eat the operands. |
| bcl_num_dtor(ctxt, a, aptr); |
| if (b.i != a.i) bcl_num_dtor(ctxt, b, bptr); |
| if (c.i != a.i && c.i != b.i) bcl_num_dtor(ctxt, c, cptr); |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, d, idx); |
| |
| return idx; |
| } |
| |
| ssize_t |
| bcl_cmp(BclNumber a, BclNumber b) |
| { |
| BclNum* aptr; |
| BclNum* bptr; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, a); |
| BCL_CHECK_NUM_VALID(ctxt, b); |
| |
| assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); |
| |
| aptr = BCL_NUM(ctxt, a); |
| bptr = BCL_NUM(ctxt, b); |
| |
| assert(aptr != NULL && bptr != NULL); |
| assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr)); |
| |
| return bc_num_cmp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr)); |
| } |
| |
| void |
| bcl_zero(BclNumber n) |
| { |
| BclNum* nptr; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| nptr = BCL_NUM(ctxt, n); |
| |
| assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); |
| |
| bc_num_zero(BCL_NUM_NUM(nptr)); |
| } |
| |
| void |
| bcl_one(BclNumber n) |
| { |
| BclNum* nptr; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| nptr = BCL_NUM(ctxt, n); |
| |
| assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); |
| |
| bc_num_one(BCL_NUM_NUM(nptr)); |
| } |
| |
| BclNumber |
| bcl_parse(const char* restrict val) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum n; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| bool neg; |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(val != NULL); |
| |
| // We have to take care of negative here because bc's number parsing does |
| // not. |
| neg = (val[0] == '-'); |
| |
| if (neg) val += 1; |
| |
| if (!bc_num_strValid(val)) |
| { |
| vm->err = BCL_ERROR_PARSE_INVALID_STR; |
| goto err; |
| } |
| |
| // Clear and initialize the number. |
| bc_num_clear(BCL_NUM_NUM_NP(n)); |
| bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); |
| |
| bc_num_parse(BCL_NUM_NUM_NP(n), val, (BcBigDig) ctxt->ibase); |
| |
| // Set the negative. |
| #if BC_ENABLE_MEMCHECK |
| n.n.rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM_NP(n), neg); |
| #else // BC_ENABLE_MEMCHECK |
| n.rdx = BC_NUM_NEG_VAL_NP(n, neg); |
| #endif // BC_ENABLE_MEMCHECK |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, n, idx); |
| |
| return idx; |
| } |
| |
| char* |
| bcl_string(BclNumber n) |
| { |
| BclNum* nptr; |
| char* str = NULL; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ASSERT(vm, ctxt); |
| |
| BCL_CHECK_NUM_VALID(ctxt, n); |
| |
| if (BC_ERR(n.i >= ctxt->nums.len)) return str; |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| nptr = BCL_NUM(ctxt, n); |
| |
| assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); |
| |
| // Clear the buffer. |
| bc_vec_popAll(&vm->out); |
| |
| // Print to the buffer. |
| bc_num_print(BCL_NUM_NUM(nptr), (BcBigDig) ctxt->obase, false); |
| bc_vec_pushByte(&vm->out, '\0'); |
| |
| // Just dup the string; the caller is responsible for it. |
| str = bc_vm_strdup(vm->out.v); |
| |
| err: |
| |
| // Eat the operand. |
| bcl_num_dtor(ctxt, n, nptr); |
| |
| BC_FUNC_FOOTER_NO_ERR(vm); |
| |
| return str; |
| } |
| |
| BclNumber |
| bcl_irand(BclNumber a) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* aptr; |
| BclNum b; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_CHECK_NUM(ctxt, a); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(a.i < ctxt->nums.len); |
| |
| aptr = BCL_NUM(ctxt, a); |
| |
| assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL); |
| |
| // Clear and initialize the result. |
| bc_num_clear(BCL_NUM_NUM_NP(b)); |
| bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE); |
| |
| bc_num_irand(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), &vm->rng); |
| |
| err: |
| |
| // Eat the operand. |
| bcl_num_dtor(ctxt, a, aptr); |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, b, idx); |
| |
| return idx; |
| } |
| |
| /** |
| * Helps bcl_frand(). This is separate because the error handling is easier that |
| * way. It is also easier to do ifrand that way. |
| * @param b The return parameter. |
| * @param places The number of decimal places to generate. |
| */ |
| static void |
| bcl_frandHelper(BcNum* restrict b, size_t places) |
| { |
| BcNum exp, pow, ten; |
| BcDig exp_digs[BC_NUM_BIGDIG_LOG10]; |
| BcDig ten_digs[BC_NUM_BIGDIG_LOG10]; |
| BcVm* vm = bcl_getspecific(); |
| |
| // Set up temporaries. |
| bc_num_setup(&exp, exp_digs, BC_NUM_BIGDIG_LOG10); |
| bc_num_setup(&ten, ten_digs, BC_NUM_BIGDIG_LOG10); |
| |
| ten.num[0] = 10; |
| ten.len = 1; |
| |
| bc_num_bigdig2num(&exp, (BcBigDig) places); |
| |
| // Clear the temporary that might need to grow. |
| bc_num_clear(&pow); |
| |
| // Initialize the temporary that might need to grow. |
| bc_num_init(&pow, bc_num_powReq(&ten, &exp, 0)); |
| |
| BC_SETJMP(vm, err); |
| |
| // Generate the number. |
| bc_num_pow(&ten, &exp, &pow, 0); |
| bc_num_irand(&pow, b, &vm->rng); |
| |
| // Make the number entirely fraction. |
| bc_num_shiftRight(b, places); |
| |
| err: |
| |
| bc_num_free(&pow); |
| BC_LONGJMP_CONT(vm); |
| } |
| |
| BclNumber |
| bcl_frand(size_t places) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum n; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| // Clear and initialize the number. |
| bc_num_clear(BCL_NUM_NUM_NP(n)); |
| bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); |
| |
| bcl_frandHelper(BCL_NUM_NUM_NP(n), places); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, n, idx); |
| |
| return idx; |
| } |
| |
| /** |
| * Helps bc_ifrand(). This is separate because error handling is easier that |
| * way. |
| * @param a The limit for bc_num_irand(). |
| * @param b The return parameter. |
| * @param places The number of decimal places to generate. |
| */ |
| static void |
| bcl_ifrandHelper(BcNum* restrict a, BcNum* restrict b, size_t places) |
| { |
| BcNum ir, fr; |
| BcVm* vm = bcl_getspecific(); |
| |
| // Clear the integer and fractional numbers. |
| bc_num_clear(&ir); |
| bc_num_clear(&fr); |
| |
| // Initialize the integer and fractional numbers. |
| bc_num_init(&ir, BC_NUM_DEF_SIZE); |
| bc_num_init(&fr, BC_NUM_DEF_SIZE); |
| |
| BC_SETJMP(vm, err); |
| |
| bc_num_irand(a, &ir, &vm->rng); |
| bcl_frandHelper(&fr, places); |
| |
| bc_num_add(&ir, &fr, b, 0); |
| |
| err: |
| |
| bc_num_free(&fr); |
| bc_num_free(&ir); |
| BC_LONGJMP_CONT(vm); |
| } |
| |
| BclNumber |
| bcl_ifrand(BclNumber a, size_t places) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* aptr; |
| BclNum b; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| BC_CHECK_NUM(ctxt, a); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| BCL_GROW_NUMS(ctxt); |
| |
| assert(a.i < ctxt->nums.len); |
| |
| aptr = BCL_NUM(ctxt, a); |
| |
| assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL); |
| |
| // Clear and initialize the number. |
| bc_num_clear(BCL_NUM_NUM_NP(b)); |
| bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE); |
| |
| bcl_ifrandHelper(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), places); |
| |
| err: |
| |
| // Eat the oprand. |
| bcl_num_dtor(ctxt, a, aptr); |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, b, idx); |
| |
| return idx; |
| } |
| |
| BclError |
| bcl_rand_seedWithNum(BclNumber n) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum* nptr; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT_ERR(vm, ctxt); |
| BC_CHECK_NUM_ERR(ctxt, n); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| assert(n.i < ctxt->nums.len); |
| |
| nptr = BCL_NUM(ctxt, n); |
| |
| assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); |
| |
| bc_num_rng(BCL_NUM_NUM(nptr), &vm->rng); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| |
| return e; |
| } |
| |
| BclError |
| bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE]) |
| { |
| BclError e = BCL_ERROR_NONE; |
| size_t i; |
| ulong vals[BCL_SEED_ULONGS]; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| // Fill the array. |
| for (i = 0; i < BCL_SEED_SIZE; ++i) |
| { |
| ulong val = ((ulong) seed[i]) |
| << (((ulong) CHAR_BIT) * (i % sizeof(ulong))); |
| vals[i / sizeof(long)] |= val; |
| } |
| |
| bc_rand_seed(&vm->rng, vals[0], vals[1], vals[2], vals[3]); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| |
| return e; |
| } |
| |
| void |
| bcl_rand_reseed(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| bc_rand_srand(bc_vec_top(&vm->rng.v)); |
| } |
| |
| BclNumber |
| bcl_rand_seed2num(void) |
| { |
| BclError e = BCL_ERROR_NONE; |
| BclNum n; |
| BclNumber idx; |
| BclContext ctxt; |
| BcVm* vm = bcl_getspecific(); |
| |
| BC_CHECK_CTXT(vm, ctxt); |
| |
| BC_FUNC_HEADER(vm, err); |
| |
| // Clear and initialize the number. |
| bc_num_clear(BCL_NUM_NUM_NP(n)); |
| bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); |
| |
| bc_num_createFromRNG(BCL_NUM_NUM_NP(n), &vm->rng); |
| |
| err: |
| |
| BC_FUNC_FOOTER(vm, e); |
| BC_MAYBE_SETUP(ctxt, e, n, idx); |
| |
| return idx; |
| } |
| |
| BclRandInt |
| bcl_rand_int(void) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| return (BclRandInt) bc_rand_int(&vm->rng); |
| } |
| |
| BclRandInt |
| bcl_rand_bounded(BclRandInt bound) |
| { |
| BcVm* vm = bcl_getspecific(); |
| |
| if (bound <= 1) return 0; |
| return (BclRandInt) bc_rand_bounded(&vm->rng, (BcRand) bound); |
| } |
| |
| #endif // BC_ENABLE_LIBRARY |