diff --git a/src/uint/boxed.rs b/src/uint/boxed.rs index 801f425cd..a44dfd9b5 100644 --- a/src/uint/boxed.rs +++ b/src/uint/boxed.rs @@ -20,6 +20,7 @@ mod sub_mod; mod bit_not; mod bit_xor; +mod neg_mod; #[cfg(feature = "rand_core")] mod rand; diff --git a/src/uint/boxed/neg_mod.rs b/src/uint/boxed/neg_mod.rs new file mode 100644 index 000000000..208046773 --- /dev/null +++ b/src/uint/boxed/neg_mod.rs @@ -0,0 +1,89 @@ +//! [`BoxedUint`] modular negation operations. + +use crate::{BoxedUint, Limb, NegMod}; +use subtle::ConditionallySelectable; + +impl BoxedUint { + /// Computes `-a mod p`. + /// Assumes `self` is in `[0, p)`. + pub fn neg_mod(&self, p: &Self) -> Self { + debug_assert_eq!(self.bits_precision(), p.bits_precision()); + let is_zero = self.is_zero(); + let mut ret = p.sbb(self, Limb::ZERO).0; + + for i in 0..self.nlimbs() { + // Set ret to 0 if the original value was 0, in which + // case ret would be p. + ret.limbs[i].conditional_assign(&Limb::ZERO, is_zero); + } + + ret + } + + /// Computes `-a mod p` for the special modulus + /// `p = MAX+1-c` where `c` is small enough to fit in a single [`Limb`]. + pub fn neg_mod_special(&self, c: Limb) -> Self { + Self::zero_with_precision(self.bits_precision()).sub_mod_special(self, c) + } +} + +impl NegMod for BoxedUint { + type Output = Self; + + fn neg_mod(&self, p: &Self) -> Self { + debug_assert!(self < p); + self.neg_mod(p) + } +} + +#[cfg(test)] +mod tests { + use crate::BoxedUint; + use hex_literal::hex; + + #[test] + fn neg_mod_random() { + let x = BoxedUint::from_be_slice( + &hex!("8d16e171674b4e6d8529edba4593802bf30b8cb161dd30aa8e550d41380007c2"), + 256, + ) + .unwrap(); + let p = BoxedUint::from_be_slice( + &hex!("928334a4e4be0843ec225a4c9c61df34bdc7a81513e4b6f76f2bfa3148e2e1b5"), + 256, + ) + .unwrap(); + + let actual = x.neg_mod(&p); + let expected = BoxedUint::from_be_slice( + &hex!("056c53337d72b9d666f86c9256ce5f08cabc1b63b207864ce0d6ecf010e2d9f3"), + 256, + ) + .unwrap(); + + assert_eq!(expected, actual); + } + + #[test] + fn neg_mod_zero() { + let x = BoxedUint::from_be_slice( + &hex!("0000000000000000000000000000000000000000000000000000000000000000"), + 256, + ) + .unwrap(); + let p = BoxedUint::from_be_slice( + &hex!("928334a4e4be0843ec225a4c9c61df34bdc7a81513e4b6f76f2bfa3148e2e1b5"), + 256, + ) + .unwrap(); + + let actual = x.neg_mod(&p); + let expected = BoxedUint::from_be_slice( + &hex!("0000000000000000000000000000000000000000000000000000000000000000"), + 256, + ) + .unwrap(); + + assert_eq!(expected, actual); + } +} diff --git a/src/uint/boxed/sub_mod.rs b/src/uint/boxed/sub_mod.rs index a93862abc..3a3f95823 100644 --- a/src/uint/boxed/sub_mod.rs +++ b/src/uint/boxed/sub_mod.rs @@ -19,6 +19,20 @@ impl BoxedUint { out.wrapping_add(&p.bitand_limb(mask)) } + /// Computes `self - rhs mod p` for the special modulus + /// `p = MAX+1-c` where `c` is small enough to fit in a single [`Limb`]. + /// + /// Assumes `self - rhs` as unbounded signed integer is in `[-p, p)`. + pub fn sub_mod_special(&self, rhs: &Self, c: Limb) -> Self { + let (out, borrow) = self.sbb(rhs, Limb::ZERO); + + // If underflow occurred, then we need to subtract `c` to account for + // the underflow. This cannot underflow due to the assumption + // `self - rhs >= -p`. + let l = borrow.0 & c.0; + out.wrapping_sub(&Self::from(l)) + } + /// Returns `(self..., carry) - (rhs...) mod (p...)`, where `carry <= 1`. /// Assumes `-(p...) <= (self..., carry) - (rhs...) < (p...)`. #[inline(always)] diff --git a/src/uint/neg_mod.rs b/src/uint/neg_mod.rs index 38580ed5d..a3481dcbd 100644 --- a/src/uint/neg_mod.rs +++ b/src/uint/neg_mod.rs @@ -1,4 +1,4 @@ -//! [`Uint`] negation modulus operations. +//! [`Uint`] modular negation operations. use crate::{Limb, NegMod, Uint};