use super::FixedMontyForm;
use crate::modular::lincomb::lincomb_monty_form;
impl<const LIMBS: usize> FixedMontyForm<LIMBS> {
#[must_use]
#[track_caller]
pub const fn lincomb_vartime(products: &[(&Self, &Self)]) -> Self {
assert!(!products.is_empty(), "empty products");
let params = &products[0].0.params;
Self {
montgomery_form: lincomb_monty_form(
products,
¶ms.modulus,
params.mod_neg_inv(),
params.mod_leading_zeros,
),
params: products[0].0.params,
}
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "rand_core")]
#[test]
fn lincomb_expected() {
use crate::U256;
use crate::{
Odd, Random, RandomMod,
modular::{FixedMontyForm, FixedMontyParams},
};
use rand_core::SeedableRng;
let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
for n in 0..1500 {
let modulus = Odd::<U256>::random_from_rng(&mut rng);
let params = FixedMontyParams::new_vartime(modulus);
let m = modulus.as_nz_ref();
let a = U256::random_mod_vartime(&mut rng, m);
let b = U256::random_mod_vartime(&mut rng, m);
let c = U256::random_mod_vartime(&mut rng, m);
let d = U256::random_mod_vartime(&mut rng, m);
assert_eq!(
a.mul_mod(&b, m).add_mod(&c.mul_mod(&d, m), m),
FixedMontyForm::lincomb_vartime(&[
(
&FixedMontyForm::new(&a, ¶ms),
&FixedMontyForm::new(&b, ¶ms)
),
(
&FixedMontyForm::new(&c, ¶ms),
&FixedMontyForm::new(&d, ¶ms)
),
])
.retrieve(),
"n={n}, a={a}, b={b}, c={c}, d={d}"
);
}
}
}