-
Notifications
You must be signed in to change notification settings - Fork 194
/
mp_log.c
168 lines (142 loc) · 4.97 KB
/
mp_log.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include "tommath_private.h"
#ifdef MP_LOG_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#define MP_LOG2_EXPT(a,b) ((mp_count_bits((a)) - 1) / mp_cnt_lsb((b)))
static mp_err s_approx_log_d(const mp_int *a, const mp_int *b, int *lb)
{
mp_word La, Lb;
mp_err err;
/* Approximation of the individual logarithms with low precision */
if ((err = s_mp_fp_log_d(a, &La)) != MP_OKAY) goto LTM_ERR;
if ((err = s_mp_fp_log_d(b, &Lb)) != MP_OKAY) goto LTM_ERR;
/* Approximation of log_b(a) with low precision. */
*lb = (int)(((La - (Lb + 1)/2) / Lb) + 1);
/* TODO: just floor it instead? Multiplication is cheaper than division. */
/* *lb = (int)(la_word / lb_word); */
err = MP_OKAY;
LTM_ERR:
return err;
}
static mp_err s_approx_log(const mp_int *a, const mp_int *b, int *lb)
{
mp_int La, Lb, t;
mp_err err;
if ((err = mp_init_multi(&La, &Lb, &t, NULL)) != MP_OKAY) {
return err;
}
if ((err = s_mp_fp_log(a, &La)) != MP_OKAY) goto LTM_ERR;
if ((err = s_mp_fp_log(b, &Lb)) != MP_OKAY) goto LTM_ERR;
if ((err = mp_add_d(&Lb, 1u, &t)) != MP_OKAY) goto LTM_ERR;
if ((err = mp_div_2(&t, &t)) != MP_OKAY) goto LTM_ERR;
if ((err = mp_sub(&La, &t, &t)) != MP_OKAY) goto LTM_ERR;
if ((err = mp_div(&t, &Lb, &t, NULL)) != MP_OKAY) goto LTM_ERR;
if ((err = mp_add_d(&t, 1u, &t)) != MP_OKAY) goto LTM_ERR;
*lb = mp_get_i32(&t);
err = MP_OKAY;
LTM_ERR:
mp_clear_multi(&t, &Lb, &La, NULL);
return err;
}
mp_err mp_log(const mp_int *a, const mp_int *b, int *lb)
{
mp_int bn;
int n, fla, flb;
mp_err err;
mp_ord cmp;
if (mp_isneg(a) || mp_iszero(a) || (mp_cmp_d(b, 2u) == MP_LT)) {
return MP_VAL;
}
if (MP_IS_POWER_OF_TWO(b)) {
*lb = MP_LOG2_EXPT(a, b);
return MP_OKAY;
}
/* floor(log_2(x)) for cut-off */
fla = mp_count_bits(a) - 1;
flb = mp_count_bits(b) - 1;
cmp = mp_cmp(a, b);
/* "a < b -> 0" and "(b == a) -> 1" */
if ((cmp == MP_LT) || (cmp == MP_EQ)) {
*lb = (cmp == MP_EQ);
return MP_OKAY;
}
/* "a < b^2 -> 1" (bit-count is sufficient, doesn't need to be exact) */
if (((2 * flb)-1) > fla) {
*lb = 1;
return MP_OKAY;
}
if (MP_HAS(S_MP_WORD_TOO_SMALL)) {
err = s_approx_log(a, b, &n);
} else {
err = s_approx_log_d(a, b, &n);
}
if (err != MP_OKAY) {
return err;
}
if ((err = mp_init(&bn)) != MP_OKAY) {
return err;
}
/* Check result. Result is wrong by 2(two) at most. */
if ((err = mp_expt_n(b, n, &bn)) != MP_OKAY) {
/* If the approximation overshot we can give it another try */
if (err == MP_OVF) {
n--;
/* But only one */
if ((err = mp_expt_n(b, n, &bn)) != MP_OKAY) goto LTM_ERR;
} else {
goto LTM_ERR;
}
}
cmp = mp_cmp(&bn, a);
/* The rare case of a perfect power makes a perfect shortcut, too. */
if (cmp == MP_EQ) {
*lb = n;
goto LTM_OUT;
}
/* We have to make at least one multiplication because it could still be a perfect power. */
if (cmp == MP_LT) {
do {
/* Full big-integer operations are to be avoided if possible */
if (b->used == 1) {
if ((err = mp_mul_d(&bn, b->dp[0], &bn)) != MP_OKAY) {
if (err == MP_OVF) {
goto LTM_OUT;
}
goto LTM_ERR;
}
} else {
if ((err = mp_mul(&bn, b, &bn)) != MP_OKAY) {
if (err == MP_OVF) {
goto LTM_OUT;
}
goto LTM_ERR;
}
}
n++;
} while ((cmp = mp_cmp(&bn, a)) == MP_LT);
/* Overshot, take it back. */
if (cmp == MP_GT) {
n--;
}
goto LTM_OUT;
}
/* But it can overestimate, too, for example if "a" is closely below some "b^k" */
if (cmp == MP_GT) {
do {
if (b->used == 1) {
/* These are cheaper exact divisions, but that function is not available in LTM */
if ((err = mp_div_d(&bn, b->dp[0], &bn, NULL)) != MP_OKAY) goto LTM_ERR;
} else {
if ((err = mp_div(&bn, b, &bn, NULL)) != MP_OKAY) goto LTM_ERR;
}
n--;
} while ((cmp = mp_cmp(&bn, a)) == MP_GT);
}
LTM_OUT:
*lb = n;
err = MP_OKAY;
LTM_ERR:
mp_clear(&bn);
return err;
}
#endif