Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 10 Mar 2023 10:34:18 GMT
From:      =?utf-8?Q?Stefan=20E=C3=9Fer?= <se@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 175a4d10428e - main - contrib/bc: update to version 6.4.0
Message-ID:  <202303101034.32AAYI1H009185@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by se:

URL: https://cgit.FreeBSD.org/src/commit/?id=175a4d10428e1c2f26f1426003757daf25d4726f

commit 175a4d10428e1c2f26f1426003757daf25d4726f
Author:     Stefan Eßer <se@FreeBSD.org>
AuthorDate: 2023-03-10 10:33:33 +0000
Commit:     Stefan Eßer <se@FreeBSD.org>
CommitDate: 2023-03-10 10:33:33 +0000

    contrib/bc: update to version 6.4.0
    
    This version contains a fix for an issue that can affect complex
    bc scripts that use multiple read() functions that receive input from
    an interactive user. The same value could be returned multiple times.
    
    MFC after:      2 weeks
---
 contrib/bc/Makefile.in       |   2 +-
 contrib/bc/NEWS.md           |  19 ++
 contrib/bc/configure.sh      |   2 +-
 contrib/bc/include/bcl.h     |  51 +++
 contrib/bc/include/library.h | 210 ++++++++++--
 contrib/bc/include/program.h | 210 +++++-------
 contrib/bc/include/version.h |   2 +-
 contrib/bc/include/vm.h      |   4 +
 contrib/bc/manuals/bcl.3     | 422 +++++++++++++++++++++++-
 contrib/bc/manuals/bcl.3.md  | 320 ++++++++++++++++++-
 contrib/bc/src/data.c        |  75 +++--
 contrib/bc/src/library.c     | 739 ++++++++++++++++++++++++++++++-------------
 contrib/bc/src/program.c     |  13 +-
 contrib/bc/src/vm.c          |   2 +
 contrib/bc/tests/bcl.c       |  52 ++-
 contrib/bc/tests/read.sh     |  13 +
 16 files changed, 1693 insertions(+), 443 deletions(-)

diff --git a/contrib/bc/Makefile.in b/contrib/bc/Makefile.in
index 89ddb7589c47..f936fc2c6de6 100644
--- a/contrib/bc/Makefile.in
+++ b/contrib/bc/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'
diff --git a/contrib/bc/NEWS.md b/contrib/bc/NEWS.md
index ad118e401c32..6dff6822fb16 100644
--- a/contrib/bc/NEWS.md
+++ b/contrib/bc/NEWS.md
@@ -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/contrib/bc/configure.sh b/contrib/bc/configure.sh
index 3ada5298e9ed..021d30807ffb 100755
--- a/contrib/bc/configure.sh
+++ b/contrib/bc/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/contrib/bc/include/bcl.h b/contrib/bc/include/bcl.h
index 253138231c66..0908e215182c 100644
--- a/contrib/bc/include/bcl.h
+++ b/contrib/bc/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,42 +241,78 @@ bcl_dup(BclNumber s);
 BclError
 bcl_bigdig(BclNumber n, BclBigDig* result);
 
+BclError
+bcl_bigdig_keep(BclNumber n, BclBigDig* result);
+
 BclNumber
 bcl_bigdig2num(BclBigDig val);
 
 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,18 +328,30 @@ 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]);
 
diff --git a/contrib/bc/include/library.h b/contrib/bc/include/library.h
index 76df91392da1..1edd3757444c 100644
--- a/contrib/bc/include/library.h
+++ b/contrib/bc/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/contrib/bc/include/program.h b/contrib/bc/include/program.h
index ff32d5db7760..1df753afad22 100644
--- a/contrib/bc/include/program.h
+++ b/contrib/bc/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/contrib/bc/include/version.h b/contrib/bc/include/version.h
index f5e345b3b189..3745ed9b5f74 100644
--- a/contrib/bc/include/version.h
+++ b/contrib/bc/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/contrib/bc/include/vm.h b/contrib/bc/include/vm.h
index dd21d43f5260..c56cc8e7370a 100644
--- a/contrib/bc/include/vm.h
+++ b/contrib/bc/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/contrib/bc/manuals/bcl.3 b/contrib/bc/manuals/bcl.3
index 5c3731a141eb..cb65a2b8b991 100644
--- a/contrib/bc/manuals/bcl.3
+++ b/contrib/bc/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.
*** 2626 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202303101034.32AAYI1H009185>