use super::reduction::montgomery_reduction;
use crate::{CtLt, Limb, Odd, Uint, UintRef, WideWord, Word, word};
#[inline]
pub(crate) fn almost_montgomery_mul(
x: &UintRef,
y: &UintRef,
out: &mut UintRef,
modulus: &UintRef,
mod_neg_inv: Limb,
) {
let overflow = montgomery_multiply_inner(
x.as_limbs(),
y.as_limbs(),
out.as_mut_limbs(),
modulus.as_limbs(),
mod_neg_inv,
);
let overflow = word::choice_from_lsb(overflow.0);
out.conditional_borrowing_sub_assign(modulus, overflow);
}
pub(crate) fn almost_montgomery_reduce(z: &mut UintRef, modulus: &UintRef) {
z.conditional_borrowing_sub_assign(modulus, !z.ct_lt(modulus));
z.conditional_borrowing_sub_assign(modulus, !z.ct_lt(modulus));
}
#[inline(always)]
#[allow(clippy::cast_possible_truncation)]
pub const fn montgomery_multiply_inner(
x: &[Limb],
y: &[Limb],
out: &mut [Limb],
modulus: &[Limb],
mod_neg_inv: Limb,
) -> Limb {
let nlimbs = modulus.len();
assert!(nlimbs == x.len() && nlimbs == y.len() && nlimbs == out.len());
let mut meta_carry = 0;
let mut i = 0;
while i < nlimbs {
let xi = x[i];
let axy = (xi.0 as WideWord) * (y[0].0 as WideWord) + out[0].0 as WideWord;
let u = (axy as Word).wrapping_mul(mod_neg_inv.0);
let mut carry;
let (a, c) = ((u as WideWord) * (modulus[0].0 as WideWord)).overflowing_add(axy);
carry = ((c as WideWord) << Word::BITS) | (a >> Word::BITS);
let mut j = 1;
while j < nlimbs {
let axy = (xi.0 as WideWord) * (y[j].0 as WideWord) + out[j].0 as WideWord;
let umc = (u as WideWord) * (modulus[j].0 as WideWord) + carry;
let (ab, c) = axy.overflowing_add(umc);
out[j - 1] = Limb(ab as Word);
carry = ((c as WideWord) << Word::BITS) | (ab >> Word::BITS);
j += 1;
}
carry += meta_carry;
(out[nlimbs - 1], meta_carry) = (Limb(carry as Word), carry >> Word::BITS);
i += 1;
}
Limb(meta_carry as Word)
}
pub(crate) const fn mul_montgomery_form<const LIMBS: usize>(
a: &Uint<LIMBS>,
b: &Uint<LIMBS>,
modulus: &Odd<Uint<LIMBS>>,
mod_neg_inv: Limb,
) -> Uint<LIMBS> {
let mut out = Uint::<LIMBS>::ZERO;
let carry = montgomery_multiply_inner(
&a.limbs,
&b.limbs,
&mut out.limbs,
&modulus.0.limbs,
mod_neg_inv,
);
out.try_sub_with_carry(carry, modulus.as_ref()).0
}
pub(crate) const fn square_montgomery_form<const LIMBS: usize>(
a: &Uint<LIMBS>,
modulus: &Odd<Uint<LIMBS>>,
mod_neg_inv: Limb,
) -> Uint<LIMBS> {
if LIMBS == 4 {
let lower_upper = a.widening_square();
return montgomery_reduction(&lower_upper, modulus, mod_neg_inv);
}
let mut out = Uint::<LIMBS>::ZERO;
let carry = montgomery_multiply_inner(
&a.limbs,
&a.limbs,
&mut out.limbs,
&modulus.0.limbs,
mod_neg_inv,
);
out.try_sub_with_carry(carry, modulus.as_ref()).0
}
#[inline(always)]
pub(crate) const fn square_repeat_montgomery_form<const LIMBS: usize>(
a: &Uint<LIMBS>,
n: u32,
modulus: &Odd<Uint<LIMBS>>,
mod_neg_inv: Limb,
) -> Uint<LIMBS> {
if n == 0 {
return *a;
}
if n == 1 {
return square_montgomery_form(a, modulus, mod_neg_inv);
}
let mut i = 0;
let mut out = *a;
let mut base;
let mut carry;
loop {
(base, out) = (out, Uint::ZERO);
carry = montgomery_multiply_inner(
&base.limbs,
&base.limbs,
&mut out.limbs,
&modulus.0.limbs,
mod_neg_inv,
);
i += 1;
if i == n {
break;
}
out = out
.conditional_borrowing_sub(modulus.as_ref(), carry.is_nonzero())
.0;
}
(out, carry) = out.try_sub_with_carry(carry, modulus.as_ref());
out.try_sub_with_carry(carry, modulus.as_ref()).0
}