pub fn strict_compute_n_prime_trial_search<T>(modulus: &T, r: &T) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ PartialEq
+ PartialOrd
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub,
for<'a> T: core::ops::RemAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>,
{
let target = r - &T::one();
let mut n_prime = T::one();
loop {
let product = modulus * &n_prime;
let remainder = &product % r;
if remainder == target {
return Some(n_prime);
}
let (incremented, _overflow) = n_prime.overflowing_add(&T::one());
n_prime = incremented;
if &n_prime >= r {
return None; }
}
}
fn strict_compute_n_prime_extended_euclidean<T>(modulus: &T, r: &T) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ PartialEq
+ PartialOrd
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>,
{
let modulus_clone = &T::zero() + modulus; if let Some(modulus_inv) = crate::inv::strict_mod_inv(modulus_clone, r) {
if modulus_inv == T::zero() {
Some(r - &T::one()) } else {
Some(r - &modulus_inv)
}
} else {
None }
}
fn strict_compute_n_prime_hensels_lifting<T>(modulus: &T, r: &T, r_bits: usize) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub
+ for<'a> core::ops::Rem<&'a T, Output = T>,
for<'a> T: core::ops::RemAssign<&'a T>,
for<'a> &'a T: core::ops::Add<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Rem<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
let mut n_prime = T::one();
for k in 2..=r_bits {
let target_mod = T::one() << k; let temp_prod = modulus * &n_prime;
let (temp_sum, _overflow) = temp_prod.overflowing_add(&T::one());
let check_val = &temp_sum % &target_mod;
if check_val != T::zero() {
let prev_power = T::one() << (k - 1);
if check_val == prev_power {
let (adjusted, _overflow) = n_prime.overflowing_add(&prev_power);
n_prime = adjusted;
}
}
}
let final_check = (modulus * &n_prime) % r;
let target = r - &T::one();
if final_check != target {
None } else {
Some(n_prime)
}
}
pub fn strict_compute_montgomery_params<T>(modulus: &T) -> Option<(T, T, T, usize)>
where
T: num_traits::Zero
+ num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub
+ for<'a> core::ops::Rem<&'a T, Output = T>,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
strict_compute_montgomery_params_with_method(
modulus,
crate::montgomery::NPrimeMethod::default(),
)
}
pub fn strict_compute_montgomery_params_with_method<T>(
modulus: &T,
method: crate::montgomery::NPrimeMethod,
) -> Option<(T, T, T, usize)>
where
T: num_traits::Zero
+ num_traits::One
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub
+ for<'a> core::ops::Rem<&'a T, Output = T>,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
let mut r = T::one();
let mut r_bits = 0usize;
while &r <= modulus {
r = r << 1; r_bits += 1;
}
let r_clone = &r + &T::zero();
let r_inv = crate::inv::strict_mod_inv(r_clone, modulus)?;
let n_prime = match method {
crate::montgomery::NPrimeMethod::TrialSearch => {
strict_compute_n_prime_trial_search(modulus, &r)?
}
crate::montgomery::NPrimeMethod::ExtendedEuclidean
| crate::montgomery::NPrimeMethod::Newton => {
strict_compute_n_prime_extended_euclidean(modulus, &r)?
}
crate::montgomery::NPrimeMethod::HenselsLifting => {
strict_compute_n_prime_hensels_lifting(modulus, &r, r_bits)?
}
};
Some((r, r_inv, n_prime, r_bits))
}
pub fn strict_montgomery_mul<T>(a_mont: T, b_mont: &T, modulus: &T, n_prime: &T, r_bits: usize) -> T
where
T: num_traits::Zero
+ num_traits::One
+ crate::parity::Parity
+ PartialOrd
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::Shl<usize, Output = T>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub,
for<'a> T: core::ops::Mul<&'a T, Output = T>
+ core::ops::RemAssign<&'a T>
+ core::ops::Sub<&'a T, Output = T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
let product = crate::mul::strict_mod_mul(a_mont, b_mont, modulus);
strict_from_montgomery(product, modulus, n_prime, r_bits)
}
pub fn strict_from_montgomery<T>(a_mont: T, modulus: &T, n_prime: &T, r_bits: usize) -> T
where
T: num_traits::Zero
+ num_traits::One
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::Sub<Output = T>
+ num_traits::ops::overflowing::OverflowingAdd,
for<'a> T: core::ops::Mul<&'a T, Output = T> + core::ops::Sub<&'a T, Output = T>,
for<'a> &'a T: core::ops::Sub<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::BitAnd<&'a T, Output = T>,
{
if r_bits == 0 {
return if &a_mont >= modulus {
a_mont - modulus
} else {
a_mont
};
}
let mask = (T::one() << r_bits) - T::one();
let a_low = &a_mont & &mask;
let product = &a_low * n_prime;
let m = &product & &mask;
let m_times_n = &m * modulus;
let (sum, _overflow) = a_mont.overflowing_add(&m_times_n);
let t = sum >> r_bits;
if &t >= modulus { t - modulus } else { t }
}
pub fn strict_to_montgomery<T>(a: T, modulus: &T, r: &T) -> T
where
T: PartialOrd
+ num_traits::Zero
+ num_traits::One
+ crate::parity::Parity
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub
+ core::ops::Shr<usize, Output = T>,
for<'a> T: core::ops::RemAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>,
{
crate::mul::strict_mod_mul(a, r, modulus)
}
pub fn strict_montgomery_mod_mul_with_method<T>(
a: T,
b: &T,
modulus: &T,
method: crate::montgomery::NPrimeMethod,
) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ crate::parity::Parity
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub
+ for<'a> core::ops::Rem<&'a T, Output = T>,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
let (r, _r_inv, n_prime, r_bits) =
strict_compute_montgomery_params_with_method(modulus, method)?;
let a_mont = strict_to_montgomery(a, modulus, &r);
let b_clone = b + &T::zero(); let b_mont = strict_to_montgomery(b_clone, modulus, &r);
let product = crate::mul::strict_mod_mul(a_mont, &b_mont, modulus);
let result_mont = strict_from_montgomery(product, modulus, &n_prime, r_bits);
Some(strict_from_montgomery(
result_mont,
modulus,
&n_prime,
r_bits,
))
}
pub fn strict_montgomery_mod_mul<T>(a: T, b: &T, modulus: &T) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ crate::parity::Parity
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub
+ for<'a> core::ops::Rem<&'a T, Output = T>,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
strict_montgomery_mod_mul_with_method(a, b, modulus, crate::montgomery::NPrimeMethod::default())
}
pub fn strict_montgomery_mod_exp_with_method<T>(
mut base: T,
exponent: &T,
modulus: &T,
method: crate::montgomery::NPrimeMethod,
) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ crate::parity::Parity
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::ShrAssign<usize>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
let (r, _r_inv, n_prime, r_bits) =
strict_compute_montgomery_params_with_method(modulus, method)?;
base.rem_assign(modulus); base = strict_to_montgomery(base, modulus, &r);
let mut result = strict_to_montgomery(T::one(), modulus, &r);
let mut exp = exponent + &T::zero();
while exp > T::zero() {
if exp.is_odd() {
result = strict_montgomery_mul(result, &base, modulus, &n_prime, r_bits);
}
exp >>= 1;
if exp > T::zero() {
let base_clone = &base + &T::zero(); base = strict_montgomery_mul(base, &base_clone, modulus, &n_prime, r_bits);
}
}
Some(strict_from_montgomery(result, modulus, &n_prime, r_bits))
}
pub fn strict_montgomery_mod_exp<T>(base: T, exponent: &T, modulus: &T) -> Option<T>
where
T: num_traits::Zero
+ num_traits::One
+ crate::parity::Parity
+ PartialEq
+ PartialOrd
+ core::ops::Shl<usize, Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Shr<usize, Output = T>
+ core::ops::ShrAssign<usize>
+ num_traits::ops::overflowing::OverflowingAdd
+ num_traits::ops::overflowing::OverflowingSub,
for<'a> T: core::ops::RemAssign<&'a T>
+ core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::AddAssign<&'a T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>
+ core::ops::Mul<&'a T, Output = T>
+ core::ops::Sub<&'a T, Output = T>
+ core::ops::Div<&'a T, Output = T>
+ core::ops::Sub<T, Output = T>
+ core::ops::Add<&'a T, Output = T>
+ core::ops::BitAnd<Output = T>,
{
strict_montgomery_mod_exp_with_method(
base,
exponent,
modulus,
crate::montgomery::NPrimeMethod::default(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strict_compute_n_prime_trial_search() {
let modulus = 13u32;
let r = 16u32;
let n_prime = strict_compute_n_prime_trial_search(&modulus, &r).unwrap();
assert_eq!(n_prime, 11);
}
#[test]
fn test_strict_with_fixed_bigint() {
type U256 = fixed_bigint::FixedUInt<u32, 4>;
let modulus = U256::from(13u32);
let r = U256::from(16u32);
let n_prime = strict_compute_n_prime_trial_search(&modulus, &r).unwrap();
assert_eq!(n_prime, U256::from(11u32));
}
#[test]
fn test_strict_compute_montgomery_params() {
let modulus = 13u32;
let (r, r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
assert_eq!(r, 16);
assert_eq!(r_inv, 9);
assert_eq!(n_prime, 11);
assert_eq!(r_bits, 4);
assert_eq!((r * r_inv) % 13, 1);
assert_eq!((13 * n_prime) % r, r - 1);
assert!(r > 13);
assert_eq!(r, 1u32 << r_bits);
}
#[test]
fn test_strict_compute_montgomery_params_with_method() {
let modulus = 13u32;
let default_result = strict_compute_montgomery_params(&modulus).unwrap();
let explicit_trial_result = strict_compute_montgomery_params_with_method(
&modulus,
crate::montgomery::NPrimeMethod::TrialSearch,
)
.unwrap();
assert_eq!(default_result, explicit_trial_result);
let (r, r_inv, n_prime, r_bits) = explicit_trial_result;
assert_eq!(r, 16);
assert_eq!(r_inv, 9);
assert_eq!(n_prime, 11);
assert_eq!(r_bits, 4);
let euclidean_result = strict_compute_montgomery_params_with_method(
&modulus,
crate::montgomery::NPrimeMethod::ExtendedEuclidean,
)
.unwrap();
let hensels_result = strict_compute_montgomery_params_with_method(
&modulus,
crate::montgomery::NPrimeMethod::HenselsLifting,
)
.unwrap();
assert_eq!(default_result, euclidean_result);
assert_eq!(default_result, hensels_result);
}
#[test]
fn test_strict_montgomery_params_with_fixed_bigint() {
type U256 = fixed_bigint::FixedUInt<u32, 4>;
let modulus = U256::from(13u32);
let (r, r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
assert_eq!(r, U256::from(16u32));
assert_eq!(r_inv, U256::from(9u32));
assert_eq!(n_prime, U256::from(11u32));
assert_eq!(r_bits, 4);
let thirteen = U256::from(13u32);
let one = U256::from(1u32);
assert_eq!((r * r_inv) % thirteen, one);
assert_eq!((thirteen * n_prime) % r, r - one);
assert!(r > thirteen);
assert_eq!(r, one << r_bits);
}
#[test]
fn test_strict_from_montgomery() {
let modulus = 13u32;
let (_r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
let mont_form = 8u32; let normal_form = strict_from_montgomery(mont_form, &modulus, &n_prime, r_bits);
assert_eq!(normal_form, 7u32);
let mont_5 = 2u32; let normal_5 = strict_from_montgomery(mont_5, &modulus, &n_prime, r_bits);
assert_eq!(normal_5, 5u32);
let mont_0 = 0u32; let normal_0 = strict_from_montgomery(mont_0, &modulus, &n_prime, r_bits);
assert_eq!(normal_0, 0u32);
}
#[test]
fn test_strict_from_montgomery_with_fixed_bigint() {
type U256 = fixed_bigint::FixedUInt<u32, 4>;
let modulus = U256::from(13u32);
let (_r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
let mont_form = U256::from(8u32); let normal_form = strict_from_montgomery(mont_form, &modulus, &n_prime, r_bits);
assert_eq!(normal_form, U256::from(7u32));
let mont_5 = U256::from(2u32); let normal_5 = strict_from_montgomery(mont_5, &modulus, &n_prime, r_bits);
assert_eq!(normal_5, U256::from(5u32));
let mont_0 = U256::from(0u32);
let normal_0 = strict_from_montgomery(mont_0, &modulus, &n_prime, r_bits);
assert_eq!(normal_0, U256::from(0u32));
}
#[test]
fn test_strict_round_trip_conversion() {
let modulus = 13u32;
let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
for i in 0u32..13u32 {
let mont = crate::montgomery::basic_mont::basic_to_montgomery(i, modulus, r);
let back = strict_from_montgomery(mont, &modulus, &n_prime, r_bits);
assert_eq!(
back, i,
"Round-trip failed for {}: {} -> {} -> {}",
i, i, mont, back
);
}
}
#[test]
fn test_strict_to_montgomery() {
let modulus = 13u32;
let (r, _r_inv, _n_prime, _r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
assert_eq!(strict_to_montgomery(7u32, &modulus, &r), 8u32);
assert_eq!(strict_to_montgomery(5u32, &modulus, &r), 2u32);
assert_eq!(strict_to_montgomery(0u32, &modulus, &r), 0u32); assert_eq!(strict_to_montgomery(1u32, &modulus, &r), 3u32); }
#[test]
fn test_strict_to_montgomery_with_fixed_bigint() {
type U256 = fixed_bigint::FixedUInt<u32, 4>;
let modulus = U256::from(13u32);
let (r, _r_inv, _n_prime, _r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
assert_eq!(
strict_to_montgomery(U256::from(7u32), &modulus, &r),
U256::from(8u32)
);
assert_eq!(
strict_to_montgomery(U256::from(5u32), &modulus, &r),
U256::from(2u32)
);
assert_eq!(
strict_to_montgomery(U256::from(0u32), &modulus, &r),
U256::from(0u32)
);
assert_eq!(
strict_to_montgomery(U256::from(1u32), &modulus, &r),
U256::from(3u32)
);
}
#[test]
fn test_strict_montgomery_mod_mul() {
let modulus = 13u32;
let a = 7u32;
let b = 5u32;
let result = strict_montgomery_mod_mul(a, &b, &modulus).unwrap();
assert_eq!(result, 9u32);
let regular_result = crate::mul::strict_mod_mul(a, &b, &modulus);
assert_eq!(result, regular_result);
let test_cases = [
(0u32, 5u32, 0u32), (7u32, 0u32, 0u32), (1u32, 7u32, 7u32), (7u32, 1u32, 7u32), (12u32, 12u32, 1u32), (6u32, 9u32, 2u32), ];
for (a, b, expected) in test_cases.iter() {
let result = strict_montgomery_mod_mul(*a, b, &modulus).unwrap();
assert_eq!(result, *expected, "Failed for {} * {} mod 13", a, b);
}
}
#[test]
fn test_strict_montgomery_mod_mul_with_fixed_bigint() {
type U256 = fixed_bigint::FixedUInt<u32, 4>;
let modulus = U256::from(13u32);
let a = U256::from(7u32);
let b = U256::from(5u32);
let result = strict_montgomery_mod_mul(a, &b, &modulus).unwrap();
assert_eq!(result, U256::from(9u32));
let regular_result = crate::mul::strict_mod_mul(U256::from(7u32), &b, &modulus);
assert_eq!(result, regular_result);
}
#[test]
fn test_strict_complete_round_trip() {
let modulus = 13u32;
let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
for i in 0u32..13u32 {
let mont = strict_to_montgomery(i, &modulus, &r);
let back = strict_from_montgomery(mont, &modulus, &n_prime, r_bits);
assert_eq!(
back, i,
"Complete round-trip failed for {}: {} -> {} -> {}",
i, i, mont, back
);
}
}
#[test]
fn test_strict_montgomery_mod_exp() {
let modulus = 13u32;
let base = 7u32;
let exponent = 5u32;
let montgomery_result = strict_montgomery_mod_exp(base, &exponent, &modulus).unwrap();
let regular_result = crate::exp::strict_mod_exp(base, &exponent, &modulus);
assert_eq!(montgomery_result, regular_result);
assert_eq!(montgomery_result, 11u32);
assert_eq!(
strict_montgomery_mod_exp(0u32, &5u32, &modulus).unwrap(),
0u32
); assert_eq!(
strict_montgomery_mod_exp(7u32, &0u32, &modulus).unwrap(),
1u32
); assert_eq!(
strict_montgomery_mod_exp(1u32, &100u32, &modulus).unwrap(),
1u32
); assert_eq!(
strict_montgomery_mod_exp(7u32, &1u32, &modulus).unwrap(),
7u32
);
let test_cases = [
(2u32, 10u32, 13u32, 10u32), (3u32, 4u32, 13u32, 3u32), (12u32, 2u32, 13u32, 1u32), (5u32, 3u32, 13u32, 8u32), ];
for (base, exp, mod_val, expected) in test_cases.iter() {
let result = strict_montgomery_mod_exp(*base, exp, mod_val).unwrap();
assert_eq!(
result, *expected,
"Failed for {}^{} mod {}",
base, exp, mod_val
);
}
}
#[test]
fn test_strict_montgomery_mod_exp_with_fixed_bigint() {
type U256 = fixed_bigint::FixedUInt<u32, 4>;
let modulus = U256::from(13u32);
let base = U256::from(7u32);
let exponent = U256::from(5u32);
let montgomery_result = strict_montgomery_mod_exp(base, &exponent, &modulus).unwrap();
let regular_result = crate::exp::strict_mod_exp(U256::from(7u32), &exponent, &modulus);
assert_eq!(montgomery_result, regular_result);
assert_eq!(montgomery_result, U256::from(11u32));
assert_eq!(
strict_montgomery_mod_exp(U256::from(0u32), &U256::from(5u32), &modulus).unwrap(),
U256::from(0u32)
);
assert_eq!(
strict_montgomery_mod_exp(U256::from(7u32), &U256::from(0u32), &modulus).unwrap(),
U256::from(1u32)
);
assert_eq!(
strict_montgomery_mod_exp(U256::from(1u32), &U256::from(100u32), &modulus).unwrap(),
U256::from(1u32)
); }
#[test]
fn test_strict_montgomery_mod_exp_comprehensive() {
let modulus = 13u32;
for base in 0u32..13u32 {
for exponent in 0u32..10u32 {
let montgomery_result =
strict_montgomery_mod_exp(base, &exponent, &modulus).unwrap();
let regular_result = crate::exp::strict_mod_exp(base, &exponent, &modulus);
assert_eq!(
montgomery_result, regular_result,
"Montgomery vs regular exp mismatch: {}^{} mod 13: {} != {}",
base, exponent, montgomery_result, regular_result
);
}
}
}
#[test]
fn test_strict_montgomery_mod_exp_large_exponents() {
let modulus = 13u32;
assert_eq!(
strict_montgomery_mod_exp(2u32, &100u32, &modulus).unwrap(),
crate::exp::strict_mod_exp(2u32, &100u32, &modulus)
);
assert_eq!(
strict_montgomery_mod_exp(3u32, &1000u32, &modulus).unwrap(),
crate::exp::strict_mod_exp(3u32, &1000u32, &modulus)
);
assert_eq!(
strict_montgomery_mod_exp(7u32, &999999u32, &modulus).unwrap(),
crate::exp::strict_mod_exp(7u32, &999999u32, &modulus)
);
}
#[test]
fn test_strict_compute_n_prime_trial_search_failure() {
let modulus = 4u32;
let r = 8u32;
let result = strict_compute_n_prime_trial_search(&modulus, &r);
assert!(
result.is_none(),
"Should return None for invalid modulus-R pair"
);
}
#[test]
fn test_strict_compute_montgomery_params_failure_even_modulus() {
let even_modulus = 4u32;
let result = strict_compute_montgomery_params(&even_modulus);
assert!(result.is_none(), "Should return None for even modulus");
}
#[test]
fn test_strict_compute_montgomery_params_failure_with_method() {
let invalid_modulus = 4u32;
let trial_result = strict_compute_montgomery_params_with_method(
&invalid_modulus,
crate::montgomery::NPrimeMethod::TrialSearch,
);
assert!(
trial_result.is_none(),
"Trial search should fail with even modulus"
);
let euclidean_result = strict_compute_montgomery_params_with_method(
&invalid_modulus,
crate::montgomery::NPrimeMethod::ExtendedEuclidean,
);
assert!(
euclidean_result.is_none(),
"Extended Euclidean should fail with even modulus"
);
let hensels_result = strict_compute_montgomery_params_with_method(
&invalid_modulus,
crate::montgomery::NPrimeMethod::HenselsLifting,
);
assert!(
hensels_result.is_none(),
"Hensel's lifting should fail with even modulus"
);
}
#[test]
fn test_strict_montgomery_mod_mul_parameter_failure() {
let invalid_modulus = 4u32;
let a = 2u32;
let b = 3u32;
let result = strict_montgomery_mod_mul(a, &b, &invalid_modulus);
assert!(
result.is_none(),
"Montgomery mod_mul should return None for invalid modulus"
);
}
#[test]
fn test_strict_montgomery_mod_exp_parameter_failure() {
let invalid_modulus = 4u32;
let base = 2u32;
let exponent = 3u32;
let result = strict_montgomery_mod_exp(base, &exponent, &invalid_modulus);
assert!(
result.is_none(),
"Montgomery mod_exp should return None for invalid modulus"
);
}
#[test]
fn test_montgomery_reduction_final_subtraction() {
let modulus = 15u32; let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
let a_mont = strict_to_montgomery(14u32, &modulus, &r);
let result = strict_from_montgomery(a_mont, &modulus, &n_prime, r_bits);
assert_eq!(result, 14u32);
let max_mont = strict_to_montgomery(14u32, &modulus, &r);
let result_max = strict_from_montgomery(max_mont, &modulus, &n_prime, r_bits);
assert_eq!(result_max, 14u32);
}
#[test]
fn test_hensel_lifting_conditional_branches() {
let modulus = 15u32;
let hensels_result = strict_compute_montgomery_params_with_method(
&modulus,
crate::montgomery::NPrimeMethod::HenselsLifting,
);
assert!(
hensels_result.is_some(),
"Hensel's lifting should work with modulus 15"
);
let large_modulus = 255u32; let result_large = strict_compute_montgomery_params_with_method(
&large_modulus,
crate::montgomery::NPrimeMethod::HenselsLifting,
);
assert!(
result_large.is_some(),
"Should handle larger composite moduli"
);
}
#[test]
fn test_specific_montgomery_edge_cases() {
let edge_modulus = 9u32; let params = strict_compute_montgomery_params(&edge_modulus);
assert!(params.is_some(), "Should handle modulus 9");
if let Some((r, _r_inv, n_prime, r_bits)) = params {
let edge_val = 8u32; let mont_edge = strict_to_montgomery(edge_val, &edge_modulus, &r);
let back = strict_from_montgomery(mont_edge, &edge_modulus, &n_prime, r_bits);
assert_eq!(back, edge_val);
let a = 7u32;
let b = 8u32;
let result = strict_montgomery_mod_mul(a, &b, &edge_modulus).unwrap();
let expected = (a * b) % edge_modulus;
assert_eq!(result, expected);
}
let modulus2 = 21u32; if let Some((_r, _r_inv, _n_prime, _r_bits)) = strict_compute_montgomery_params(&modulus2) {
let result = strict_montgomery_mod_mul(20u32, &19u32, &modulus2).unwrap();
let expected = (20u32 * 19u32) % modulus2;
assert_eq!(result, expected);
}
}
#[test]
fn test_montgomery_exponentiation_edge_paths() {
let modulus = 11u32;
let base = 10u32; let exponent = 10u32;
let result = strict_montgomery_mod_exp(base, &exponent, &modulus).unwrap();
let expected = crate::exp::strict_mod_exp(base, &exponent, &modulus);
assert_eq!(result, expected);
let small_exp = 1u32;
let result_small = strict_montgomery_mod_exp(base, &small_exp, &modulus).unwrap();
assert_eq!(result_small, base);
let large_exp = 100u32;
let result_large = strict_montgomery_mod_exp(2u32, &large_exp, &modulus).unwrap();
let expected_large = crate::exp::strict_mod_exp(2u32, &large_exp, &modulus);
assert_eq!(result_large, expected_large);
}
#[test]
fn test_strict_montgomery_mul_basic() {
let modulus = 13u32;
let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
let a = 7u32;
let b = 5u32;
let a_mont = strict_to_montgomery(a, &modulus, &r);
let b_mont = strict_to_montgomery(b, &modulus, &r);
let result_mont = strict_montgomery_mul(a_mont, &b_mont, &modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, &modulus, &n_prime, r_bits);
let expected = (a * b) % modulus;
assert_eq!(
result, expected,
"Montgomery multiplication failed: {} * {} = {} (expected {})",
a, b, result, expected
);
}
#[test]
fn test_strict_montgomery_mul_edge_cases() {
let modulus = 17u32;
let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
let zero_mont = strict_to_montgomery(0u32, &modulus, &r);
let a_mont = strict_to_montgomery(5u32, &modulus, &r);
let result_mont = strict_montgomery_mul(zero_mont, &a_mont, &modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, &modulus, &n_prime, r_bits);
assert_eq!(result, 0u32, "Montgomery multiplication with zero failed");
let one_mont = strict_to_montgomery(1u32, &modulus, &r);
let result_mont = strict_montgomery_mul(one_mont, &a_mont, &modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, &modulus, &n_prime, r_bits);
assert_eq!(result, 5u32, "Montgomery multiplication with one failed");
let max_val = modulus - 1;
let max_mont = strict_to_montgomery(max_val, &modulus, &r);
let result_mont = strict_montgomery_mul(max_mont, &max_mont, &modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, &modulus, &n_prime, r_bits);
let expected = (max_val * max_val) % modulus;
assert_eq!(
result, expected,
"Montgomery multiplication with max values failed"
);
}
#[test]
fn test_strict_montgomery_mul_large_modulus() {
let modulus = 65521u32; let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
let a = 65520u32; let b = 65519u32; let a_mont = strict_to_montgomery(a, &modulus, &r);
let b_mont = strict_to_montgomery(b, &modulus, &r);
let result_mont = strict_montgomery_mul(a_mont, &b_mont, &modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, &modulus, &n_prime, r_bits);
let expected = crate::mul::strict_mod_mul(a, &b, &modulus);
assert_eq!(result, expected, "Overflow prevention test failed");
}
#[test]
fn test_strict_montgomery_mul_various_moduli() {
let test_cases = [
(7u32, 3u32, 5u32), (11u32, 8u32, 9u32), (15u32, 14u32, 13u32), (21u32, 20u32, 19u32), (97u32, 96u32, 95u32), ];
for (modulus, a, b) in test_cases.iter() {
if let Some((r, _r_inv, n_prime, r_bits)) = strict_compute_montgomery_params(modulus) {
let a_mont = strict_to_montgomery(*a, modulus, &r);
let b_mont = strict_to_montgomery(*b, modulus, &r);
let result_mont = strict_montgomery_mul(a_mont, &b_mont, modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, modulus, &n_prime, r_bits);
let expected = (a * b) % modulus;
assert_eq!(
result, expected,
"Montgomery multiplication failed for modulus {}: {} * {} = {} (expected {})",
modulus, a, b, result, expected
);
}
}
}
#[test]
fn test_strict_montgomery_mul_consistency() {
let modulus = 23u32;
let (r, _r_inv, n_prime, r_bits) = strict_compute_montgomery_params(&modulus).unwrap();
for a in 1u32..10u32 {
for b in 1u32..10u32 {
let a_mont = strict_to_montgomery(a, &modulus, &r);
let b_mont = strict_to_montgomery(b, &modulus, &r);
let result_mont =
strict_montgomery_mul(a_mont, &b_mont, &modulus, &n_prime, r_bits);
let result = strict_from_montgomery(result_mont, &modulus, &n_prime, r_bits);
let expected = (a * b) % modulus;
assert_eq!(
result, expected,
"Consistency check failed: {} * {} mod {} = {} (expected {})",
a, b, modulus, result, expected
);
}
}
}
}