#[cfg(feature = "nightly")]
use const_num_traits::{OverflowingAdd, OverflowingSub};
#[cfg(feature = "nightly")]
c0nst::c0nst! {
pub c0nst fn const_mod_sub<T>(a: T, b: T, m: T) -> T
where
T: [c0nst] core::cmp::PartialOrd
+ Copy
+ [c0nst] OverflowingAdd
+ [c0nst] core::ops::Add<Output = T>
+ [c0nst] OverflowingSub
+ [c0nst] core::ops::Sub<Output = T>
+ [c0nst] core::ops::Rem<Output = T>,
{
let a = a % m;
let (diff, overflow) = a.overflowing_sub(b % m);
if overflow {
m.overflowing_add(diff).0
} else {
diff
}
}
}
pub fn basic_mod_sub<T>(a: T, b: T, m: T) -> T
where
T: core::cmp::PartialOrd
+ Copy
+ const_num_traits::ops::wrapping::WrappingAdd
+ const_num_traits::ops::wrapping::WrappingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ core::ops::Rem<Output = T>
+ crate::NonCt,
{
basic_mod_sub_pr(a % m, b % m, m)
}
pub fn basic_mod_sub_nz<T>(a: T, b: T, m: T::NonZero) -> T
where
T: core::cmp::PartialOrd
+ Copy
+ const_num_traits::HasNonZero
+ const_num_traits::DivNonZero<Output = T>
+ const_num_traits::ops::wrapping::WrappingAdd
+ const_num_traits::ops::wrapping::WrappingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
{
let m_raw = T::nonzero_get(m);
basic_mod_sub_pr(a.rem_nonzero(m), b.rem_nonzero(m), m_raw)
}
pub fn basic_mod_sub_pr<T>(a: T, b: T, m: T) -> T
where
T: core::cmp::PartialOrd
+ Copy
+ const_num_traits::ops::wrapping::WrappingAdd
+ const_num_traits::ops::wrapping::WrappingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
{
let diff = a.wrapping_sub(b);
if diff > a {
diff.wrapping_add(m)
} else {
diff
}
}
pub fn constrained_mod_sub<T>(a: T, b: &T, m: &T) -> T
where
T: core::cmp::PartialOrd
+ Clone
+ const_num_traits::ops::wrapping::WrappingAdd
+ const_num_traits::ops::wrapping::WrappingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>,
{
let a_mod = &a % m;
let b_mod = b % m;
constrained_mod_sub_pr(a_mod, &b_mod, m)
}
pub fn constrained_mod_sub_nz<T>(a: T, b: &T, m: T::NonZero) -> T
where
T: core::cmp::PartialOrd
+ Clone
+ const_num_traits::HasNonZero
+ const_num_traits::DivNonZero<Output = T>
+ const_num_traits::ops::wrapping::WrappingAdd
+ const_num_traits::ops::wrapping::WrappingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
{
let m_raw = T::nonzero_get(m);
let b_mod = b.clone().rem_nonzero(m);
constrained_mod_sub_pr(a.rem_nonzero(m), &b_mod, &m_raw)
}
pub fn constrained_mod_sub_pr<T>(a: T, b: &T, m: &T) -> T
where
T: core::cmp::PartialOrd
+ Clone
+ const_num_traits::ops::wrapping::WrappingAdd
+ const_num_traits::ops::wrapping::WrappingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
{
let diff = a.clone().wrapping_sub(b.clone());
if diff > a {
diff.wrapping_add(m.clone())
} else {
diff
}
}
pub fn strict_mod_sub<T>(mut a: T, b: &T, m: &T) -> T
where
T: core::cmp::PartialOrd
+ Clone
+ const_num_traits::ops::overflowing::OverflowingAdd
+ const_num_traits::ops::overflowing::OverflowingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
for<'b> T: core::ops::RemAssign<&'b T>,
for<'a> &'a T: core::ops::Rem<&'a T, Output = T>,
{
a.rem_assign(m);
let b_mod = b % m;
strict_mod_sub_pr(a, &b_mod, m)
}
pub fn strict_mod_sub_nz<T>(a: T, b: &T, m: T::NonZero) -> T
where
T: core::cmp::PartialOrd
+ Clone
+ const_num_traits::HasNonZero
+ const_num_traits::DivNonZero<Output = T>
+ const_num_traits::ops::overflowing::OverflowingAdd
+ const_num_traits::ops::overflowing::OverflowingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
{
let m_raw = T::nonzero_get(m);
let b_mod = b.clone().rem_nonzero(m);
strict_mod_sub_pr(a.rem_nonzero(m), &b_mod, &m_raw)
}
pub fn strict_mod_sub_pr<T>(a: T, b: &T, m: &T) -> T
where
T: core::cmp::PartialOrd
+ Clone
+ const_num_traits::ops::overflowing::OverflowingAdd
+ const_num_traits::ops::overflowing::OverflowingSub
+ core::ops::Add<Output = T>
+ core::ops::Sub<Output = T>
+ crate::NonCt,
{
let (diff, overflow) = a.overflowing_sub(b.clone());
if overflow {
m.clone().overflowing_add(diff).0
} else {
diff
}
}
#[cfg(test)]
macro_rules! select_mod_sub {
($mod_sub:path, $t:ty, by_ref) => {
fn mod_sub(a: $t, b: &$t, m: &$t) -> $t {
$mod_sub(a, b, m)
}
};
($mod_sub:path, $t:ty, by_val) => {
fn mod_sub(a: $t, b: &$t, m: &$t) -> $t {
$mod_sub(a, *b, *m)
}
};
}
#[cfg(test)]
macro_rules! generate_mod_sub_tests {
($mod_sub:path, $t:ty, $by_ref:tt) => {
select_mod_sub!($mod_sub, $t, $by_ref);
#[test]
fn test_mod_sub_basic() {
assert_eq!(mod_sub(5u8, &10u8, &20u8), 15u8); assert_eq!(mod_sub(7u8, &3u8, &11u8), 4u8); assert_eq!(mod_sub(0u8, &0u8, &10u8), 0u8); }
#[test]
fn test_mod_sub_res_equals_modulus() {
assert_eq!(mod_sub(10u8, &10u8, &20u8), 0u8); assert_eq!(mod_sub(5u8, &5u8, &7u8), 0u8); }
#[test]
fn test_mod_sub_res_less_than_modulus() {
assert_eq!(mod_sub(15u8, &5u8, &20u8), 10u8); assert_eq!(mod_sub(8u8, &3u8, &10u8), 5u8); }
#[test]
fn test_mod_sub_with_large_numbers() {
assert_eq!(mod_sub(255u8, &254u8, &100u8), 1u8); assert_eq!(mod_sub(200u8, &100u8, &50u8), 0u8); }
#[test]
fn test_mod_sub_with_zero() {
assert_eq!(mod_sub(0u8, &5u8, &10u8), 5u8); assert_eq!(mod_sub(5u8, &0u8, &10u8), 5u8); }
#[test]
fn test_mod_sub_with_max_values() {
assert_eq!(mod_sub(255u8, &255u8, &100u8), 0u8); assert_eq!(mod_sub(255u8, &1u8, &100u8), 54u8); }
#[test]
fn test_mod_sub_modulus_is_one() {
assert_eq!(mod_sub(10u8, &20u8, &1u8), 0u8); assert_eq!(mod_sub(255u8, &255u8, &1u8), 0u8);
}
#[test]
#[should_panic]
fn test_mod_sub_modulus_is_zero() {
mod_sub(10u8, &20u8, &0u8); }
#[test]
fn test_mod_sub_operands_exceed_modulus() {
assert_eq!(mod_sub(100u8, &50u8, &30u8), 20u8); assert_eq!(mod_sub(75u8, &80u8, &20u8), 15u8); }
#[test]
fn test_mod_sub_edge_cases() {
assert_eq!(mod_sub(u8::MAX, &u8::MAX, &100u8), 0u8);
assert_eq!(mod_sub(u8::MAX, &0u8, &100u8), 55u8); assert_eq!(mod_sub(0u8, &u8::MAX, &100u8), 45u8); }
#[test]
fn test_mod_sub_result_equals_modulus() {
assert_eq!(mod_sub(25u8, &5u8, &20u8), 0u8); assert_eq!(mod_sub(45u8, &25u8, &20u8), 0u8); }
#[test]
fn test_mod_sub_overflow() {
assert_eq!(mod_sub(200u8, &100u8, &50u8), 0u8); assert_eq!(mod_sub(255u8, &254u8, &100u8), 1u8); }
};
}
#[cfg(test)]
mod strict_mod_sub_tests {
generate_mod_sub_tests!(super::strict_mod_sub, u8, by_ref);
}
#[cfg(test)]
mod constrained_mod_sub_tests {
generate_mod_sub_tests!(super::constrained_mod_sub, u8, by_ref);
}
#[cfg(test)]
mod basic_mod_sub_tests {
generate_mod_sub_tests!(super::basic_mod_sub, u8, by_val);
}
#[cfg(test)]
#[cfg(feature = "nightly")]
const _: () = {
let result = const_mod_sub(5u8, 10u8, 20u8);
assert!(result == 15u8);
};
#[cfg(test)]
#[cfg(feature = "nightly")]
mod const_mod_sub_tests {
generate_mod_sub_tests!(super::const_mod_sub, u8, by_val);
}
#[cfg(test)]
macro_rules! sub_test_module {
(
$stem:ident, // Base name (e.g., "bnum")
$type_path:path, $(type $type_def:ty = $type_expr:ty;)? strict: $strict:expr,
constrained: $constrained:expr,
basic: $basic:expr,
) => {
paste::paste! {
mod [<$stem _tests>] { #[allow(unused_imports)]
use $type_path;
$( type $type_def = $type_expr; )?
#[test]
#[allow(unused_variables)]
fn test_mod_sub_basic() {
let a = U256::from(10u8);
let b = U256::from(5u8);
let m = U256::from(20u8);
let result = U256::from(5u8);
crate::maybe_test!($strict, assert_eq!(super::strict_mod_sub(a, &b, &m), result));
let a = U256::from(10u8);
crate::maybe_test!($constrained, assert_eq!(super::constrained_mod_sub(a, &b, &m), result));
let a = U256::from(10u8);
crate::maybe_test!($basic, assert_eq!(super::basic_mod_sub(a, b, m), result));
}
}
}
};
}
#[cfg(test)]
mod bnum_sub_tests {
use super::basic_mod_sub;
use super::constrained_mod_sub;
use super::strict_mod_sub;
sub_test_module!(
fixed_bigint,
fixed_bigint::FixedUInt,
type U256 = fixed_bigint::FixedUInt<u32, 4>;
strict: on,
constrained: on,
basic: on,
);
}
#[cfg(test)]
mod fixed_bigint_pr_tests {
use super::{basic_mod_sub_pr, constrained_mod_sub_pr, strict_mod_sub_pr};
type U256 = fixed_bigint::FixedUInt<u32, 4>;
#[test]
fn test_mod_sub_basic_pr() {
let m = U256::from(20u8);
let a = U256::from(10u8);
let b = U256::from(5u8);
let expected = U256::from(5u8);
assert_eq!(strict_mod_sub_pr(a, &b, &m), expected);
let a = U256::from(10u8);
assert_eq!(constrained_mod_sub_pr(a, &b, &m), expected);
let a = U256::from(10u8);
assert_eq!(basic_mod_sub_pr(a, b, m), expected);
}
#[test]
fn test_mod_sub_underflow_pr() {
let m = U256::from(20u8);
let a = U256::from(3u8);
let b = U256::from(7u8);
let expected = U256::from(16u8);
assert_eq!(strict_mod_sub_pr(a, &b, &m), expected);
let a = U256::from(3u8);
assert_eq!(constrained_mod_sub_pr(a, &b, &m), expected);
let a = U256::from(3u8);
assert_eq!(basic_mod_sub_pr(a, b, m), expected);
}
}
#[cfg(test)]
mod nz_tests {
use super::*;
use const_num_traits::HasNonZero;
#[test]
fn basic_mod_sub_nz_matches_basic_mod_sub() {
let m: u32 = 97;
let m_nz = m.into_nonzero().unwrap();
for a in [0u32, 1, 5, 96, 200, u32::MAX] {
for b in [0u32, 1, 50, 96, 300, u32::MAX - 1] {
assert_eq!(basic_mod_sub_nz(a, b, m_nz), basic_mod_sub(a, b, m));
}
}
}
#[test]
fn constrained_mod_sub_nz_matches_constrained_mod_sub() {
let m: u32 = 97;
let m_nz = m.into_nonzero().unwrap();
for a in [0u32, 1, 5, 96, 200, u32::MAX] {
for b in [0u32, 1, 50, 96, 300, u32::MAX - 1] {
assert_eq!(
constrained_mod_sub_nz(a, &b, m_nz),
constrained_mod_sub(a, &b, &m)
);
}
}
}
#[test]
fn strict_mod_sub_nz_matches_strict_mod_sub() {
let m: u32 = 97;
let m_nz = m.into_nonzero().unwrap();
for a in [0u32, 1, 5, 96, 200, u32::MAX] {
for b in [0u32, 1, 50, 96, 300, u32::MAX - 1] {
assert_eq!(strict_mod_sub_nz(a, &b, m_nz), strict_mod_sub(a, &b, &m));
}
}
}
}