Hash :
1cc289d2
Author :
Date :
2019-11-09T20:23:03
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
#include "tommath_private.h"
#ifdef S_MP_DIV_SCHOOL_C
/* LibTomMath, multiple-precision integer library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
/* integer signed division.
* c*b + d == a [e.g. a/b, c=quotient, d=remainder]
* HAC pp.598 Algorithm 14.20
*
* Note that the description in HAC is horribly
* incomplete. For example, it doesn't consider
* the case where digits are removed from 'x' in
* the inner loop. It also doesn't consider the
* case that y has fewer than three digits, etc..
*
* The overall algorithm is as described as
* 14.20 from HAC but fixed to treat these cases.
*/
mp_err s_mp_div_school(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d)
{
mp_int q, x, y, t1, t2;
int n, t, i, norm;
bool neg;
mp_err err;
if ((err = mp_init_size(&q, a->used + 2)) != MP_OKAY) {
return err;
}
q.used = a->used + 2;
if ((err = mp_init(&t1)) != MP_OKAY) goto LBL_Q;
if ((err = mp_init(&t2)) != MP_OKAY) goto LBL_T1;
if ((err = mp_init_copy(&x, a)) != MP_OKAY) goto LBL_T2;
if ((err = mp_init_copy(&y, b)) != MP_OKAY) goto LBL_X;
/* fix the sign */
neg = (a->sign != b->sign);
x.sign = y.sign = MP_ZPOS;
/* normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] */
norm = mp_count_bits(&y) % MP_DIGIT_BIT;
if (norm < (MP_DIGIT_BIT - 1)) {
norm = (MP_DIGIT_BIT - 1) - norm;
if ((err = mp_mul_2d(&x, norm, &x)) != MP_OKAY) goto LBL_Y;
if ((err = mp_mul_2d(&y, norm, &y)) != MP_OKAY) goto LBL_Y;
} else {
norm = 0;
}
/* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
n = x.used - 1;
t = y.used - 1;
/* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
/* y = y*b**{n-t} */
if ((err = mp_lshd(&y, n - t)) != MP_OKAY) goto LBL_Y;
while (mp_cmp(&x, &y) != MP_LT) {
++(q.dp[n - t]);
if ((err = mp_sub(&x, &y, &x)) != MP_OKAY) goto LBL_Y;
}
/* reset y by shifting it back down */
mp_rshd(&y, n - t);
/* step 3. for i from n down to (t + 1) */
for (i = n; i >= (t + 1); i--) {
if (i > x.used) {
continue;
}
/* step 3.1 if xi == yt then set q{i-t-1} to b-1,
* otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
if (x.dp[i] == y.dp[t]) {
q.dp[(i - t) - 1] = ((mp_digit)1 << (mp_digit)MP_DIGIT_BIT) - (mp_digit)1;
} else {
mp_word tmp;
tmp = (mp_word)x.dp[i] << (mp_word)MP_DIGIT_BIT;
tmp |= (mp_word)x.dp[i - 1];
tmp /= (mp_word)y.dp[t];
if (tmp > (mp_word)MP_MASK) {
tmp = MP_MASK;
}
q.dp[(i - t) - 1] = (mp_digit)(tmp & (mp_word)MP_MASK);
}
/* while (q{i-t-1} * (yt * b + y{t-1})) >
xi * b**2 + xi-1 * b + xi-2
do q{i-t-1} -= 1;
*/
q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1uL) & (mp_digit)MP_MASK;
do {
q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & (mp_digit)MP_MASK;
/* find left hand */
mp_zero(&t1);
t1.dp[0] = ((t - 1) < 0) ? 0u : y.dp[t - 1];
t1.dp[1] = y.dp[t];
t1.used = 2;
if ((err = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) goto LBL_Y;
/* find right hand */
t2.dp[0] = ((i - 2) < 0) ? 0u : x.dp[i - 2];
t2.dp[1] = x.dp[i - 1]; /* i >= 1 always holds */
t2.dp[2] = x.dp[i];
t2.used = 3;
} while (mp_cmp_mag(&t1, &t2) == MP_GT);
/* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
if ((err = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) goto LBL_Y;
if ((err = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) goto LBL_Y;
if ((err = mp_sub(&x, &t1, &x)) != MP_OKAY) goto LBL_Y;
/* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
if (mp_isneg(&x)) {
if ((err = mp_copy(&y, &t1)) != MP_OKAY) goto LBL_Y;
if ((err = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) goto LBL_Y;
if ((err = mp_add(&x, &t1, &x)) != MP_OKAY) goto LBL_Y;
q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & MP_MASK;
}
}
/* now q is the quotient and x is the remainder
* [which we have to normalize]
*/
/* get sign before writing to c */
x.sign = mp_iszero(&x) ? MP_ZPOS : a->sign;
if (c != NULL) {
mp_clamp(&q);
mp_exch(&q, c);
c->sign = (neg ? MP_NEG : MP_ZPOS);
}
if (d != NULL) {
if ((err = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) goto LBL_Y;
mp_exch(&x, d);
}
LBL_Y:
mp_clear(&y);
LBL_X:
mp_clear(&x);
LBL_T2:
mp_clear(&t2);
LBL_T1:
mp_clear(&t1);
LBL_Q:
mp_clear(&q);
return err;
}
#endif