diff options
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | NEWS.md | 19 | ||||
-rwxr-xr-x | configure.sh | 2 | ||||
-rw-r--r-- | include/bcl.h | 51 | ||||
-rw-r--r-- | include/library.h | 210 | ||||
-rw-r--r-- | include/program.h | 210 | ||||
-rw-r--r-- | include/version.h | 2 | ||||
-rw-r--r-- | include/vm.h | 4 | ||||
-rw-r--r-- | manuals/bcl.3 | 422 | ||||
-rw-r--r-- | manuals/bcl.3.md | 320 | ||||
-rw-r--r-- | src/data.c | 75 | ||||
-rw-r--r-- | src/library.c | 739 | ||||
-rw-r--r-- | src/program.c | 13 | ||||
-rw-r--r-- | src/vm.c | 2 | ||||
-rw-r--r-- | tests/bcl.c | 52 | ||||
-rwxr-xr-x | tests/read.sh | 13 |
16 files changed, 1693 insertions, 443 deletions
diff --git a/Makefile.in b/Makefile.in index 89ddb7589c47..f936fc2c6de6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -536,7 +536,6 @@ clean:%%CLEAN_PREREQS%% @$(RM) -f $(BC_HELP_C) $(BC_HELP_O) @$(RM) -f $(DC_HELP_C) $(DC_HELP_O) @$(RM) -fr vs/bin/ vs/lib/ - @$(RM) -f $(BCL_PC) clean_benchmarks: @printf 'Cleaning benchmarks...\n' @@ -550,6 +549,7 @@ clean_config: clean clean_benchmarks @$(RM) -f $(BC_MD) $(BC_MANPAGE) @$(RM) -f $(DC_MD) $(DC_MANPAGE) @$(RM) -f compile_commands.json + @$(RM) -f $(BCL_PC) clean_coverage: @printf 'Cleaning coverage files...\n' @@ -1,5 +1,24 @@ # News +## 6.4.0 + +This is a production release that fixes a `read()`/`?` bug and adds features to +`bcl`. + +The bug was that multiple read calls could repeat old data. + +The new features in `bcl` are functions to preserve `BclNumber` arguments and +not free them. + +***WARNING for `bcl` Users***: The `bcl_rand_seedWithNum()` function used to not +consume its arguments. Now it does. This change could have made this version +`7.0.0`, but I'm 99.9% confident that there are no `bcl` users, or if there are, +they probably don't use the PRNG. So I took a risk and didn't update the major +version. + +`bcl` now includes more capacity to check for invalid numbers when built to run +under Valgrind. + ## 6.3.1 This is a production release that fixes a `bc` dependency loop for minimal diff --git a/configure.sh b/configure.sh index 3ada5298e9ed..021d30807ffb 100755 --- a/configure.sh +++ b/configure.sh @@ -1801,7 +1801,7 @@ if [ "$library" -ne 0 ]; then contents=$(replace "$contents" "LIBDIR" "$LIBDIR") contents=$(replace "$contents" "VERSION" "$version") - printf '%s\n' "$contents" > "./bcl.pc" + printf '%s\n' "$contents" > "$scriptdir/bcl.pc" pkg_config_install="\$(SAFE_INSTALL) \$(PC_INSTALL_ARGS) \"\$(BCL_PC)\" \"\$(DESTDIR)\$(PC_PATH)/\$(BCL_PC)\"" pkg_config_uninstall="\$(RM) -f \"\$(DESTDIR)\$(PC_PATH)/\$(BCL_PC)\"" diff --git a/include/bcl.h b/include/bcl.h index 253138231c66..0908e215182c 100644 --- a/include/bcl.h +++ b/include/bcl.h @@ -36,6 +36,9 @@ #ifndef BC_BCL_H #define BC_BCL_H +// TODO: Add a generation index when building with Valgrind to check for +// use-after-free's or double frees. + #include <stdbool.h> #include <stdlib.h> #include <limits.h> @@ -238,6 +241,9 @@ bcl_dup(BclNumber s); BclError bcl_bigdig(BclNumber n, BclBigDig* result); +BclError +bcl_bigdig_keep(BclNumber n, BclBigDig* result); + BclNumber bcl_bigdig2num(BclBigDig val); @@ -245,35 +251,68 @@ BclNumber bcl_add(BclNumber a, BclNumber b); BclNumber +bcl_add_keep(BclNumber a, BclNumber b); + +BclNumber bcl_sub(BclNumber a, BclNumber b); BclNumber +bcl_sub_keep(BclNumber a, BclNumber b); + +BclNumber bcl_mul(BclNumber a, BclNumber b); BclNumber +bcl_mul_keep(BclNumber a, BclNumber b); + +BclNumber bcl_div(BclNumber a, BclNumber b); BclNumber +bcl_div_keep(BclNumber a, BclNumber b); + +BclNumber bcl_mod(BclNumber a, BclNumber b); BclNumber +bcl_mod_keep(BclNumber a, BclNumber b); + +BclNumber bcl_pow(BclNumber a, BclNumber b); BclNumber +bcl_pow_keep(BclNumber a, BclNumber b); + +BclNumber bcl_lshift(BclNumber a, BclNumber b); BclNumber +bcl_lshift_keep(BclNumber a, BclNumber b); + +BclNumber bcl_rshift(BclNumber a, BclNumber b); BclNumber +bcl_rshift_keep(BclNumber a, BclNumber b); + +BclNumber bcl_sqrt(BclNumber a); +BclNumber +bcl_sqrt_keep(BclNumber a); + BclError bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d); +BclError +bcl_divmod_keep(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d); + BclNumber bcl_modexp(BclNumber a, BclNumber b, BclNumber c); +BclNumber +bcl_modexp_keep(BclNumber a, BclNumber b, BclNumber c); + ssize_t bcl_cmp(BclNumber a, BclNumber b); @@ -289,19 +328,31 @@ bcl_parse(const char* restrict val); char* bcl_string(BclNumber n); +char* +bcl_string_keep(BclNumber n); + BclNumber bcl_irand(BclNumber a); BclNumber +bcl_irand_keep(BclNumber a); + +BclNumber bcl_frand(size_t places); BclNumber bcl_ifrand(BclNumber a, size_t places); +BclNumber +bcl_ifrand_keep(BclNumber a, size_t places); + BclError bcl_rand_seedWithNum(BclNumber n); BclError +bcl_rand_seedWithNum_keep(BclNumber n); + +BclError bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE]); void diff --git a/include/library.h b/include/library.h index 76df91392da1..1edd3757444c 100644 --- a/include/library.h +++ b/include/library.h @@ -47,6 +47,145 @@ #include <num.h> #include <vm.h> +#if BC_ENABLE_MEMCHECK + +/** + * A typedef for Valgrind builds. This is to add a generation index for error + * checking. + */ +typedef struct BclNum +{ + /// The number. + BcNum n; + + /// The generation index. + size_t gen_idx; + +} BclNum; + +/** + * Clears the generation byte in a BclNumber and returns the value. + * @param n The BclNumber. + * @return The value of the index. + */ +#define BCL_NO_GEN(n) \ + ((n).i & ~(((size_t) UCHAR_MAX) << ((sizeof(size_t) - 1) * CHAR_BIT))) + +/** + * Gets the generation index in a BclNumber. + * @param n The BclNumber. + * @return The generation index. + */ +#define BCL_GET_GEN(n) ((n).i >> ((sizeof(size_t) - 1) * CHAR_BIT)) + +/** + * Turns a BclNumber into a BcNum. + * @param c The context. + * @param n The BclNumber. + */ +#define BCL_NUM(c, n) ((BclNum*) bc_vec_item(&(c)->nums, BCL_NO_GEN(n))) + +/** + * Clears the generation index top byte in the BclNumber. + * @param n The BclNumber. + */ +#define BCL_CLEAR_GEN(n) \ + do \ + { \ + (n).i &= ~(((size_t) UCHAR_MAX) << ((sizeof(size_t) - 1) * CHAR_BIT)); \ + } \ + while (0) + +#define BCL_CHECK_NUM_GEN(c, bn) \ + do \ + { \ + size_t gen_ = BCL_GET_GEN(bn); \ + BclNum* ptr_ = BCL_NUM(c, bn); \ + if (BCL_NUM_ARRAY(ptr_) == NULL) \ + { \ + bcl_nonexistentNum(); \ + } \ + if (gen_ != ptr_->gen_idx) \ + { \ + bcl_invalidGeneration(); \ + } \ + } \ + while (0) + +#define BCL_CHECK_NUM_VALID(c, bn) \ + do \ + { \ + size_t idx_ = BCL_NO_GEN(bn); \ + if ((c)->nums.len <= idx_) \ + { \ + bcl_numIdxOutOfRange(); \ + } \ + BCL_CHECK_NUM_GEN(c, bn); \ + } \ + while (0) + +/** + * Returns the limb array of the number. + * @param bn The number. + * @return The limb array. + */ +#define BCL_NUM_ARRAY(bn) ((bn)->n.num) + +/** + * Returns the limb array of the number for a non-pointer. + * @param bn The number. + * @return The limb array. + */ +#define BCL_NUM_ARRAY_NP(bn) ((bn).n.num) + +/** + * Returns the BcNum pointer. + * @param bn The number. + * @return The BcNum pointer. + */ +#define BCL_NUM_NUM(bn) (&(bn)->n) + +/** + * Returns the BcNum pointer for a non-pointer. + * @param bn The number. + * @return The BcNum pointer. + */ +#define BCL_NUM_NUM_NP(bn) (&(bn).n) + +// These functions only abort. They exist to give developers some idea of what +// went wrong when bugs are found, if they look at the Valgrind stack trace. + +BC_NORETURN void +bcl_invalidGeneration(void); + +BC_NORETURN void +bcl_nonexistentNum(void); + +BC_NORETURN void +bcl_numIdxOutOfRange(void); + +#else // BC_ENABLE_MEMCHECK + +/** + * A typedef for non-Valgrind builds. + */ +typedef BcNum BclNum; + +#define BCL_NO_GEN(n) ((n).i) +#define BCL_NUM(c, n) ((BclNum*) bc_vec_item(&(c)->nums, (n).i)) +#define BCL_CLEAR_GEN(n) ((void) (n)) + +#define BCL_CHECK_NUM_GEN(c, bn) +#define BCL_CHECK_NUM_VALID(c, n) + +#define BCL_NUM_ARRAY(bn) ((bn)->num) +#define BCL_NUM_ARRAY_NP(bn) ((bn).num) + +#define BCL_NUM_NUM(bn) (bn) +#define BCL_NUM_NUM_NP(bn) (&(bn)) + +#endif // BC_ENABLE_MEMCHECK + /** * A header that sets a jump. * @param vm The thread data. @@ -88,19 +227,19 @@ * idx. * @param c The context. * @param e The error. - * @param n The number. + * @param bn The number. * @param idx The idx to set as the return value. */ -#define BC_MAYBE_SETUP(c, e, n, idx) \ - do \ - { \ - if (BC_ERR((e) != BCL_ERROR_NONE)) \ - { \ - if ((n).num != NULL) bc_num_free(&(n)); \ - idx.i = 0 - (size_t) (e); \ - } \ - else idx = bcl_num_insert(c, &(n)); \ - } \ +#define BC_MAYBE_SETUP(c, e, bn, idx) \ + do \ + { \ + if (BC_ERR((e) != BCL_ERROR_NONE)) \ + { \ + if (BCL_NUM_ARRAY_NP(bn) != NULL) bc_num_free(BCL_NUM_NUM_NP(bn)); \ + idx.i = 0 - (size_t) (e); \ + } \ + else idx = bcl_num_insert(c, &(bn)); \ + } \ while (0) /** @@ -108,17 +247,17 @@ * is bad. * @param c The context. */ -#define BC_CHECK_CTXT(vm, c) \ - do \ - { \ - c = bcl_contextHelper(vm); \ - if (BC_ERR(c == NULL)) \ - { \ - BclNumber n_num; \ - n_num.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \ - return n_num; \ - } \ - } \ +#define BC_CHECK_CTXT(vm, c) \ + do \ + { \ + c = bcl_contextHelper(vm); \ + if (BC_ERR(c == NULL)) \ + { \ + BclNumber n_num_; \ + n_num_.i = 0 - (size_t) BCL_ERROR_INVALID_CONTEXT; \ + return n_num_; \ + } \ + } \ while (0) /** @@ -157,16 +296,18 @@ #define BC_CHECK_NUM(c, n) \ do \ { \ - if (BC_ERR((n).i >= (c)->nums.len)) \ + size_t no_gen_ = BCL_NO_GEN(n); \ + if (BC_ERR(no_gen_ >= (c)->nums.len)) \ { \ if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) return (n); \ else \ { \ - BclNumber n_num; \ - n_num.i = 0 - (size_t) BCL_ERROR_INVALID_NUM; \ - return n_num; \ + BclNumber n_num_; \ + n_num_.i = 0 - (size_t) BCL_ERROR_INVALID_NUM; \ + return n_num_; \ } \ } \ + BCL_CHECK_NUM_GEN(c, n); \ } \ while (0) @@ -181,7 +322,8 @@ #define BC_CHECK_NUM_ERR(c, n) \ do \ { \ - if (BC_ERR((n).i >= (c)->nums.len)) \ + size_t no_gen_ = BCL_NO_GEN(n); \ + if (BC_ERR(no_gen_ >= (c)->nums.len)) \ { \ if ((n).i > 0 - (size_t) BCL_ERROR_NELEMS) \ { \ @@ -189,17 +331,25 @@ } \ else return BCL_ERROR_INVALID_NUM; \ } \ + BCL_CHECK_NUM_GEN(c, n); \ } \ while (0) //clang-format on /** - * Turns a BclNumber into a BcNum. + * Grows the context's nums array if necessary. * @param c The context. - * @param n The BclNumber. */ -#define BC_NUM(c, n) ((BcNum*) bc_vec_item(&(c)->nums, (n).i)) +#define BCL_GROW_NUMS(c) \ + do \ + { \ + if ((c)->free_nums.len == 0) \ + { \ + bc_vec_grow(&((c)->nums), 1); \ + } \ + } \ + while (0) /** * Frees a BcNum for bcl. This is a destructor. diff --git a/include/program.h b/include/program.h index ff32d5db7760..1df753afad22 100644 --- a/include/program.h +++ b/include/program.h @@ -904,148 +904,82 @@ extern const char bc_program_esc_seqs[]; #if BC_ENABLE_EXTRA_MATH -#define BC_PROG_LBLS \ - static const void* const bc_program_inst_lbls[] = { \ - &&lbl_BC_INST_NEG, \ - &&lbl_BC_INST_BOOL_NOT, \ - &&lbl_BC_INST_TRUNC, \ - &&lbl_BC_INST_POWER, \ - &&lbl_BC_INST_MULTIPLY, \ - &&lbl_BC_INST_DIVIDE, \ - &&lbl_BC_INST_MODULUS, \ - &&lbl_BC_INST_PLUS, \ - &&lbl_BC_INST_MINUS, \ - &&lbl_BC_INST_PLACES, \ - &&lbl_BC_INST_LSHIFT, \ - &&lbl_BC_INST_RSHIFT, \ - &&lbl_BC_INST_REL_EQ, \ - &&lbl_BC_INST_REL_LE, \ - &&lbl_BC_INST_REL_GE, \ - &&lbl_BC_INST_REL_NE, \ - &&lbl_BC_INST_REL_LT, \ - &&lbl_BC_INST_REL_GT, \ - &&lbl_BC_INST_BOOL_OR, \ - &&lbl_BC_INST_BOOL_AND, \ - &&lbl_BC_INST_ASSIGN_NO_VAL, \ - &&lbl_BC_INST_NUM, \ - &&lbl_BC_INST_VAR, \ - &&lbl_BC_INST_ARRAY_ELEM, \ - &&lbl_BC_INST_ARRAY, \ - &&lbl_BC_INST_ZERO, \ - &&lbl_BC_INST_ONE, \ - &&lbl_BC_INST_IBASE, \ - &&lbl_BC_INST_OBASE, \ - &&lbl_BC_INST_SCALE, \ - &&lbl_BC_INST_SEED, \ - &&lbl_BC_INST_LENGTH, \ - &&lbl_BC_INST_SCALE_FUNC, \ - &&lbl_BC_INST_SQRT, \ - &&lbl_BC_INST_ABS, \ - &&lbl_BC_INST_IS_NUMBER, \ - &&lbl_BC_INST_IS_STRING, \ - &&lbl_BC_INST_IRAND, \ - &&lbl_BC_INST_ASCIIFY, \ - &&lbl_BC_INST_READ, \ - &&lbl_BC_INST_RAND, \ - &&lbl_BC_INST_MAXIBASE, \ - &&lbl_BC_INST_MAXOBASE, \ - &&lbl_BC_INST_MAXSCALE, \ - &&lbl_BC_INST_MAXRAND, \ - &&lbl_BC_INST_LINE_LENGTH, \ - &&lbl_BC_INST_LEADING_ZERO, \ - &&lbl_BC_INST_PRINT, \ - &&lbl_BC_INST_PRINT_POP, \ - &&lbl_BC_INST_STR, \ - &&lbl_BC_INST_POP, \ - &&lbl_BC_INST_SWAP, \ - &&lbl_BC_INST_MODEXP, \ - &&lbl_BC_INST_DIVMOD, \ - &&lbl_BC_INST_PRINT_STREAM, \ - &&lbl_BC_INST_EXTENDED_REGISTERS, \ - &&lbl_BC_INST_POP_EXEC, \ - &&lbl_BC_INST_EXECUTE, \ - &&lbl_BC_INST_EXEC_COND, \ - &&lbl_BC_INST_PRINT_STACK, \ - &&lbl_BC_INST_CLEAR_STACK, \ - &&lbl_BC_INST_REG_STACK_LEN, \ - &&lbl_BC_INST_STACK_LEN, \ - &&lbl_BC_INST_DUPLICATE, \ - &&lbl_BC_INST_LOAD, \ - &&lbl_BC_INST_PUSH_VAR, \ - &&lbl_BC_INST_PUSH_TO_VAR, \ - &&lbl_BC_INST_QUIT, \ - &&lbl_BC_INST_NQUIT, \ - &&lbl_BC_INST_EXEC_STACK_LEN, \ - &&lbl_BC_INST_INVALID, \ +#define BC_PROG_LBLS \ + static const void* const bc_program_inst_lbls[] = { \ + &&lbl_BC_INST_NEG, &&lbl_BC_INST_BOOL_NOT, \ + &&lbl_BC_INST_TRUNC, &&lbl_BC_INST_POWER, \ + &&lbl_BC_INST_MULTIPLY, &&lbl_BC_INST_DIVIDE, \ + &&lbl_BC_INST_MODULUS, &&lbl_BC_INST_PLUS, \ + &&lbl_BC_INST_MINUS, &&lbl_BC_INST_PLACES, \ + &&lbl_BC_INST_LSHIFT, &&lbl_BC_INST_RSHIFT, \ + &&lbl_BC_INST_REL_EQ, &&lbl_BC_INST_REL_LE, \ + &&lbl_BC_INST_REL_GE, &&lbl_BC_INST_REL_NE, \ + &&lbl_BC_INST_REL_LT, &&lbl_BC_INST_REL_GT, \ + &&lbl_BC_INST_BOOL_OR, &&lbl_BC_INST_BOOL_AND, \ + &&lbl_BC_INST_ASSIGN_NO_VAL, &&lbl_BC_INST_NUM, \ + &&lbl_BC_INST_VAR, &&lbl_BC_INST_ARRAY_ELEM, \ + &&lbl_BC_INST_ARRAY, &&lbl_BC_INST_ZERO, \ + &&lbl_BC_INST_ONE, &&lbl_BC_INST_IBASE, \ + &&lbl_BC_INST_OBASE, &&lbl_BC_INST_SCALE, \ + &&lbl_BC_INST_SEED, &&lbl_BC_INST_LENGTH, \ + &&lbl_BC_INST_SCALE_FUNC, &&lbl_BC_INST_SQRT, \ + &&lbl_BC_INST_ABS, &&lbl_BC_INST_IS_NUMBER, \ + &&lbl_BC_INST_IS_STRING, &&lbl_BC_INST_IRAND, \ + &&lbl_BC_INST_ASCIIFY, &&lbl_BC_INST_READ, \ + &&lbl_BC_INST_RAND, &&lbl_BC_INST_MAXIBASE, \ + &&lbl_BC_INST_MAXOBASE, &&lbl_BC_INST_MAXSCALE, \ + &&lbl_BC_INST_MAXRAND, &&lbl_BC_INST_LINE_LENGTH, \ + &&lbl_BC_INST_LEADING_ZERO, &&lbl_BC_INST_PRINT, \ + &&lbl_BC_INST_PRINT_POP, &&lbl_BC_INST_STR, \ + &&lbl_BC_INST_POP, &&lbl_BC_INST_SWAP, \ + &&lbl_BC_INST_MODEXP, &&lbl_BC_INST_DIVMOD, \ + &&lbl_BC_INST_PRINT_STREAM, &&lbl_BC_INST_EXTENDED_REGISTERS, \ + &&lbl_BC_INST_POP_EXEC, &&lbl_BC_INST_EXECUTE, \ + &&lbl_BC_INST_EXEC_COND, &&lbl_BC_INST_PRINT_STACK, \ + &&lbl_BC_INST_CLEAR_STACK, &&lbl_BC_INST_REG_STACK_LEN, \ + &&lbl_BC_INST_STACK_LEN, &&lbl_BC_INST_DUPLICATE, \ + &&lbl_BC_INST_LOAD, &&lbl_BC_INST_PUSH_VAR, \ + &&lbl_BC_INST_PUSH_TO_VAR, &&lbl_BC_INST_QUIT, \ + &&lbl_BC_INST_NQUIT, &&lbl_BC_INST_EXEC_STACK_LEN, \ + &&lbl_BC_INST_INVALID, \ } #else // BC_ENABLE_EXTRA_MATH -#define BC_PROG_LBLS \ - static const void* const bc_program_inst_lbls[] = { \ - &&lbl_BC_INST_NEG, \ - &&lbl_BC_INST_BOOL_NOT, \ - &&lbl_BC_INST_POWER, \ - &&lbl_BC_INST_MULTIPLY, \ - &&lbl_BC_INST_DIVIDE, \ - &&lbl_BC_INST_MODULUS, \ - &&lbl_BC_INST_PLUS, \ - &&lbl_BC_INST_MINUS, \ - &&lbl_BC_INST_REL_EQ, \ - &&lbl_BC_INST_REL_LE, \ - &&lbl_BC_INST_REL_GE, \ - &&lbl_BC_INST_REL_NE, \ - &&lbl_BC_INST_REL_LT, \ - &&lbl_BC_INST_REL_GT, \ - &&lbl_BC_INST_BOOL_OR, \ - &&lbl_BC_INST_BOOL_AND, \ - &&lbl_BC_INST_ASSIGN_NO_VAL, \ - &&lbl_BC_INST_NUM, \ - &&lbl_BC_INST_VAR, \ - &&lbl_BC_INST_ARRAY_ELEM, \ - &&lbl_BC_INST_ARRAY, \ - &&lbl_BC_INST_ZERO, \ - &&lbl_BC_INST_ONE, \ - &&lbl_BC_INST_IBASE, \ - &&lbl_BC_INST_OBASE, \ - &&lbl_BC_INST_SCALE, \ - &&lbl_BC_INST_LENGTH, \ - &&lbl_BC_INST_SCALE_FUNC, \ - &&lbl_BC_INST_SQRT, \ - &&lbl_BC_INST_ABS, \ - &&lbl_BC_INST_IS_NUMBER, \ - &&lbl_BC_INST_IS_STRING, \ - &&lbl_BC_INST_ASCIIFY, \ - &&lbl_BC_INST_READ, \ - &&lbl_BC_INST_MAXIBASE, \ - &&lbl_BC_INST_MAXOBASE, \ - &&lbl_BC_INST_MAXSCALE, \ - &&lbl_BC_INST_LINE_LENGTH, \ - &&lbl_BC_INST_LEADING_ZERO, \ - &&lbl_BC_INST_PRINT, \ - &&lbl_BC_INST_PRINT_POP, \ - &&lbl_BC_INST_STR, \ - &&lbl_BC_INST_POP, \ - &&lbl_BC_INST_SWAP, \ - &&lbl_BC_INST_MODEXP, \ - &&lbl_BC_INST_DIVMOD, \ - &&lbl_BC_INST_PRINT_STREAM, \ - &&lbl_BC_INST_EXTENDED_REGISTERS, \ - &&lbl_BC_INST_POP_EXEC, \ - &&lbl_BC_INST_EXECUTE, \ - &&lbl_BC_INST_EXEC_COND, \ - &&lbl_BC_INST_PRINT_STACK, \ - &&lbl_BC_INST_CLEAR_STACK, \ - &&lbl_BC_INST_REG_STACK_LEN, \ - &&lbl_BC_INST_STACK_LEN, \ - &&lbl_BC_INST_DUPLICATE, \ - &&lbl_BC_INST_LOAD, \ - &&lbl_BC_INST_PUSH_VAR, \ - &&lbl_BC_INST_PUSH_TO_VAR, \ - &&lbl_BC_INST_QUIT, \ - &&lbl_BC_INST_NQUIT, \ - &&lbl_BC_INST_EXEC_STACK_LEN, \ - &&lbl_BC_INST_INVALID, \ +#define BC_PROG_LBLS \ + static const void* const bc_program_inst_lbls[] = { \ + &&lbl_BC_INST_NEG, &&lbl_BC_INST_BOOL_NOT, \ + &&lbl_BC_INST_POWER, &&lbl_BC_INST_MULTIPLY, \ + &&lbl_BC_INST_DIVIDE, &&lbl_BC_INST_MODULUS, \ + &&lbl_BC_INST_PLUS, &&lbl_BC_INST_MINUS, \ + &&lbl_BC_INST_REL_EQ, &&lbl_BC_INST_REL_LE, \ + &&lbl_BC_INST_REL_GE, &&lbl_BC_INST_REL_NE, \ + &&lbl_BC_INST_REL_LT, &&lbl_BC_INST_REL_GT, \ + &&lbl_BC_INST_BOOL_OR, &&lbl_BC_INST_BOOL_AND, \ + &&lbl_BC_INST_ASSIGN_NO_VAL, &&lbl_BC_INST_NUM, \ + &&lbl_BC_INST_VAR, &&lbl_BC_INST_ARRAY_ELEM, \ + &&lbl_BC_INST_ARRAY, &&lbl_BC_INST_ZERO, \ + &&lbl_BC_INST_ONE, &&lbl_BC_INST_IBASE, \ + &&lbl_BC_INST_OBASE, &&lbl_BC_INST_SCALE, \ + &&lbl_BC_INST_LENGTH, &&lbl_BC_INST_SCALE_FUNC, \ + &&lbl_BC_INST_SQRT, &&lbl_BC_INST_ABS, \ + &&lbl_BC_INST_IS_NUMBER, &&lbl_BC_INST_IS_STRING, \ + &&lbl_BC_INST_ASCIIFY, &&lbl_BC_INST_READ, \ + &&lbl_BC_INST_MAXIBASE, &&lbl_BC_INST_MAXOBASE, \ + &&lbl_BC_INST_MAXSCALE, &&lbl_BC_INST_LINE_LENGTH, \ + &&lbl_BC_INST_LEADING_ZERO, &&lbl_BC_INST_PRINT, \ + &&lbl_BC_INST_PRINT_POP, &&lbl_BC_INST_STR, \ + &&lbl_BC_INST_POP, &&lbl_BC_INST_SWAP, \ + &&lbl_BC_INST_MODEXP, &&lbl_BC_INST_DIVMOD, \ + &&lbl_BC_INST_PRINT_STREAM, &&lbl_BC_INST_EXTENDED_REGISTERS, \ + &&lbl_BC_INST_POP_EXEC, &&lbl_BC_INST_EXECUTE, \ + &&lbl_BC_INST_EXEC_COND, &&lbl_BC_INST_PRINT_STACK, \ + &&lbl_BC_INST_CLEAR_STACK, &&lbl_BC_INST_REG_STACK_LEN, \ + &&lbl_BC_INST_STACK_LEN, &&lbl_BC_INST_DUPLICATE, \ + &&lbl_BC_INST_LOAD, &&lbl_BC_INST_PUSH_VAR, \ + &&lbl_BC_INST_PUSH_TO_VAR, &&lbl_BC_INST_QUIT, \ + &&lbl_BC_INST_NQUIT, &&lbl_BC_INST_EXEC_STACK_LEN, \ + &&lbl_BC_INST_INVALID, \ } #endif // BC_ENABLE_EXTRA_MATH diff --git a/include/version.h b/include/version.h index f5e345b3b189..3745ed9b5f74 100644 --- a/include/version.h +++ b/include/version.h @@ -37,6 +37,6 @@ #define BC_VERSION_H /// The current version. -#define VERSION 6.3.1 +#define VERSION 6.4.0 #endif // BC_VERSION_H diff --git a/include/vm.h b/include/vm.h index dd21d43f5260..c56cc8e7370a 100644 --- a/include/vm.h +++ b/include/vm.h @@ -560,9 +560,13 @@ typedef struct BcVm /// The vector for creating strings to pass to the client. BcVec out; +#if BC_ENABLE_EXTRA_MATH + /// The PRNG. BcRNG rng; +#endif // BC_ENABLE_EXTRA_MATH + /// The current error. BclError err; diff --git a/manuals/bcl.3 b/manuals/bcl.3 index 5c3731a141eb..cb65a2b8b991 100644 --- a/manuals/bcl.3 +++ b/manuals/bcl.3 @@ -139,9 +139,14 @@ integers. .PP \f[B]char* bcl_string(BclNumber\f[R] \f[I]n\f[R]\f[B]);\f[R] .PP +\f[B]char* bcl_string_keep(BclNumber\f[R] \f[I]n\f[R]\f[B]);\f[R] +.PP \f[B]BclError bcl_bigdig(BclNumber\f[R] \f[I]n\f[R]\f[B], BclBigDig *\f[R]\f[I]result\f[R]\f[B]);\f[R] .PP +\f[B]BclError bcl_bigdig_keep(BclNumber\f[R] \f[I]n\f[R]\f[B], BclBigDig +*\f[R]\f[I]result\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_bigdig2num(BclBigDig\f[R] \f[I]val\f[R]\f[B]);\f[R] .SS Math .PP @@ -150,35 +155,68 @@ These items allow clients to run math on numbers. \f[B]BclNumber bcl_add(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_add_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_sub(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_sub_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_mul(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_mul_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_div(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_div_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_mod(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_mod_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_pow(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_pow_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_lshift(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_lshift_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_rshift(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_rshift_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_sqrt(BclNumber\f[R] \f[I]a\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_sqrt_keep(BclNumber\f[R] \f[I]a\f[R]\f[B]);\f[R] +.PP \f[B]BclError bcl_divmod(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber *\f[R]\f[I]c\f[R]\f[B], BclNumber *\f[R]\f[I]d\f[R]\f[B]);\f[R] .PP +\f[B]BclError bcl_divmod_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber *\f[R]\f[I]c\f[R]\f[B], +BclNumber *\f[R]\f[I]d\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_modexp(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber\f[R] \f[I]c\f[R]\f[B]);\f[R] +.PP +\f[B]BclNumber bcl_modexp_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber\f[R] \f[I]c\f[R]\f[B]);\f[R] .SS Miscellaneous .PP These items are miscellaneous. @@ -209,14 +247,22 @@ generator in bcl(3). .PP \f[B]BclNumber bcl_irand(BclNumber\f[R] \f[I]a\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_irand_keep(BclNumber\f[R] \f[I]a\f[R]\f[B]);\f[R] +.PP \f[B]BclNumber bcl_frand(size_t\f[R] \f[I]places\f[R]\f[B]);\f[R] .PP \f[B]BclNumber bcl_ifrand(BclNumber\f[R] \f[I]a\f[R]\f[B], size_t\f[R] \f[I]places\f[R]\f[B]);\f[R] .PP +\f[B]BclNumber bcl_ifrand_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], +size_t\f[R] \f[I]places\f[R]\f[B]);\f[R] +.PP \f[B]BclError bcl_rand_seedWithNum(BclNumber\f[R] \f[I]n\f[R]\f[B]);\f[R] .PP +\f[B]BclError bcl_rand_seedWithNum_keep(BclNumber\f[R] +\f[I]n\f[R]\f[B]);\f[R] +.PP \f[B]BclError bcl_rand_seed(unsigned char\f[R] \f[I]seed\f[R]\f[B][\f[R]\f[I]BCL_SEED_SIZE\f[R]\f[B]]);\f[R] .PP @@ -608,8 +654,9 @@ Returns the number of \f[I]significant decimal digits\f[R] in .PP All procedures in this section require a valid current context. .PP -All procedures in this section consume the given \f[B]BclNumber\f[R] -arguments that are not given to pointer arguments. +All procedures in this section without the \f[B]_keep\f[R] suffix in +their name consume the given \f[B]BclNumber\f[R] arguments that are not +given to pointer arguments. See the \f[B]Consumption and Propagation\f[R] subsection below. .TP \f[B]BclNumber bcl_parse(const char *restrict\f[R] \f[I]val\f[R]\f[B])\f[R] @@ -644,6 +691,11 @@ The string is dynamically allocated and must be freed by the caller. See the \f[B]Consumption and Propagation\f[R] subsection below. .RE .TP +\f[B]char* bcl_string_keep(BclNumber\f[R] \f[I]n\f[R]\f[B])\f[R] +Returns a string representation of \f[I]n\f[R] according the the current +context\[cq]s \f[B]ibase\f[R]. +The string is dynamically allocated and must be freed by the caller. +.TP \f[B]BclError bcl_bigdig(BclNumber\f[R] \f[I]n\f[R]\f[B], BclBigDig *\f[R]\f[I]result\f[R]\f[B])\f[R] Converts \f[I]n\f[R] into a \f[B]BclBigDig\f[R] and returns the result in the space pointed to by \f[I]result\f[R]. @@ -665,6 +717,24 @@ Otherwise, this function can return: See the \f[B]Consumption and Propagation\f[R] subsection below. .RE .TP +\f[B]BclError bcl_bigdig_keep(BclNumber\f[R] \f[I]n\f[R]\f[B], BclBigDig *\f[R]\f[I]result\f[R]\f[B])\f[R] +Converts \f[I]n\f[R] into a \f[B]BclBigDig\f[R] and returns the result +in the space pointed to by \f[I]result\f[R]. +.RS +.PP +\f[I]a\f[R] must be smaller than \f[B]BC_OVERFLOW_MAX\f[R]. +See the \f[B]LIMITS\f[R] section. +.PP +If there was no error, \f[B]BCL_ERROR_NONE\f[R] is returned. +Otherwise, this function can return: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_OVERFLOW\f[R] +.RE +.TP \f[B]BclNumber bcl_bigdig2num(BclBigDig\f[R] \f[I]val\f[R]\f[B])\f[R] Creates a \f[B]BclNumber\f[R] from \f[I]val\f[R]. .RS @@ -681,6 +751,11 @@ Possible errors include: .PP All procedures in this section require a valid current context. .PP +All procedures in this section without the \f[B]_keep\f[R] suffix in +their name consume the given \f[B]BclNumber\f[R] arguments that are not +given to pointer arguments. +See the \f[B]Consumption and Propagation\f[R] subsection below. +.PP All procedures in this section can return the following errors: .IP \[bu] 2 \f[B]BCL_ERROR_INVALID_NUM\f[R] @@ -712,6 +787,25 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_add_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Adds \f[I]a\f[R] and \f[I]b\f[R] and returns the result. +The \f[I]scale\f[R] of the result is the max of the \f[I]scale\f[R]s of +\f[I]a\f[R] and \f[I]b\f[R]. +.RS +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_sub(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Subtracts \f[I]b\f[R] from \f[I]a\f[R] and returns the result. The \f[I]scale\f[R] of the result is the max of the \f[I]scale\f[R]s of @@ -735,6 +829,25 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_sub_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Subtracts \f[I]b\f[R] from \f[I]a\f[R] and returns the result. +The \f[I]scale\f[R] of the result is the max of the \f[I]scale\f[R]s of +\f[I]a\f[R] and \f[I]b\f[R]. +.RS +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_mul(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Multiplies \f[I]a\f[R] and \f[I]b\f[R] and returns the result. If \f[I]ascale\f[R] is the \f[I]scale\f[R] of \f[I]a\f[R] and @@ -761,6 +874,28 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_mul_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Multiplies \f[I]a\f[R] and \f[I]b\f[R] and returns the result. +If \f[I]ascale\f[R] is the \f[I]scale\f[R] of \f[I]a\f[R] and +\f[I]bscale\f[R] is the \f[I]scale\f[R] of \f[I]b\f[R], the +\f[I]scale\f[R] of the result is equal to +\f[B]min(ascale+bscale,max(scale,ascale,bscale))\f[R], where +\f[B]min()\f[R] and \f[B]max()\f[R] return the obvious values. +.RS +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_div(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Divides \f[I]a\f[R] by \f[I]b\f[R] and returns the result. The \f[I]scale\f[R] of the result is the \f[I]scale\f[R] of the current @@ -788,6 +923,29 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_div_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Divides \f[I]a\f[R] by \f[I]b\f[R] and returns the result. +The \f[I]scale\f[R] of the result is the \f[I]scale\f[R] of the current +context. +.RS +.PP +\f[I]b\f[R] cannot be \f[B]0\f[R]. +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_DIVIDE_BY_ZERO\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_mod(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Divides \f[I]a\f[R] by \f[I]b\f[R] to the \f[I]scale\f[R] of the current context, computes the modulus \f[B]a-(a/b)*b\f[R], and returns the @@ -815,6 +973,29 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_mod_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Divides \f[I]a\f[R] by \f[I]b\f[R] to the \f[I]scale\f[R] of the current +context, computes the modulus \f[B]a-(a/b)*b\f[R], and returns the +modulus. +.RS +.PP +\f[I]b\f[R] cannot be \f[B]0\f[R]. +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_DIVIDE_BY_ZERO\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_pow(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Calculates \f[I]a\f[R] to the power of \f[I]b\f[R] to the \f[I]scale\f[R] of the current context. @@ -851,6 +1032,38 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_pow_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Calculates \f[I]a\f[R] to the power of \f[I]b\f[R] to the +\f[I]scale\f[R] of the current context. +\f[I]b\f[R] must be an integer, but can be negative. +If it is negative, \f[I]a\f[R] must be non-zero. +.RS +.PP +\f[I]b\f[R] must be an integer. +If \f[I]b\f[R] is negative, \f[I]a\f[R] must not be \f[B]0\f[R]. +.PP +\f[I]a\f[R] must be smaller than \f[B]BC_OVERFLOW_MAX\f[R]. +See the \f[B]LIMITS\f[R] section. +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NON_INTEGER\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_OVERFLOW\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_DIVIDE_BY_ZERO\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_lshift(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Shifts \f[I]a\f[R] left (moves the radix right) by \f[I]b\f[R] places and returns the result. @@ -879,6 +1092,30 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_lshift_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Shifts \f[I]a\f[R] left (moves the radix right) by \f[I]b\f[R] places +and returns the result. +This is done in decimal. +\f[I]b\f[R] must be an integer. +.RS +.PP +\f[I]b\f[R] must be an integer. +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NON_INTEGER\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_rshift(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] Shifts \f[I]a\f[R] right (moves the radix left) by \f[I]b\f[R] places and returns the result. @@ -907,6 +1144,30 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_rshift_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B])\f[R] +Shifts \f[I]a\f[R] right (moves the radix left) by \f[I]b\f[R] places +and returns the result. +This is done in decimal. +\f[I]b\f[R] must be an integer. +.RS +.PP +\f[I]b\f[R] must be an integer. +.PP +\f[I]a\f[R] and \f[I]b\f[R] can be the same number. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NON_INTEGER\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_sqrt(BclNumber\f[R] \f[I]a\f[R]\f[B])\f[R] Calculates the square root of \f[I]a\f[R] and returns the result. The \f[I]scale\f[R] of the result is equal to the \f[B]scale\f[R] of the @@ -931,6 +1192,27 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_sqrt_keep(BclNumber\f[R] \f[I]a\f[R]\f[B])\f[R] +Calculates the square root of \f[I]a\f[R] and returns the result. +The \f[I]scale\f[R] of the result is equal to the \f[B]scale\f[R] of the +current context. +.RS +.PP +\f[I]a\f[R] cannot be negative. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NEGATIVE\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclError bcl_divmod(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber *\f[R]\f[I]c\f[R]\f[B], BclNumber *\f[R]\f[I]d\f[R]\f[B])\f[R] Divides \f[I]a\f[R] by \f[I]b\f[R] and returns the quotient in a new number which is put into the space pointed to by \f[I]c\f[R], and puts @@ -959,6 +1241,30 @@ Otherwise, this function can return: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclError bcl_divmod_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber *\f[R]\f[I]c\f[R]\f[B], BclNumber *\f[R]\f[I]d\f[R]\f[B])\f[R] +Divides \f[I]a\f[R] by \f[I]b\f[R] and returns the quotient in a new +number which is put into the space pointed to by \f[I]c\f[R], and puts +the modulus in a new number which is put into the space pointed to by +\f[I]d\f[R]. +.RS +.PP +\f[I]b\f[R] cannot be \f[B]0\f[R]. +.PP +\f[I]c\f[R] and \f[I]d\f[R] cannot point to the same place, nor can they +point to the space occupied by \f[I]a\f[R] or \f[I]b\f[R]. +.PP +If there was no error, \f[B]BCL_ERROR_NONE\f[R] is returned. +Otherwise, this function can return: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_DIVIDE_BY_ZERO\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_modexp(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber\f[R] \f[I]c\f[R]\f[B])\f[R] Computes a modular exponentiation where \f[I]a\f[R] is the base, \f[I]b\f[R] is the exponent, and \f[I]c\f[R] is the modulus, and returns @@ -991,6 +1297,35 @@ Possible errors include: .IP \[bu] 2 \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE +.TP +\f[B]BclNumber bcl_modexp_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], BclNumber\f[R] \f[I]b\f[R]\f[B], BclNumber\f[R] \f[I]c\f[R]\f[B])\f[R] +Computes a modular exponentiation where \f[I]a\f[R] is the base, +\f[I]b\f[R] is the exponent, and \f[I]c\f[R] is the modulus, and returns +the result. +The \f[I]scale\f[R] of the result is equal to the \f[B]scale\f[R] of the +current context. +.RS +.PP +\f[I]a\f[R], \f[I]b\f[R], and \f[I]c\f[R] must be integers. +\f[I]c\f[R] must not be \f[B]0\f[R]. +\f[I]b\f[R] must not be negative. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NEGATIVE\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NON_INTEGER\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_DIVIDE_BY_ZERO\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE .SS Miscellaneous .TP \f[B]void bcl_zero(BclNumber\f[R] \f[I]n\f[R]\f[B])\f[R] @@ -1060,6 +1395,11 @@ char[\f[R]\f[I]BCL_SEED_SIZE\f[R]\f[B]])\f[R] .IP \[bu] 2 \f[B]bcl_rand_reseed(\f[R]\f[I]void\f[R]\f[B])\f[R] .PP +All procedures in this section without the \f[B]_keep\f[R] suffix in +their name consume the given \f[B]BclNumber\f[R] arguments that are not +given to pointer arguments. +See the \f[B]Consumption and Propagation\f[R] subsection below. +.PP The following items allow clients to use the pseudo-random number generator. All procedures require a valid current context. @@ -1112,6 +1452,36 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_irand_keep(BclNumber\f[R] \f[I]a\f[R]\f[B])\f[R] +Returns a random number that is not larger than \f[I]a\f[R] in a new +number. +If \f[I]a\f[R] is \f[B]0\f[R] or \f[B]1\f[R], the new number is equal to +\f[B]0\f[R]. +The bound is unlimited, so it is not bound to the size of +\f[B]BclRandInt\f[R]. +This is done by generating as many random numbers as necessary, +multiplying them by certain exponents, and adding them all together. +.RS +.PP +\f[I]a\f[R] must be an integer and non-negative. +.PP +This procedure requires a valid current context. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NEGATIVE\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NON_INTEGER\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclNumber bcl_frand(size_t\f[R] \f[I]places\f[R]\f[B])\f[R] Returns a random number between \f[B]0\f[R] (inclusive) and \f[B]1\f[R] (exclusive) that has \f[I]places\f[R] decimal digits after the radix @@ -1158,11 +1528,55 @@ Possible errors include: \f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] .RE .TP +\f[B]BclNumber bcl_ifrand_keep(BclNumber\f[R] \f[I]a\f[R]\f[B], size_t\f[R] \f[I]places\f[R]\f[B])\f[R] +Returns a random number less than \f[I]a\f[R] with \f[I]places\f[R] +decimal digits after the radix (decimal point). +There are no limits on \f[I]a\f[R] or \f[I]places\f[R]. +.RS +.PP +\f[I]a\f[R] must be an integer and non-negative. +.PP +This procedure requires a valid current context. +.PP +bcl(3) will encode an error in the return value, if there was one. +The error can be queried with \f[B]bcl_err(BclNumber)\f[R]. +Possible errors include: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NEGATIVE\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_MATH_NON_INTEGER\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_FATAL_ALLOC_ERR\f[R] +.RE +.TP \f[B]BclError bcl_rand_seedWithNum(BclNumber\f[R] \f[I]n\f[R]\f[B])\f[R] Seeds the PRNG with \f[I]n\f[R]. .RS .PP -\f[I]n\f[R] is \f[I]not\f[R] consumed. +\f[I]n\f[R] is consumed. +.PP +This procedure requires a valid current context. +.PP +If there was no error, \f[B]BCL_ERROR_NONE\f[R] is returned. +Otherwise, this function can return: +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_NUM\f[R] +.IP \[bu] 2 +\f[B]BCL_ERROR_INVALID_CONTEXT\f[R] +.PP +Note that if \f[B]bcl_rand_seed2num(\f[R]\f[I]void\f[R]\f[B])\f[R] or +\f[B]bcl_rand_seed2num_err(BclNumber)\f[R] are called right after this +function, they are not guaranteed to return a number equal to +\f[I]n\f[R]. +.RE +.TP +\f[B]BclError bcl_rand_seedWithNum_keep(BclNumber\f[R] \f[I]n\f[R]\f[B])\f[R] +Seeds the PRNG with \f[I]n\f[R]. +.RS .PP This procedure requires a valid current context. .PP @@ -1253,7 +1667,7 @@ so the example above should properly be: .nf \f[C] BclNumber n = bcl_num_add(bcl_num_mul(a, b), bcl_num_div(c, d)); -if (bc_num_err(n) != BCL_ERROR_NONE) { +if (bcl_err(n) != BCL_ERROR_NONE) { // Handle the error. } \f[R] diff --git a/manuals/bcl.3.md b/manuals/bcl.3.md index 6c6967b44770..fa566d161c43 100644 --- a/manuals/bcl.3.md +++ b/manuals/bcl.3.md @@ -136,8 +136,12 @@ These items allow clients to convert numbers into and from strings and integers. **char\* bcl_string(BclNumber** _n_**);** +**char\* bcl_string_keep(BclNumber** _n_**);** + **BclError bcl_bigdig(BclNumber** _n_**, BclBigDig \***_result_**);** +**BclError bcl_bigdig_keep(BclNumber** _n_**, BclBigDig \***_result_**);** + **BclNumber bcl_bigdig2num(BclBigDig** _val_**);** ## Math @@ -146,26 +150,48 @@ These items allow clients to run math on numbers. **BclNumber bcl_add(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_add_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_sub(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_sub_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_mul(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_mul_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_div(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_div_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_mod(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_mod_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_pow(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_pow_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_lshift(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_lshift_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_rshift(BclNumber** _a_**, BclNumber** _b_**);** +**BclNumber bcl_rshift_keep(BclNumber** _a_**, BclNumber** _b_**);** + **BclNumber bcl_sqrt(BclNumber** _a_**);** +**BclNumber bcl_sqrt_keep(BclNumber** _a_**);** + **BclError bcl_divmod(BclNumber** _a_**, BclNumber** _b_**, BclNumber \***_c_**, BclNumber \***_d_**);** +**BclError bcl_divmod_keep(BclNumber** _a_**, BclNumber** _b_**, BclNumber \***_c_**, BclNumber \***_d_**);** + **BclNumber bcl_modexp(BclNumber** _a_**, BclNumber** _b_**, BclNumber** _c_**);** +**BclNumber bcl_modexp_keep(BclNumber** _a_**, BclNumber** _b_**, BclNumber** _c_**);** + ## Miscellaneous These items are miscellaneous. @@ -195,12 +221,18 @@ generator in bcl(3). **BclNumber bcl_irand(BclNumber** _a_**);** +**BclNumber bcl_irand_keep(BclNumber** _a_**);** + **BclNumber bcl_frand(size_t** _places_**);** **BclNumber bcl_ifrand(BclNumber** _a_**, size_t** _places_**);** +**BclNumber bcl_ifrand_keep(BclNumber** _a_**, size_t** _places_**);** + **BclError bcl_rand_seedWithNum(BclNumber** _n_**);** +**BclError bcl_rand_seedWithNum_keep(BclNumber** _n_**);** + **BclError bcl_rand_seed(unsigned char** _seed_**[**_BCL_SEED_SIZE_**]);** **void bcl_rand_reseed(**_void_**);** @@ -548,9 +580,9 @@ All procedures in this section require a valid current context. All procedures in this section require a valid current context. -All procedures in this section consume the given **BclNumber** arguments that -are not given to pointer arguments. See the **Consumption and Propagation** -subsection below. +All procedures in this section without the **_keep** suffix in their name +consume the given **BclNumber** arguments that are not given to pointer +arguments. See the **Consumption and Propagation** subsection below. **BclNumber bcl_parse(const char \*restrict** _val_**)** @@ -578,6 +610,12 @@ subsection below. *n* is consumed; it cannot be used after the call. See the **Consumption and Propagation** subsection below. +**char\* bcl_string_keep(BclNumber** _n_**)** + +: Returns a string representation of *n* according the the current context's + **ibase**. The string is dynamically allocated and must be freed by the + caller. + **BclError bcl_bigdig(BclNumber** _n_**, BclBigDig \***_result_**)** : Converts *n* into a **BclBigDig** and returns the result in the space @@ -595,6 +633,20 @@ subsection below. *n* is consumed; it cannot be used after the call. See the **Consumption and Propagation** subsection below. +**BclError bcl_bigdig_keep(BclNumber** _n_**, BclBigDig \***_result_**)** + +: Converts *n* into a **BclBigDig** and returns the result in the space + pointed to by *result*. + + *a* must be smaller than **BC_OVERFLOW_MAX**. See the **LIMITS** section. + + If there was no error, **BCL_ERROR_NONE** is returned. Otherwise, this + function can return: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_OVERFLOW** + **BclNumber bcl_bigdig2num(BclBigDig** _val_**)** : Creates a **BclNumber** from *val*. @@ -609,6 +661,10 @@ subsection below. All procedures in this section require a valid current context. +All procedures in this section without the **_keep** suffix in their name +consume the given **BclNumber** arguments that are not given to pointer +arguments. See the **Consumption and Propagation** subsection below. + All procedures in this section can return the following errors: * **BCL_ERROR_INVALID_NUM** @@ -632,6 +688,20 @@ All procedures in this section can return the following errors: * **BCL_ERROR_INVALID_CONTEXT** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_add_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Adds *a* and *b* and returns the result. The *scale* of the result is the + max of the *scale*s of *a* and *b*. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_sub(BclNumber** _a_**, BclNumber** _b_**)** : Subtracts *b* from *a* and returns the result. The *scale* of the result is @@ -649,6 +719,20 @@ All procedures in this section can return the following errors: * **BCL_ERROR_INVALID_CONTEXT** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_sub_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Subtracts *b* from *a* and returns the result. The *scale* of the result is + the max of the *scale*s of *a* and *b*. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_mul(BclNumber** _a_**, BclNumber** _b_**)** : Multiplies *a* and *b* and returns the result. If *ascale* is the *scale* of @@ -668,6 +752,22 @@ All procedures in this section can return the following errors: * **BCL_ERROR_INVALID_CONTEXT** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_mul_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Multiplies *a* and *b* and returns the result. If *ascale* is the *scale* of + *a* and *bscale* is the *scale* of *b*, the *scale* of the result is equal + to **min(ascale+bscale,max(scale,ascale,bscale))**, where **min()** and + **max()** return the obvious values. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_div(BclNumber** _a_**, BclNumber** _b_**)** : Divides *a* by *b* and returns the result. The *scale* of the result is the @@ -688,6 +788,23 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_div_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Divides *a* by *b* and returns the result. The *scale* of the result is the + *scale* of the current context. + + *b* cannot be **0**. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_mod(BclNumber** _a_**, BclNumber** _b_**)** : Divides *a* by *b* to the *scale* of the current context, computes the @@ -708,6 +825,23 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_mod_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Divides *a* by *b* to the *scale* of the current context, computes the + modulus **a-(a/b)\*b**, and returns the modulus. + + *b* cannot be **0**. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_pow(BclNumber** _a_**, BclNumber** _b_**)** : Calculates *a* to the power of *b* to the *scale* of the current context. @@ -733,6 +867,28 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_pow_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Calculates *a* to the power of *b* to the *scale* of the current context. + *b* must be an integer, but can be negative. If it is negative, *a* must + be non-zero. + + *b* must be an integer. If *b* is negative, *a* must not be **0**. + + *a* must be smaller than **BC_OVERFLOW_MAX**. See the **LIMITS** section. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NON_INTEGER** + * **BCL_ERROR_MATH_OVERFLOW** + * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_lshift(BclNumber** _a_**, BclNumber** _b_**)** : Shifts *a* left (moves the radix right) by *b* places and returns the @@ -753,6 +909,23 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_NON_INTEGER** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_lshift_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Shifts *a* left (moves the radix right) by *b* places and returns the + result. This is done in decimal. *b* must be an integer. + + *b* must be an integer. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NON_INTEGER** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_rshift(BclNumber** _a_**, BclNumber** _b_**)** : Shifts *a* right (moves the radix left) by *b* places and returns the @@ -773,6 +946,23 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_NON_INTEGER** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_rshift_keep(BclNumber** _a_**, BclNumber** _b_**)** + +: Shifts *a* right (moves the radix left) by *b* places and returns the + result. This is done in decimal. *b* must be an integer. + + *b* must be an integer. + + *a* and *b* can be the same number. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NON_INTEGER** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_sqrt(BclNumber** _a_**)** : Calculates the square root of *a* and returns the result. The *scale* of the @@ -791,6 +981,21 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_NEGATIVE** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_sqrt_keep(BclNumber** _a_**)** + +: Calculates the square root of *a* and returns the result. The *scale* of the + result is equal to the **scale** of the current context. + + *a* cannot be negative. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NEGATIVE** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclError bcl_divmod(BclNumber** _a_**, BclNumber** _b_**, BclNumber \***_c_**, BclNumber \***_d_**)** : Divides *a* by *b* and returns the quotient in a new number which is put @@ -813,6 +1018,25 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclError bcl_divmod_keep(BclNumber** _a_**, BclNumber** _b_**, BclNumber \***_c_**, BclNumber \***_d_**)** + +: Divides *a* by *b* and returns the quotient in a new number which is put + into the space pointed to by *c*, and puts the modulus in a new number which + is put into the space pointed to by *d*. + + *b* cannot be **0**. + + *c* and *d* cannot point to the same place, nor can they point to the space + occupied by *a* or *b*. + + If there was no error, **BCL_ERROR_NONE** is returned. Otherwise, this + function can return: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** + * **BCL_ERROR_FATAL_ALLOC_ERR** + **BclNumber bcl_modexp(BclNumber** _a_**, BclNumber** _b_**, BclNumber** _c_**)** : Computes a modular exponentiation where *a* is the base, *b* is the @@ -835,6 +1059,25 @@ All procedures in this section can return the following errors: * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** * **BCL_ERROR_FATAL_ALLOC_ERR** +**BclNumber bcl_modexp_keep(BclNumber** _a_**, BclNumber** _b_**, BclNumber** _c_**)** + +: Computes a modular exponentiation where *a* is the base, *b* is the + exponent, and *c* is the modulus, and returns the result. The *scale* of the + result is equal to the **scale** of the current context. + + *a*, *b*, and *c* must be integers. *c* must not be **0**. *b* must not be + negative. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NEGATIVE** + * **BCL_ERROR_MATH_NON_INTEGER** + * **BCL_ERROR_MATH_DIVIDE_BY_ZERO** + * **BCL_ERROR_FATAL_ALLOC_ERR** + ## Miscellaneous **void bcl_zero(BclNumber** _n_**)** @@ -891,6 +1134,10 @@ If necessary, the PRNG can be reseeded with one of the following functions: * **bcl_rand_seed(unsigned char[**_BCL_SEED_SIZE_**])** * **bcl_rand_reseed(**_void_**)** +All procedures in this section without the **_keep** suffix in their name +consume the given **BclNumber** arguments that are not given to pointer +arguments. See the **Consumption and Propagation** subsection below. + The following items allow clients to use the pseudo-random number generator. All procedures require a valid current context. @@ -921,8 +1168,29 @@ procedures require a valid current context. *a* must be an integer and non-negative. - *a* is consumed; it cannot be used after the call. See the - **Consumption and Propagation** subsection below. + *a* is consumed; it cannot be used after the call. See the **Consumption and + Propagation** subsection below. + + This procedure requires a valid current context. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NEGATIVE** + * **BCL_ERROR_MATH_NON_INTEGER** + * **BCL_ERROR_FATAL_ALLOC_ERR** + +**BclNumber bcl_irand_keep(BclNumber** _a_**)** + +: Returns a random number that is not larger than *a* in a new number. If *a* + is **0** or **1**, the new number is equal to **0**. The bound is unlimited, + so it is not bound to the size of **BclRandInt**. This is done by generating + as many random numbers as necessary, multiplying them by certain exponents, + and adding them all together. + + *a* must be an integer and non-negative. This procedure requires a valid current context. @@ -956,8 +1224,26 @@ procedures require a valid current context. *a* must be an integer and non-negative. - *a* is consumed; it cannot be used after the call. See the - **Consumption and Propagation** subsection below. + *a* is consumed; it cannot be used after the call. See the **Consumption and + Propagation** subsection below. + + This procedure requires a valid current context. + + bcl(3) will encode an error in the return value, if there was one. The error + can be queried with **bcl_err(BclNumber)**. Possible errors include: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + * **BCL_ERROR_MATH_NEGATIVE** + * **BCL_ERROR_MATH_NON_INTEGER** + * **BCL_ERROR_FATAL_ALLOC_ERR** + +**BclNumber bcl_ifrand_keep(BclNumber** _a_**, size_t** _places_**)** + +: Returns a random number less than *a* with *places* decimal digits after the + radix (decimal point). There are no limits on *a* or *places*. + + *a* must be an integer and non-negative. This procedure requires a valid current context. @@ -974,7 +1260,23 @@ procedures require a valid current context. : Seeds the PRNG with *n*. - *n* is *not* consumed. + *n* is consumed. + + This procedure requires a valid current context. + + If there was no error, **BCL_ERROR_NONE** is returned. Otherwise, this + function can return: + + * **BCL_ERROR_INVALID_NUM** + * **BCL_ERROR_INVALID_CONTEXT** + + Note that if **bcl_rand_seed2num(**_void_**)** or + **bcl_rand_seed2num_err(BclNumber)** are called right after this function, + they are not guaranteed to return a number equal to *n*. + +**BclError bcl_rand_seedWithNum_keep(BclNumber** _n_**)** + +: Seeds the PRNG with *n*. This procedure requires a valid current context. @@ -1046,7 +1348,7 @@ checked with **bcl_err(BclNumber)**, so the example above should properly be: BclNumber n = bcl_num_add(bcl_num_mul(a, b), bcl_num_div(c, d)); - if (bc_num_err(n) != BCL_ERROR_NONE) { + if (bcl_err(n) != BCL_ERROR_NONE) { // Handle the error. } diff --git a/src/data.c b/src/data.c index abaf3b8e39ab..b57e1fc4c02d 100644 --- a/src/data.c +++ b/src/data.c @@ -1151,47 +1151,60 @@ const uchar dc_parse_insts[] = { #if BC_ENABLE_EXTRA_MATH BC_INST_TRUNC, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_POWER, BC_INST_MULTIPLY, BC_INST_DIVIDE, - BC_INST_MODULUS, BC_INST_PLUS, BC_INST_MINUS, + BC_INST_POWER, BC_INST_MULTIPLY, + BC_INST_DIVIDE, BC_INST_MODULUS, + BC_INST_PLUS, BC_INST_MINUS, #if BC_ENABLE_EXTRA_MATH - BC_INST_PLACES, BC_INST_LSHIFT, BC_INST_RSHIFT, + BC_INST_PLACES, BC_INST_LSHIFT, + BC_INST_RSHIFT, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, BC_INST_BOOL_OR, BC_INST_BOOL_AND, #if BC_ENABLED - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLE_EXTRA_MATH - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, #endif // BC_ENABLE_EXTRA_MATH #endif // BC_ENABLED - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, - BC_INST_REL_GT, BC_INST_REL_LT, BC_INST_INVALID, - BC_INST_INVALID, BC_INST_INVALID, BC_INST_REL_GE, - BC_INST_INVALID, BC_INST_REL_LE, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_REL_GT, + BC_INST_REL_LT, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_REL_GE, BC_INST_INVALID, + BC_INST_REL_LE, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLED - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, #endif // BC_ENABLED - BC_INST_IBASE, BC_INST_OBASE, BC_INST_SCALE, + BC_INST_IBASE, BC_INST_OBASE, + BC_INST_SCALE, #if BC_ENABLE_EXTRA_MATH BC_INST_SEED, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_LENGTH, BC_INST_PRINT, BC_INST_SQRT, - BC_INST_ABS, BC_INST_IS_NUMBER, BC_INST_IS_STRING, + BC_INST_LENGTH, BC_INST_PRINT, + BC_INST_SQRT, BC_INST_ABS, + BC_INST_IS_NUMBER, BC_INST_IS_STRING, #if BC_ENABLE_EXTRA_MATH BC_INST_IRAND, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_ASCIIFY, BC_INST_MODEXP, BC_INST_DIVMOD, - BC_INST_QUIT, BC_INST_INVALID, + BC_INST_ASCIIFY, BC_INST_MODEXP, + BC_INST_DIVMOD, BC_INST_QUIT, + BC_INST_INVALID, #if BC_ENABLE_EXTRA_MATH BC_INST_RAND, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_MAXIBASE, BC_INST_MAXOBASE, BC_INST_MAXSCALE, + BC_INST_MAXIBASE, BC_INST_MAXOBASE, + BC_INST_MAXSCALE, #if BC_ENABLE_EXTRA_MATH BC_INST_MAXRAND, #endif // BC_ENABLE_EXTRA_MATH @@ -1199,17 +1212,21 @@ const uchar dc_parse_insts[] = { #if BC_ENABLED BC_INST_INVALID, #endif // BC_ENABLED - BC_INST_LEADING_ZERO, BC_INST_PRINT_STREAM, BC_INST_INVALID, - BC_INST_EXTENDED_REGISTERS, BC_INST_REL_EQ, BC_INST_INVALID, - BC_INST_EXECUTE, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK, - BC_INST_INVALID, BC_INST_STACK_LEN, BC_INST_DUPLICATE, - BC_INST_SWAP, BC_INST_POP, BC_INST_INVALID, - BC_INST_INVALID, BC_INST_INVALID, + BC_INST_LEADING_ZERO, BC_INST_PRINT_STREAM, + BC_INST_INVALID, BC_INST_EXTENDED_REGISTERS, + BC_INST_REL_EQ, BC_INST_INVALID, + BC_INST_EXECUTE, BC_INST_PRINT_STACK, + BC_INST_CLEAR_STACK, BC_INST_INVALID, + BC_INST_STACK_LEN, BC_INST_DUPLICATE, + BC_INST_SWAP, BC_INST_POP, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, #if BC_ENABLE_EXTRA_MATH BC_INST_INVALID, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_INVALID, BC_INST_INVALID, BC_INST_INVALID, - BC_INST_PRINT_POP, BC_INST_NQUIT, BC_INST_EXEC_STACK_LEN, + BC_INST_INVALID, BC_INST_INVALID, + BC_INST_INVALID, BC_INST_PRINT_POP, + BC_INST_NQUIT, BC_INST_EXEC_STACK_LEN, BC_INST_SCALE_FUNC, BC_INST_INVALID, }; #endif // DC_ENABLED diff --git a/src/library.c b/src/library.c index 6283d198139e..cc32a3a3a98c 100644 --- a/src/library.c +++ b/src/library.c @@ -60,6 +60,28 @@ // 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; @@ -195,10 +217,14 @@ bcl_init(void) 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/urandm don't work. +#if BC_ENABLE_EXTRA_MATH + + // 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); +#endif // BC_ENABLE_EXTRA_MATH + err: BC_FUNC_FOOTER(vm, e); @@ -227,6 +253,7 @@ bcl_pushContext(BclContext ctxt) bc_vec_push(&vm->ctxts, &ctxt); err: + BC_FUNC_FOOTER(vm, e); return e; } @@ -262,7 +289,9 @@ bcl_free(void) vm->refs -= 1; if (vm->refs) return; +#if BC_ENABLE_EXTRA_MATH bc_rand_free(&vm->rng); +#endif // BC_ENABLE_EXTRA_MATH bc_vec_free(&vm->out); for (i = 0; i < vm->ctxts.len; ++i) @@ -363,7 +392,7 @@ bcl_ctxt_create(void) // malloc() is appropriate here. ctxt = bc_vm_malloc(sizeof(BclCtxt)); - bc_vec_init(&ctxt->nums, sizeof(BcNum), BC_DTOR_BCL_NUM); + 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; @@ -445,6 +474,10 @@ bcl_err(BclNumber n) 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) @@ -462,14 +495,14 @@ bcl_err(BclNumber n) * @return The resulting BclNumber from the insert. */ static BclNumber -bcl_num_insert(BclContext ctxt, BcNum* restrict n) +bcl_num_insert(BclContext ctxt, BclNum* restrict n) { BclNumber idx; // If there is a free spot... if (ctxt->free_nums.len) { - BcNum* ptr; + BclNum* ptr; // Get the index of the free spot and remove it. idx = *((BclNumber*) bc_vec_top(&ctxt->free_nums)); @@ -477,11 +510,30 @@ bcl_num_insert(BclContext ctxt, BcNum* restrict n) // Copy the number into the spot. ptr = bc_vec_item(&ctxt->nums, idx.i); - memcpy(ptr, n, sizeof(BcNum)); + + 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 { - // Just push the number onto the vector. +#if BC_ENABLE_MEMCHECK + n->gen_idx = 0; +#endif // BC_ENABLE_MEMCHECK + + // Just push the number onto the vector because the generation index is + // 0. idx.i = ctxt->nums.len; bc_vec_push(&ctxt->nums, n); } @@ -493,7 +545,7 @@ BclNumber bcl_num_create(void) { BclError e = BCL_ERROR_NONE; - BcNum n; + BclNum n; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -502,11 +554,12 @@ bcl_num_create(void) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - bc_num_init(&n, BC_NUM_DEF_SIZE); + 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); @@ -520,26 +573,34 @@ err: * @param num The number to destroy. */ static void -bcl_num_dtor(BclContext ctxt, BclNumber n, BcNum* restrict num) +bcl_num_dtor(BclContext ctxt, BclNumber n, BclNum* restrict num) { - assert(num != NULL && num->num != NULL); + 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) { - BcNum* num; + BclNum* num; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); + + assert(BCL_NO_GEN(n) < ctxt->nums.len); - num = BC_NUM(ctxt, n); + num = BCL_NUM(ctxt, n); bcl_num_dtor(ctxt, n, num); } @@ -548,26 +609,31 @@ BclError bcl_copy(BclNumber d, BclNumber s) { BclError e = BCL_ERROR_NONE; - BcNum* dest; - BcNum* src; + 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(d.i < ctxt->nums.len && s.i < ctxt->nums.len); + assert(BCL_NO_GEN(d) < ctxt->nums.len); + assert(BCL_NO_GEN(s) < ctxt->nums.len); - dest = BC_NUM(ctxt, d); - src = BC_NUM(ctxt, s); + dest = BCL_NUM(ctxt, d); + src = BCL_NUM(ctxt, s); assert(dest != NULL && src != NULL); - assert(dest->num != NULL && src->num != NULL); + assert(BCL_NUM_ARRAY(dest) != NULL && BCL_NUM_ARRAY(src) != NULL); - bc_num_copy(dest, src); + bc_num_copy(BCL_NUM_NUM(dest), BCL_NUM_NUM(src)); err: + BC_FUNC_FOOTER(vm, e); return e; @@ -577,28 +643,31 @@ BclNumber bcl_dup(BclNumber s) { BclError e = BCL_ERROR_NONE; - BcNum *src, dest; + 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); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - assert(s.i < ctxt->nums.len); + assert(BCL_NO_GEN(s) < ctxt->nums.len); - src = BC_NUM(ctxt, s); + src = BCL_NUM(ctxt, s); - assert(src != NULL && src->num != NULL); + assert(src != NULL && BCL_NUM_NUM(src) != NULL); // Copy the number. - bc_num_clear(&dest); - bc_num_createCopy(&dest, src); + 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); @@ -608,75 +677,81 @@ err: void bcl_num_destruct(void* num) { - BcNum* n = (BcNum*) num; + BclNum* n = (BclNum*) num; assert(n != NULL); - if (n->num == NULL) return; + if (BCL_NUM_ARRAY(n) == NULL) return; - bc_num_free(num); - bc_num_clear(num); + bc_num_free(BCL_NUM_NUM(n)); + bc_num_clear(BCL_NUM_NUM(n)); } bool bcl_num_neg(BclNumber n) { - BcNum* num; + BclNum* num; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); - num = BC_NUM(ctxt, n); + assert(BCL_NO_GEN(n) < ctxt->nums.len); - assert(num != NULL && num->num != NULL); + num = BCL_NUM(ctxt, n); - return BC_NUM_NEG(num) != 0; + 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) { - BcNum* num; + BclNum* num; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); + + assert(BCL_NO_GEN(n) < ctxt->nums.len); - num = BC_NUM(ctxt, n); + num = BCL_NUM(ctxt, n); - assert(num != NULL && num->num != NULL); + assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); - num->rdx = BC_NUM_NEG_VAL(num, neg); + BCL_NUM_NUM(num)->rdx = BC_NUM_NEG_VAL(BCL_NUM_NUM(num), neg); } size_t bcl_num_scale(BclNumber n) { - BcNum* num; + BclNum* num; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); + + assert(BCL_NO_GEN(n) < ctxt->nums.len); - num = BC_NUM(ctxt, n); + num = BCL_NUM(ctxt, n); - assert(num != NULL && num->num != NULL); + assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); - return bc_num_scale(num); + return bc_num_scale(BCL_NUM_NUM(num)); } BclError bcl_num_setScale(BclNumber n, size_t scale) { BclError e = BCL_ERROR_NONE; - BcNum* nptr; + BclNum* nptr; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -684,18 +759,27 @@ bcl_num_setScale(BclNumber n, size_t scale) BC_CHECK_NUM_ERR(ctxt, n); + BCL_CHECK_NUM_VALID(ctxt, n); + BC_FUNC_HEADER(vm, err); - assert(n.i < ctxt->nums.len); + assert(BCL_NO_GEN(n) < ctxt->nums.len); - nptr = BC_NUM(ctxt, n); + nptr = BCL_NUM(ctxt, n); - assert(nptr != NULL && nptr->num != NULL); + assert(nptr != NULL && BCL_NUM_ARRAY(nptr) != NULL); - if (scale > nptr->scale) bc_num_extend(nptr, scale - nptr->scale); - else if (scale < nptr->scale) bc_num_truncate(nptr, nptr->scale - scale); + 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; @@ -704,54 +788,75 @@ err: size_t bcl_num_len(BclNumber n) { - BcNum* num; + BclNum* num; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); - num = BC_NUM(ctxt, n); + assert(BCL_NO_GEN(n) < ctxt->nums.len); - assert(num != NULL && num->num != NULL); + num = BCL_NUM(ctxt, n); - return bc_num_len(num); + assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); + + return bc_num_len(BCL_NUM_NUM(num)); } -BclError -bcl_bigdig(BclNumber n, BclBigDig* result) +static BclError +bcl_bigdig_helper(BclNumber n, BclBigDig* result, bool destruct) { BclError e = BCL_ERROR_NONE; - BcNum* num; + 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(BCL_NO_GEN(n) < ctxt->nums.len); assert(result != NULL); - num = BC_NUM(ctxt, n); + num = BCL_NUM(ctxt, n); - assert(num != NULL && num->num != NULL); + assert(num != NULL && BCL_NUM_ARRAY(num) != NULL); - *result = bc_num_bigdig(num); + *result = bc_num_bigdig(BCL_NUM_NUM(num)); err: - bcl_num_dtor(ctxt, n, num); + + if (destruct) + { + bcl_num_dtor(ctxt, n, num); + } + BC_FUNC_FOOTER(vm, e); return e; } +BclError +bcl_bigdig(BclNumber n, BclBigDig* result) +{ + return bcl_bigdig_helper(n, result, true); +} + +BclError +bcl_bigdig_keep(BclNumber n, BclBigDig* result) +{ + return bcl_bigdig_helper(n, result, false); +} + BclNumber bcl_bigdig2num(BclBigDig val) { BclError e = BCL_ERROR_NONE; - BcNum n; + BclNum n; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -760,11 +865,12 @@ bcl_bigdig2num(BclBigDig val) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - bc_num_createFromBigdig(&n, val); + bc_num_createFromBigdig(BCL_NUM_NUM_NP(n), val); err: + BC_FUNC_FOOTER(vm, e); BC_MAYBE_SETUP(ctxt, e, n, idx); @@ -773,20 +879,22 @@ err: /** * 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. + * @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. + * @param destruct True if the parameters should be consumed, false otherwise. + * @return The result of the operation. */ static BclNumber bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op, - const BcNumBinaryOpReq req) + const BcNumBinaryOpReq req, bool destruct) { BclError e = BCL_ERROR_NONE; - BcNum* aptr; - BcNum* bptr; - BcNum c; + BclNum* aptr; + BclNum* bptr; + BclNum c; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -798,27 +906,31 @@ bcl_binary(BclNumber a, BclNumber b, const BcNumBinaryOp op, BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); + assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len); - aptr = BC_NUM(ctxt, a); - bptr = BC_NUM(ctxt, b); + aptr = BCL_NUM(ctxt, a); + bptr = BCL_NUM(ctxt, b); assert(aptr != NULL && bptr != NULL); - assert(aptr->num != NULL && bptr->num != NULL); + assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL); // Clear and initialize the result. - bc_num_clear(&c); - bc_num_init(&c, req(aptr, bptr, ctxt->scale)); + 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(aptr, bptr, &c, 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); + if (destruct) + { + // 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); @@ -829,57 +941,105 @@ err: BclNumber bcl_add(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_add, bc_num_addReq); + return bcl_binary(a, b, bc_num_add, bc_num_addReq, true); +} + +BclNumber +bcl_add_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_add, bc_num_addReq, false); } BclNumber bcl_sub(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_sub, bc_num_addReq); + return bcl_binary(a, b, bc_num_sub, bc_num_addReq, true); +} + +BclNumber +bcl_sub_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_sub, bc_num_addReq, false); } BclNumber bcl_mul(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_mul, bc_num_mulReq); + return bcl_binary(a, b, bc_num_mul, bc_num_mulReq, true); +} + +BclNumber +bcl_mul_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_mul, bc_num_mulReq, false); } BclNumber bcl_div(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_div, bc_num_divReq); + return bcl_binary(a, b, bc_num_div, bc_num_divReq, true); +} + +BclNumber +bcl_div_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_div, bc_num_divReq, false); } BclNumber bcl_mod(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_mod, bc_num_divReq); + return bcl_binary(a, b, bc_num_mod, bc_num_divReq, true); +} + +BclNumber +bcl_mod_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_mod, bc_num_divReq, false); } BclNumber bcl_pow(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_pow, bc_num_powReq); + return bcl_binary(a, b, bc_num_pow, bc_num_powReq, true); +} + +BclNumber +bcl_pow_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_pow, bc_num_powReq, false); } BclNumber bcl_lshift(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq); + return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq, true); +} + +BclNumber +bcl_lshift_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_lshift, bc_num_placesReq, false); } BclNumber bcl_rshift(BclNumber a, BclNumber b) { - return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq); + return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq, true); } BclNumber -bcl_sqrt(BclNumber a) +bcl_rshift_keep(BclNumber a, BclNumber b) +{ + return bcl_binary(a, b, bc_num_rshift, bc_num_placesReq, false); +} + +static BclNumber +bcl_sqrt_helper(BclNumber a, bool destruct) { BclError e = BCL_ERROR_NONE; - BcNum* aptr; - BcNum b; + BclNum* aptr; + BclNum b; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -890,30 +1050,48 @@ bcl_sqrt(BclNumber a) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - assert(a.i < ctxt->nums.len); + assert(BCL_NO_GEN(a) < ctxt->nums.len); - aptr = BC_NUM(ctxt, a); + aptr = BCL_NUM(ctxt, a); - bc_num_sqrt(aptr, &b, ctxt->scale); + bc_num_sqrt(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), ctxt->scale); err: - bcl_num_dtor(ctxt, a, aptr); + + if (destruct) + { + 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) +BclNumber +bcl_sqrt(BclNumber a) +{ + return bcl_sqrt_helper(a, true); +} + +BclNumber +bcl_sqrt_keep(BclNumber a) +{ + return bcl_sqrt_helper(a, false); +} + +static BclError +bcl_divmod_helper(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d, + bool destruct) { BclError e = BCL_ERROR_NONE; size_t req; - BcNum* aptr; - BcNum* bptr; - BcNum cnum, dnum; + BclNum* aptr; + BclNum* bptr; + BclNum cnum, dnum; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -924,41 +1102,45 @@ bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 2); + BCL_GROW_NUMS(ctxt); assert(c != NULL && d != NULL); - aptr = BC_NUM(ctxt, a); - bptr = BC_NUM(ctxt, b); + aptr = BCL_NUM(ctxt, a); + bptr = BCL_NUM(ctxt, b); assert(aptr != NULL && bptr != NULL); - assert(aptr->num != NULL && bptr->num != NULL); + assert(BCL_NUM_ARRAY(aptr) != NULL && BCL_NUM_ARRAY(bptr) != NULL); - bc_num_clear(&cnum); - bc_num_clear(&dnum); + bc_num_clear(BCL_NUM_NUM_NP(cnum)); + bc_num_clear(BCL_NUM_NUM_NP(dnum)); - req = bc_num_divReq(aptr, bptr, ctxt->scale); + req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr), ctxt->scale); // Initialize the numbers. - bc_num_init(&cnum, req); + bc_num_init(BCL_NUM_NUM_NP(cnum), req); BC_UNSETJMP(vm); BC_SETJMP(vm, err); - bc_num_init(&dnum, req); + bc_num_init(BCL_NUM_NUM_NP(dnum), req); - bc_num_divmod(aptr, bptr, &cnum, &dnum, ctxt->scale); + 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 (destruct) + { + // 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 (cnum.num != NULL) bc_num_free(&cnum); - if (dnum.num != NULL) bc_num_free(&dnum); + 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; @@ -978,15 +1160,27 @@ err: return e; } -BclNumber -bcl_modexp(BclNumber a, BclNumber b, BclNumber c) +BclError +bcl_divmod(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d) +{ + return bcl_divmod_helper(a, b, c, d, true); +} + +BclError +bcl_divmod_keep(BclNumber a, BclNumber b, BclNumber* c, BclNumber* d) +{ + return bcl_divmod_helper(a, b, c, d, false); +} + +static BclNumber +bcl_modexp_helper(BclNumber a, BclNumber b, BclNumber c, bool destruct) { BclError e = BCL_ERROR_NONE; size_t req; - BcNum* aptr; - BcNum* bptr; - BcNum* cptr; - BcNum d; + BclNum* aptr; + BclNum* bptr; + BclNum* cptr; + BclNum d; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -999,34 +1193,39 @@ bcl_modexp(BclNumber a, BclNumber b, BclNumber c) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); - assert(c.i < ctxt->nums.len); + assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len); + assert(BCL_NO_GEN(c) < ctxt->nums.len); - aptr = BC_NUM(ctxt, a); - bptr = BC_NUM(ctxt, b); - cptr = BC_NUM(ctxt, c); + aptr = BCL_NUM(ctxt, a); + bptr = BCL_NUM(ctxt, b); + cptr = BCL_NUM(ctxt, c); assert(aptr != NULL && bptr != NULL && cptr != NULL); - assert(aptr->num != NULL && bptr->num != NULL && cptr->num != NULL); + assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr) != NULL && + BCL_NUM_NUM(cptr) != NULL); // Prepare the result. - bc_num_clear(&d); + bc_num_clear(BCL_NUM_NUM_NP(d)); - req = bc_num_divReq(aptr, cptr, 0); + req = bc_num_divReq(BCL_NUM_NUM(aptr), BCL_NUM_NUM(cptr), 0); // Initialize the result. - bc_num_init(&d, req); + bc_num_init(BCL_NUM_NUM_NP(d), req); - bc_num_modexp(aptr, bptr, cptr, &d); + 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); + if (destruct) + { + // 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); @@ -1034,68 +1233,87 @@ err: return idx; } +BclNumber +bcl_modexp(BclNumber a, BclNumber b, BclNumber c) +{ + return bcl_modexp_helper(a, b, c, true); +} + +BclNumber +bcl_modexp_keep(BclNumber a, BclNumber b, BclNumber c) +{ + return bcl_modexp_helper(a, b, c, false); +} + ssize_t bcl_cmp(BclNumber a, BclNumber b) { - BcNum* aptr; - BcNum* bptr; + BclNum* aptr; + BclNum* bptr; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(a.i < ctxt->nums.len && b.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, a); + BCL_CHECK_NUM_VALID(ctxt, b); + + assert(BCL_NO_GEN(a) < ctxt->nums.len && BCL_NO_GEN(b) < ctxt->nums.len); - aptr = BC_NUM(ctxt, a); - bptr = BC_NUM(ctxt, b); + aptr = BCL_NUM(ctxt, a); + bptr = BCL_NUM(ctxt, b); assert(aptr != NULL && bptr != NULL); - assert(aptr->num != NULL && bptr->num != NULL); + assert(BCL_NUM_NUM(aptr) != NULL && BCL_NUM_NUM(bptr)); - return bc_num_cmp(aptr, bptr); + return bc_num_cmp(BCL_NUM_NUM(aptr), BCL_NUM_NUM(bptr)); } void bcl_zero(BclNumber n) { - BcNum* nptr; + BclNum* nptr; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); - nptr = BC_NUM(ctxt, n); + assert(BCL_NO_GEN(n) < ctxt->nums.len); - assert(nptr != NULL && nptr->num != NULL); + nptr = BCL_NUM(ctxt, n); - bc_num_zero(nptr); + assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); + + bc_num_zero(BCL_NUM_NUM(nptr)); } void bcl_one(BclNumber n) { - BcNum* nptr; + BclNum* nptr; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - assert(n.i < ctxt->nums.len); + BCL_CHECK_NUM_VALID(ctxt, n); + + assert(BCL_NO_GEN(n) < ctxt->nums.len); - nptr = BC_NUM(ctxt, n); + nptr = BCL_NUM(ctxt, n); - assert(nptr != NULL && nptr->num != NULL); + assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); - bc_num_one(nptr); + bc_num_one(BCL_NUM_NUM(nptr)); } BclNumber bcl_parse(const char* restrict val) { BclError e = BCL_ERROR_NONE; - BcNum n; + BclNum n; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -1105,7 +1323,7 @@ bcl_parse(const char* restrict val) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); assert(val != NULL); @@ -1122,46 +1340,53 @@ bcl_parse(const char* restrict val) } // Clear and initialize the number. - bc_num_clear(&n); - bc_num_init(&n, BC_NUM_DEF_SIZE); + bc_num_clear(BCL_NUM_NUM_NP(n)); + bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); - bc_num_parse(&n, val, (BcBigDig) ctxt->ibase); + 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) +static char* +bcl_string_helper(BclNumber n, bool destruct) { - BcNum* nptr; + BclNum* nptr; char* str = NULL; BclContext ctxt; BcVm* vm = bcl_getspecific(); BC_CHECK_CTXT_ASSERT(vm, ctxt); - if (BC_ERR(n.i >= ctxt->nums.len)) return str; + BCL_CHECK_NUM_VALID(ctxt, n); + + if (BC_ERR(BCL_NO_GEN(n) >= ctxt->nums.len)) return str; BC_FUNC_HEADER(vm, err); - assert(n.i < ctxt->nums.len); + assert(BCL_NO_GEN(n) < ctxt->nums.len); - nptr = BC_NUM(ctxt, n); + nptr = BCL_NUM(ctxt, n); - assert(nptr != NULL && nptr->num != NULL); + assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); // Clear the buffer. bc_vec_popAll(&vm->out); // Print to the buffer. - bc_num_print(nptr, (BcBigDig) ctxt->obase, false); + 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. @@ -1169,20 +1394,37 @@ bcl_string(BclNumber n) err: - // Eat the operand. - bcl_num_dtor(ctxt, n, nptr); + if (destruct) + { + // Eat the operand. + bcl_num_dtor(ctxt, n, nptr); + } BC_FUNC_FOOTER_NO_ERR(vm); return str; } -BclNumber -bcl_irand(BclNumber a) +char* +bcl_string(BclNumber n) +{ + return bcl_string_helper(n, true); +} + +char* +bcl_string_keep(BclNumber n) +{ + return bcl_string_helper(n, false); +} + +#if BC_ENABLE_EXTRA_MATH + +static BclNumber +bcl_irand_helper(BclNumber a, bool destruct) { BclError e = BCL_ERROR_NONE; - BcNum* aptr; - BcNum b; + BclNum* aptr; + BclNum b; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -1193,24 +1435,27 @@ bcl_irand(BclNumber a) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - assert(a.i < ctxt->nums.len); + assert(BCL_NO_GEN(a) < ctxt->nums.len); - aptr = BC_NUM(ctxt, a); + aptr = BCL_NUM(ctxt, a); - assert(aptr != NULL && aptr->num != NULL); + assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL); // Clear and initialize the result. - bc_num_clear(&b); - bc_num_init(&b, BC_NUM_DEF_SIZE); + bc_num_clear(BCL_NUM_NUM_NP(b)); + bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE); - bc_num_irand(aptr, &b, &vm->rng); + bc_num_irand(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), &vm->rng); err: - // Eat the operand. - bcl_num_dtor(ctxt, a, aptr); + if (destruct) + { + // Eat the operand. + bcl_num_dtor(ctxt, a, aptr); + } BC_FUNC_FOOTER(vm, e); BC_MAYBE_SETUP(ctxt, e, b, idx); @@ -1218,6 +1463,18 @@ err: return idx; } +BclNumber +bcl_irand(BclNumber a) +{ + return bcl_irand_helper(a, true); +} + +BclNumber +bcl_irand_keep(BclNumber a) +{ + return bcl_irand_helper(a, false); +} + /** * Helps bcl_frand(). This is separate because the error handling is easier that * way. It is also easier to do ifrand that way. @@ -1257,6 +1514,7 @@ bcl_frandHelper(BcNum* restrict b, size_t places) bc_num_shiftRight(b, places); err: + bc_num_free(&pow); BC_LONGJMP_CONT(vm); } @@ -1265,7 +1523,7 @@ BclNumber bcl_frand(size_t places) { BclError e = BCL_ERROR_NONE; - BcNum n; + BclNum n; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -1274,13 +1532,13 @@ bcl_frand(size_t places) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); // Clear and initialize the number. - bc_num_clear(&n); - bc_num_init(&n, BC_NUM_DEF_SIZE); + bc_num_clear(BCL_NUM_NUM_NP(n)); + bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); - bcl_frandHelper(&n, places); + bcl_frandHelper(BCL_NUM_NUM_NP(n), places); err: @@ -1319,17 +1577,18 @@ bcl_ifrandHelper(BcNum* restrict a, BcNum* restrict b, size_t 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) +static BclNumber +bcl_ifrand_helper(BclNumber a, size_t places, bool destruct) { BclError e = BCL_ERROR_NONE; - BcNum* aptr; - BcNum b; + BclNum* aptr; + BclNum b; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -1339,24 +1598,27 @@ bcl_ifrand(BclNumber a, size_t places) BC_FUNC_HEADER(vm, err); - bc_vec_grow(&ctxt->nums, 1); + BCL_GROW_NUMS(ctxt); - assert(a.i < ctxt->nums.len); + assert(BCL_NO_GEN(a) < ctxt->nums.len); - aptr = BC_NUM(ctxt, a); + aptr = BCL_NUM(ctxt, a); - assert(aptr != NULL && aptr->num != NULL); + assert(aptr != NULL && BCL_NUM_NUM(aptr) != NULL); // Clear and initialize the number. - bc_num_clear(&b); - bc_num_init(&b, BC_NUM_DEF_SIZE); + bc_num_clear(BCL_NUM_NUM_NP(b)); + bc_num_init(BCL_NUM_NUM_NP(b), BC_NUM_DEF_SIZE); - bcl_ifrandHelper(aptr, &b, places); + bcl_ifrandHelper(BCL_NUM_NUM(aptr), BCL_NUM_NUM_NP(b), places); err: - // Eat the oprand. - bcl_num_dtor(ctxt, a, aptr); + if (destruct) + { + // Eat the oprand. + bcl_num_dtor(ctxt, a, aptr); + } BC_FUNC_FOOTER(vm, e); BC_MAYBE_SETUP(ctxt, e, b, idx); @@ -1364,11 +1626,23 @@ err: return idx; } -BclError -bcl_rand_seedWithNum(BclNumber n) +BclNumber +bcl_ifrand(BclNumber a, size_t places) +{ + return bcl_ifrand_helper(a, places, true); +} + +BclNumber +bcl_ifrand_keep(BclNumber a, size_t places) +{ + return bcl_ifrand_helper(a, places, false); +} + +static BclError +bcl_rand_seedWithNum_helper(BclNumber n, bool destruct) { BclError e = BCL_ERROR_NONE; - BcNum* nptr; + BclNum* nptr; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -1377,20 +1651,40 @@ bcl_rand_seedWithNum(BclNumber n) BC_FUNC_HEADER(vm, err); - assert(n.i < ctxt->nums.len); + assert(BCL_NO_GEN(n) < ctxt->nums.len); - nptr = BC_NUM(ctxt, n); + nptr = BCL_NUM(ctxt, n); - assert(nptr != NULL && nptr->num != NULL); + assert(nptr != NULL && BCL_NUM_NUM(nptr) != NULL); - bc_num_rng(nptr, &vm->rng); + bc_num_rng(BCL_NUM_NUM(nptr), &vm->rng); err: + + if (destruct) + { + // Eat the oprand. + bcl_num_dtor(ctxt, n, nptr); + } + BC_FUNC_FOOTER(vm, e); + return e; } BclError +bcl_rand_seedWithNum(BclNumber n) +{ + return bcl_rand_seedWithNum_helper(n, true); +} + +BclError +bcl_rand_seedWithNum_keep(BclNumber n) +{ + return bcl_rand_seedWithNum_helper(n, false); +} + +BclError bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE]) { BclError e = BCL_ERROR_NONE; @@ -1411,7 +1705,9 @@ bcl_rand_seed(unsigned char seed[BCL_SEED_SIZE]) bc_rand_seed(&vm->rng, vals[0], vals[1], vals[2], vals[3]); err: + BC_FUNC_FOOTER(vm, e); + return e; } @@ -1427,7 +1723,7 @@ BclNumber bcl_rand_seed2num(void) { BclError e = BCL_ERROR_NONE; - BcNum n; + BclNum n; BclNumber idx; BclContext ctxt; BcVm* vm = bcl_getspecific(); @@ -1437,12 +1733,13 @@ bcl_rand_seed2num(void) BC_FUNC_HEADER(vm, err); // Clear and initialize the number. - bc_num_clear(&n); - bc_num_init(&n, BC_NUM_DEF_SIZE); + bc_num_clear(BCL_NUM_NUM_NP(n)); + bc_num_init(BCL_NUM_NUM_NP(n), BC_NUM_DEF_SIZE); - bc_num_createFromRNG(&n, &vm->rng); + bc_num_createFromRNG(BCL_NUM_NUM_NP(n), &vm->rng); err: + BC_FUNC_FOOTER(vm, e); BC_MAYBE_SETUP(ctxt, e, n, idx); @@ -1466,4 +1763,6 @@ bcl_rand_bounded(BclRandInt bound) return (BclRandInt) bc_rand_bounded(&vm->rng, (BcRand) bound); } +#endif // BC_ENABLE_EXTRA_MATH + #endif // BC_ENABLE_LIBRARY diff --git a/src/program.c b/src/program.c index b6fac12c7d65..243f827fe98c 100644 --- a/src/program.c +++ b/src/program.c @@ -757,9 +757,16 @@ bc_program_read(BcProgram* p) // struct. bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE); } - // This needs to be updated because the parser could have been used - // somewhere else - else bc_parse_updateFunc(&vm->read_prs, BC_PROG_READ); + else + { + // This needs to be updated because the parser could have been used + // somewhere else. + bc_parse_updateFunc(&vm->read_prs, BC_PROG_READ); + + // The read buffer also needs to be emptied or else it will still + // contain previous read expressions. + bc_vec_empty(&vm->read_buf); + } BC_SETJMP_LOCKED(vm, exec_err); @@ -643,12 +643,14 @@ bc_vm_shutdown(void) if (vm->catalog != BC_VM_INVALID_CATALOG) catclose(vm->catalog); #endif // BC_ENABLE_NLS +#if !BC_ENABLE_LIBRARY #if BC_ENABLE_HISTORY // This must always run to ensure that the terminal is back to normal, i.e., // has raw mode disabled. But we should only do it if we did not have a bad // terminal because history was not initialized if it is a bad terminal. if (BC_TTY && !vm->history.badTerm) bc_history_free(&vm->history); #endif // BC_ENABLE_HISTORY +#endif // !BC_ENABLE_LIBRARY #if BC_DEBUG #if !BC_ENABLE_LIBRARY diff --git a/tests/bcl.c b/tests/bcl.c index cea63f457cd4..5bb50c29a753 100644 --- a/tests/bcl.c +++ b/tests/bcl.c @@ -55,7 +55,7 @@ main(void) BclError e; BclContext ctxt; size_t scale; - BclNumber n, n2, n3, n4, n5, n6; + BclNumber n, n2, n3, n4, n5, n6, n7; char* res; BclBigDig b = 0; @@ -124,9 +124,21 @@ main(void) if (!bcl_num_neg(n4)) err(BCL_ERROR_FATAL_UNKNOWN_ERR); // Add them and check the result. + n5 = bcl_add_keep(n3, n4); + err(bcl_err(n5)); + res = bcl_string(n5); + if (res == NULL) err(BCL_ERROR_FATAL_ALLOC_ERR); + if (strcmp(res, "-25452.9108273")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); + + // We want to ensure all memory gets freed because we run this under + // Valgrind. + free(res); + + // Add them and check the result. n3 = bcl_add(n3, n4); err(bcl_err(n3)); - res = bcl_string(bcl_dup(n3)); + res = bcl_string_keep(n3); + if (res == NULL) err(BCL_ERROR_FATAL_ALLOC_ERR); if (strcmp(res, "-25452.9108273")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); // We want to ensure all memory gets freed because we run this under @@ -136,19 +148,29 @@ main(void) // Ensure that divmod, a special case, works. n4 = bcl_parse("8937458902.2890347"); err(bcl_err(n4)); - e = bcl_divmod(bcl_dup(n4), n3, &n5, &n6); + e = bcl_divmod_keep(n4, n3, &n5, &n6); err(e); res = bcl_string(n5); - if (strcmp(res, "-351137.0060159482")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); - free(res); res = bcl_string(n6); - if (strcmp(res, ".00000152374405414")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); + free(res); + + // Ensure that divmod, a special case, works. + n4 = bcl_parse("8937458902.2890347"); + err(bcl_err(n4)); + e = bcl_divmod(bcl_dup(n4), n3, &n5, &n6); + err(e); + + res = bcl_string(n5); + if (strcmp(res, "-351137.0060159482")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); + free(res); + res = bcl_string(n6); + if (strcmp(res, ".00000152374405414")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); free(res); // Ensure that sqrt works. This is also a special case. The reason is @@ -214,10 +236,18 @@ main(void) err(bcl_err(n3)); // Repeat. + n2 = bcl_ifrand_keep(n3, 10); + err(bcl_err(n2)); + + // Repeat. n2 = bcl_ifrand(bcl_dup(n3), 10); err(bcl_err(n2)); // Still checking asserts. + e = bcl_rand_seedWithNum_keep(n3); + err(e); + + // Still checking asserts. e = bcl_rand_seedWithNum(n3); err(e); @@ -229,9 +259,12 @@ main(void) n5 = bcl_parse("10"); err(bcl_err(n5)); - n6 = bcl_modexp(bcl_dup(n5), bcl_dup(n5), bcl_dup(n5)); + n6 = bcl_modexp_keep(n5, n5, n5); err(bcl_err(n6)); + n7 = bcl_modexp(bcl_dup(n5), bcl_dup(n5), bcl_dup(n5)); + err(bcl_err(n7)); + // Clean up. bcl_num_free(n); @@ -250,6 +283,11 @@ main(void) n4 = bcl_parse("-1.01"); err(bcl_err(n4)); + res = bcl_string_keep(n); + if (strcmp(res, ".01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); + + free(res); + res = bcl_string(bcl_dup(n)); if (strcmp(res, ".01")) err(BCL_ERROR_FATAL_UNKNOWN_ERR); diff --git a/tests/read.sh b/tests/read.sh index d7be18fdcecb..4881c10db58c 100755 --- a/tests/read.sh +++ b/tests/read.sh @@ -74,6 +74,7 @@ results="$testdir/$d/read_results.txt" errors="$testdir/$d/read_errors.txt" out="$outputdir/${d}_outputs/read_results.txt" +multiple_res="$outputdir/${d}_outputs/read_multiple_results.txt" outdir=$(dirname "$out") # Make sure the directory exists. @@ -89,11 +90,13 @@ if [ "$d" = "bc" ]; then halt="halt" read_call="read()" read_expr="${read_call}\n5+5;" + read_multiple=$(printf '%s\n%s\n%s\n' "3" "2" "1") else options="-x" halt="q" read_call="?" read_expr="${read_call}" + read_multiple=$(printf '%spR\n%spR\n%spR\n' "3" "2" "1") fi # I use these, so unset them to make the tests work. @@ -116,6 +119,16 @@ done < "$name" printf 'pass\n' +printf 'Running %s read multiple...' "$d" + +printf '3\n2\n1\n' > "$multiple_res" + +# Run multiple read() calls. +printf '%s\n' "$read_multiple" | "$exe" "$@" "$options" -e "$read_call" -e "$read_call" -e "$read_call" > "$out" +checktest "$d" "$?" 'read multiple' "$multiple_res" "$out" + +printf 'pass\n' + printf 'Running %s read errors...' "$d" # Run read on every line. |