/** * Subtracts two multiple precision integers, where a >= b. * * @param[out] c - the destination. * @param[in] a - the first multiple precision integer to subtract. * @param[in] b - the second multiple precision integer to subtract. */ static void bn_sub_imp(bn_t c, bn_t a, bn_t b) { int max, min; dig_t carry; max = a->used; min = b->used; /* Grow the destination to accomodate the result. */ bn_grow(c, max); if (a->used == b->used) { carry = bn_subn_low(c->dp, a->dp, b->dp, min); } else { carry = bn_subn_low(c->dp, a->dp, b->dp, min); carry = bn_sub1_low(c->dp + min, a->dp + min, carry, max - min); } c->used = max; bn_trim(c); }
void bn_modn_low(dig_t *c, const dig_t *a, int sa, const dig_t *m, int sm, dig_t u) { int i, j; dig_t r0, r1, r2; dig_t *tmp, *tmpc; const dig_t *tmpm; tmpc = c; r0 = r1 = r2 = 0; for (i = 0; i < sm; i++, tmpc++, a++) { tmp = c; tmpm = m + i; for (j = 0; j < i; j++, tmp++, tmpm--) { COMBA_STEP(r2, r1, r0, *tmp, *tmpm); } if (i < sa) { COMBA_ADD(r2, r1, r0, *a); } *tmpc = (dig_t)(r0 * u); COMBA_STEP(r2, r1, r0, *tmpc, *m); r0 = r1; r1 = r2; r2 = 0; } for (i = sm; i < 2 * sm - 1; i++, a++) { tmp = c + (i - sm + 1); tmpm = m + sm - 1; for (j = i - sm + 1; j < sm; j++, tmp++, tmpm--) { COMBA_STEP(r2, r1, r0, *tmp, *tmpm); } if (i < sa) { COMBA_ADD(r2, r1, r0, *a); } c[i - sm] = r0; r0 = r1; r1 = r2; r2 = 0; } if (i < sa) { COMBA_ADD(r2, r1, r0, *a); } c[sm - 1] = r0; if (r1) { bn_subn_low(c, c, m, sm); } }
/** * Multiplies two prime field elements using recursive Karatsuba * multiplication. * * @param[out] c - the result. * @param[in] a - the first prime field element. * @param[in] b - the second prime field element. * @param[in] size - the number of digits to multiply. * @param[in] level - the number of Karatsuba steps to apply. */ static void fp_mul_karat_imp(dv_t c, const fp_t a, const fp_t b, int size, int level) { int i, h, h1; dv_t a1, b1, a0b0, a1b1, t; dig_t carry; /* Compute half the digits of a or b. */ h = size >> 1; h1 = size - h; dv_null(a1); dv_null(b1); dv_null(a0b0); dv_null(a1b1); TRY { /* Allocate the temp variables. */ dv_new(a1); dv_new(b1); dv_new(a0b0); dv_new(a1b1); dv_new(t); dv_zero(a1, h1 + 1); dv_zero(b1, h1 + 1); dv_zero(a0b0, 2 * h); dv_zero(a1b1, 2 * h1); dv_zero(t, 2 * h1 + 1); /* a0b0 = a0 * b0 and a1b1 = a1 * b1 */ if (level <= 1) { #if FP_MUL == BASIC for (i = 0; i < h; i++) { carry = bn_mula_low(a0b0 + i, a, *(b + i), h); *(a0b0 + i + h) = carry; } for (i = 0; i < h1; i++) { carry = bn_mula_low(a1b1 + i, a + h, *(b + h + i), h1); *(a1b1 + i + h1) = carry; } #elif FP_MUL == COMBA || FP_MUL == INTEG bn_muln_low(a0b0, a, b, h); bn_muln_low(a1b1, a + h, b + h, h1); #endif } else { fp_mul_karat_imp(a0b0, a, b, h, level - 1); fp_mul_karat_imp(a1b1, a + h, b + h, h1, level - 1); } for (i = 0; i < 2 * h; i++) { c[i] = a0b0[i]; } for (i = 0; i < 2 * h1 + 1; i++) { c[2 * h + i] = a1b1[i]; } /* a1 = (a1 + a0) */ carry = bn_addn_low(a1, a, a + h, h); bn_add1_low(a1 + h, a1 + h, carry, 2); if (h1 > h) { bn_add1_low(a1 + h, a1 + h, *(a + 2 * h), 2); } /* b1 = (b1 + b0) */ carry = bn_addn_low(b1, b, b + h, h); bn_add1_low(b1 + h, b1 + h, carry, 2); if (h1 > h) { bn_add1_low(b1 + h, b1 + h, *(b + 2 * h), 2); } if (level <= 1) { /* t = (a1 + a0)*(b1 + b0) */ #if FP_MUL == BASIC for (i = 0; i < h1 + 1; i++) { carry = bn_mula_low(t + i, a1, *(b1 + i), h1 + 1); *(t + i + h1 + 1) = carry; } #elif FP_MUL == COMBA || FP_MUL == INTEG bn_muln_low(t, a1, b1, h1 + 1); #endif } else { fp_mul_karat_imp(t, a1, b1, h1 + 1, level - 1); } /* t = t - (a0*b0 << h digits) */ carry = bn_subn_low(t, t, a0b0, 2 * h); bn_sub1_low(t + 2 * h, t + 2 * h, carry, 2 * (h1 + 1) - 2 * h); /* t = t - (a1*b1 << h digits) */ carry = bn_subn_low(t, t, a1b1, 2 * h1); bn_sub1_low(t + 2 * h1, t + 2 * h1, carry, 2 * (h1 + 1) - 2 * h1); /* c = c + [(a1 + a0)*(b1 + b0) << digits] */ c += h; carry = bn_addn_low(c, c, t, 2 * (h1 + 1)); c += 2 * (h1 + 1); bn_add1_low(c, c, carry, 2 * size - h - 2 * (h1 + 1)); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(a1); dv_free(b1); dv_free(a0b0); dv_free(a1b1); dv_free(t); } }
void fp_rdcs_low(dig_t *c, dig_t *a, dig_t *m) { align dig_t q[2 * FP_DIGS], _q[2 * FP_DIGS]; align dig_t _r[2 * FP_DIGS], r[2 * FP_DIGS], t[2 * FP_DIGS]; int *sform, len; int first, i, j, b0, d0, b1, d1; dig_t carry; sform = fp_prime_get_sps(&len); SPLIT(b0, d0, FP_BITS, FP_DIG_LOG); first = (d0) + (b0 == 0 ? 0 : 1); /* q = floor(a/b^k) */ dv_zero(q, 2 * FP_DIGS); bn_rshd_low(q, a, 2 * FP_DIGS, d0); if (b0 > 0) { bn_rshb_low(q, q, 2 * FP_DIGS, b0); } /* r = a - qb^k. */ dv_copy(r, a, first); if (b0 > 0) { r[first - 1] &= MASK(b0); } carry = 0; while (!fp_is_zero(q)) { dv_zero(_q, 2 * FP_DIGS); for (i = len - 1; i > 0; i--) { j = (sform[i] < 0 ? -sform[i] : sform[i]); SPLIT(b1, d1, j, FP_DIG_LOG); dv_zero(t, 2 * FP_DIGS); bn_lshd_low(t, q, FP_DIGS, d1); if (b1 > 0) { bn_lshb_low(t, t, 2 * FP_DIGS, b1); } if (sform[i] > 0) { bn_subn_low(_q, _q, t, 2 * FP_DIGS); } else { bn_addn_low(_q, _q, t, 2 * FP_DIGS); } } if (sform[0] > 0) { bn_subn_low(_q, _q, q, 2 * FP_DIGS); } else { bn_addn_low(_q, _q, q, 2 * FP_DIGS); } bn_rshd_low(q, _q, 2 * FP_DIGS, d0); if (b0 > 0) { bn_rshb_low(q, q, 2 * FP_DIGS, b0); } dv_copy(_r, _q, first); if (b0 > 0) { _r[first - 1] &= MASK(b0); } fp_add(r, r, _r); } while (fp_cmpn_low(r, m) != CMP_LT) { fp_subn_low(r, r, m); } fp_copy(c, r); }
/** * Computes the square of a multiple precision integer using recursive Karatsuba * squaring. * * @param[out] c - the result. * @param[in] a - the prime field element to square. * @param[in] size - the number of digits to square. * @param[in] level - the number of Karatsuba steps to apply. */ static void fp_sqr_karat_imp(dv_t c, const fp_t a, int size, int level) { int i, h, h1; dv_t t0, t1, a0a0, a1a1; dig_t carry; /* Compute half the digits of a or b. */ h = size >> 1; h1 = size - h; dv_null(t0); dv_null(t1); dv_null(a0a0); dv_null(a1a1); TRY { /* Allocate the temp variables. */ dv_new(t0); dv_new(t1); dv_new(a0a0); dv_new(a1a1); dv_zero(t0, 2 * h1); dv_zero(t1, 2 * (h1 + 1)); dv_zero(a0a0, 2 * h); dv_zero(a1a1, 2 * h1); if (level <= 1) { /* a0a0 = a0 * a0 and a1a1 = a1 * a1 */ #if FP_SQR == BASIC for (i = 0; i < h; i++) { bn_sqra_low(a0a0 + (2 * i), a + i, h - i); } for (i = 0; i < h1; i++) { bn_sqra_low(a1a1 + (2 * i), a + h + i, h1 - i); } #elif FP_SQR == COMBA || FP_SQR == INTEG bn_sqrn_low(a0a0, a, h); bn_sqrn_low(a1a1, a + h, h1); #elif FP_SQR == MULTP bn_muln_low(a0a0, a, a, h); bn_muln_low(a1a1, a + h, a + h, h1); #endif } else { fp_sqr_karat_imp(a0a0, a, h, level - 1); fp_sqr_karat_imp(a1a1, a + h, h1, level - 1); } /* t2 = a1 * a1 << 2*h digits + a0 * a0. */ for (i = 0; i < 2 * h; i++) { c[i] = a0a0[i]; } for (i = 0; i < 2 * h1; i++) { c[2 * h + i] = a1a1[i]; } /* t = (a1 + a0) */ carry = bn_addn_low(t0, a, a + h, h); carry = bn_add1_low(t0 + h, t0 + h, carry, 2); if (h1 > h) { carry = bn_add1_low(t0 + h, t0 + h, *(a + 2 * h), 2); } if (level <= 1) { /* a1a1 = (a1 + a0)*(a1 + a0) */ #if FP_SQR == BASIC for (i = 0; i < h1 + 1; i++) { bn_sqra_low(t1 + (2 * i), t0 + i, h1 + 1 - i); } #elif FP_SQR == COMBA || FP_SQR == INTEG bn_sqrn_low(t1, t0, h1 + 1); #elif FP_SQR == MULTP bn_muln_low(t1, t0, t0, h1 + 1); #endif } else { fp_sqr_karat_imp(t1, t0, h1 + 1, level - 1); } /* t = t - (a0*a0 << h digits) */ carry = bn_subn_low(t1, t1, a0a0, 2 * h); bn_sub1_low(t1 + 2 * h, t1 + 2 * h, carry, 2 * (h1 + 1) - 2 * h); /* t = t - (a1*a1 << h digits) */ carry = bn_subn_low(t1, t1, a1a1, 2 * h1); bn_sub1_low(t1 + 2 * h, t1 + 2 * h, carry, 2 * (h1 + 1) - 2 * h); /* c = c + [(a1 + a0)*(a1 + a0) << digits] */ c += h; carry = bn_addn_low(c, c, t1, 2 * (h1 + 1)); c += 2 * (h1 + 1); carry = bn_add1_low(c, c, carry, 2 * size - h - 2 * (h1 + 1)); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t0); dv_free(t1); dv_free(a0a0); dv_free(a1a1); } }
void bn_divn_low(dig_t *c, dig_t *d, dig_t *a, int sa, dig_t *b, int sb) { int norm, i, n, t, sd; dig_t carry, t1[3], t2[3]; /* Normalize x and y so that the leading digit of y is bigger than * 2^(BN_DIGIT-1). */ norm = util_bits_dig(b[sb - 1]) % BN_DIGIT; if (norm < (int)(BN_DIGIT - 1)) { norm = (BN_DIGIT - 1) - norm; carry = bn_lshb_low(a, a, sa, norm); if (carry) { a[sa++] = carry; } carry = bn_lshb_low(b, b, sb, norm); if (carry) { b[sb++] = carry; } } else { norm = 0; } n = sa - 1; t = sb - 1; /* Shift y so that the most significant digit of y is aligned with the * most significant digit of x. */ bn_lshd_low(b, b, sb, (n - t)); /* Find the most significant digit of the quotient. */ while (bn_cmpn_low(a, b, sa) != CMP_LT) { c[n - t]++; bn_subn_low(a, a, b, sa); } /* Shift y back. */ bn_rshd_low(b, b, sb + (n - t), (n - t)); /* Find the remaining digits. */ for (i = n; i >= (t + 1); i--) { if (i > sa) { continue; } if (a[i] == b[t]) { c[i - t - 1] = ((((dbl_t)1) << BN_DIGIT) - 1); } else { dbl_t tmp; tmp = ((dbl_t)a[i]) << ((dbl_t)BN_DIGIT); tmp |= (dbl_t)(a[i - 1]); tmp /= (dbl_t)(b[t]); c[i - t - 1] = (dig_t)tmp; } c[i - t - 1]++; do { c[i - t - 1]--; t1[0] = (t - 1 < 0) ? 0 : b[t - 1]; t1[1] = b[t]; carry = bn_mul1_low(t1, t1, c[i - t - 1], 2); t1[2] = carry; t2[0] = (i - 2 < 0) ? 0 : a[i - 2]; t2[1] = (i - 1 < 0) ? 0 : a[i - 1]; t2[2] = a[i]; } while (bn_cmpn_low(t1, t2, 3) == CMP_GT); carry = bn_mul1_low(d, b, c[i - t - 1], sb); sd = sb; if (carry) { d[sd++] = carry; } carry = bn_subn_low(a + (i - t - 1), a + (i - t - 1), d, sd); sd += (i - t - 1); if (sa - sd > 0) { carry = bn_sub1_low(a + sd, a + sd, carry, sa - sd); } if (carry) { sd = sb + (i - t - 1); carry = bn_addn_low(a + (i - t - 1), a + (i - t - 1), b, sb); carry = bn_add1_low(a + sd, a + sd, carry, sa - sd); c[i - t - 1]--; } } /* Remainder should be not be longer than the divisor. */ bn_rshb_low(d, a, sb, norm); }