|
| 1 | +#include "tommath_private.h" |
| 2 | +#ifdef BN_MP_ILOGB_C |
| 3 | +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ |
| 4 | +/* SPDX-License-Identifier: Unlicense */ |
| 5 | + |
| 6 | +/* Compute log_{base}(a) */ |
| 7 | +static mp_word s_pow(mp_word base, mp_word exponent) |
| 8 | +{ |
| 9 | + mp_word result = 1uLL; |
| 10 | + while (exponent) { |
| 11 | + if ((exponent & 0x1) == 1) { |
| 12 | + result *= base; |
| 13 | + } |
| 14 | + exponent >>= 1; |
| 15 | + base *= base; |
| 16 | + } |
| 17 | + |
| 18 | + return result; |
| 19 | +} |
| 20 | + |
| 21 | +static mp_digit s_digit_ilogb(mp_digit base, mp_digit n) |
| 22 | +{ |
| 23 | + mp_word bracket_low = 1uLL, bracket_mid, bracket_high, N; |
| 24 | + mp_digit ret, high = 1uL, low = 0uL, mid; |
| 25 | + |
| 26 | + if (n < base) { |
| 27 | + return (mp_digit)0uL; |
| 28 | + } |
| 29 | + if (n == base) { |
| 30 | + return (mp_digit)1uL; |
| 31 | + } |
| 32 | + |
| 33 | + bracket_high = (mp_word) base ; |
| 34 | + N = (mp_word) n; |
| 35 | + |
| 36 | + while (bracket_high < N) { |
| 37 | + low = high; |
| 38 | + bracket_low = bracket_high; |
| 39 | + high <<= 1; |
| 40 | + bracket_high *= bracket_high; |
| 41 | + } |
| 42 | + |
| 43 | + while (((mp_digit)(high - low)) > 1uL) { |
| 44 | + mid = (low + high) >> 1; |
| 45 | + bracket_mid = bracket_low * s_pow(base, mid - low) ; |
| 46 | + |
| 47 | + if (N < bracket_mid) { |
| 48 | + high = mid ; |
| 49 | + bracket_high = bracket_mid ; |
| 50 | + } |
| 51 | + if (N > bracket_mid) { |
| 52 | + low = mid ; |
| 53 | + bracket_low = bracket_mid ; |
| 54 | + } |
| 55 | + if (N == bracket_mid) { |
| 56 | + return (mp_digit) mid; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + if (bracket_high == N) { |
| 61 | + ret = high; |
| 62 | + } else { |
| 63 | + ret = low; |
| 64 | + } |
| 65 | + |
| 66 | + return ret; |
| 67 | +} |
| 68 | + |
| 69 | +/* TODO: output could be "int" because the output of mp_radix_size is int, too, |
| 70 | + as is the output of mp_bitcount. |
| 71 | + With the same problem: max size is INT_MAX * MP_DIGIT not INT_MAX only! |
| 72 | +*/ |
| 73 | +int mp_ilogb(mp_int *a, mp_digit base, mp_int *c) |
| 74 | +{ |
| 75 | + int err, cmp; |
| 76 | + unsigned int high, low, mid; |
| 77 | + mp_int bracket_low, bracket_high, bracket_mid, t, bi_base; |
| 78 | + mp_digit tmp; |
| 79 | + |
| 80 | + err = MP_OKAY; |
| 81 | + if (a->sign == MP_NEG) { |
| 82 | + return MP_VAL; |
| 83 | + } |
| 84 | + if (IS_ZERO(a)) { |
| 85 | + return MP_VAL; |
| 86 | + } |
| 87 | + |
| 88 | + if (base < 2) { |
| 89 | + return MP_VAL; |
| 90 | + } else if (base == 2) { |
| 91 | + cmp = mp_count_bits(a) - 1; |
| 92 | + mp_set_int(c, (unsigned long)cmp); |
| 93 | + return err; |
| 94 | + } else if (a->used == 1) { |
| 95 | + tmp = s_digit_ilogb(base, a->dp[0]); |
| 96 | + mp_set(c, tmp); |
| 97 | + return err; |
| 98 | + } |
| 99 | + |
| 100 | + |
| 101 | + cmp = mp_cmp_d(a, base); |
| 102 | + |
| 103 | + if (cmp == MP_LT) { |
| 104 | + mp_zero(c); |
| 105 | + return err; |
| 106 | + } else if (cmp == MP_EQ) { |
| 107 | + mp_set(c, (mp_digit)1uL); |
| 108 | + return err; |
| 109 | + } |
| 110 | + |
| 111 | + if ((err = |
| 112 | + mp_init_multi(&bracket_low, &bracket_high, |
| 113 | + &bracket_mid, &t, &bi_base, NULL)) != MP_OKAY) { |
| 114 | + return err; |
| 115 | + } |
| 116 | + |
| 117 | + low = 0uL; |
| 118 | + mp_set(&bracket_low, 1uL); |
| 119 | + high = 1uL; |
| 120 | + |
| 121 | + mp_set(&bracket_high, base); |
| 122 | + |
| 123 | + /* |
| 124 | + A kind of Giant-step/baby-step algorithm. |
| 125 | + Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ |
| 126 | + The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped |
| 127 | + for small n. |
| 128 | + */ |
| 129 | + while (mp_cmp(&bracket_high, a) == MP_LT) { |
| 130 | + low = high; |
| 131 | + if ((err = mp_copy(&bracket_high, &bracket_low)) != MP_OKAY) { |
| 132 | + goto LBL_ERR; |
| 133 | + } |
| 134 | + high <<= 1; |
| 135 | + if ((err = mp_sqr(&bracket_high, &bracket_high)) != MP_OKAY) { |
| 136 | + goto LBL_ERR; |
| 137 | + } |
| 138 | + } |
| 139 | + mp_set(&bi_base, base); |
| 140 | + |
| 141 | + while ((high - low) > 1) { |
| 142 | + mid = (high + low) >> 1; |
| 143 | + /* Difference can be larger then the type behind mp_digit can hold */ |
| 144 | + if ((mid - low) > (unsigned int)(MP_MASK)) { |
| 145 | + err = MP_VAL; |
| 146 | + goto LBL_ERR; |
| 147 | + } |
| 148 | + if ((err = mp_expt_d(&bi_base, (mid - low), &t)) != MP_OKAY) { |
| 149 | + goto LBL_ERR; |
| 150 | + } |
| 151 | + if ((err = mp_mul(&bracket_low, &t, &bracket_mid)) != MP_OKAY) { |
| 152 | + goto LBL_ERR; |
| 153 | + } |
| 154 | + cmp = mp_cmp(a, &bracket_mid); |
| 155 | + if (cmp == MP_LT) { |
| 156 | + high = mid; |
| 157 | + mp_exch(&bracket_mid, &bracket_high); |
| 158 | + } |
| 159 | + if (cmp == MP_GT) { |
| 160 | + low = mid; |
| 161 | + mp_exch(&bracket_mid, &bracket_low); |
| 162 | + } |
| 163 | + if (cmp == MP_EQ) { |
| 164 | + mp_set_int(c, (unsigned long)mid); |
| 165 | + goto LBL_END; |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + if (mp_cmp(&bracket_high, a) == MP_EQ) { |
| 170 | + mp_set_int(c, (unsigned long)high); |
| 171 | + } else { |
| 172 | + mp_set_int(c, (unsigned long)low); |
| 173 | + } |
| 174 | + |
| 175 | +LBL_END: |
| 176 | +LBL_ERR: |
| 177 | + mp_clear_multi(&bracket_low, &bracket_high, &bracket_mid, |
| 178 | + &t, &bi_base, NULL); |
| 179 | + return err; |
| 180 | +} |
| 181 | + |
| 182 | + |
| 183 | +#endif |
0 commit comments