From 3ee4c5ebe32e5dc6da734b7d10924bb27dec2521 Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Tue, 20 Aug 2019 17:10:44 -0400
Subject: [PATCH 1/7] Replace the mp_to_decimal macro with a function...

that chooses a new internal function that uses Barrett reduction to
speed up stringifying large integers to base 10 if it's available and
the number is above a cutoff size, otherwise it just falls back to
mp_to_radix.
---
 bn_mp_to_decimal.c        |  23 ++++
 bn_s_mp_to_decimal_fast.c | 234 ++++++++++++++++++++++++++++++++++++++
 demo/test.c               |  50 ++++++++
 libtommath_VS2008.vcproj  |   8 ++
 makefile                  |  15 +--
 makefile.mingw            |  15 +--
 makefile.msvc             |  15 +--
 makefile.shared           |  15 +--
 makefile.unix             |  15 +--
 tommath.def               |   1 +
 tommath.h                 |   2 +-
 tommath_class.h           |  27 +++++
 tommath_private.h         |   1 +
 13 files changed, 385 insertions(+), 36 deletions(-)
 create mode 100644 bn_mp_to_decimal.c
 create mode 100644 bn_s_mp_to_decimal_fast.c

diff --git a/bn_mp_to_decimal.c b/bn_mp_to_decimal.c
new file mode 100644
index 000000000..43398dd64
--- /dev/null
+++ b/bn_mp_to_decimal.c
@@ -0,0 +1,23 @@
+#include "tommath_private.h"
+#ifdef BN_MP_TO_DECIMAL_C
+/* LibTomMath, multiple-precision integer library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+/* stores a bignum as a decimal ASCII string, using Barrett
+ * reduction if available.
+ */
+
+mp_err mp_to_decimal(const mp_int *a, char *str, size_t maxlen)
+{
+   mp_err err;
+
+   if (MP_HAS(S_MP_TO_DECIMAL_FAST) && (a->used > 10)) {
+      err = s_mp_to_decimal_fast(a, str, maxlen);
+   } else {
+      err = mp_to_radix(a, str, maxlen, 10);
+   }
+
+   return err;
+}
+
+#endif
diff --git a/bn_s_mp_to_decimal_fast.c b/bn_s_mp_to_decimal_fast.c
new file mode 100644
index 000000000..5ce597b57
--- /dev/null
+++ b/bn_s_mp_to_decimal_fast.c
@@ -0,0 +1,234 @@
+#include "tommath_private.h"
+#include <string.h>
+#ifdef BN_S_MP_TO_DECIMAL_FAST_C
+/* LibTomMath, multiple-precision integer library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+/* store a bignum as a decimal ASCII string */
+mp_err s_mp_to_decimal_fast_rec(const mp_int *number, mp_int *nL, mp_int *shiftL, mp_int *mL, int precalc_array_index,
+                                int left,
+                                char **result,
+                                size_t *maxlen)
+{
+   mp_int q, nLq, r;
+   mp_err err;
+
+   if (precalc_array_index < 0) {
+      int n = mp_get_i32(number), n2 = n, t = 0, c;
+      char *i = *result;
+      char s[4] = "000";
+
+      while (n) {
+         s[2 - t] = mp_s_rmap[n % 10];
+         t++;
+         n /= 10;
+      }
+
+      if (!left && n2 < 100) {
+         t++;
+         if (n2 < 10) {
+            t++;
+         }
+         if (n2 == 0) {
+            t++;
+         }
+      }
+
+      if (*maxlen < (size_t)t || (*maxlen -= (size_t)t) < 1) {
+         /* no more room */
+         return MP_VAL;
+      }
+
+      for (c = 0; c < t; c++) {
+         i[c] = s[3 - t + c];
+      }
+
+      *result += t;
+
+      return MP_OKAY;
+   }
+
+   if ((err = mp_init_multi(&q, &nLq, &r, NULL)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+   if ((err = mp_mul(number, &mL[precalc_array_index], &q)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+   if ((err = mp_div_2d(&q, mp_get_i32(&shiftL[precalc_array_index]), &q, NULL)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   if ((err = mp_mul(&nL[precalc_array_index], &q, &nLq)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   if ((err = mp_sub(number, &nLq, &r)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   if (mp_isneg(&r)) {
+      if ((err = mp_sub_d(&q, 1, &q)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((err = mp_add(&r, &nL[precalc_array_index], &r)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+   }
+
+   --precalc_array_index;
+   if (left && mp_iszero(&q)) {
+      if ((err = s_mp_to_decimal_fast_rec(&r, nL, shiftL, mL, precalc_array_index, 1, result, maxlen)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+   } else {
+      if ((err = s_mp_to_decimal_fast_rec(&q, nL, shiftL, mL, precalc_array_index, left, result, maxlen)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((err = s_mp_to_decimal_fast_rec(&r, nL, shiftL, mL, precalc_array_index, 0, result, maxlen)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+   }
+
+   err = MP_OKAY;
+
+LBL_ERR:
+   mp_clear_multi(&q, &nLq, &r, NULL);
+   return err;
+}
+
+mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
+{
+   mp_int number, n, shift, M, M2, M22, M4, M44;
+   mp_int nL[20], shiftL[20], mL[20];
+   mp_err err;
+   char **result_addr = &result;
+   int precalc_array_index = 1, c;
+
+   if ((err = mp_init_multi(&n, &M, &M2, &M22, &M4, &M44, &mL[0], NULL)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   if ((err = mp_init_copy(&number, a)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+   if (mp_isneg(&number)) {
+      if ((err = mp_neg(&number, &number)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      result[0] = '-';
+      *result_addr += 1;
+      maxlen -= 1;
+   }
+   mp_set_u32(&n, 1000);
+
+   if ((err = mp_init_copy(&nL[0], &n)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   if ((err = mp_init_set(&shift, (mp_digit)20)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   if ((err = mp_init_copy(&shiftL[0], &shift)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+
+   /* (8 * 2**$shift) / $n rounded up */
+   mp_set_u32(&M, 8389);
+
+   /* $M / 8, rounded up */
+   mp_set_u32(&mL[0], 1049);
+
+   while (1) {
+      if ((err = mp_sqr(&n, &n)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if (mp_cmp(&n, &number) == MP_GT) {
+         break;
+      }
+
+      if ((err = mp_mul_2(&shift, &shift)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+
+      /* The following is a Newton-Raphson step, to restore the invariant
+       * that $M is (8 * 2**$shift) / $n, rounded up. */
+      {
+         if ((err = mp_sqr(&M, &M2)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_sqr(&M2, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+
+         if ((err = mp_mul(&M4, &n, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_div_2d(&M4, mp_get_i32(&shift) + 6, &M4, NULL)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_mul_2(&M2, &M2)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_sub(&M4, &M2, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_add_d(&M4, 1, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_div_2d(&M4, 3, &M4, NULL)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_sub_d(&M4, 1, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_neg(&M4, &M)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+      }
+
+      if ((err = mp_init_copy(&nL[precalc_array_index], &n)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((err = mp_init_copy(&shiftL[precalc_array_index], &shift)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+
+      /* Divide by 8, round up */
+      {
+         if ((err = mp_add_d(&M4, 1, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_div_2d(&M4, 3, &M4, NULL)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_sub_d(&M4, 1, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+         if ((err = mp_neg(&M4, &M4)) != MP_OKAY) {
+            goto LBL_ERR;
+         }
+      }
+      if ((err = mp_init_copy(&mL[precalc_array_index], &M4)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      precalc_array_index++;
+   }
+
+   if ((err = s_mp_to_decimal_fast_rec(&number, nL, shiftL, mL, precalc_array_index - 1, 1, result_addr,
+                                       &maxlen)) != MP_OKAY) {
+      goto LBL_ERR;
+   }
+   *result_addr[0] = '\0';
+
+   err = MP_OKAY;
+
+LBL_ERR:
+   mp_clear_multi(&number, &n, &shift, &M, &M2, &M22, &M4, &M44, NULL);
+   for (c = 0; c < precalc_array_index; c++) {
+      mp_clear_multi(&nL[c], &shiftL[c], &mL[c], NULL);
+   }
+   return err;
+}
+
+#endif
diff --git a/demo/test.c b/demo/test.c
index 79cfb3855..f4a406dca 100644
--- a/demo/test.c
+++ b/demo/test.c
@@ -2214,6 +2214,54 @@ static int test_s_mp_toom_sqr(void)
    return EXIT_FAILURE;
 }
 
+static int test_mp_to_decimal(void)
+{
+   mp_int a, b;
+   int size, err, strlength;
+   char *str;
+
+   if ((err = mp_init_multi(&a, &b, NULL)) != MP_OKAY) {
+      goto LTM_ERR;
+   }
+   for (size = 1; size < 1000; size += 10) {
+      int times;
+      printf("Testing mp_to_decimal: %5d bits    \r", size);
+      fflush(stdout);
+      for (times = 0; times < 5; times++) {
+         if ((err = mp_rand(&a, size)) != MP_OKAY) {
+            goto LTM_ERR;
+         }
+         if (times % 2) {
+            /* also test some negative numbers */
+            if ((err = mp_neg(&a, &a)) != MP_OKAY) {
+               goto LTM_ERR;
+            }
+         }
+         if ((err = mp_radix_size(&a, 10, &strlength)) != MP_OKAY) {
+            goto LTM_ERR;
+         }
+         str = (char *)malloc((size_t)strlength);
+         if ((err = mp_to_decimal(&a, str, (size_t)strlength)) != MP_OKAY) {
+            goto LTM_ERR;
+         }
+         if ((err = mp_read_radix(&b, str, 10)) != MP_OKAY) {
+            goto LTM_ERR;
+         }
+         free(str);
+         if (mp_cmp(&a, &b) != MP_EQ) {
+            fprintf(stderr, "s_mp_to_decimal_fast failed at size %d\n", size);
+            goto LTM_ERR;
+         }
+      }
+   }
+
+   mp_clear_multi(&a, &b, NULL);
+   return EXIT_SUCCESS;
+LTM_ERR:
+   mp_clear_multi(&a, &b, NULL);
+   return EXIT_FAILURE;
+}
+
 int unit_tests(int argc, char **argv)
 {
    static const struct {
@@ -2264,8 +2312,10 @@ int unit_tests(int argc, char **argv)
       T1(s_mp_karatsuba_sqr, S_MP_KARATSUBA_SQR),
       T1(s_mp_toom_mul, S_MP_TOOM_MUL),
       T1(s_mp_toom_sqr, S_MP_TOOM_SQR),
+      T1(mp_to_decimal, S_MP_TO_DECIMAL_FAST)
 #undef T2
 #undef T1
+#undef T
    };
    unsigned long i, ok, fail, nop;
    uint64_t t;
diff --git a/libtommath_VS2008.vcproj b/libtommath_VS2008.vcproj
index f61123e2a..e8b5b6adf 100644
--- a/libtommath_VS2008.vcproj
+++ b/libtommath_VS2008.vcproj
@@ -816,6 +816,10 @@
 			RelativePath="bn_mp_submod.c"
 			>
 		</File>
+		<File
+			RelativePath="bn_mp_to_decimal.c"
+			>
+		</File>
 		<File
 			RelativePath="bn_mp_to_radix.c"
 			>
@@ -936,6 +940,10 @@
 			RelativePath="bn_s_mp_sub.c"
 			>
 		</File>
+		<File
+			RelativePath="bn_s_mp_to_decimal_fast.c"
+			>
+		</File>
 		<File
 			RelativePath="bn_s_mp_toom_mul.c"
 			>
diff --git a/makefile b/makefile
index 89d2b3b89..f7aa5a826 100644
--- a/makefile
+++ b/makefile
@@ -48,13 +48,14 @@ bn_mp_reduce_is_2k.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_setup.o bn_mp_root_u32.
 bn_mp_set.o bn_mp_set_double.o bn_mp_set_i32.o bn_mp_set_i64.o bn_mp_set_l.o bn_mp_set_ll.o \
 bn_mp_set_u32.o bn_mp_set_u64.o bn_mp_set_ul.o bn_mp_set_ull.o bn_mp_shrink.o bn_mp_signed_bin_size.o \
 bn_mp_signed_rsh.o bn_mp_sqr.o bn_mp_sqrmod.o bn_mp_sqrt.o bn_mp_sqrtmod_prime.o bn_mp_sub.o bn_mp_sub_d.o \
-bn_mp_submod.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin.o \
-bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o bn_prime_tab.o bn_s_mp_add.o \
-bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o \
-bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o \
-bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o \
-bn_s_mp_prime_is_divisible.o bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o \
-bn_s_mp_sqr.o bn_s_mp_sqr_fast.o bn_s_mp_sub.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
+bn_mp_submod.o bn_mp_to_decimal.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o \
+bn_mp_to_unsigned_bin.o bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o \
+bn_prime_tab.o bn_s_mp_add.o bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o \
+bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o \
+bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o \
+bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o bn_s_mp_prime_is_divisible.o \
+bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o bn_s_mp_sqr.o bn_s_mp_sqr_fast.o \
+bn_s_mp_sub.o bn_s_mp_to_decimal_fast.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
 
 #END_INS
 
diff --git a/makefile.mingw b/makefile.mingw
index 5497a1f7a..3b858385b 100644
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -51,13 +51,14 @@ bn_mp_reduce_is_2k.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_setup.o bn_mp_root_u32.
 bn_mp_set.o bn_mp_set_double.o bn_mp_set_i32.o bn_mp_set_i64.o bn_mp_set_l.o bn_mp_set_ll.o \
 bn_mp_set_u32.o bn_mp_set_u64.o bn_mp_set_ul.o bn_mp_set_ull.o bn_mp_shrink.o bn_mp_signed_bin_size.o \
 bn_mp_signed_rsh.o bn_mp_sqr.o bn_mp_sqrmod.o bn_mp_sqrt.o bn_mp_sqrtmod_prime.o bn_mp_sub.o bn_mp_sub_d.o \
-bn_mp_submod.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin.o \
-bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o bn_prime_tab.o bn_s_mp_add.o \
-bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o \
-bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o \
-bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o \
-bn_s_mp_prime_is_divisible.o bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o \
-bn_s_mp_sqr.o bn_s_mp_sqr_fast.o bn_s_mp_sub.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
+bn_mp_submod.o bn_mp_to_decimal.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o \
+bn_mp_to_unsigned_bin.o bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o \
+bn_prime_tab.o bn_s_mp_add.o bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o \
+bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o \
+bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o \
+bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o bn_s_mp_prime_is_divisible.o \
+bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o bn_s_mp_sqr.o bn_s_mp_sqr_fast.o \
+bn_s_mp_sub.o bn_s_mp_to_decimal_fast.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
 
 HEADERS_PUB=tommath.h
 HEADERS=tommath_private.h tommath_class.h tommath_superclass.h $(HEADERS_PUB)
diff --git a/makefile.msvc b/makefile.msvc
index bddadd100..e06a47cbb 100644
--- a/makefile.msvc
+++ b/makefile.msvc
@@ -43,13 +43,14 @@ bn_mp_reduce_is_2k.obj bn_mp_reduce_is_2k_l.obj bn_mp_reduce_setup.obj bn_mp_roo
 bn_mp_set.obj bn_mp_set_double.obj bn_mp_set_i32.obj bn_mp_set_i64.obj bn_mp_set_l.obj bn_mp_set_ll.obj \
 bn_mp_set_u32.obj bn_mp_set_u64.obj bn_mp_set_ul.obj bn_mp_set_ull.obj bn_mp_shrink.obj bn_mp_signed_bin_size.obj \
 bn_mp_signed_rsh.obj bn_mp_sqr.obj bn_mp_sqrmod.obj bn_mp_sqrt.obj bn_mp_sqrtmod_prime.obj bn_mp_sub.obj bn_mp_sub_d.obj \
-bn_mp_submod.obj bn_mp_to_radix.obj bn_mp_to_signed_bin.obj bn_mp_to_signed_bin_n.obj bn_mp_to_unsigned_bin.obj \
-bn_mp_to_unsigned_bin_n.obj bn_mp_unsigned_bin_size.obj bn_mp_xor.obj bn_mp_zero.obj bn_prime_tab.obj bn_s_mp_add.obj \
-bn_s_mp_balance_mul.obj bn_s_mp_exptmod.obj bn_s_mp_exptmod_fast.obj bn_s_mp_get_bit.obj bn_s_mp_invmod_fast.obj \
-bn_s_mp_invmod_slow.obj bn_s_mp_karatsuba_mul.obj bn_s_mp_karatsuba_sqr.obj bn_s_mp_montgomery_reduce_fast.obj \
-bn_s_mp_mul_digs.obj bn_s_mp_mul_digs_fast.obj bn_s_mp_mul_high_digs.obj bn_s_mp_mul_high_digs_fast.obj \
-bn_s_mp_prime_is_divisible.obj bn_s_mp_rand_jenkins.obj bn_s_mp_rand_platform.obj bn_s_mp_reverse.obj \
-bn_s_mp_sqr.obj bn_s_mp_sqr_fast.obj bn_s_mp_sub.obj bn_s_mp_toom_mul.obj bn_s_mp_toom_sqr.obj
+bn_mp_submod.obj bn_mp_to_decimal.obj bn_mp_to_radix.obj bn_mp_to_signed_bin.obj bn_mp_to_signed_bin_n.obj \
+bn_mp_to_unsigned_bin.obj bn_mp_to_unsigned_bin_n.obj bn_mp_unsigned_bin_size.obj bn_mp_xor.obj bn_mp_zero.obj \
+bn_prime_tab.obj bn_s_mp_add.obj bn_s_mp_balance_mul.obj bn_s_mp_exptmod.obj bn_s_mp_exptmod_fast.obj \
+bn_s_mp_get_bit.obj bn_s_mp_invmod_fast.obj bn_s_mp_invmod_slow.obj bn_s_mp_karatsuba_mul.obj \
+bn_s_mp_karatsuba_sqr.obj bn_s_mp_montgomery_reduce_fast.obj bn_s_mp_mul_digs.obj bn_s_mp_mul_digs_fast.obj \
+bn_s_mp_mul_high_digs.obj bn_s_mp_mul_high_digs_fast.obj bn_s_mp_prime_is_divisible.obj \
+bn_s_mp_rand_jenkins.obj bn_s_mp_rand_platform.obj bn_s_mp_reverse.obj bn_s_mp_sqr.obj bn_s_mp_sqr_fast.obj \
+bn_s_mp_sub.obj bn_s_mp_to_decimal_fast.obj bn_s_mp_toom_mul.obj bn_s_mp_toom_sqr.obj
 
 HEADERS_PUB=tommath.h
 HEADERS=tommath_private.h tommath_class.h tommath_superclass.h $(HEADERS_PUB)
diff --git a/makefile.shared b/makefile.shared
index f77b5e592..6dd88dd2c 100644
--- a/makefile.shared
+++ b/makefile.shared
@@ -45,13 +45,14 @@ bn_mp_reduce_is_2k.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_setup.o bn_mp_root_u32.
 bn_mp_set.o bn_mp_set_double.o bn_mp_set_i32.o bn_mp_set_i64.o bn_mp_set_l.o bn_mp_set_ll.o \
 bn_mp_set_u32.o bn_mp_set_u64.o bn_mp_set_ul.o bn_mp_set_ull.o bn_mp_shrink.o bn_mp_signed_bin_size.o \
 bn_mp_signed_rsh.o bn_mp_sqr.o bn_mp_sqrmod.o bn_mp_sqrt.o bn_mp_sqrtmod_prime.o bn_mp_sub.o bn_mp_sub_d.o \
-bn_mp_submod.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin.o \
-bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o bn_prime_tab.o bn_s_mp_add.o \
-bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o \
-bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o \
-bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o \
-bn_s_mp_prime_is_divisible.o bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o \
-bn_s_mp_sqr.o bn_s_mp_sqr_fast.o bn_s_mp_sub.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
+bn_mp_submod.o bn_mp_to_decimal.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o \
+bn_mp_to_unsigned_bin.o bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o \
+bn_prime_tab.o bn_s_mp_add.o bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o \
+bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o \
+bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o \
+bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o bn_s_mp_prime_is_divisible.o \
+bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o bn_s_mp_sqr.o bn_s_mp_sqr_fast.o \
+bn_s_mp_sub.o bn_s_mp_to_decimal_fast.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
 
 #END_INS
 
diff --git a/makefile.unix b/makefile.unix
index 4bdb5d5b6..365036578 100644
--- a/makefile.unix
+++ b/makefile.unix
@@ -52,13 +52,14 @@ bn_mp_reduce_is_2k.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_setup.o bn_mp_root_u32.
 bn_mp_set.o bn_mp_set_double.o bn_mp_set_i32.o bn_mp_set_i64.o bn_mp_set_l.o bn_mp_set_ll.o \
 bn_mp_set_u32.o bn_mp_set_u64.o bn_mp_set_ul.o bn_mp_set_ull.o bn_mp_shrink.o bn_mp_signed_bin_size.o \
 bn_mp_signed_rsh.o bn_mp_sqr.o bn_mp_sqrmod.o bn_mp_sqrt.o bn_mp_sqrtmod_prime.o bn_mp_sub.o bn_mp_sub_d.o \
-bn_mp_submod.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin.o \
-bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o bn_prime_tab.o bn_s_mp_add.o \
-bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o \
-bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o \
-bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o \
-bn_s_mp_prime_is_divisible.o bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o \
-bn_s_mp_sqr.o bn_s_mp_sqr_fast.o bn_s_mp_sub.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
+bn_mp_submod.o bn_mp_to_decimal.o bn_mp_to_radix.o bn_mp_to_signed_bin.o bn_mp_to_signed_bin_n.o \
+bn_mp_to_unsigned_bin.o bn_mp_to_unsigned_bin_n.o bn_mp_unsigned_bin_size.o bn_mp_xor.o bn_mp_zero.o \
+bn_prime_tab.o bn_s_mp_add.o bn_s_mp_balance_mul.o bn_s_mp_exptmod.o bn_s_mp_exptmod_fast.o \
+bn_s_mp_get_bit.o bn_s_mp_invmod_fast.o bn_s_mp_invmod_slow.o bn_s_mp_karatsuba_mul.o \
+bn_s_mp_karatsuba_sqr.o bn_s_mp_montgomery_reduce_fast.o bn_s_mp_mul_digs.o bn_s_mp_mul_digs_fast.o \
+bn_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs_fast.o bn_s_mp_prime_is_divisible.o \
+bn_s_mp_rand_jenkins.o bn_s_mp_rand_platform.o bn_s_mp_reverse.o bn_s_mp_sqr.o bn_s_mp_sqr_fast.o \
+bn_s_mp_sub.o bn_s_mp_to_decimal_fast.o bn_s_mp_toom_mul.o bn_s_mp_toom_sqr.o
 
 HEADERS_PUB=tommath.h
 HEADERS=tommath_private.h tommath_class.h tommath_superclass.h $(HEADERS_PUB)
diff --git a/tommath.def b/tommath.def
index 1deeb948b..9a2614e3c 100644
--- a/tommath.def
+++ b/tommath.def
@@ -136,6 +136,7 @@ EXPORTS
     mp_sub
     mp_sub_d
     mp_submod
+    mp_to_decimal
     mp_to_radix
     mp_to_signed_bin
     mp_to_signed_bin_n
diff --git a/tommath.h b/tommath.h
index 8550340cd..126eaab83 100644
--- a/tommath.h
+++ b/tommath.h
@@ -703,6 +703,7 @@ MP_DEPRECATED(mp_to_radix) mp_err mp_toradix(const mp_int *a, char *str, int rad
 MP_DEPRECATED(mp_to_radix) mp_err mp_toradix_n(const mp_int *a, char *str, int radix, int maxlen) MP_WUR;
 mp_err mp_to_radix(const mp_int *a, char *str, size_t maxlen, int radix) MP_WUR;
 mp_err mp_radix_size(const mp_int *a, int radix, int *size) MP_WUR;
+mp_err mp_to_decimal(const mp_int *a, char *str, size_t maxlen) MP_WUR;
 
 #ifndef MP_NO_FILE
 mp_err mp_fread(mp_int *a, int radix, FILE *stream) MP_WUR;
@@ -723,7 +724,6 @@ mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream) MP_WUR;
 
 #define mp_to_binary(M, S, N)  mp_to_radix((M), (S), (N), 2)
 #define mp_to_octal(M, S, N)   mp_to_radix((M), (S), (N), 8)
-#define mp_to_decimal(M, S, N) mp_to_radix((M), (S), (N), 10)
 #define mp_to_hex(M, S, N)     mp_to_radix((M), (S), (N), 16)
 
 #ifdef __cplusplus
diff --git a/tommath_class.h b/tommath_class.h
index 32d1b2dcf..8a921d063 100644
--- a/tommath_class.h
+++ b/tommath_class.h
@@ -137,6 +137,7 @@
 #   define BN_MP_SUB_C
 #   define BN_MP_SUB_D_C
 #   define BN_MP_SUBMOD_C
+#   define BN_MP_TO_DECIMAL_C
 #   define BN_MP_TO_RADIX_C
 #   define BN_MP_TO_SIGNED_BIN_C
 #   define BN_MP_TO_SIGNED_BIN_N_C
@@ -167,6 +168,7 @@
 #   define BN_S_MP_SQR_C
 #   define BN_S_MP_SQR_FAST_C
 #   define BN_S_MP_SUB_C
+#   define BN_S_MP_TO_DECIMAL_FAST_C
 #   define BN_S_MP_TOOM_MUL_C
 #   define BN_S_MP_TOOM_SQR_C
 #endif
@@ -1052,6 +1054,11 @@
 #   define BN_MP_SUB_C
 #endif
 
+#if defined(BN_MP_TO_DECIMAL_C)
+#   define BN_MP_TO_RADIX_C
+#   define BN_S_MP_TO_DECIMAL_FAST_C
+#endif
+
 #if defined(BN_MP_TO_RADIX_C)
 #   define BN_MP_CLEAR_C
 #   define BN_MP_DIV_D_C
@@ -1267,6 +1274,26 @@
 #   define BN_MP_GROW_C
 #endif
 
+#if defined(BN_S_MP_TO_DECIMAL_FAST_C)
+#   define BN_MP_ADD_C
+#   define BN_MP_ADD_D_C
+#   define BN_MP_CLEAR_MULTI_C
+#   define BN_MP_CMP_C
+#   define BN_MP_DIV_2D_C
+#   define BN_MP_GET_I32_C
+#   define BN_MP_INIT_COPY_C
+#   define BN_MP_INIT_MULTI_C
+#   define BN_MP_INIT_SET_C
+#   define BN_MP_MUL_2_C
+#   define BN_MP_MUL_C
+#   define BN_MP_NEG_C
+#   define BN_MP_SET_U32_C
+#   define BN_MP_SQR_C
+#   define BN_MP_SUB_C
+#   define BN_MP_SUB_D_C
+#   define BN_S_MP_TO_DECIMAL_FAST_REC_C
+#endif
+
 #if defined(BN_S_MP_TOOM_MUL_C)
 #   define BN_MP_ADD_C
 #   define BN_MP_CLAMP_C
diff --git a/tommath_private.h b/tommath_private.h
index b5650424a..a98fee515 100644
--- a/tommath_private.h
+++ b/tommath_private.h
@@ -205,6 +205,7 @@ MP_PRIVATE mp_err s_mp_rand_platform(void *p, size_t n) MP_WUR;
 MP_PRIVATE mp_err s_mp_prime_random_ex(mp_int *a, int t, int size, int flags, private_mp_prime_callback cb, void *dat);
 MP_PRIVATE void s_mp_reverse(unsigned char *s, int len);
 MP_PRIVATE mp_err s_mp_prime_is_divisible(const mp_int *a, mp_bool *result);
+MP_PRIVATE mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen) MP_WUR;
 
 /* TODO: jenkins prng is not thread safe as of now */
 MP_PRIVATE mp_err s_mp_rand_jenkins(void *p, size_t n) MP_WUR;

From 3c0162d29268f3c74e9047f313a5a11996a0a4ec Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Fri, 6 Sep 2019 16:43:50 -0400
Subject: [PATCH 2/7] Address comments

---
 bn_s_mp_to_decimal_fast.c | 62 ++++++++++++++++++++++-----------------
 demo/test.c               |  6 ++--
 tommath_class.h           |  4 ++-
 3 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/bn_s_mp_to_decimal_fast.c b/bn_s_mp_to_decimal_fast.c
index 5ce597b57..0f836d466 100644
--- a/bn_s_mp_to_decimal_fast.c
+++ b/bn_s_mp_to_decimal_fast.c
@@ -14,36 +14,37 @@ mp_err s_mp_to_decimal_fast_rec(const mp_int *number, mp_int *nL, mp_int *shiftL
    mp_err err;
 
    if (precalc_array_index < 0) {
-      int n = mp_get_i32(number), n2 = n, t = 0, c;
-      char *i = *result;
-      char s[4] = "000";
+      int n = mp_get_i32(number), n_orig = n, digits_to_copy = 0, copy_counter;
+      char *result_str = *result;
+      char sprintf_str[4] = "000";
 
       while (n) {
-         s[2 - t] = mp_s_rmap[n % 10];
-         t++;
+         sprintf_str[2 - digits_to_copy] = mp_s_rmap[n % 10];
+         digits_to_copy++;
          n /= 10;
       }
 
-      if (!left && n2 < 100) {
-         t++;
-         if (n2 < 10) {
-            t++;
+      if (!left && n_orig < 100) {
+         digits_to_copy++;
+         if (n_orig < 10) {
+            digits_to_copy++;
          }
-         if (n2 == 0) {
-            t++;
+         if (n_orig == 0) {
+            digits_to_copy++;
          }
       }
 
-      if (*maxlen < (size_t)t || (*maxlen -= (size_t)t) < 1) {
+      if (*maxlen < ((size_t)digits_to_copy + 1)) {
          /* no more room */
          return MP_VAL;
       }
 
-      for (c = 0; c < t; c++) {
-         i[c] = s[3 - t + c];
+      for (copy_counter = 0; copy_counter < digits_to_copy; copy_counter++) {
+         result_str[copy_counter] = sprintf_str[3 - digits_to_copy + copy_counter];
       }
 
-      *result += t;
+      *maxlen -= (size_t)digits_to_copy;
+      *result += digits_to_copy;
 
       return MP_OKAY;
    }
@@ -67,7 +68,7 @@ mp_err s_mp_to_decimal_fast_rec(const mp_int *number, mp_int *nL, mp_int *shiftL
    }
 
    if (mp_isneg(&r)) {
-      if ((err = mp_sub_d(&q, 1, &q)) != MP_OKAY) {
+      if ((err = mp_decr(&q)) != MP_OKAY) {
          goto LBL_ERR;
       }
       if ((err = mp_add(&r, &nL[precalc_array_index], &r)) != MP_OKAY) {
@@ -104,11 +105,16 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
    char **result_addr = &result;
    int precalc_array_index = 1, c;
 
-   if ((err = mp_init_multi(&n, &M, &M2, &M22, &M4, &M44, &mL[0], NULL)) != MP_OKAY) {
+   /* check range of the maxlen */
+   if (maxlen < 2) {
+      return MP_VAL;
+   }
+
+   if ((err = mp_init_multi(&number, &n, &shift, &M, &M2, &M22, &M4, &M44, &nL[0], &shiftL[0], &mL[0], NULL)) != MP_OKAY) {
       goto LBL_ERR;
    }
 
-   if ((err = mp_init_copy(&number, a)) != MP_OKAY) {
+   if ((err = mp_copy(a, &number)) != MP_OKAY) {
       goto LBL_ERR;
    }
    if (mp_isneg(&number)) {
@@ -121,15 +127,13 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
    }
    mp_set_u32(&n, 1000);
 
-   if ((err = mp_init_copy(&nL[0], &n)) != MP_OKAY) {
+   if ((err = mp_copy(&n, &nL[0])) != MP_OKAY) {
       goto LBL_ERR;
    }
 
-   if ((err = mp_init_set(&shift, (mp_digit)20)) != MP_OKAY) {
-      goto LBL_ERR;
-   }
+   mp_set_u32(&shift, 20);
 
-   if ((err = mp_init_copy(&shiftL[0], &shift)) != MP_OKAY) {
+   if ((err = mp_copy(&shift, &shiftL[0])) != MP_OKAY) {
       goto LBL_ERR;
    }
 
@@ -173,13 +177,13 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
          if ((err = mp_sub(&M4, &M2, &M4)) != MP_OKAY) {
             goto LBL_ERR;
          }
-         if ((err = mp_add_d(&M4, 1, &M4)) != MP_OKAY) {
+         if ((err = mp_incr(&M4)) != MP_OKAY) {
             goto LBL_ERR;
          }
          if ((err = mp_div_2d(&M4, 3, &M4, NULL)) != MP_OKAY) {
             goto LBL_ERR;
          }
-         if ((err = mp_sub_d(&M4, 1, &M4)) != MP_OKAY) {
+         if ((err = mp_decr(&M4)) != MP_OKAY) {
             goto LBL_ERR;
          }
          if ((err = mp_neg(&M4, &M)) != MP_OKAY) {
@@ -209,6 +213,10 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
             goto LBL_ERR;
          }
       }
+      if (precalc_array_index >= 20) {
+         err = MP_VAL;
+         goto LBL_ERR;
+      }
       if ((err = mp_init_copy(&mL[precalc_array_index], &M4)) != MP_OKAY) {
          goto LBL_ERR;
       }
@@ -224,8 +232,8 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
    err = MP_OKAY;
 
 LBL_ERR:
-   mp_clear_multi(&number, &n, &shift, &M, &M2, &M22, &M4, &M44, NULL);
-   for (c = 0; c < precalc_array_index; c++) {
+   mp_clear_multi(&number, &n, &shift, &M, &M2, &M22, &M4, &M44, &nL[0], &shiftL[0], &mL[0], NULL);
+   for (c = 1; c < precalc_array_index; c++) {
       mp_clear_multi(&nL[c], &shiftL[c], &mL[c], NULL);
    }
    return err;
diff --git a/demo/test.c b/demo/test.c
index f4a406dca..5623cee0e 100644
--- a/demo/test.c
+++ b/demo/test.c
@@ -2223,9 +2223,9 @@ static int test_mp_to_decimal(void)
    if ((err = mp_init_multi(&a, &b, NULL)) != MP_OKAY) {
       goto LTM_ERR;
    }
-   for (size = 1; size < 1000; size += 10) {
+   for (size = 1; size <= 1001; size += 10) {
       int times;
-      printf("Testing mp_to_decimal: %5d bits    \r", size);
+      printf("Testing mp_to_decimal: %5d digits    \r", size);
       fflush(stdout);
       for (times = 0; times < 5; times++) {
          if ((err = mp_rand(&a, size)) != MP_OKAY) {
@@ -2249,7 +2249,7 @@ static int test_mp_to_decimal(void)
          }
          free(str);
          if (mp_cmp(&a, &b) != MP_EQ) {
-            fprintf(stderr, "s_mp_to_decimal_fast failed at size %d\n", size);
+            fprintf(stderr, "mp_to_decimal failed at size %d\n", size);
             goto LTM_ERR;
          }
       }
diff --git a/tommath_class.h b/tommath_class.h
index 8a921d063..5baf8c0d7 100644
--- a/tommath_class.h
+++ b/tommath_class.h
@@ -1279,11 +1279,13 @@
 #   define BN_MP_ADD_D_C
 #   define BN_MP_CLEAR_MULTI_C
 #   define BN_MP_CMP_C
+#   define BN_MP_COPY_C
+#   define BN_MP_DECR_C
 #   define BN_MP_DIV_2D_C
 #   define BN_MP_GET_I32_C
+#   define BN_MP_INCR_C
 #   define BN_MP_INIT_COPY_C
 #   define BN_MP_INIT_MULTI_C
-#   define BN_MP_INIT_SET_C
 #   define BN_MP_MUL_2_C
 #   define BN_MP_MUL_C
 #   define BN_MP_NEG_C

From 91543f4d1069bc37a2a688e83ca2e38c1dc9cf60 Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Sat, 7 Sep 2019 11:59:40 -0400
Subject: [PATCH 3/7] Turn while into a for

---
 bn_s_mp_to_decimal_fast.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/bn_s_mp_to_decimal_fast.c b/bn_s_mp_to_decimal_fast.c
index 0f836d466..c8b7aa227 100644
--- a/bn_s_mp_to_decimal_fast.c
+++ b/bn_s_mp_to_decimal_fast.c
@@ -143,7 +143,7 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
    /* $M / 8, rounded up */
    mp_set_u32(&mL[0], 1049);
 
-   while (1) {
+   for (precalc_array_index = 1; precalc_array_index < 20; precalc_array_index++) {
       if ((err = mp_sqr(&n, &n)) != MP_OKAY) {
          goto LBL_ERR;
       }
@@ -213,14 +213,13 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
             goto LBL_ERR;
          }
       }
-      if (precalc_array_index >= 20) {
-         err = MP_VAL;
-         goto LBL_ERR;
-      }
       if ((err = mp_init_copy(&mL[precalc_array_index], &M4)) != MP_OKAY) {
          goto LBL_ERR;
       }
-      precalc_array_index++;
+   }
+   if (precalc_array_index >= 20) {
+      err = MP_VAL;
+      goto LBL_ERR;
    }
 
    if ((err = s_mp_to_decimal_fast_rec(&number, nL, shiftL, mL, precalc_array_index - 1, 1, result_addr,

From 8d0387838285c40d551b163c3161d438e5ddd60d Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Sat, 7 Sep 2019 12:58:12 -0400
Subject: [PATCH 4/7] Fix undef in test.c

---
 demo/test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/demo/test.c b/demo/test.c
index 5623cee0e..57eda2c79 100644
--- a/demo/test.c
+++ b/demo/test.c
@@ -2315,7 +2315,7 @@ int unit_tests(int argc, char **argv)
       T1(mp_to_decimal, S_MP_TO_DECIMAL_FAST)
 #undef T2
 #undef T1
-#undef T
+#undef T0
    };
    unsigned long i, ok, fail, nop;
    uint64_t t;

From 8f8a58b33d53e3a019573b369cf8e322e6ddcdca Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Sat, 7 Sep 2019 15:23:53 -0400
Subject: [PATCH 5/7] Add BN_MP_TO_DECIMAL_C to tommath_superclass.h

---
 tommath_superclass.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tommath_superclass.h b/tommath_superclass.h
index b007c243d..e2b13b521 100644
--- a/tommath_superclass.h
+++ b/tommath_superclass.h
@@ -37,6 +37,7 @@
 #   define BN_MP_SET_UL_C
 #   define BN_MP_SIGNED_BIN_SIZE_C
 #   define BN_MP_TO_RADIX_C
+#   define BN_MP_TO_DECIMAL_C
 #   define BN_MP_TO_SIGNED_BIN_C
 #   define BN_S_MP_RAND_JENKINS_C
 #   define BN_S_MP_RAND_PLATFORM_C

From fee03ddcfed4098c4035892fd6419ba4dc748557 Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Sun, 8 Sep 2019 08:01:33 -0400
Subject: [PATCH 6/7] Zero-out arrays after declaration

---
 bn_s_mp_to_decimal_fast.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/bn_s_mp_to_decimal_fast.c b/bn_s_mp_to_decimal_fast.c
index c8b7aa227..533f3f632 100644
--- a/bn_s_mp_to_decimal_fast.c
+++ b/bn_s_mp_to_decimal_fast.c
@@ -110,6 +110,10 @@ mp_err s_mp_to_decimal_fast(const mp_int *a, char *result, size_t maxlen)
       return MP_VAL;
    }
 
+   MP_ZERO_BUFFER(nL, sizeof(nL));
+   MP_ZERO_BUFFER(shiftL, sizeof(shiftL));
+   MP_ZERO_BUFFER(mL, sizeof(mL));
+
    if ((err = mp_init_multi(&number, &n, &shift, &M, &M2, &M22, &M4, &M44, &nL[0], &shiftL[0], &mL[0], NULL)) != MP_OKAY) {
       goto LBL_ERR;
    }

From d2678b00f547fc83be7413dd02285df55f3c609e Mon Sep 17 00:00:00 2001
From: Daniel Green <ddgreen@gmail.com>
Date: Wed, 11 Sep 2019 11:00:08 -0400
Subject: [PATCH 7/7] Make s_mp_to_decimal_fast_rec static

---
 bn_s_mp_to_decimal_fast.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/bn_s_mp_to_decimal_fast.c b/bn_s_mp_to_decimal_fast.c
index 533f3f632..476f786a2 100644
--- a/bn_s_mp_to_decimal_fast.c
+++ b/bn_s_mp_to_decimal_fast.c
@@ -5,10 +5,11 @@
 /* SPDX-License-Identifier: Unlicense */
 
 /* store a bignum as a decimal ASCII string */
-mp_err s_mp_to_decimal_fast_rec(const mp_int *number, mp_int *nL, mp_int *shiftL, mp_int *mL, int precalc_array_index,
-                                int left,
-                                char **result,
-                                size_t *maxlen)
+static mp_err s_mp_to_decimal_fast_rec(const mp_int *number, mp_int *nL, mp_int *shiftL, mp_int *mL,
+                                       int precalc_array_index,
+                                       int left,
+                                       char **result,
+                                       size_t *maxlen)
 {
    mp_int q, nLq, r;
    mp_err err;