1 #include "tommath_private.h"
3 /* LibTomMath, multiple-precision integer library -- Tom St Denis */
4 /* SPDX-License-Identifier: Unlicense */
6 #define MP_LOG2_EXPT(a,b) ((mp_count_bits((a)) - 1) / mp_cnt_lsb((b)))
8 static mp_err
s_approx_log_d(const mp_int
*a
, const mp_int
*b
, int *lb
)
13 /* Approximation of the individual logarithms with low precision */
14 if ((err
= s_mp_fp_log_d(a
, &La
)) != MP_OKAY
) goto LTM_ERR
;
15 if ((err
= s_mp_fp_log_d(b
, &Lb
)) != MP_OKAY
) goto LTM_ERR
;
17 /* Approximation of log_b(a) with low precision. */
18 *lb
= (int)(((La
- (Lb
+ 1)/2) / Lb
) + 1);
19 /* TODO: just floor it instead? Multiplication is cheaper than division. */
20 /* *lb = (int)(la_word / lb_word); */
26 static mp_err
s_approx_log(const mp_int
*a
, const mp_int
*b
, int *lb
)
31 if ((err
= mp_init_multi(&La
, &Lb
, &t
, NULL
)) != MP_OKAY
) {
35 if ((err
= s_mp_fp_log(a
, &La
)) != MP_OKAY
) goto LTM_ERR
;
36 if ((err
= s_mp_fp_log(b
, &Lb
)) != MP_OKAY
) goto LTM_ERR
;
38 if ((err
= mp_add_d(&Lb
, 1u, &t
)) != MP_OKAY
) goto LTM_ERR
;
39 if ((err
= mp_div_2(&t
, &t
)) != MP_OKAY
) goto LTM_ERR
;
40 if ((err
= mp_sub(&La
, &t
, &t
)) != MP_OKAY
) goto LTM_ERR
;
41 if ((err
= mp_div(&t
, &Lb
, &t
, NULL
)) != MP_OKAY
) goto LTM_ERR
;
42 if ((err
= mp_add_d(&t
, 1u, &t
)) != MP_OKAY
) goto LTM_ERR
;
47 mp_clear_multi(&t
, &Lb
, &La
, NULL
);
51 mp_err
mp_log(const mp_int
*a
, const mp_int
*b
, int *lb
)
58 if (mp_isneg(a
) || mp_iszero(a
) || (mp_cmp_d(b
, 2u) == MP_LT
)) {
62 if (MP_IS_POWER_OF_TWO(b
)) {
63 *lb
= MP_LOG2_EXPT(a
, b
);
67 /* floor(log_2(x)) for cut-off */
68 fla
= mp_count_bits(a
) - 1;
69 flb
= mp_count_bits(b
) - 1;
73 /* "a < b -> 0" and "(b == a) -> 1" */
74 if ((cmp
== MP_LT
) || (cmp
== MP_EQ
)) {
79 /* "a < b^2 -> 1" (bit-count is sufficient, doesn't need to be exact) */
80 if (((2 * flb
)-1) > fla
) {
85 if (MP_HAS(S_MP_WORD_TOO_SMALL
)) {
86 err
= s_approx_log(a
, b
, &n
);
88 err
= s_approx_log_d(a
, b
, &n
);
94 if ((err
= mp_init(&bn
)) != MP_OKAY
) {
98 /* Check result. Result is wrong by 2(two) at most. */
99 if ((err
= mp_expt_n(b
, n
, &bn
)) != MP_OKAY
) {
100 /* If the approximation overshot we can give it another try */
104 if ((err
= mp_expt_n(b
, n
, &bn
)) != MP_OKAY
) goto LTM_ERR
;
110 cmp
= mp_cmp(&bn
, a
);
112 /* The rare case of a perfect power makes a perfect shortcut, too. */
118 /* We have to make at least one multiplication because it could still be a perfect power. */
121 /* Full big-integer operations are to be avoided if possible */
123 if ((err
= mp_mul_d(&bn
, b
->dp
[0], &bn
)) != MP_OKAY
) {
130 if ((err
= mp_mul(&bn
, b
, &bn
)) != MP_OKAY
) {
138 } while ((cmp
= mp_cmp(&bn
, a
)) == MP_LT
);
139 /* Overshot, take it back. */
146 /* But it can overestimate, too, for example if "a" is closely below some "b^k" */
150 /* These are cheaper exact divisions, but that function is not available in LTM */
151 if ((err
= mp_div_d(&bn
, b
->dp
[0], &bn
, NULL
)) != MP_OKAY
) goto LTM_ERR
;
154 if ((err
= mp_div(&bn
, b
, &bn
, NULL
)) != MP_OKAY
) goto LTM_ERR
;
157 } while ((cmp
= mp_cmp(&bn
, a
)) == MP_GT
);