diff --git a/demo/shared.c b/demo/shared.c
index 7ef4756..85c26ed 100644
--- a/demo/shared.c
+++ b/demo/shared.c
@@ -6,8 +6,8 @@ void ndraw(const mp_int *a, const char *name)
size_t size = 0;
mp_err err;
- if ((err = mp_radix_size(a, 10, &size)) != MP_OKAY) {
- fprintf(stderr, "\nndraw: mp_radix_size(a, 10, %zu) failed - %s\n", size, mp_error_to_string(err));
+ if ((err = mp_radix_size_overestimate(a, 10, &size)) != MP_OKAY) {
+ fprintf(stderr, "\nndraw: mp_radix_size_overestimate(a, 10, %zu) failed - %s\n", size, mp_error_to_string(err));
exit(EXIT_FAILURE);
}
buf = (char *)malloc(size);
diff --git a/demo/test.c b/demo/test.c
index e1b6f0f..32652d0 100644
--- a/demo/test.c
+++ b/demo/test.c
@@ -2016,6 +2016,7 @@ LBL_ERR:
return EXIT_FAILURE;
}
+
static int test_mp_radix_size(void)
{
mp_int a;
@@ -2201,6 +2202,108 @@ LBL_ERR:
}
+static int test_s_mp_radix_size_overestimate(void)
+{
+
+ mp_err err;
+ mp_int a;
+ int radix;
+ size_t size;
+/* *INDENT-OFF* */
+ size_t results[65] = {
+ 0u, 0u, 1627u, 1027u, 814u, 702u, 630u, 581u, 543u,
+ 514u, 491u, 471u, 455u, 441u, 428u, 418u, 408u, 399u,
+ 391u, 384u, 378u, 372u, 366u, 361u, 356u, 352u, 347u,
+ 343u, 340u, 336u, 333u, 330u, 327u, 324u, 321u, 318u,
+ 316u, 314u, 311u, 309u, 307u, 305u, 303u, 301u, 299u,
+ 298u, 296u, 294u, 293u, 291u, 290u, 288u, 287u, 285u,
+ 284u, 283u, 281u, 280u, 279u, 278u, 277u, 276u, 275u,
+ 273u, 272u
+ };
+#ifdef MP_RADIX_SIZE_OVERESTIMATE_TEST_BIG
+ size_t big_results[65] = {
+ 0u, 0u, 0u, 1354911329u, 1073741825u,
+ 924870867u, 830760078u, 764949110u, 715827883u, 677455665u,
+ 646456994u, 620761988u, 599025415u, 580332018u, 564035582u,
+ 549665673u, 536870913u, 525383039u, 514993351u, 505536793u,
+ 496880930u, 488918137u, 481559946u, 474732892u, 468375401u,
+ 462435434u, 456868672u, 451637110u, 446707948u, 442052707u,
+ 437646532u, 433467613u, 429496730u, 425716865u, 422112892u,
+ 418671312u, 415380039u, 412228213u, 409206043u, 406304679u,
+ 403516096u, 400833001u, 398248746u, 395757256u, 393352972u,
+ 391030789u, 388786017u, 386614331u, 384511740u, 382474555u,
+ 380499357u, 378582973u, 376722456u, 374915062u, 373158233u,
+ 371449582u, 369786879u, 368168034u, 366591092u, 365054217u,
+ 363555684u, 362093873u, 360667257u, 359274399u, 357913942
+ };
+#endif
+
+/* *INDENT-ON* */
+ if ((err = mp_init(&a)) != MP_OKAY) goto LBL_ERR;
+
+ /* number to result in a different size for every base: 67^(4 * 67) */
+ mp_set(&a, 67);
+ if ((err = mp_expt_n(&a, 268, &a)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+
+ for (radix = 2; radix < 65; radix++) {
+ if ((err = s_mp_radix_size_overestimate(&a, radix, &size)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if (size < results[radix]) {
+ fprintf(stderr, "s_mp_radix_size_overestimate: result for base %d was %zu instead of %zu\n",
+ radix, size, results[radix]);
+ goto LBL_ERR;
+ }
+ a.sign = MP_NEG;
+ if ((err = s_mp_radix_size_overestimate(&a, radix, &size)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if (size < results[radix]) {
+ fprintf(stderr, "s_mp_radix_size_overestimate: result for base %d was %zu instead of %zu\n",
+ radix, size, results[radix]);
+ goto LBL_ERR;
+ }
+ a.sign = MP_ZPOS;
+ }
+#ifdef MP_RADIX_SIZE_OVERESTIMATE_TEST_BIG
+ if ((err = mp_2expt(&a, INT_MAX - 1)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ printf("bitcount = %d, alloc = %d\n", mp_count_bits(&a), a.alloc);
+ /* Start at 3 to avoid integer overflow */
+ for (radix = 3; radix < 65; radix++) {
+ printf("radix = %d, ",radix);
+ if ((err = s_mp_radix_size_overestimate(&a, radix, &size)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ printf("size = %zu, diff = %zu\n", size, size - big_results[radix]);
+ if (size < big_results[radix]) {
+ fprintf(stderr, "s_mp_radix_size_overestimate: result for base %d was %zu instead of %zu\n",
+ radix, size, results[radix]);
+ goto LBL_ERR;
+ }
+ a.sign = MP_NEG;
+ if ((err = s_mp_radix_size_overestimate(&a, radix, &size)) != MP_OKAY) {
+ goto LBL_ERR;
+ }
+ if (size < big_results[radix]) {
+ fprintf(stderr, "s_mp_radix_size_overestimate: result for base %d was %zu instead of %zu\n",
+ radix, size, results[radix]);
+ goto LBL_ERR;
+ }
+ a.sign = MP_ZPOS;
+ }
+#endif
+ mp_clear(&a);
+ return EXIT_SUCCESS;
+LBL_ERR:
+ mp_clear(&a);
+ return EXIT_FAILURE;
+}
+
+
static int test_mp_read_write_ubin(void)
{
mp_int a, b, c;
@@ -2359,6 +2462,7 @@ static int unit_tests(int argc, char **argv)
T1(mp_reduce_2k, MP_REDUCE_2K),
T1(mp_reduce_2k_l, MP_REDUCE_2K_L),
T1(mp_radix_size, MP_RADIX_SIZE),
+ T1(s_mp_radix_size_overestimate, S_MP_RADIX_SIZE_OVERESTIMATE),
#if defined(__STDC_IEC_559__) || defined(__GCC_IEC_559)
T1(mp_set_double, MP_SET_DOUBLE),
#endif
diff --git a/doc/bn.tex b/doc/bn.tex
index 711ebf8..a0956b3 100644
--- a/doc/bn.tex
+++ b/doc/bn.tex
@@ -2380,7 +2380,21 @@ mp_err mp_radix_size (const mp_int *a, int radix, int *size)
\end{alltt}
This stores in \texttt{size} the number of characters (including space for the \texttt{NUL}
terminator) required. Upon error this function returns an error code and \texttt{size} will be
-zero.
+zero. This version of \texttt{mp\_radix\_size} uses \texttt{mp\_log} to calculate the size. It
+is exact but slow for larger numbers.
+
+\index{mp\_radix\_size\_overestimate}
+\begin{alltt}
+mp_err mp_radix_size_overestimate (const mp_int *a, int radix, int *size)
+\end{alltt}
+This stores in \texttt{size} the number of characters (including space for the \texttt{NUL}
+terminator) required. Upon error this function returns an error code and \texttt{size} will be
+zero. This version of \texttt{mp\_radix\_size} is much faster than the exact version above but
+introduces the relative error $\approx 10^{-8}$. That would be $22$ for $2^{2^{31}}-1$, the
+largest possible number in LibTomMath. Experiments gave no absolute error over $+5$.
+
+The result is \emph{always} either exact or too large but it is \emph{never} too small.
+
If \texttt{MP\_NO\_FILE} is not defined a function to write to a file is also available.
diff --git a/libtommath_VS2008.vcproj b/libtommath_VS2008.vcproj
index 6f1a423..192ed0f 100644
--- a/libtommath_VS2008.vcproj
+++ b/libtommath_VS2008.vcproj
@@ -640,6 +640,10 @@
RelativePath="mp_radix_size.c"
>
+
+
@@ -884,6 +888,10 @@
RelativePath="s_mp_radix_map.c"
>
+
+
diff --git a/makefile b/makefile
index 63930bc..1cd5778 100644
--- a/makefile
+++ b/makefile
@@ -38,17 +38,18 @@ mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o
mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
mp_prime_rabin_miller_trials.o mp_prime_rand.o mp_prime_strong_lucas_selfridge.o mp_radix_size.o \
-mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o mp_reduce_2k_setup.o \
-mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o mp_root_n.o mp_rshd.o \
-mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o mp_set_u32.o mp_set_u64.o \
-mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o mp_sub.o mp_sub_d.o \
-mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o mp_zero.o s_mp_add.o \
-s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o s_mp_exptmod.o \
-s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o s_mp_log_2expt.o \
-s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o s_mp_mul_comba.o \
-s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o s_mp_prime_is_divisible.o \
-s_mp_prime_tab.o s_mp_radix_map.o s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o \
-s_mp_sqr_karatsuba.o s_mp_sqr_toom.o s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
+mp_radix_size_overestimate.o mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o \
+mp_reduce_2k_setup.o mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o \
+mp_root_n.o mp_rshd.o mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o \
+mp_set_u32.o mp_set_u64.o mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o \
+mp_sub.o mp_sub_d.o mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o \
+mp_zero.o s_mp_add.o s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o \
+s_mp_exptmod.o s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o \
+s_mp_log_2expt.o s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o \
+s_mp_mul_comba.o s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o \
+s_mp_prime_is_divisible.o s_mp_prime_tab.o s_mp_radix_map.o s_mp_radix_size_overestimate.o \
+s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o s_mp_sqr_karatsuba.o s_mp_sqr_toom.o \
+s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
#END_INS
diff --git a/makefile.mingw b/makefile.mingw
index ae98a5c..aab267f 100644
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -40,17 +40,18 @@ mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o
mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
mp_prime_rabin_miller_trials.o mp_prime_rand.o mp_prime_strong_lucas_selfridge.o mp_radix_size.o \
-mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o mp_reduce_2k_setup.o \
-mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o mp_root_n.o mp_rshd.o \
-mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o mp_set_u32.o mp_set_u64.o \
-mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o mp_sub.o mp_sub_d.o \
-mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o mp_zero.o s_mp_add.o \
-s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o s_mp_exptmod.o \
-s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o s_mp_log_2expt.o \
-s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o s_mp_mul_comba.o \
-s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o s_mp_prime_is_divisible.o \
-s_mp_prime_tab.o s_mp_radix_map.o s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o \
-s_mp_sqr_karatsuba.o s_mp_sqr_toom.o s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
+mp_radix_size_overestimate.o mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o \
+mp_reduce_2k_setup.o mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o \
+mp_root_n.o mp_rshd.o mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o \
+mp_set_u32.o mp_set_u64.o mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o \
+mp_sub.o mp_sub_d.o mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o \
+mp_zero.o s_mp_add.o s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o \
+s_mp_exptmod.o s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o \
+s_mp_log_2expt.o s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o \
+s_mp_mul_comba.o s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o \
+s_mp_prime_is_divisible.o s_mp_prime_tab.o s_mp_radix_map.o s_mp_radix_size_overestimate.o \
+s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o s_mp_sqr_karatsuba.o s_mp_sqr_toom.o \
+s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
HEADERS_PUB=tommath.h
HEADERS=tommath_private.h tommath_class.h tommath_superclass.h tommath_cutoffs.h $(HEADERS_PUB)
diff --git a/makefile.msvc b/makefile.msvc
index 7dcbf3d..031f1c8 100644
--- a/makefile.msvc
+++ b/makefile.msvc
@@ -33,17 +33,18 @@ mp_montgomery_calc_normalization.obj mp_montgomery_reduce.obj mp_montgomery_setu
mp_mul_2d.obj mp_mul_d.obj mp_mulmod.obj mp_neg.obj mp_or.obj mp_pack.obj mp_pack_count.obj mp_prime_fermat.obj \
mp_prime_frobenius_underwood.obj mp_prime_is_prime.obj mp_prime_miller_rabin.obj mp_prime_next_prime.obj \
mp_prime_rabin_miller_trials.obj mp_prime_rand.obj mp_prime_strong_lucas_selfridge.obj mp_radix_size.obj \
-mp_rand.obj mp_read_radix.obj mp_reduce.obj mp_reduce_2k.obj mp_reduce_2k_l.obj mp_reduce_2k_setup.obj \
-mp_reduce_2k_setup_l.obj mp_reduce_is_2k.obj mp_reduce_is_2k_l.obj mp_reduce_setup.obj mp_root_n.obj mp_rshd.obj \
-mp_sbin_size.obj mp_set.obj mp_set_double.obj mp_set_i32.obj mp_set_i64.obj mp_set_l.obj mp_set_u32.obj mp_set_u64.obj \
-mp_set_ul.obj mp_shrink.obj mp_signed_rsh.obj mp_sqrmod.obj mp_sqrt.obj mp_sqrtmod_prime.obj mp_sub.obj mp_sub_d.obj \
-mp_submod.obj mp_to_radix.obj mp_to_sbin.obj mp_to_ubin.obj mp_ubin_size.obj mp_unpack.obj mp_xor.obj mp_zero.obj s_mp_add.obj \
-s_mp_copy_digs.obj s_mp_div_3.obj s_mp_div_recursive.obj s_mp_div_school.obj s_mp_div_small.obj s_mp_exptmod.obj \
-s_mp_exptmod_fast.obj s_mp_get_bit.obj s_mp_invmod.obj s_mp_invmod_odd.obj s_mp_log.obj s_mp_log_2expt.obj \
-s_mp_log_d.obj s_mp_montgomery_reduce_comba.obj s_mp_mul.obj s_mp_mul_balance.obj s_mp_mul_comba.obj \
-s_mp_mul_high.obj s_mp_mul_high_comba.obj s_mp_mul_karatsuba.obj s_mp_mul_toom.obj s_mp_prime_is_divisible.obj \
-s_mp_prime_tab.obj s_mp_radix_map.obj s_mp_rand_jenkins.obj s_mp_rand_platform.obj s_mp_sqr.obj s_mp_sqr_comba.obj \
-s_mp_sqr_karatsuba.obj s_mp_sqr_toom.obj s_mp_sub.obj s_mp_zero_buf.obj s_mp_zero_digs.obj
+mp_radix_size_overestimate.obj mp_rand.obj mp_read_radix.obj mp_reduce.obj mp_reduce_2k.obj mp_reduce_2k_l.obj \
+mp_reduce_2k_setup.obj mp_reduce_2k_setup_l.obj mp_reduce_is_2k.obj mp_reduce_is_2k_l.obj mp_reduce_setup.obj \
+mp_root_n.obj mp_rshd.obj mp_sbin_size.obj mp_set.obj mp_set_double.obj mp_set_i32.obj mp_set_i64.obj mp_set_l.obj \
+mp_set_u32.obj mp_set_u64.obj mp_set_ul.obj mp_shrink.obj mp_signed_rsh.obj mp_sqrmod.obj mp_sqrt.obj mp_sqrtmod_prime.obj \
+mp_sub.obj mp_sub_d.obj mp_submod.obj mp_to_radix.obj mp_to_sbin.obj mp_to_ubin.obj mp_ubin_size.obj mp_unpack.obj mp_xor.obj \
+mp_zero.obj s_mp_add.obj s_mp_copy_digs.obj s_mp_div_3.obj s_mp_div_recursive.obj s_mp_div_school.obj s_mp_div_small.obj \
+s_mp_exptmod.obj s_mp_exptmod_fast.obj s_mp_get_bit.obj s_mp_invmod.obj s_mp_invmod_odd.obj s_mp_log.obj \
+s_mp_log_2expt.obj s_mp_log_d.obj s_mp_montgomery_reduce_comba.obj s_mp_mul.obj s_mp_mul_balance.obj \
+s_mp_mul_comba.obj s_mp_mul_high.obj s_mp_mul_high_comba.obj s_mp_mul_karatsuba.obj s_mp_mul_toom.obj \
+s_mp_prime_is_divisible.obj s_mp_prime_tab.obj s_mp_radix_map.obj s_mp_radix_size_overestimate.obj \
+s_mp_rand_jenkins.obj s_mp_rand_platform.obj s_mp_sqr.obj s_mp_sqr_comba.obj s_mp_sqr_karatsuba.obj s_mp_sqr_toom.obj \
+s_mp_sub.obj s_mp_zero_buf.obj s_mp_zero_digs.obj
HEADERS_PUB=tommath.h
HEADERS=tommath_private.h tommath_class.h tommath_superclass.h tommath_cutoffs.h $(HEADERS_PUB)
diff --git a/makefile.shared b/makefile.shared
index 2e24a43..5e19c3b 100644
--- a/makefile.shared
+++ b/makefile.shared
@@ -35,17 +35,18 @@ mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o
mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
mp_prime_rabin_miller_trials.o mp_prime_rand.o mp_prime_strong_lucas_selfridge.o mp_radix_size.o \
-mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o mp_reduce_2k_setup.o \
-mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o mp_root_n.o mp_rshd.o \
-mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o mp_set_u32.o mp_set_u64.o \
-mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o mp_sub.o mp_sub_d.o \
-mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o mp_zero.o s_mp_add.o \
-s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o s_mp_exptmod.o \
-s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o s_mp_log_2expt.o \
-s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o s_mp_mul_comba.o \
-s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o s_mp_prime_is_divisible.o \
-s_mp_prime_tab.o s_mp_radix_map.o s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o \
-s_mp_sqr_karatsuba.o s_mp_sqr_toom.o s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
+mp_radix_size_overestimate.o mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o \
+mp_reduce_2k_setup.o mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o \
+mp_root_n.o mp_rshd.o mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o \
+mp_set_u32.o mp_set_u64.o mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o \
+mp_sub.o mp_sub_d.o mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o \
+mp_zero.o s_mp_add.o s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o \
+s_mp_exptmod.o s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o \
+s_mp_log_2expt.o s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o \
+s_mp_mul_comba.o s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o \
+s_mp_prime_is_divisible.o s_mp_prime_tab.o s_mp_radix_map.o s_mp_radix_size_overestimate.o \
+s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o s_mp_sqr_karatsuba.o s_mp_sqr_toom.o \
+s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
#END_INS
diff --git a/makefile.unix b/makefile.unix
index 2b2589c..f2a918c 100644
--- a/makefile.unix
+++ b/makefile.unix
@@ -41,17 +41,19 @@ mp_montgomery_calc_normalization.o mp_montgomery_reduce.o mp_montgomery_setup.o
mp_mul_2d.o mp_mul_d.o mp_mulmod.o mp_neg.o mp_or.o mp_pack.o mp_pack_count.o mp_prime_fermat.o \
mp_prime_frobenius_underwood.o mp_prime_is_prime.o mp_prime_miller_rabin.o mp_prime_next_prime.o \
mp_prime_rabin_miller_trials.o mp_prime_rand.o mp_prime_strong_lucas_selfridge.o mp_radix_size.o \
-mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o mp_reduce_2k_setup.o \
-mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o mp_root_n.o mp_rshd.o \
-mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o mp_set_u32.o mp_set_u64.o \
-mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o mp_sub.o mp_sub_d.o \
-mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o mp_zero.o s_mp_add.o \
-s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o s_mp_exptmod.o \
-s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o s_mp_log_2expt.o \
-s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o s_mp_mul_comba.o \
-s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o s_mp_prime_is_divisible.o \
-s_mp_prime_tab.o s_mp_radix_map.o s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o \
-s_mp_sqr_karatsuba.o s_mp_sqr_toom.o s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
+mp_radix_size_overestimate.o mp_rand.o mp_read_radix.o mp_reduce.o mp_reduce_2k.o mp_reduce_2k_l.o \
+mp_reduce_2k_setup.o mp_reduce_2k_setup_l.o mp_reduce_is_2k.o mp_reduce_is_2k_l.o mp_reduce_setup.o \
+mp_root_n.o mp_rshd.o mp_sbin_size.o mp_set.o mp_set_double.o mp_set_i32.o mp_set_i64.o mp_set_l.o \
+mp_set_u32.o mp_set_u64.o mp_set_ul.o mp_shrink.o mp_signed_rsh.o mp_sqrmod.o mp_sqrt.o mp_sqrtmod_prime.o \
+mp_sub.o mp_sub_d.o mp_submod.o mp_to_radix.o mp_to_sbin.o mp_to_ubin.o mp_ubin_size.o mp_unpack.o mp_xor.o \
+mp_zero.o s_mp_add.o s_mp_copy_digs.o s_mp_div_3.o s_mp_div_recursive.o s_mp_div_school.o s_mp_div_small.o \
+s_mp_exptmod.o s_mp_exptmod_fast.o s_mp_get_bit.o s_mp_invmod.o s_mp_invmod_odd.o s_mp_log.o \
+s_mp_log_2expt.o s_mp_log_d.o s_mp_montgomery_reduce_comba.o s_mp_mul.o s_mp_mul_balance.o \
+s_mp_mul_comba.o s_mp_mul_high.o s_mp_mul_high_comba.o s_mp_mul_karatsuba.o s_mp_mul_toom.o \
+s_mp_prime_is_divisible.o s_mp_prime_tab.o s_mp_radix_map.o s_mp_radix_size_overestimate.o \
+s_mp_rand_jenkins.o s_mp_rand_platform.o s_mp_sqr.o s_mp_sqr_comba.o s_mp_sqr_karatsuba.o s_mp_sqr_toom.o \
+s_mp_sub.o s_mp_zero_buf.o s_mp_zero_digs.o
+
HEADERS_PUB=tommath.h
HEADERS=tommath_private.h tommath_class.h tommath_superclass.h tommath_cutoffs.h $(HEADERS_PUB)
diff --git a/mp_fwrite.c b/mp_fwrite.c
index 6b8ea13..3d8141f 100644
--- a/mp_fwrite.c
+++ b/mp_fwrite.c
@@ -10,7 +10,7 @@ mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream)
mp_err err;
size_t size, written;
- if ((err = mp_radix_size(a, radix, &size)) != MP_OKAY) {
+ if ((err = mp_radix_size_overestimate(a, radix, &size)) != MP_OKAY) {
return err;
}
diff --git a/mp_radix_size_overestimate.c b/mp_radix_size_overestimate.c
new file mode 100644
index 0000000..3fe81d7
--- /dev/null
+++ b/mp_radix_size_overestimate.c
@@ -0,0 +1,17 @@
+#include "tommath_private.h"
+#ifdef MP_RADIX_SIZE_OVERESTIMATE_C
+/* LibTomMath, multiple-precision integer library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+mp_err mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size)
+{
+ if (MP_HAS(S_MP_RADIX_SIZE_OVERESTIMATE)) {
+ return s_mp_radix_size_overestimate(a, radix, size);
+ }
+ if (MP_HAS(MP_RADIX_SIZE)) {
+ return mp_radix_size(a, radix, size);
+ }
+ return MP_ERR;
+}
+
+#endif
diff --git a/s_mp_radix_size_overestimate.c b/s_mp_radix_size_overestimate.c
new file mode 100644
index 0000000..35dd1e6
--- /dev/null
+++ b/s_mp_radix_size_overestimate.c
@@ -0,0 +1,87 @@
+#include "tommath_private.h"
+#ifdef S_MP_RADIX_SIZE_OVERESTIMATE_C
+/* LibTomMath, multiple-precision integer library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+/*
+ Overestimate the size needed for the bigint to string conversion by a very small amount.
+ The error is about 10^-8; it will overestimate the result by at most 11 elements for
+ a number of the size 2^(2^31)-1 which is currently the largest possible in this library.
+ Some short tests gave no results larger than 5 (plus 2 for sign and EOS).
+ */
+
+/*
+ Table of {0, INT(log_2([1..64])*2^p)+1 } where p is the scale
+ factor defined in MP_RADIX_SIZE_SCALE and INT() extracts the integer part (truncating).
+ Good for 32 bit "int". Set MP_RADIX_SIZE_SCALE = 61 and recompute values
+ for 64 bit "int".
+ */
+/* *INDENT-OFF* */
+#define MP_RADIX_SIZE_SCALE 29
+static const uint32_t s_log_bases[65] = {
+ 0u, 0u, 0x20000001u, 0x14309399u, 0x10000001u,
+ 0xdc81a35u, 0xc611924u, 0xb660c9eu, 0xaaaaaabu, 0xa1849cdu,
+ 0x9a209a9u, 0x94004e1u, 0x8ed19c2u, 0x8a5ca7du, 0x867a000u,
+ 0x830cee3u, 0x8000001u, 0x7d42d60u, 0x7ac8b32u, 0x7887847u,
+ 0x7677349u, 0x749131fu, 0x72d0163u, 0x712f657u, 0x6fab5dbu,
+ 0x6e40d1bu, 0x6ced0d0u, 0x6badbdeu, 0x6a80e3bu, 0x6964c19u,
+ 0x6857d31u, 0x6758c38u, 0x6666667u, 0x657fb21u, 0x64a3b9fu,
+ 0x63d1ab4u, 0x6308c92u, 0x624869eu, 0x618ff47u, 0x60dedeau,
+ 0x6034ab0u, 0x5f90e7bu, 0x5ef32cbu, 0x5e5b1b2u, 0x5dc85c3u,
+ 0x5d3aa02u, 0x5cb19d9u, 0x5c2d10fu, 0x5bacbbfu, 0x5b3064fu,
+ 0x5ab7d68u, 0x5a42df0u, 0x59d1506u, 0x5962ffeu, 0x58f7c57u,
+ 0x588f7bcu, 0x582a000u, 0x57c7319u, 0x5766f1du, 0x5709243u,
+ 0x56adad9u, 0x565474du, 0x55fd61fu, 0x55a85e8u, 0x5555556u
+};
+/* *INDENT-ON* */
+
+mp_err s_mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size)
+{
+ int bit_count;
+ mp_int bi_bit_count, bi_k;
+ mp_err err = MP_OKAY;
+
+ if ((radix < 2) || (radix > 64)) {
+ return MP_VAL;
+ }
+
+ if (mp_iszero(a)) {
+ *size = 2U;
+ return MP_OKAY;
+ }
+
+ if (MP_HAS(S_MP_LOG_2EXPT) && MP_IS_2EXPT((mp_digit)radix)) {
+ /* floor(log_{2^n}(a)) + 1 + EOS + sign */
+ *size = (size_t)(s_mp_log_2expt(a, (mp_digit)radix));
+ /* Would overflow with base 2 otherwise */
+ if (*size > (INT_MAX - 4)) {
+ return MP_VAL;
+ }
+ *size += 3u;
+ return MP_OKAY;
+ }
+
+ if ((err = mp_init_multi(&bi_bit_count, &bi_k, NULL)) != MP_OKAY) {
+ return err;
+ }
+
+ /* la = floor(log_2(a)) + 1 */
+ bit_count = mp_count_bits(a);
+
+ mp_set_u32(&bi_bit_count, (uint32_t)bit_count);
+ /* k = floor(2^29/log_2(radix)) + 1 */
+ mp_set_u32(&bi_k, s_log_bases[radix]);
+ /* n = floor((la * k) / 2^29) + 1 */
+ if ((err = mp_mul(&bi_bit_count, &bi_k, &bi_bit_count)) != MP_OKAY) goto LBL_ERR;
+ if ((err = mp_div_2d(&bi_bit_count, MP_RADIX_SIZE_SCALE, &bi_bit_count, NULL)) != MP_OKAY) goto LBL_ERR;
+
+ /* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */
+ /* n = n + 1 + EOS + sign */
+ *size = (size_t)(mp_get_u64(&bi_bit_count) + 3U);
+
+LBL_ERR:
+ mp_clear_multi(&bi_bit_count, &bi_k, NULL);
+ return err;
+}
+
+#endif
diff --git a/tommath.def b/tommath.def
index 88733ca..7d53508 100644
--- a/tommath.def
+++ b/tommath.def
@@ -87,6 +87,7 @@ EXPORTS
mp_prime_rand
mp_prime_strong_lucas_selfridge
mp_radix_size
+ mp_radix_size_overestimate
mp_rand
mp_read_radix
mp_reduce
diff --git a/tommath.h b/tommath.h
index 95f7127..a2e7585 100644
--- a/tommath.h
+++ b/tommath.h
@@ -565,7 +565,9 @@ mp_err mp_to_sbin(const mp_int *a, uint8_t *buf, size_t maxlen, size_t *written)
mp_err mp_read_radix(mp_int *a, const char *str, int radix) MP_WUR;
mp_err mp_to_radix(const mp_int *a, char *str, size_t maxlen, size_t *written, int radix) MP_WUR;
+
mp_err mp_radix_size(const mp_int *a, int radix, size_t *size) MP_WUR;
+mp_err mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size) MP_WUR;
#ifndef MP_NO_FILE
mp_err mp_fread(mp_int *a, int radix, FILE *stream) MP_WUR;
diff --git a/tommath_class.h b/tommath_class.h
index 936a17e..5e76e6a 100644
--- a/tommath_class.h
+++ b/tommath_class.h
@@ -93,6 +93,7 @@
# define MP_PRIME_RAND_C
# define MP_PRIME_STRONG_LUCAS_SELFRIDGE_C
# define MP_RADIX_SIZE_C
+# define MP_RADIX_SIZE_OVERESTIMATE_C
# define MP_RAND_C
# define MP_READ_RADIX_C
# define MP_REDUCE_C
@@ -154,6 +155,7 @@
# define S_MP_PRIME_IS_DIVISIBLE_C
# define S_MP_PRIME_TAB_C
# define S_MP_RADIX_MAP_C
+# define S_MP_RADIX_SIZE_OVERESTIMATE_C
# define S_MP_RAND_JENKINS_C
# define S_MP_RAND_PLATFORM_C
# define S_MP_SQR_C
@@ -339,7 +341,7 @@
#endif
#if defined(MP_FWRITE_C)
-# define MP_RADIX_SIZE_C
+# define MP_RADIX_SIZE_OVERESTIMATE_C
# define MP_TO_RADIX_C
# define S_MP_ZERO_BUF_C
#endif
@@ -688,6 +690,11 @@
# define MP_LOG_N_C
#endif
+#if defined(MP_RADIX_SIZE_OVERESTIMATE_C)
+# define MP_RADIX_SIZE_C
+# define S_MP_RADIX_SIZE_OVERESTIMATE_C
+#endif
+
#if defined(MP_RAND_C)
# define MP_GROW_C
# define MP_RAND_SOURCE_C
@@ -1172,6 +1179,17 @@
#if defined(S_MP_RADIX_MAP_C)
#endif
+#if defined(S_MP_RADIX_SIZE_OVERESTIMATE_C)
+# define MP_CLEAR_MULTI_C
+# define MP_COUNT_BITS_C
+# define MP_DIV_2D_C
+# define MP_GET_I64_C
+# define MP_INIT_MULTI_C
+# define MP_MUL_C
+# define MP_SET_U32_C
+# define S_MP_LOG_2EXPT_C
+#endif
+
#if defined(S_MP_RAND_JENKINS_C)
# define S_MP_RAND_JENKINS_INIT_C
#endif
diff --git a/tommath_private.h b/tommath_private.h
index eb566dd..24e7781 100644
--- a/tommath_private.h
+++ b/tommath_private.h
@@ -192,6 +192,7 @@ MP_PRIVATE mp_err s_mp_sub(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR;
MP_PRIVATE void s_mp_copy_digs(mp_digit *d, const mp_digit *s, int digits);
MP_PRIVATE void s_mp_zero_buf(void *mem, size_t size);
MP_PRIVATE void s_mp_zero_digs(mp_digit *d, int digits);
+MP_PRIVATE mp_err s_mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size);
/* 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;
diff --git a/tommath_superclass.h b/tommath_superclass.h
index 9e85d98..9245e00 100644
--- a/tommath_superclass.h
+++ b/tommath_superclass.h
@@ -28,6 +28,7 @@
# define MP_NEG_C
# define MP_PRIME_FROBENIUS_UNDERWOOD_C
# define MP_RADIX_SIZE_C
+# define MP_RADIX_SIZE_OVERESTIMATE_C
# define MP_LOG_N_C
# define MP_RAND_C
# define MP_REDUCE_C
@@ -36,6 +37,8 @@
# define MP_ROOT_N_C
# define MP_SET_L_C
# define MP_SET_UL_C
+# define MP_SET_U64_C
+# define MP_SET_I64_C
# define MP_SBIN_SIZE_C
# define MP_TO_RADIX_C
# define MP_TO_SBIN_C