void fb_mul_lcomb(fb_t c, const fb_t a, const fb_t b) { dv_t t; dig_t carry; dv_null(t); TRY { dv_new(t); dv_zero(t, 2 * FB_DIGS); for (int i = FB_DIGIT - 1; i >= 0; i--) { for (int j = 0; j < FB_DIGS; j++) { if (a[j] & ((dig_t)1 << i)) { /* This cannot use fb_addn_low() because there is no * guarantee that operands will be aligned. */ fb_addd_low(t + j, t + j, b, FB_DIGS); } } if (i != 0) { carry = fb_lsh1_low(t, t); fb_lsh1_low(t + FB_DIGS, t + FB_DIGS); t[FB_DIGS] |= carry; } } fb_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); } }
/** * Multiplies two binary field elements using right-to-left comb multiplication. * * @param c - the result. * @param a - the first binary field element. * @param b - the second binary field element. * @param size - the number of digits to multiply. */ static void fb_mul_rcomb_imp(dig_t *c, const dig_t *a, const dig_t *b, int size) { dv_t _b; dv_null(_b); TRY { dv_new(_b); dv_zero(c, 2 * size); for (int i = 0; i < size; i++) _b[i] = b[i]; _b[size] = 0; for (int i = 0; i < FB_DIGIT; i++) { for (int j = 0; j < size; j++) { if (a[j] & ((dig_t)1 << i)) { fb_addd_low(c + j, c + j, _b, size + 1); } } if (i != FB_DIGIT - 1) { bn_lsh1_low(_b, _b, size + 1); } } } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(_b); } }
void fb_rdc_basic(fb_t c, dv_t a) { int j, k; dig_t *tmpa; dv_t r; dv_null(r); TRY { dv_new(r); tmpa = a + FB_DIGS; /* First reduce the high part. */ for (int i = fb_bits(tmpa) - 1; i >= 0; i--) { if (fb_get_bit(tmpa, i)) { SPLIT(k, j, i - FB_BITS, FB_DIG_LOG); if (k <= 0) { fb_addd_low(tmpa + j, tmpa + j, fb_poly_get(), FB_DIGS); } else { r[FB_DIGS] = fb_lshb_low(r, fb_poly_get(), k); fb_addd_low(tmpa + j, tmpa + j, r, FB_DIGS + 1); } } } for (int i = fb_bits(a) - 1; i >= FB_BITS; i--) { if (fb_get_bit(a, i)) { SPLIT(k, j, i - FB_BITS, FB_DIG_LOG); if (k == 0) { fb_addd_low(a + j, a + j, fb_poly_get(), FB_DIGS); } else { r[FB_DIGS] = fb_lshb_low(r, fb_poly_get(), k); fb_addd_low(a + j, a + j, r, FB_DIGS + 1); } } } fb_copy(c, a); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { fb_free(r); } }
/** * Multiplies two binary field elements using left-to-right comb multiplication. * * @param c - the result. * @param a - the first binary field element. * @param b - the second binary field element. * @param size - the number of digits to multiply. */ static void fb_mul_lcomb_imp(dig_t *c, const dig_t *a, const dig_t *b, int size) { dv_zero(c, 2 * size); for (int i = FB_DIGIT - 1; i >= 0; i--) { for (int j = 0; j < size; j++) { if (a[j] & ((dig_t)1 << i)) { fb_addd_low(c + j, c + j, b, size); } } if (i != 0) { bn_lsh1_low(c, c, 2 * size); } } }
void fb_mul_rcomb(fb_t c, const fb_t a, const fb_t b) { dv_t t, _b; dig_t carry; dv_null(t); dv_null(_b); TRY { dv_new(t); dv_new(_b); dv_zero(t, 2 * FB_DIGS); dv_zero(_b, FB_DIGS + 1); fb_copy(_b, b); for (int i = 0; i < FB_DIGIT; i++) { for (int j = 0; j < FB_DIGS; j++) { if (a[j] & ((dig_t)1 << i)) { fb_addd_low(t + j, t + j, _b, FB_DIGS + 1); } } if (i != FB_DIGIT - 1) { carry = fb_lsh1_low(_b, _b); _b[FB_DIGS] = (_b[FB_DIGS] << 1) | carry; } } fb_rdc(c, t); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(t); dv_free(_b); } }
/** * Multiplies two binary field elements using recursive Karatsuba * multiplication. * * @param[out] c - the result. * @param[in] a - the first binary field element. * @param[in] b - the second binary field element. * @param[in] size - the number of digits to multiply. * @param[in] level - the number of Karatsuba steps to apply. */ static void fb_mul_karat_imp(dv_t c, const fb_t a, const fb_t b, int size, int level) { int i, h, h1; dv_t a1, b1, ab; dig_t *a0b0, *a1b1; dv_null(a1); dv_null(b1); dv_null(ab); /* Compute half the digits of a or b. */ h = size >> 1; h1 = size - h; TRY { /* Allocate the temp variables. */ dv_new(a1); dv_new(b1); dv_new(ab); a0b0 = ab; a1b1 = ab + 2 * h; /* a0b0 = a0 * b0 and a1b1 = a1 * b1 */ if (level <= 1) { #if FB_MUL == BASIC fb_mul_basic_imp(a0b0, a, b, h); fb_mul_basic_imp(a1b1, a + h, b + h, h1); #elif FB_MUL == LCOMB fb_mul_lcomb_imp(a0b0, a, b, h); fb_mul_lcomb_imp(a1b1, a + h, b + h, h1); #elif FB_MUL == RCOMB fb_mul_rcomb_imp(a0b0, a, b, h); fb_mul_rcomb_imp(a1b1, a + h, b + h, h1); #elif FB_MUL == INTEG || FB_MUL == LODAH fb_muld_low(a0b0, a, b, h); fb_muld_low(a1b1, a + h, b + h, h1); #endif } else { fb_mul_karat_imp(a0b0, a, b, h, level - 1); fb_mul_karat_imp(a1b1, a + h, b + h, h1, level - 1); } for (i = 0; i < 2 * size; i++) { c[i] = ab[i]; } /* c = c - (a0*b0 << h digits) */ fb_addd_low(c + h, c + h, a0b0, 2 * h); /* c = c - (a1*b1 << h digits) */ fb_addd_low(c + h, c + h, a1b1, 2 * h1); /* a1 = (a1 + a0) */ fb_addd_low(a1, a, a + h, h); /* b1 = (b1 + b0) */ fb_addd_low(b1, b, b + h, h); if (h1 > h) { a1[h1 - 1] = a[h + h1 - 1]; b1[h1 - 1] = b[h + h1 - 1]; } if (level <= 1) { /* a1b1 = (a1 + a0)*(b1 + b0) */ #if FB_MUL == BASIC fb_mul_basic_imp(a1b1, a1, b1, h1); #elif FB_MUL == LCOMB fb_mul_lcomb_imp(a1b1, a1, b1, h1); #elif FB_MUL == RCOMB fb_mul_rcomb_imp(a1b1, a1, b1, h1); #elif FB_MUL == INTEG || FB_MUL == LODAH fb_muld_low(a1b1, a1, b1, h1); #endif } else { fb_mul_karat_imp(a1b1, a1, b1, h1, level - 1); } /* c = c + [(a1 + a0)*(b1 + b0) << digits] */ fb_addd_low(c + h, c + h, a1b1, 2 * h1); } CATCH_ANY { THROW(ERR_CAUGHT); } FINALLY { dv_free(a1); dv_free(b1); dv_free(ab); } }
void fb_invn_low(dig_t *c, const dig_t *a) { int j, d, lu, lv, lt, l1, l2, bu, bv; align dig_t _u[2 * FB_DIGS], _v[2 * FB_DIGS]; align dig_t _g1[2 * FB_DIGS], _g2[2 * FB_DIGS]; dig_t *t = NULL, *u = NULL, *v = NULL, *g1 = NULL, *g2 = NULL, carry; dv_zero(_g1, FB_DIGS + 1); dv_zero(_g2, FB_DIGS + 1); u = _u; v = _v; g1 = _g1; g2 = _g2; /* u = a, v = f, g1 = 1, g2 = 0. */ dv_copy(u, a, FB_DIGS); dv_copy(v, fb_poly_get(), FB_DIGS); g1[0] = 1; lu = lv = FB_DIGS; l1 = l2 = 1; bu = fb_bits(u); bv = FB_BITS + 1; j = bu - bv; /* While (u != 1). */ while (1) { /* If j < 0 then swap(u, v), swap(g1, g2), j = -j. */ if (j < 0) { t = u; u = v; v = t; lt = lu; lu = lv; lv = lt; t = g1; g1 = g2; g2 = t; lt = l1; l1 = l2; l2 = lt; j = -j; } SPLIT(j, d, j, FB_DIG_LOG); /* u = u + v * z^j. */ if (j > 0) { carry = fb_lsha_low(u + d, v, j, lv); u[d + lv] ^= carry; } else { fb_addd_low(u + d, u + d, v, lv); } /* g1 = g1 + g2 * z^j. */ if (j > 0) { carry = fb_lsha_low(g1 + d, g2, j, l2); l1 = (l2 + d >= l1 ? l2 + d : l1); if (carry) { g1[d + l2] ^= carry; l1 = (l2 + d >= l1 ? l1 + 1 : l1); } } else { fb_addd_low(g1 + d, g1 + d, g2, l2); l1 = (l2 + d > l1 ? l2 + d : l1); } while (u[lu - 1] == 0) lu--; while (v[lv - 1] == 0) lv--; if (lu == 1 && u[0] == 1) break; /* j = deg(u) - deg(v). */ lt = util_bits_dig(u[lu - 1]) - util_bits_dig(v[lv - 1]); j = ((lu - lv) << FB_DIG_LOG) + lt; } /* Return g1. */ fb_copy(c, g1); }