use crypto_bigint::{
BoxedUint, Resize,
modular::{BoxedMontyForm, BoxedMontyParams},
};
pub(crate) fn to_monty(x: &BoxedUint, p: &BoxedMontyParams) -> BoxedMontyForm {
let bits = p.bits_precision();
if x.bits_precision() <= bits {
return BoxedMontyForm::new(x.resize_unchecked(bits), p);
}
let radix = BoxedMontyForm::from_montgomery(p.as_ref().r2().clone(), p);
let digit = |limbs: &[crypto_bigint::Limb]| {
let mut value = BoxedUint::zero_with_precision(bits);
value.as_mut_limbs()[..limbs.len()].copy_from_slice(limbs);
BoxedMontyForm::new(value, p)
};
let mut chunks = x.as_limbs().chunks(p.modulus().as_limbs().len()).rev();
let mut acc = digit(chunks.next().expect("nonempty integer"));
for chunk in chunks {
acc *= &radix;
acc += &digit(chunk);
}
acc
}
#[cfg(test)]
mod tests {
use super::*;
use crypto_bigint::Odd;
#[test]
fn reduction_with_leading_zero_moduli_and_partial_digits() {
for bits in [64, 128, 192] {
for modulus in [
BoxedUint::from(1u64),
BoxedUint::from(3u64),
BoxedUint::from(u64::MAX),
BoxedUint::max(bits),
] {
let modulus = Odd::new(modulus.resize_unchecked(bits)).expect("odd modulus");
let params = BoxedMontyParams::new(modulus.clone());
for input_bits in [64u32, 128, 192, 256, 320, 512] {
let bytes: Vec<u8> = (0..input_bits.div_ceil(8))
.map(|i| i.wrapping_mul(73).wrapping_add(19).to_le_bytes()[0])
.collect();
for input in [
BoxedUint::zero_with_precision(input_bits),
BoxedUint::one_with_precision(input_bits),
BoxedUint::max(input_bits),
BoxedUint::from_be_slice(&bytes, input_bits).expect("pattern"),
] {
let expected = input
.rem_vartime(modulus.as_nz_ref())
.resize_unchecked(bits);
assert_eq!(to_monty(&input, ¶ms).retrieve(), expected);
}
}
}
}
}
}