diff --git a/src/const_choice.rs b/src/const_choice.rs index 2d9f99966..5e43e38c3 100644 --- a/src/const_choice.rs +++ b/src/const_choice.rs @@ -395,7 +395,7 @@ impl ConstCtOption> { /// Returns the contained value, interpreting the underlying [`Uint`] value as an [`Int`]. #[inline] pub const fn as_int(&self) -> ConstCtOption> { - ConstCtOption::new(Int::from_bits(self.value), self.is_some) + ConstCtOption::new(self.value.as_int(), self.is_some) } } diff --git a/src/int/add.rs b/src/int/add.rs index 6212c3192..a18e4b238 100644 --- a/src/int/add.rs +++ b/src/int/add.rs @@ -14,8 +14,7 @@ impl Int { ConstCtOption::new(value, overflow.not()) } - /// Perform `self + rhs`, returns the result, as well as a flag indicating whether the - /// addition overflowed. + /// Perform addition, raising the `overflow` flag on overflow. pub const fn overflowing_add(&self, rhs: &Self) -> (Self, ConstChoice) { // Step 1. add operands let res = Self(self.0.wrapping_add(&rhs.0)); @@ -31,6 +30,7 @@ impl Int { .eq(rhs.is_negative()) .and(self_msb.ne(res.is_negative())); + // Step 3. Construct result (res, overflow) } @@ -106,107 +106,220 @@ impl WrappingAdd for Int { #[cfg(test)] mod tests { + use crate::{ConstChoice, I128, U128}; - #[cfg(test)] - mod tests { - use crate::{I128, U128}; + #[test] + fn checked_add() { + let min_plus_one = I128 { + 0: I128::MIN.0.wrapping_add(&I128::ONE.0), + }; + let max_minus_one = I128 { + 0: I128::MAX.0.wrapping_sub(&I128::ONE.0), + }; + let two = I128 { + 0: U128::from(2u32), + }; - #[test] - fn checked_add() { - let min_plus_one = I128 { - 0: I128::MIN.0.wrapping_add(&I128::ONE.0), - }; - let max_minus_one = I128 { - 0: I128::MAX.0.wrapping_sub(&I128::ONE.0), - }; - let two = I128 { - 0: U128::from(2u32), - }; + // lhs = MIN - // lhs = MIN + let result = I128::MIN.checked_add(&I128::MIN); + assert!(bool::from(result.is_none())); - let result = I128::MIN.checked_add(&I128::MIN); - assert!(bool::from(result.is_none())); + let result = I128::MIN.checked_add(&I128::MINUS_ONE); + assert!(bool::from(result.is_none())); - let result = I128::MIN.checked_add(&I128::MINUS_ONE); - assert!(bool::from(result.is_none())); + let result = I128::MIN.checked_add(&I128::ZERO); + assert_eq!(result.unwrap(), I128::MIN); - let result = I128::MIN.checked_add(&I128::ZERO); - assert_eq!(result.unwrap(), I128::MIN); + let result = I128::MIN.checked_add(&I128::ONE); + assert_eq!(result.unwrap(), min_plus_one); - let result = I128::MIN.checked_add(&I128::ONE); - assert_eq!(result.unwrap(), min_plus_one); + let result = I128::MIN.checked_add(&I128::MAX); + assert_eq!(result.unwrap(), I128::MINUS_ONE); - let result = I128::MIN.checked_add(&I128::MAX); - assert_eq!(result.unwrap(), I128::MINUS_ONE); + // lhs = -1 - // lhs = -1 + let result = I128::MINUS_ONE.checked_add(&I128::MIN); + assert!(bool::from(result.is_none())); - let result = I128::MINUS_ONE.checked_add(&I128::MIN); - assert!(bool::from(result.is_none())); + let result = I128::MINUS_ONE.checked_add(&I128::MINUS_ONE); + assert_eq!(result.unwrap(), two.checked_neg().unwrap()); - let result = I128::MINUS_ONE.checked_add(&I128::MINUS_ONE); - assert_eq!(result.unwrap(), two.checked_neg().unwrap()); + let result = I128::MINUS_ONE.checked_add(&I128::ZERO); + assert_eq!(result.unwrap(), I128::MINUS_ONE); - let result = I128::MINUS_ONE.checked_add(&I128::ZERO); - assert_eq!(result.unwrap(), I128::MINUS_ONE); + let result = I128::MINUS_ONE.checked_add(&I128::ONE); + assert_eq!(result.unwrap(), I128::ZERO); - let result = I128::MINUS_ONE.checked_add(&I128::ONE); - assert_eq!(result.unwrap(), I128::ZERO); + let result = I128::MINUS_ONE.checked_add(&I128::MAX); + assert_eq!(result.unwrap(), max_minus_one); - let result = I128::MINUS_ONE.checked_add(&I128::MAX); - assert_eq!(result.unwrap(), max_minus_one); + // lhs = 0 - // lhs = 0 + let result = I128::ZERO.checked_add(&I128::MIN); + assert_eq!(result.unwrap(), I128::MIN); - let result = I128::ZERO.checked_add(&I128::MIN); - assert_eq!(result.unwrap(), I128::MIN); + let result = I128::ZERO.checked_add(&I128::MINUS_ONE); + assert_eq!(result.unwrap(), I128::MINUS_ONE); - let result = I128::ZERO.checked_add(&I128::MINUS_ONE); - assert_eq!(result.unwrap(), I128::MINUS_ONE); + let result = I128::ZERO.checked_add(&I128::ZERO); + assert_eq!(result.unwrap(), I128::ZERO); - let result = I128::ZERO.checked_add(&I128::ZERO); - assert_eq!(result.unwrap(), I128::ZERO); + let result = I128::ZERO.checked_add(&I128::ONE); + assert_eq!(result.unwrap(), I128::ONE); - let result = I128::ZERO.checked_add(&I128::ONE); - assert_eq!(result.unwrap(), I128::ONE); + let result = I128::ZERO.checked_add(&I128::MAX); + assert_eq!(result.unwrap(), I128::MAX); - let result = I128::ZERO.checked_add(&I128::MAX); - assert_eq!(result.unwrap(), I128::MAX); + // lhs = 1 - // lhs = 1 + let result = I128::ONE.checked_add(&I128::MIN); + assert_eq!(result.unwrap(), min_plus_one); - let result = I128::ONE.checked_add(&I128::MIN); - assert_eq!(result.unwrap(), min_plus_one); + let result = I128::ONE.checked_add(&I128::MINUS_ONE); + assert_eq!(result.unwrap(), I128::ZERO); - let result = I128::ONE.checked_add(&I128::MINUS_ONE); - assert_eq!(result.unwrap(), I128::ZERO); + let result = I128::ONE.checked_add(&I128::ZERO); + assert_eq!(result.unwrap(), I128::ONE); - let result = I128::ONE.checked_add(&I128::ZERO); - assert_eq!(result.unwrap(), I128::ONE); + let result = I128::ONE.checked_add(&I128::ONE); + assert_eq!(result.unwrap(), two); - let result = I128::ONE.checked_add(&I128::ONE); - assert_eq!(result.unwrap(), two); + let result = I128::ONE.checked_add(&I128::MAX); + assert!(bool::from(result.is_none())); - let result = I128::ONE.checked_add(&I128::MAX); - assert!(bool::from(result.is_none())); + // lhs = MAX - // lhs = MAX + let result = I128::MAX.checked_add(&I128::MIN); + assert_eq!(result.unwrap(), I128::MINUS_ONE); - let result = I128::MAX.checked_add(&I128::MIN); - assert_eq!(result.unwrap(), I128::MINUS_ONE); + let result = I128::MAX.checked_add(&I128::MINUS_ONE); + assert_eq!(result.unwrap(), max_minus_one); - let result = I128::MAX.checked_add(&I128::MINUS_ONE); - assert_eq!(result.unwrap(), max_minus_one); + let result = I128::MAX.checked_add(&I128::ZERO); + assert_eq!(result.unwrap(), I128::MAX); - let result = I128::MAX.checked_add(&I128::ZERO); - assert_eq!(result.unwrap(), I128::MAX); + let result = I128::MAX.checked_add(&I128::ONE); + assert!(bool::from(result.is_none())); - let result = I128::MAX.checked_add(&I128::ONE); - assert!(bool::from(result.is_none())); + let result = I128::MAX.checked_add(&I128::MAX); + assert!(bool::from(result.is_none())); + } + + #[test] + fn overflowing_add() { + let min_plus_one = I128 { + 0: I128::MIN.0.wrapping_add(&I128::ONE.0), + }; + let max_minus_one = I128 { + 0: I128::MAX.0.wrapping_sub(&I128::ONE.0), + }; + let two = I128 { + 0: U128::from(2u32), + }; + + // lhs = MIN + + let (_val, overflow) = I128::MIN.overflowing_add(&I128::MIN); + assert_eq!(overflow, ConstChoice::TRUE); + + let (_val, overflow) = I128::MIN.overflowing_add(&I128::MINUS_ONE); + assert_eq!(overflow, ConstChoice::TRUE); + + let (val, overflow) = I128::MIN.overflowing_add(&I128::ZERO); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MIN); + + let (val, overflow) = I128::MIN.overflowing_add(&I128::ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, min_plus_one); + + let (val, overflow) = I128::MIN.overflowing_add(&I128::MAX); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MINUS_ONE); + + // lhs = -1 + + let (_val, overflow) = I128::MINUS_ONE.overflowing_add(&I128::MIN); + assert_eq!(overflow, ConstChoice::TRUE); + + let (val, overflow) = I128::MINUS_ONE.overflowing_add(&I128::MINUS_ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, two.wrapping_neg()); + + let (val, overflow) = I128::MINUS_ONE.overflowing_add(&I128::ZERO); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MINUS_ONE); + + let (val, overflow) = I128::MINUS_ONE.overflowing_add(&I128::ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::ZERO); + + let (val, overflow) = I128::MINUS_ONE.overflowing_add(&I128::MAX); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, max_minus_one); + + // lhs = 0 + + let (val, overflow) = I128::ZERO.overflowing_add(&I128::MIN); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MIN); + + let (val, overflow) = I128::ZERO.overflowing_add(&I128::MINUS_ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MINUS_ONE); + + let (val, overflow) = I128::ZERO.overflowing_add(&I128::ZERO); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::ZERO); + + let (val, overflow) = I128::ZERO.overflowing_add(&I128::ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::ONE); + + let (val, overflow) = I128::ZERO.overflowing_add(&I128::MAX); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MAX); + + // lhs = 1 + + let (val, overflow) = I128::ONE.overflowing_add(&I128::MIN); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, min_plus_one); + + let (val, overflow) = I128::ONE.overflowing_add(&I128::MINUS_ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::ZERO); + + let (val, overflow) = I128::ONE.overflowing_add(&I128::ZERO); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::ONE); + + let (val, overflow) = I128::ONE.overflowing_add(&I128::ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, two); + + let (_val, overflow) = I128::ONE.overflowing_add(&I128::MAX); + assert_eq!(overflow, ConstChoice::TRUE); + + // lhs = MAX + + let (val, overflow) = I128::MAX.overflowing_add(&I128::MIN); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MINUS_ONE); + + let (val, overflow) = I128::MAX.overflowing_add(&I128::MINUS_ONE); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, max_minus_one); + + let (val, overflow) = I128::MAX.overflowing_add(&I128::ZERO); + assert_eq!(overflow, ConstChoice::FALSE); + assert_eq!(val, I128::MAX); + + let (_val, overflow) = I128::MAX.overflowing_add(&I128::ONE); + assert_eq!(overflow, ConstChoice::TRUE); - let result = I128::MAX.checked_add(&I128::MAX); - assert!(bool::from(result.is_none())); - } + let (_val, overflow) = I128::MAX.overflowing_add(&I128::MAX); + assert_eq!(overflow, ConstChoice::TRUE); } } diff --git a/src/int/bit_and.rs b/src/int/bit_and.rs index ff6c750d9..4dca3cb44 100644 --- a/src/int/bit_and.rs +++ b/src/int/bit_and.rs @@ -131,10 +131,14 @@ mod tests { #[test] fn checked_and_ok() { assert_eq!(I128::ZERO.checked_and(&I128::ONE).unwrap(), I128::ZERO); + assert_eq!(I128::ONE.checked_and(&I128::ONE).unwrap(), I128::ONE); + assert_eq!(I128::MAX.checked_and(&I128::ONE).unwrap(), I128::ONE); } #[test] - fn overlapping_and_ok() { + fn wrapping_and_ok() { + assert_eq!(I128::ZERO.wrapping_and(&I128::ONE), I128::ZERO); + assert_eq!(I128::ONE.wrapping_and(&I128::ONE), I128::ONE); assert_eq!(I128::MAX.wrapping_and(&I128::ONE), I128::ONE); } } diff --git a/src/int/bit_or.rs b/src/int/bit_or.rs index baccb3b85..866112a91 100644 --- a/src/int/bit_or.rs +++ b/src/int/bit_or.rs @@ -123,10 +123,14 @@ mod tests { #[test] fn checked_or_ok() { assert_eq!(I128::ZERO.checked_or(&I128::ONE).unwrap(), I128::ONE); + assert_eq!(I128::ONE.checked_or(&I128::ONE).unwrap(), I128::ONE); + assert_eq!(I128::MAX.checked_or(&I128::ONE).unwrap(), I128::MAX); } #[test] - fn overlapping_or_ok() { + fn wrapping_or_ok() { + assert_eq!(I128::ZERO.wrapping_or(&I128::ONE), I128::ONE); + assert_eq!(I128::ONE.wrapping_or(&I128::ONE), I128::ONE); assert_eq!(I128::MAX.wrapping_or(&I128::ONE), I128::MAX); } } diff --git a/src/int/bit_xor.rs b/src/int/bit_xor.rs index eae4e593e..ea1408534 100644 --- a/src/int/bit_xor.rs +++ b/src/int/bit_xor.rs @@ -123,10 +123,17 @@ mod tests { #[test] fn checked_xor_ok() { assert_eq!(I128::ZERO.checked_xor(&I128::ONE).unwrap(), I128::ONE); + assert_eq!(I128::ONE.checked_xor(&I128::ONE).unwrap(), I128::ZERO); + assert_eq!( + I128::MAX.checked_xor(&I128::ONE).unwrap(), + I128::MAX - I128::ONE + ); } #[test] - fn overlapping_xor_ok() { + fn wrapping_xor_ok() { assert_eq!(I128::ZERO.wrapping_xor(&I128::ONE), I128::ONE); + assert_eq!(I128::ONE.wrapping_xor(&I128::ONE), I128::ZERO); + assert_eq!(I128::MAX.wrapping_xor(&I128::ONE), I128::MAX - I128::ONE); } } diff --git a/src/int/div.rs b/src/int/div.rs index 86aec7830..3918fbc2d 100644 --- a/src/int/div.rs +++ b/src/int/div.rs @@ -1,17 +1,18 @@ //! [`Int`] division operations. -use core::ops::Div; +use core::ops::{Div, DivAssign, Rem, RemAssign}; -use subtle::{Choice, CtOption}; +use subtle::CtOption; -use crate::{CheckedDiv, ConstChoice, ConstCtOption, Int, NonZero, Uint}; +use crate::{CheckedDiv, ConstChoice, ConstCtOption, Int, NonZero, Uint, Wrapping}; +/// Checked division operations. impl Int { #[inline] - /// Base div_rem operation. + /// Base div_rem operation on dividing [`Int`]s. /// - /// Given `(a, b)`, computes the quotient and remainder of their absolute values. Furthermore, - /// returns the signs of `a` and `b`. + /// Computes the quotient and remainder of `self / rhs`. + /// Furthermore, returns the signs of `self` and `rhs`. const fn div_rem_base( &self, rhs: &NonZero, @@ -27,45 +28,60 @@ impl Int { (quotient, remainder, lhs_sgn, rhs_sgn) } + /// Compute the quotient and remainder of `self / rhs`. + /// + /// Returns `none` for the quotient when `Int::MIN / Int::MINUS_ONE`; that quotient cannot + /// be captured in an `Int`. /// /// Example: /// ``` /// use crypto_bigint::{I128, NonZero}; /// let (quotient, remainder) = I128::from(8).checked_div_rem(&I128::from(3).to_nz().unwrap()); /// assert_eq!(quotient.unwrap(), I128::from(2)); - /// assert_eq!(remainder.unwrap(), I128::from(2)); + /// assert_eq!(remainder, I128::from(2)); /// /// let (quotient, remainder) = I128::from(-8).checked_div_rem(&I128::from(3).to_nz().unwrap()); /// assert_eq!(quotient.unwrap(), I128::from(-2)); - /// assert_eq!(remainder.unwrap(), I128::from(-2)); + /// assert_eq!(remainder, I128::from(-2)); /// /// let (quotient, remainder) = I128::from(8).checked_div_rem(&I128::from(-3).to_nz().unwrap()); /// assert_eq!(quotient.unwrap(), I128::from(-2)); - /// assert_eq!(remainder.unwrap(), I128::from(2)); + /// assert_eq!(remainder, I128::from(2)); /// /// let (quotient, remainder) = I128::from(-8).checked_div_rem(&I128::from(-3).to_nz().unwrap()); /// assert_eq!(quotient.unwrap(), I128::from(2)); - /// assert_eq!(remainder.unwrap(), I128::from(-2)); + /// assert_eq!(remainder, I128::from(-2)); /// ``` - pub const fn checked_div_rem( - &self, - rhs: &NonZero, - ) -> (ConstCtOption, ConstCtOption) { + pub const fn checked_div_rem(&self, rhs: &NonZero) -> (ConstCtOption, Self) { let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(rhs); let opposing_signs = lhs_sgn.ne(rhs_sgn); ( Self::new_from_abs_sign(quotient, opposing_signs), - Self::new_from_abs_sign(remainder, lhs_sgn), + remainder.as_int().wrapping_neg_if(lhs_sgn), // as_int mapping is safe; remainder < 2^{k-1} by construction. ) } - /// Perform checked division, returning a [`CtOption`] which `is_some` only if the rhs != 0. + /// Perform checked division, returning a [`CtOption`] which `is_some` if + /// - the `rhs != 0`, and + /// - `self != MIN` or `rhs != MINUS_ONE`. + /// /// Note: this operation rounds towards zero, truncating any fractional part of the exact result. pub fn checked_div(&self, rhs: &Self) -> CtOption { NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem(&rhs).0.into()) } - /// Perform checked division, returning a [`CtOption`] which `is_some` only if the rhs != 0. + /// Computes `self` % `rhs`, returns the remainder. + pub const fn rem(&self, rhs: &NonZero) -> Self { + self.checked_div_rem(rhs).1 + } +} + +/// Checked div-floor operations. +impl Int { + /// Perform checked floored division, returning a [`ConstCtOption`] which `is_some` only if + /// - the `rhs != 0`, and + /// - `self != MIN` or `rhs != MINUS_ONE`. + /// /// Note: this operation rounds down. /// /// Example: @@ -89,68 +105,62 @@ impl Int { /// ) /// ``` pub fn checked_div_floor(&self, rhs: &Self) -> CtOption { - NonZero::new(*rhs).and_then(|rhs| { - let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(&rhs); - - // Increment the quotient when - // - lhs and rhs have opposing signs, and - // - there is a non-zero remainder. - let opposing_signs = lhs_sgn.ne(rhs_sgn); - let increment_quotient = remainder.is_nonzero().and(opposing_signs); - let quotient_sub_one = quotient.wrapping_add(&Uint::ONE); - let quotient = Uint::select("ient, "ient_sub_one, increment_quotient); - - Self::new_from_abs_sign(quotient, opposing_signs).into() - }) + NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_floor(&rhs).0.into()) } - /// Perform checked division and mod, returning a [`CtOption`] which `is_some` only - /// if the rhs != 0. + /// Perform checked division and mod, returning the quotient and remainder. + /// + /// The quotient is a [`ConstCtOption`] which `is_some` only if + /// - the `rhs != 0`, and + /// - `self != MIN` or `rhs != MINUS_ONE`. + /// /// Note: this operation rounds down. /// /// Example: /// ``` /// use crypto_bigint::I128; - /// assert_eq!( - /// I128::from(8).checked_div_mod_floor(&I128::from(3)).unwrap(), - /// (I128::from(2), I128::from(2)) - /// ); - /// assert_eq!( - /// I128::from(-8).checked_div_mod_floor(&I128::from(3)).unwrap(), - /// (I128::from(-3), I128::from(-1)) - /// ); - /// assert_eq!( - /// I128::from(8).checked_div_mod_floor(&I128::from(-3)).unwrap(), - /// (I128::from(-3), I128::from(-1)) - /// ); - /// assert_eq!( - /// I128::from(-8).checked_div_mod_floor(&I128::from(-3)).unwrap(), - /// (I128::from(2), I128::from(2)) - /// ); + /// + /// let three = I128::from(3).to_nz().unwrap(); + /// let (quotient, remainder) = I128::from(8).checked_div_rem_floor(&three); + /// assert_eq!(quotient.unwrap(), I128::from(2)); + /// assert_eq!(remainder, I128::from(2)); + /// + /// let (quotient, remainder) = I128::from(-8).checked_div_rem_floor(&three); + /// assert_eq!(quotient.unwrap(), I128::from(-3)); + /// assert_eq!(remainder, I128::from(-1)); + /// + /// let minus_three = I128::from(-3).to_nz().unwrap(); + /// let (quotient, remainder) = I128::from(8).checked_div_rem_floor(&minus_three); + /// assert_eq!(quotient.unwrap(), I128::from(-3)); + /// assert_eq!(remainder, I128::from(-1)); + /// + /// let (quotient, remainder) = I128::from(-8).checked_div_rem_floor(&minus_three); + /// assert_eq!(quotient.unwrap(), I128::from(2)); + /// assert_eq!(remainder, I128::from(2)); /// ``` - pub fn checked_div_mod_floor(&self, rhs: &Self) -> CtOption<(Self, Self)> { + pub const fn checked_div_rem_floor(&self, rhs: &NonZero) -> (ConstCtOption, Self) { let (lhs_mag, lhs_sgn) = self.abs_sign(); let (rhs_mag, rhs_sgn) = rhs.abs_sign(); + let (quotient, remainder) = lhs_mag.div_rem(&rhs_mag); + + // Modify quotient and remainder when lhs and rhs have opposing signs and the remainder is + // non-zero. let opposing_signs = lhs_sgn.xor(rhs_sgn); - NonZero::new(rhs_mag).and_then(|rhs_mag| { - let (quotient, remainder) = lhs_mag.div_rem(&rhs_mag); - - // Increase the quotient by one when lhs and rhs have opposing signs and there - // is a non-zero remainder. - let modify = remainder.is_nonzero().and(opposing_signs); - let quotient_sub_one = quotient.wrapping_add(&Uint::ONE); - let quotient = Uint::select("ient, "ient_sub_one, modify); - - // Invert the remainder and add one to remainder when lhs and rhs have opposing signs - // and the remainder is non-zero. - let inv_remainder = rhs_mag.wrapping_sub(&remainder); - let remainder = Uint::select(&remainder, &inv_remainder, modify); - - CtOption::from(Int::new_from_abs_sign(quotient, opposing_signs)).and_then(|quotient| { - CtOption::from(Int::new_from_abs_sign(remainder, opposing_signs)) - .and_then(|remainder| CtOption::new((quotient, remainder), Choice::from(1u8))) - }) - }) + let modify = remainder.is_nonzero().and(opposing_signs); + + // Increase the quotient by one. + let quotient_plus_one = quotient.wrapping_add(&Uint::ONE); // cannot wrap. + let quotient = Uint::select("ient, "ient_plus_one, modify); + + // Invert the remainder. + let inv_remainder = rhs_mag.0.wrapping_sub(&remainder); + let remainder = Uint::select(&remainder, &inv_remainder, modify); + + // Negate output when lhs and rhs have opposing signs. + let quotient = Int::new_from_abs_sign(quotient, opposing_signs); + let remainder = remainder.as_int().wrapping_neg_if(opposing_signs); // rem always small enough for safe as_int conversion + + (quotient, remainder) } } @@ -192,9 +202,155 @@ impl Div>> for Int { } } +impl DivAssign<&NonZero>> for Int { + fn div_assign(&mut self, rhs: &NonZero>) { + *self /= *rhs + } +} + +impl DivAssign>> for Int { + fn div_assign(&mut self, rhs: NonZero>) { + *self = (*self / rhs).expect("cannot represent positive equivalent of Int::MIN as int"); + } +} + +impl Div>> for Wrapping> { + type Output = Wrapping>; + + fn div(self, rhs: NonZero>) -> Self::Output { + Wrapping((self.0 / rhs).expect("cannot represent positive equivalent of Int::MIN as int")) + } +} + +impl Div>> for &Wrapping> { + type Output = Wrapping>; + + fn div(self, rhs: NonZero>) -> Self::Output { + *self / rhs + } +} + +impl Div<&NonZero>> for &Wrapping> { + type Output = Wrapping>; + + fn div(self, rhs: &NonZero>) -> Self::Output { + *self / *rhs + } +} + +impl Div<&NonZero>> for Wrapping> { + type Output = Wrapping>; + + fn div(self, rhs: &NonZero>) -> Self::Output { + self / *rhs + } +} + +impl DivAssign<&NonZero>> for Wrapping> { + fn div_assign(&mut self, rhs: &NonZero>) { + *self = Wrapping( + (self.0 / rhs).expect("cannot represent positive equivalent of Int::MIN as int"), + ); + } +} + +impl DivAssign>> for Wrapping> { + fn div_assign(&mut self, rhs: NonZero>) { + *self /= &rhs; + } +} + +impl Rem<&NonZero>> for &Int { + type Output = Int; + + fn rem(self, rhs: &NonZero>) -> Self::Output { + *self % *rhs + } +} + +impl Rem<&NonZero>> for Int { + type Output = Int; + + fn rem(self, rhs: &NonZero>) -> Self::Output { + self % *rhs + } +} + +impl Rem>> for &Int { + type Output = Int; + + fn rem(self, rhs: NonZero>) -> Self::Output { + *self % rhs + } +} + +impl Rem>> for Int { + type Output = Int; + + fn rem(self, rhs: NonZero>) -> Self::Output { + Self::rem(&self, &rhs) + } +} + +impl RemAssign<&NonZero>> for Int { + fn rem_assign(&mut self, rhs: &NonZero>) { + *self %= *rhs + } +} + +impl RemAssign>> for Int { + fn rem_assign(&mut self, rhs: NonZero>) { + *self = *self % rhs; + } +} + +impl Rem>> for Wrapping> { + type Output = Wrapping>; + + fn rem(self, rhs: NonZero>) -> Self::Output { + Wrapping(self.0 % rhs) + } +} + +impl Rem>> for &Wrapping> { + type Output = Wrapping>; + + fn rem(self, rhs: NonZero>) -> Self::Output { + *self % rhs + } +} + +impl Rem<&NonZero>> for &Wrapping> { + type Output = Wrapping>; + + fn rem(self, rhs: &NonZero>) -> Self::Output { + *self % *rhs + } +} + +impl Rem<&NonZero>> for Wrapping> { + type Output = Wrapping>; + + fn rem(self, rhs: &NonZero>) -> Self::Output { + self % *rhs + } +} + +impl RemAssign>> for Wrapping> { + fn rem_assign(&mut self, rhs: NonZero>) { + *self %= &rhs; + } +} + +impl RemAssign<&NonZero>> for Wrapping> { + fn rem_assign(&mut self, rhs: &NonZero>) { + *self = Wrapping(self.0 % rhs) + } +} + #[cfg(test)] mod tests { - use crate::{Int, I128}; + use crate::{ConstChoice, Int, I128}; #[test] fn test_checked_div() { @@ -310,27 +466,9 @@ mod tests { #[test] fn test_checked_div_mod_floor() { - assert_eq!( - I128::from(8).checked_div_mod_floor(&I128::from(3)).unwrap(), - (I128::from(2), I128::from(2)) - ); - assert_eq!( - I128::from(-8) - .checked_div_mod_floor(&I128::from(3)) - .unwrap(), - (I128::from(-3), I128::from(-1)) - ); - assert_eq!( - I128::from(8) - .checked_div_mod_floor(&I128::from(-3)) - .unwrap(), - (I128::from(-3), I128::from(-1)) - ); - assert_eq!( - I128::from(-8) - .checked_div_mod_floor(&I128::from(-3)) - .unwrap(), - (I128::from(2), I128::from(2)) - ); + let (quotient, remainder) = + I128::MIN.checked_div_rem_floor(&I128::MINUS_ONE.to_nz().unwrap()); + assert_eq!(quotient.is_some(), ConstChoice::FALSE); + assert_eq!(remainder, I128::ZERO); } } diff --git a/src/int/mul.rs b/src/int/mul.rs index a36944b77..315564c4b 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -4,14 +4,15 @@ use core::ops::{Mul, MulAssign}; use subtle::CtOption; -use crate::{Checked, CheckedMul, ConcatMixed, ConstChoice, Int, Uint, Zero}; +use crate::{Checked, CheckedMul, ConcatMixed, ConstChoice, ConstCtOption, Int, Uint, Zero}; impl Int { /// Compute "wide" multiplication as a 3-tuple `(lo, hi, negate)`. /// The `(lo, hi)` components contain the _magnitude of the product_, with sizes /// corresponding to the sizes of the operands; `negate` indicates whether the result should be - /// negated when converted from `Uint` to `Int`. Note: even if `negate` is truthy, the magnitude - /// might be zero! + /// negated when converted from [`Uint`] to [`Int`]. + /// + /// Note: even if `negate` is truthy, the magnitude might be zero! pub const fn split_mul( &self, rhs: &Int, @@ -50,6 +51,32 @@ impl Int { } } +/// Squaring operations. +impl Int { + /// Square self, returning a concatenated "wide" result. + pub fn widening_square(&self) -> Uint + where + Uint: ConcatMixed, MixedOutput = Uint>, + { + self.abs().widening_square() + } + + /// Square self, checking that the result fits in the original [`Uint`] size. + pub fn checked_square(&self) -> ConstCtOption> { + self.abs().checked_square() + } + + /// Perform wrapping square, discarding overflow. + pub const fn wrapping_square(&self) -> Uint { + self.abs().wrapping_square() + } + + /// Perform saturating squaring, returning `MAX` on overflow. + pub const fn saturating_square(&self) -> Uint { + self.abs().saturating_square() + } +} + impl CheckedMul> for Int { #[inline] fn checked_mul(&self, rhs: &Int) -> CtOption { @@ -120,7 +147,7 @@ impl MulAssign<&Checked>> for Checked> #[cfg(test)] mod tests { - use crate::{CheckedMul, Int, I128, I256}; + use crate::{CheckedMul, ConstChoice, Int, I128, I256, U128, U256}; #[test] fn test_checked_mul() { @@ -282,4 +309,58 @@ mod tests { I256::from_be_hex("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000001") ); } + + #[test] + fn test_widening_square() { + let res = I128::from_i64(i64::MIN).widening_square(); + assert_eq!( + res, + U256::from_be_hex("0000000000000000000000000000000040000000000000000000000000000000") + ); + + let x: I128 = I128::MINUS_ONE << 64; + let res = x.widening_square(); + assert_eq!( + res, + U256::from_be_hex("0000000000000000000000000000000100000000000000000000000000000000") + ) + } + + #[test] + fn test_checked_square() { + let res = I128::from_i64(i64::MIN).checked_square(); + assert_eq!(res.is_some(), ConstChoice::TRUE); + assert_eq!( + res.unwrap(), + U128::from_be_hex("40000000000000000000000000000000") + ); + + let x: I128 = I128::MINUS_ONE << 64; + let res = x.checked_square(); + assert_eq!(res.is_none(), ConstChoice::TRUE) + } + + #[test] + fn test_wrapping_square() { + let res = I128::from_i64(i64::MIN).wrapping_square(); + assert_eq!(res, U128::from_be_hex("40000000000000000000000000000000")); + + let x: I128 = I128::MINUS_ONE << 64; + let res = x.wrapping_square(); + assert_eq!(res, U128::ZERO); + + let x: I128 = I128::from_i64(i64::MAX); + let res = x.wrapping_square(); + assert_eq!(res, U128::from_be_hex("3FFFFFFFFFFFFFFF0000000000000001")) + } + + #[test] + fn test_saturating_square() { + assert_eq!( + I128::from_i64(i64::MIN).saturating_square(), + U128::from_be_hex("40000000000000000000000000000000") + ); + let x: I128 = I128::MINUS_ONE << 64; + assert_eq!(x.saturating_square(), U128::MAX); + } } diff --git a/src/int/neg.rs b/src/int/neg.rs index 0556a2823..4c576fa9b 100644 --- a/src/int/neg.rs +++ b/src/int/neg.rs @@ -95,7 +95,7 @@ mod tests { } #[test] - fn neg() { + fn checked_neg() { assert_eq!(I128::MIN.checked_neg().is_none(), ConstChoice::TRUE); assert_eq!(I128::MINUS_ONE.checked_neg().unwrap(), I128::ONE); assert_eq!(I128::ZERO.checked_neg().unwrap(), I128::ZERO); diff --git a/src/int/shr.rs b/src/int/shr.rs index 562da63e5..333da21c3 100644 --- a/src/int/shr.rs +++ b/src/int/shr.rs @@ -180,6 +180,8 @@ impl ShrVartime for Int { #[cfg(test)] mod tests { + use core::ops::Div; + use crate::I256; const N: I256 = @@ -203,11 +205,11 @@ mod tests { fn shr5() { assert_eq!( I256::MAX >> 5, - I256::from_be_hex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + I256::MAX.div(I256::from(32).to_nz().unwrap()).unwrap() ); assert_eq!( I256::MIN >> 5, - I256::from_be_hex("FC00000000000000000000000000000000000000000000000000000000000000") + I256::MIN.div(I256::from(32).to_nz().unwrap()).unwrap() ); } @@ -215,11 +217,11 @@ mod tests { fn shr7_vartime() { assert_eq!( I256::MAX.shr_vartime(7), - I256::from_be_hex("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + I256::MAX.div(I256::from(128).to_nz().unwrap()).unwrap() ); assert_eq!( I256::MIN.shr_vartime(7), - I256::from_be_hex("FF00000000000000000000000000000000000000000000000000000000000000") + I256::MIN.div(I256::from(128).to_nz().unwrap()).unwrap() ); } diff --git a/src/int/sub.rs b/src/int/sub.rs index 1cb1d73bc..bb94f6429 100644 --- a/src/int/sub.rs +++ b/src/int/sub.rs @@ -127,7 +127,7 @@ mod tests { assert_eq!(result.unwrap(), I128::MINUS_ONE); let result = I128::MINUS_ONE.checked_sub(&I128::ONE); - assert_eq!(result.unwrap(), two.checked_neg().unwrap()); + assert_eq!(result.unwrap(), two.wrapping_neg()); let result = I128::MINUS_ONE.checked_sub(&I128::MAX); assert_eq!(result.unwrap(), I128::MIN); diff --git a/src/uint/mul.rs b/src/uint/mul.rs index 4a122e963..87154cb3d 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -1,12 +1,16 @@ //! [`Uint`] multiplication operations. -use self::karatsuba::UintKaratsubaMul; -use crate::{ - Checked, CheckedMul, Concat, ConcatMixed, Limb, Uint, WideningMul, Wrapping, WrappingMul, Zero, -}; use core::ops::{Mul, MulAssign}; + use subtle::CtOption; +use crate::{ + Checked, CheckedMul, Concat, ConcatMixed, ConstCtOption, Limb, Uint, WideningMul, Wrapping, + WrappingMul, Zero, +}; + +use self::karatsuba::UintKaratsubaMul; + pub(crate) mod karatsuba; /// Implement the core schoolbook multiplication algorithm. @@ -182,7 +186,10 @@ impl Uint { let (res, overflow) = self.split_mul(rhs); Self::select(&res, &Self::MAX, overflow.is_nonzero()) } +} +/// Squaring operations +impl Uint { /// Square self, returning a "wide" result in two parts as (lo, hi). pub const fn square_wide(&self) -> (Self, Self) { if LIMBS == 128 { @@ -197,6 +204,32 @@ impl Uint { uint_square_limbs(&self.limbs) } + + /// Square self, returning a concatenated "wide" result. + pub const fn widening_square(&self) -> Uint + where + Self: ConcatMixed, MixedOutput = Uint>, + { + let (lo, hi) = self.square_wide(); + Uint::concat_mixed(&lo, &hi) + } + + /// Square self, checking that the result fits in the original [`Uint`] size. + pub const fn checked_square(&self) -> ConstCtOption> { + let (lo, hi) = self.square_wide(); + ConstCtOption::new(lo, Self::eq(&hi, &Self::ZERO)) + } + + /// Perform wrapping square, discarding overflow. + pub const fn wrapping_square(&self) -> Uint { + self.square_wide().0 + } + + /// Perform saturating squaring, returning `MAX` on overflow. + pub const fn saturating_square(&self) -> Self { + let (res, overflow) = self.square_wide(); + Self::select(&res, &Self::MAX, overflow.is_nonzero()) + } } impl Uint @@ -349,7 +382,7 @@ pub(crate) fn square_limbs(limbs: &[Limb], out: &mut [Limb]) { #[cfg(test)] mod tests { - use crate::{CheckedMul, Zero, U128, U192, U256, U64}; + use crate::{CheckedMul, ConstChoice, Zero, U128, U192, U256, U64}; #[test] fn mul_wide_zero_and_one() { @@ -439,6 +472,33 @@ mod tests { assert_eq!(hi, U256::MAX.wrapping_sub(&U256::ONE)); } + #[test] + fn checked_square() { + let n = U256::from_u64(u64::MAX).wrapping_add(&U256::ONE); + let n2 = n.checked_square(); + assert_eq!(n2.is_some(), ConstChoice::TRUE); + let n4 = n2.unwrap().checked_square(); + assert_eq!(n4.is_none(), ConstChoice::TRUE); + } + + #[test] + fn wrapping_square() { + let n = U256::from_u64(u64::MAX).wrapping_add(&U256::ONE); + let n2 = n.wrapping_square(); + assert_eq!(n2, U256::from_u128(u128::MAX).wrapping_add(&U256::ONE)); + let n4 = n2.wrapping_square(); + assert_eq!(n4, U256::ZERO); + } + + #[test] + fn saturating_square() { + let n = U256::from_u64(u64::MAX).wrapping_add(&U256::ONE); + let n2 = n.saturating_square(); + assert_eq!(n2, U256::from_u128(u128::MAX).wrapping_add(&U256::ONE)); + let n4 = n2.saturating_square(); + assert_eq!(n4, U256::MAX); + } + #[cfg(feature = "rand_core")] #[test] fn mul_cmp() {