From 51dd348c4b37c6f325cbeed30e962b3fae6b5540 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 14 Dec 2023 08:28:54 -0700 Subject: [PATCH] Impl `Wrapping*` traits from `num-traits` Until now we have used traits defined in this crate, primarily because many of these traits are predicates that return `bool` and in constant-time code we want those to return `Choice`/`CtOption`. `Wrapping*` is an example of the sort of trait we'd want to incorporate unchanged, and there are potentially others, so this adds `num-traits` as a hard dependency (previously our only hard dependency was `subtle`). Note that `num-traits` itself has no transitive dependencies (or rather, they're optional but not enabled). The `Wrapping*` traits have bounds on the corresponding op traits being impl'd with `Self` operands e.g. `WrappingAdd: Add` so this PR also adds impls of those traits. We've previously avoided these as in `std` they panic on overflow/underflow in debug builds and silently wrap in release builds. This PR always panics. This required some changes to the `Mul` impls which were conditional on `ConcatMixed` and implicitly widened. To accomodate impls which are always available and require no bounds (in order to allow us to impl `WideningMul`), and renames the following: - `Uint::mul` -> `Uint::widening_mul` - `Uint::mul_wide` -> `Uint::widening_mul_split` --- Cargo.toml | 2 +- src/modular.rs | 6 +- src/modular/dyn_residue.rs | 2 +- src/modular/inv.rs | 2 +- src/modular/mul.rs | 2 +- src/modular/residue.rs | 2 +- src/traits.rs | 7 ++ src/uint/add.rs | 33 +++++++-- src/uint/boxed/add.rs | 42 +++++++++-- src/uint/boxed/mul.rs | 63 +++++++++++++--- src/uint/boxed/mul_mod.rs | 2 +- src/uint/boxed/sub.rs | 34 ++++++++- src/uint/concat.rs | 2 +- src/uint/mul.rs | 142 ++++++++++++++++++++----------------- src/uint/mul_mod.rs | 4 +- src/uint/sub.rs | 25 ++++++- 16 files changed, 268 insertions(+), 102 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3b1a54ea1..b8252d391 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ subtle = { version = "2.5", default-features = false } # optional dependencies der = { version = "0.7", optional = true, default-features = false } generic-array = { version = "0.14", optional = true } +num-traits = "0.2" rand_core = { version = "0.6.4", optional = true } rlp = { version = "0.5", optional = true, default-features = false } serdect = { version = "0.2", optional = true, default-features = false } @@ -33,7 +34,6 @@ criterion = { version = "0.5", features = ["html_reports"] } hex-literal = "0.4" num-bigint = { package = "num-bigint-dig", version = "0.8" } num-integer = "0.1" -num-traits = "0.2" proptest = "1" rand_core = { version = "0.6", features = ["std"] } rand_chacha = "0.3" diff --git a/src/modular.rs b/src/modular.rs index eb2854be7..4af7c433f 100644 --- a/src/modular.rs +++ b/src/modular.rs @@ -135,7 +135,7 @@ mod tests { // Reducing xR should return x let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let product = x.mul_wide(&Modulus2::R); + let product = x.widening_mul_split(&Modulus2::R); assert_eq!( montgomery_reduction::<{ Modulus2::LIMBS }>( &product, @@ -151,10 +151,10 @@ mod tests { // Reducing xR^2 should return xR let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let product = x.mul_wide(&Modulus2::R2); + let product = x.widening_mul_split(&Modulus2::R2); // Computing xR mod modulus without Montgomery reduction - let (lo, hi) = x.mul_wide(&Modulus2::R); + let (lo, hi) = x.widening_mul_split(&Modulus2::R); let c = hi.concat(&lo); let red = c.rem(&NonZero::new(U256::ZERO.concat(&Modulus2::MODULUS)).unwrap()); let (hi, lo) = red.split(); diff --git a/src/modular/dyn_residue.rs b/src/modular/dyn_residue.rs index fadde5b7e..a07cf825a 100644 --- a/src/modular/dyn_residue.rs +++ b/src/modular/dyn_residue.rs @@ -119,7 +119,7 @@ pub struct DynResidue { impl DynResidue { /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. pub const fn new(integer: &Uint, residue_params: DynResidueParams) -> Self { - let product = integer.mul_wide(&residue_params.r2); + let product = integer.widening_mul_split(&residue_params.r2); let montgomery_form = montgomery_reduction( &product, &residue_params.modulus, diff --git a/src/modular/inv.rs b/src/modular/inv.rs index e4daff198..a20c5a086 100644 --- a/src/modular/inv.rs +++ b/src/modular/inv.rs @@ -8,7 +8,7 @@ pub const fn inv_montgomery_form( ) -> (Uint, ConstChoice) { let (inverse, is_some) = x.inv_odd_mod(modulus); ( - montgomery_reduction(&inverse.mul_wide(r3), modulus, mod_neg_inv), + montgomery_reduction(&inverse.widening_mul_split(r3), modulus, mod_neg_inv), is_some, ) } diff --git a/src/modular/mul.rs b/src/modular/mul.rs index b84ceb5ca..8ddcafb5a 100644 --- a/src/modular/mul.rs +++ b/src/modular/mul.rs @@ -8,7 +8,7 @@ pub(crate) const fn mul_montgomery_form( modulus: &Uint, mod_neg_inv: Limb, ) -> Uint { - let product = a.mul_wide(b); + let product = a.widening_mul_split(b); montgomery_reduction::(&product, modulus, mod_neg_inv) } diff --git a/src/modular/residue.rs b/src/modular/residue.rs index e58348f4e..0d341d9c1 100644 --- a/src/modular/residue.rs +++ b/src/modular/residue.rs @@ -84,7 +84,7 @@ impl, const LIMBS: usize> Residue { /// Internal helper function to generate a residue; this lets us cleanly wrap the constructors. const fn generate_residue(integer: &Uint) -> Self { - let product = integer.mul_wide(&MOD::R2); + let product = integer.widening_mul_split(&MOD::R2); let montgomery_form = montgomery_reduction::(&product, &MOD::MODULUS.0, MOD::MOD_NEG_INV); diff --git a/src/traits.rs b/src/traits.rs index 81edcd15d..267c2efce 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,5 +1,9 @@ //! Traits provided by this crate +pub use num_traits::{ + WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, +}; + use crate::{Limb, NonZero}; use core::fmt::Debug; use core::ops::{ @@ -71,6 +75,9 @@ pub trait Integer: + ShrAssign + SubMod + Sync + + WrappingAdd + + WrappingSub + + WrappingMul + Zero { /// The value `1`. diff --git a/src/uint/add.rs b/src/uint/add.rs index 402c48a63..0861993b5 100644 --- a/src/uint/add.rs +++ b/src/uint/add.rs @@ -1,6 +1,6 @@ //! [`Uint`] addition operations. -use crate::{Checked, CheckedAdd, ConstChoice, Limb, Uint, Wrapping, Zero}; +use crate::{Checked, CheckedAdd, ConstChoice, Limb, Uint, Wrapping, WrappingAdd, Zero}; use core::ops::{Add, AddAssign}; use subtle::CtOption; @@ -45,12 +45,20 @@ impl Uint { } } -impl CheckedAdd<&Uint> for Uint { +impl Add for Uint { type Output = Self; - fn checked_add(&self, rhs: &Self) -> CtOption { - let (result, carry) = self.adc(rhs, Limb::ZERO); - CtOption::new(result, carry.is_zero()) + fn add(self, rhs: Self) -> Self { + self.add(&rhs) + } +} + +impl Add<&Uint> for Uint { + type Output = Self; + + fn add(self, rhs: &Self) -> Self { + self.checked_add(rhs) + .expect("attempted to add with overflow") } } @@ -154,6 +162,21 @@ impl AddAssign<&Checked>> for Checked CheckedAdd<&Uint> for Uint { + type Output = Self; + + fn checked_add(&self, rhs: &Self) -> CtOption { + let (result, carry) = self.adc(rhs, Limb::ZERO); + CtOption::new(result, carry.is_zero()) + } +} + +impl WrappingAdd for Uint { + fn wrapping_add(&self, v: &Self) -> Self { + self.wrapping_add(v) + } +} + #[cfg(test)] mod tests { use crate::{CheckedAdd, Limb, U128}; diff --git a/src/uint/boxed/add.rs b/src/uint/boxed/add.rs index 5a0064430..1adf2b4fc 100644 --- a/src/uint/boxed/add.rs +++ b/src/uint/boxed/add.rs @@ -1,8 +1,7 @@ //! [`BoxedUint`] addition operations. +use crate::{BoxedUint, CheckedAdd, Limb, Wrapping, WrappingAdd, Zero}; use core::ops::{Add, AddAssign}; - -use crate::{BoxedUint, CheckedAdd, Limb, Wrapping, Zero}; use subtle::{Choice, ConditionallySelectable, CtOption}; impl BoxedUint { @@ -51,12 +50,28 @@ impl BoxedUint { } } -impl CheckedAdd<&BoxedUint> for BoxedUint { +impl Add for BoxedUint { type Output = Self; - fn checked_add(&self, rhs: &Self) -> CtOption { - let (result, carry) = self.adc(rhs, Limb::ZERO); - CtOption::new(result, carry.is_zero()) + fn add(self, rhs: Self) -> Self { + self.add(&rhs) + } +} + +impl Add<&BoxedUint> for BoxedUint { + type Output = Self; + + fn add(self, rhs: &Self) -> Self { + Add::add(&self, rhs) + } +} + +impl Add<&BoxedUint> for &BoxedUint { + type Output = BoxedUint; + + fn add(self, rhs: &BoxedUint) -> BoxedUint { + self.checked_add(rhs) + .expect("attempted to add with overflow") } } @@ -104,6 +119,21 @@ impl AddAssign<&Wrapping> for Wrapping { } } +impl CheckedAdd<&BoxedUint> for BoxedUint { + type Output = Self; + + fn checked_add(&self, rhs: &Self) -> CtOption { + let (result, carry) = self.adc(rhs, Limb::ZERO); + CtOption::new(result, carry.is_zero()) + } +} + +impl WrappingAdd for BoxedUint { + fn wrapping_add(&self, v: &Self) -> Self { + self.wrapping_add(v) + } +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { diff --git a/src/uint/boxed/mul.rs b/src/uint/boxed/mul.rs index 1a747e53d..c6411674c 100644 --- a/src/uint/boxed/mul.rs +++ b/src/uint/boxed/mul.rs @@ -1,6 +1,8 @@ //! [`BoxedUint`] multiplication operations. -use crate::{uint::mul::mul_limbs, BoxedUint, CheckedMul, Limb, WideningMul, Wrapping, Zero}; +use crate::{ + uint::mul::mul_limbs, BoxedUint, CheckedMul, Limb, WideningMul, Wrapping, WrappingMul, Zero, +}; use core::ops::{Mul, MulAssign}; use subtle::{Choice, CtOption}; @@ -51,21 +53,36 @@ impl CheckedMul<&BoxedUint> for BoxedUint { } } -impl WideningMul for BoxedUint { - type Output = Self; +impl Mul for BoxedUint { + type Output = BoxedUint; - #[inline] - fn widening_mul(&self, rhs: BoxedUint) -> Self { - self.mul(&rhs) + fn mul(self, rhs: BoxedUint) -> Self { + BoxedUint::mul(&self, &rhs) } } -impl WideningMul<&BoxedUint> for BoxedUint { - type Output = Self; +impl Mul<&BoxedUint> for BoxedUint { + type Output = BoxedUint; - #[inline] - fn widening_mul(&self, rhs: &BoxedUint) -> Self { - self.mul(rhs) + fn mul(self, rhs: &BoxedUint) -> Self { + BoxedUint::mul(&self, rhs) + } +} + +impl Mul for &BoxedUint { + type Output = BoxedUint; + + fn mul(self, rhs: BoxedUint) -> Self::Output { + BoxedUint::mul(self, &rhs) + } +} + +impl Mul<&BoxedUint> for &BoxedUint { + type Output = BoxedUint; + + fn mul(self, rhs: &BoxedUint) -> Self::Output { + self.checked_mul(rhs) + .expect("attempted to multiply with overflow") } } @@ -113,6 +130,30 @@ impl MulAssign<&Wrapping> for Wrapping { } } +impl WideningMul for BoxedUint { + type Output = Self; + + #[inline] + fn widening_mul(&self, rhs: BoxedUint) -> Self { + self.mul(&rhs) + } +} + +impl WideningMul<&BoxedUint> for BoxedUint { + type Output = Self; + + #[inline] + fn widening_mul(&self, rhs: &BoxedUint) -> Self { + self.mul(rhs) + } +} + +impl WrappingMul for BoxedUint { + fn wrapping_mul(&self, v: &Self) -> Self { + self.wrapping_mul(v) + } +} + #[cfg(test)] mod tests { use crate::BoxedUint; diff --git a/src/uint/boxed/mul_mod.rs b/src/uint/boxed/mul_mod.rs index 32d3cf76e..573f0d90a 100644 --- a/src/uint/boxed/mul_mod.rs +++ b/src/uint/boxed/mul_mod.rs @@ -132,7 +132,7 @@ mod tests { assert!(c < **p, "not reduced: {} >= {} ", c, p); let expected = { - let (lo, hi) = a.mul_wide(&b); + let (lo, hi) = a.widening_mul_split(&b); let mut prod = Uint::<{ 2 * $size }>::ZERO; prod.limbs[..$size].clone_from_slice(&lo.limbs); prod.limbs[$size..].clone_from_slice(&hi.limbs); diff --git a/src/uint/boxed/sub.rs b/src/uint/boxed/sub.rs index 7a3351f99..fdad6c89e 100644 --- a/src/uint/boxed/sub.rs +++ b/src/uint/boxed/sub.rs @@ -1,6 +1,7 @@ //! [`BoxedUint`] subtraction operations. -use crate::{BoxedUint, CheckedSub, Limb, Zero}; +use crate::{BoxedUint, CheckedSub, Limb, WrappingSub, Zero}; +use core::ops::Sub; use subtle::{Choice, ConditionallySelectable, CtOption}; impl BoxedUint { @@ -58,6 +59,37 @@ impl CheckedSub<&BoxedUint> for BoxedUint { } } +impl Sub for BoxedUint { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self.sub(&rhs) + } +} + +impl Sub<&BoxedUint> for BoxedUint { + type Output = Self; + + fn sub(self, rhs: &Self) -> Self { + Sub::sub(&self, rhs) + } +} + +impl Sub<&BoxedUint> for &BoxedUint { + type Output = BoxedUint; + + fn sub(self, rhs: &BoxedUint) -> BoxedUint { + self.checked_sub(rhs) + .expect("attempted to subtract with underflow") + } +} + +impl WrappingSub for BoxedUint { + fn wrapping_sub(&self, v: &Self) -> Self { + self.wrapping_sub(v) + } +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { diff --git a/src/uint/concat.rs b/src/uint/concat.rs index dde5242a0..1aac3b063 100644 --- a/src/uint/concat.rs +++ b/src/uint/concat.rs @@ -61,7 +61,7 @@ mod tests { #[test] fn convert() { - let res: U128 = U64::ONE.mul_wide(&U64::ONE).into(); + let res: U128 = U64::ONE.widening_mul_split(&U64::ONE).into(); assert_eq!(res, U128::ONE); let res: U128 = U64::ONE.square_wide().into(); diff --git a/src/uint/mul.rs b/src/uint/mul.rs index 54d78cc58..d69c083c2 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -4,7 +4,7 @@ use crate::{ Checked, CheckedMul, Concat, ConcatMixed, Limb, Uint, WideWord, WideningMul, Word, Wrapping, - Zero, + WrappingMul, Zero, }; use core::ops::{Mul, MulAssign}; use subtle::CtOption; @@ -52,38 +52,41 @@ macro_rules! impl_schoolbook_multiplication { impl Uint { /// Multiply `self` by `rhs`, returning a concatenated "wide" result. - pub fn mul( + pub fn widening_mul( &self, rhs: &Uint, ) -> as ConcatMixed>::MixedOutput where Uint: ConcatMixed, { - let (lo, hi) = self.mul_wide(rhs); + let (lo, hi) = self.widening_mul_split(rhs); hi.concat_mixed(&lo) } /// Compute "wide" multiplication, with a product twice the size of the input. /// /// Returns a tuple containing the `(lo, hi)` components of the product. - pub const fn mul_wide(&self, rhs: &Uint) -> (Self, Uint) { + pub const fn widening_mul_split( + &self, + rhs: &Uint, + ) -> (Self, Uint) { let mut lo = Self::ZERO; let mut hi = Uint::::ZERO; impl_schoolbook_multiplication!(&self.limbs, &rhs.limbs, lo.limbs, hi.limbs); (lo, hi) } + /// Perform wrapping multiplication, discarding overflow. + pub const fn wrapping_mul(&self, rhs: &Uint) -> Self { + self.widening_mul_split(rhs).0 + } + /// Perform saturating multiplication, returning `MAX` on overflow. pub const fn saturating_mul(&self, rhs: &Uint) -> Self { - let (res, overflow) = self.mul_wide(rhs); + let (res, overflow) = self.widening_mul_split(rhs); Self::select(&res, &Self::MAX, overflow.is_nonzero()) } - /// Perform wrapping multiplication, discarding overflow. - pub const fn wrapping_mul(&self, rhs: &Uint) -> Self { - self.mul_wide(rhs).0 - } - /// Square self, returning a concatenated "wide" result. pub fn square(&self) -> ::Output where @@ -169,7 +172,7 @@ impl Uint { } impl CheckedMul> for Uint { - type Output = Self; + type Output = Uint; #[inline] fn checked_mul(&self, rhs: Uint) -> CtOption { @@ -178,36 +181,45 @@ impl CheckedMul> for Uint< } impl CheckedMul<&Uint> for Uint { - type Output = Self; + type Output = Uint; #[inline] fn checked_mul(&self, rhs: &Uint) -> CtOption { - let (lo, hi) = self.mul_wide(rhs); + let (lo, hi) = self.widening_mul_split(rhs); CtOption::new(lo, hi.is_zero()) } } -impl WideningMul> for Uint -where - Uint: ConcatMixed, -{ - type Output = as ConcatMixed>::MixedOutput; +impl Mul> for Uint { + type Output = Uint; - #[inline] - fn widening_mul(&self, rhs: Uint) -> Self::Output { + fn mul(self, rhs: Uint) -> Self { self.mul(&rhs) } } -impl WideningMul<&Uint> for Uint -where - Uint: ConcatMixed, -{ - type Output = as ConcatMixed>::MixedOutput; +impl Mul<&Uint> for Uint { + type Output = Uint; - #[inline] - fn widening_mul(&self, rhs: &Uint) -> Self::Output { - self.mul(rhs) + fn mul(self, rhs: &Uint) -> Self { + (&self).mul(rhs) + } +} + +impl Mul> for &Uint { + type Output = Uint; + + fn mul(self, rhs: Uint) -> Self::Output { + self.mul(&rhs) + } +} + +impl Mul<&Uint> for &Uint { + type Output = Uint; + + fn mul(self, rhs: &Uint) -> Self::Output { + self.checked_mul(rhs) + .expect("attempted to multiply with overflow") } } @@ -317,47 +329,33 @@ impl MulAssign<&Checked>> } } -impl Mul> for Uint +impl WideningMul> for Uint where - Uint: ConcatMixed>, + Uint: ConcatMixed, { type Output = as ConcatMixed>::MixedOutput; - fn mul(self, other: Uint) -> Self::Output { - Uint::mul(&self, &other) + #[inline] + fn widening_mul(&self, rhs: Uint) -> Self::Output { + self.widening_mul(&rhs) } } -impl Mul<&Uint> for Uint +impl WideningMul<&Uint> for Uint where - Uint: ConcatMixed>, + Uint: ConcatMixed, { type Output = as ConcatMixed>::MixedOutput; - fn mul(self, other: &Uint) -> Self::Output { - Uint::mul(&self, other) - } -} - -impl Mul> for &Uint -where - Uint: ConcatMixed>, -{ - type Output = as ConcatMixed>>::MixedOutput; - - fn mul(self, other: Uint) -> Self::Output { - Uint::mul(self, &other) + #[inline] + fn widening_mul(&self, rhs: &Uint) -> Self::Output { + self.widening_mul(rhs) } } -impl Mul<&Uint> for &Uint -where - Uint: ConcatMixed>, -{ - type Output = as ConcatMixed>>::MixedOutput; - - fn mul(self, other: &Uint) -> Self::Output { - Uint::mul(self, other) +impl WrappingMul for Uint { + fn wrapping_mul(&self, v: &Self) -> Self { + self.wrapping_mul(v) } } @@ -375,10 +373,22 @@ mod tests { #[test] fn mul_wide_zero_and_one() { - assert_eq!(U64::ZERO.mul_wide(&U64::ZERO), (U64::ZERO, U64::ZERO)); - assert_eq!(U64::ZERO.mul_wide(&U64::ONE), (U64::ZERO, U64::ZERO)); - assert_eq!(U64::ONE.mul_wide(&U64::ZERO), (U64::ZERO, U64::ZERO)); - assert_eq!(U64::ONE.mul_wide(&U64::ONE), (U64::ONE, U64::ZERO)); + assert_eq!( + U64::ZERO.widening_mul_split(&U64::ZERO), + (U64::ZERO, U64::ZERO) + ); + assert_eq!( + U64::ZERO.widening_mul_split(&U64::ONE), + (U64::ZERO, U64::ZERO) + ); + assert_eq!( + U64::ONE.widening_mul_split(&U64::ZERO), + (U64::ZERO, U64::ZERO) + ); + assert_eq!( + U64::ONE.widening_mul_split(&U64::ONE), + (U64::ONE, U64::ZERO) + ); } #[test] @@ -387,7 +397,7 @@ mod tests { for &a_int in primes { for &b_int in primes { - let (lo, hi) = U64::from_u32(a_int).mul_wide(&U64::from_u32(b_int)); + let (lo, hi) = U64::from_u32(a_int).widening_mul_split(&U64::from_u32(b_int)); let expected = U64::from_u64(a_int as u64 * b_int as u64); assert_eq!(lo, expected); assert!(bool::from(hi.is_zero())); @@ -397,14 +407,14 @@ mod tests { #[test] fn mul_concat_even() { - assert_eq!(U64::ZERO * U64::MAX, U128::ZERO); - assert_eq!(U64::MAX * U64::ZERO, U128::ZERO); + assert_eq!(U64::ZERO.widening_mul(&U64::MAX), U128::ZERO); + assert_eq!(U64::MAX.widening_mul(&U64::ZERO), U128::ZERO); assert_eq!( - U64::MAX * U64::MAX, + U64::MAX.widening_mul(&U64::MAX), U128::from_u128(0xfffffffffffffffe_0000000000000001) ); assert_eq!( - U64::ONE * U64::MAX, + U64::ONE.widening_mul(&U64::MAX), U128::from_u128(0x0000000000000000_ffffffffffffffff) ); } @@ -413,8 +423,8 @@ mod tests { fn mul_concat_mixed() { let a = U64::from_u64(0x0011223344556677); let b = U128::from_u128(0x8899aabbccddeeff_8899aabbccddeeff); - assert_eq!(a * b, U192::from(&a).saturating_mul(&b)); - assert_eq!(b * a, U192::from(&b).saturating_mul(&a)); + assert_eq!(a.widening_mul(&b), U192::from(&a).saturating_mul(&b)); + assert_eq!(b.widening_mul(&a), U192::from(&b).saturating_mul(&a)); } #[test] diff --git a/src/uint/mul_mod.rs b/src/uint/mul_mod.rs index a2cac75e0..f844422c5 100644 --- a/src/uint/mul_mod.rs +++ b/src/uint/mul_mod.rs @@ -43,7 +43,7 @@ impl Uint { return Self::from_word(reduced as Word); } - let (lo, hi) = self.mul_wide(rhs); + let (lo, hi) = self.widening_mul_split(rhs); // Now use Algorithm 14.47 for the reduction let (lo, carry) = mac_by_limb(&lo, &hi, c, Limb::ZERO); @@ -134,7 +134,7 @@ mod tests { assert!(c < **p, "not reduced: {} >= {} ", c, p); let expected = { - let (lo, hi) = a.mul_wide(&b); + let (lo, hi) = a.widening_mul_split(&b); let mut prod = Uint::<{ 2 * $size }>::ZERO; prod.limbs[..$size].clone_from_slice(&lo.limbs); prod.limbs[$size..].clone_from_slice(&hi.limbs); diff --git a/src/uint/sub.rs b/src/uint/sub.rs index 5048e81a7..5663c5cea 100644 --- a/src/uint/sub.rs +++ b/src/uint/sub.rs @@ -1,7 +1,7 @@ //! [`Uint`] addition operations. use super::Uint; -use crate::{Checked, CheckedSub, ConstChoice, Limb, Wrapping, Zero}; +use crate::{Checked, CheckedSub, ConstChoice, Limb, Wrapping, WrappingSub, Zero}; use core::ops::{Sub, SubAssign}; use subtle::CtOption; @@ -56,6 +56,23 @@ impl CheckedSub<&Uint> for Uint { } } +impl Sub for Uint { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self.sub(&rhs) + } +} + +impl Sub<&Uint> for Uint { + type Output = Self; + + fn sub(self, rhs: &Self) -> Self { + self.checked_sub(rhs) + .expect("attempted to subtract with underflow") + } +} + impl Sub for Wrapping> { type Output = Self; @@ -156,6 +173,12 @@ impl SubAssign<&Checked>> for Checked WrappingSub for Uint { + fn wrapping_sub(&self, v: &Self) -> Self { + self.wrapping_sub(v) + } +} + #[cfg(test)] mod tests { use crate::{CheckedSub, Limb, U128};