From 45164bd3f5c1087dd85d08c3daf9a27d578e6942 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Tue, 7 Jan 2025 17:33:07 +0100 Subject: [PATCH 1/7] Introduce `Int::div_uint` --- src/int.rs | 3 +- src/int/div_uint.rs | 539 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 src/int/div_uint.rs diff --git a/src/int.rs b/src/int.rs index 66a8d3e8a..58603f3fd 100644 --- a/src/int.rs +++ b/src/int.rs @@ -7,9 +7,9 @@ use num_traits::ConstZero; use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use crate::{Bounded, Constants, ConstChoice, ConstCtOption, Limb, NonZero, Odd, Uint, Word}; #[cfg(feature = "serde")] use crate::Encoding; -use crate::{Bounded, ConstChoice, ConstCtOption, Constants, Limb, NonZero, Odd, Uint, Word}; mod add; mod bit_and; @@ -18,6 +18,7 @@ mod bit_or; mod bit_xor; mod cmp; mod div; +mod div_uint; mod encoding; mod from; mod mul; diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs new file mode 100644 index 000000000..3e354d8e6 --- /dev/null +++ b/src/int/div_uint.rs @@ -0,0 +1,539 @@ +//! Operations related to dividing an [`Int`] by a [`Uint`]. +use core::ops::{Div, DivAssign, Rem, RemAssign}; + +use crate::{ConstChoice, Int, NonZero, Uint, Wrapping}; + +/// Checked division operations. +impl Int { + #[inline] + /// Base div_rem operation on dividing an [`Int`] by a [`Uint`]. + /// Computes the quotient and remainder of `self / rhs`. + /// Furthermore, returns the sign of `self`. + const fn div_rem_base_uint( + &self, + rhs: &NonZero>, + ) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice) { + let (lhs_mag, lhs_sgn) = self.abs_sign(); + let (quotient, remainder) = lhs_mag.div_rem(rhs); + (quotient, remainder, lhs_sgn) + } + + /// Compute the quotient and remainder of `self / rhs`. + /// + /// Example: + /// ``` + /// use crypto_bigint::{I128, NonZero, U128}; + /// + /// let (quotient, remainder) = I128::from(8).div_rem_uint(&U128::from(3u32).to_nz().unwrap()); + /// assert_eq!(quotient, I128::from(2)); + /// assert_eq!(remainder, I128::from(2)); + /// + /// let (quotient, remainder) = I128::from(-8).div_rem_uint(&U128::from(3u32).to_nz().unwrap()); + /// assert_eq!(quotient, I128::from(-2)); + /// assert_eq!(remainder, I128::from(-2)); + /// ``` + pub const fn div_rem_uint(&self, rhs: &NonZero>) -> (Self, Self) { + let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint(rhs); + ( + Self(quotient).wrapping_neg_if(lhs_sgn), + Self(remainder).wrapping_neg_if(lhs_sgn), + ) + } + + /// Perform division. + /// Note: this operation rounds towards zero, truncating any fractional part of the exact result. + pub const fn div_uint(&self, rhs: &NonZero>) -> Self { + self.div_rem_uint(rhs).0 + } + + /// Compute the remainder. + /// The remainder will have the same sign as `self` (or be zero). + pub const fn rem_uint(&self, rhs: &NonZero>) -> Self { + self.div_rem_uint(rhs).1 + } +} + +/// Vartime checked division operations. +impl Int { + #[inline] + /// Variable time equivalent of [Self::div_rem_base_uint`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + const fn div_rem_base_uint_vartime( + &self, + rhs: &NonZero>, + ) -> (Uint, Uint, ConstChoice) { + let (lhs_mag, lhs_sgn) = self.abs_sign(); + let (quotient, remainder) = lhs_mag.div_rem_vartime(rhs); + (quotient, remainder, lhs_sgn) + } + + /// Variable time equivalent of [Self::div_rem_uint`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub const fn div_rem_uint_vartime( + &self, + rhs: &NonZero>, + ) -> (Self, Int) { + let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint_vartime(rhs); + ( + Self(quotient).wrapping_neg_if(lhs_sgn), + remainder.as_int().wrapping_neg_if(lhs_sgn), + ) + } + + /// Variable time equivalent of [Self::div_uint`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub const fn div_uint_vartime( + &self, + rhs: &NonZero>, + ) -> Self { + self.div_rem_uint_vartime(rhs).0 + } + + /// Variable time equivalent of [Self::rem_uint`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub const fn rem_uint_vartime( + &self, + rhs: &NonZero>, + ) -> Int { + self.div_rem_uint_vartime(rhs).1 + } +} + +/// Checked div-floor operations +impl Int { + /// Perform floored division and mod: + /// given `n` and `d`, computes `q` and `r` s.t. `n = qd + r` and `q = ⌊n/d⌋`. + /// Note: this operation rounds **down**, not towards zero. + /// + /// Example: + /// ``` + /// use crypto_bigint::{I128, U128}; + /// + /// let three = U128::from(3u32).to_nz().unwrap(); + /// assert_eq!( + /// I128::from(8).div_rem_floor_uint(&three), + /// (I128::from(2), U128::from(2u32)) + /// ); + /// assert_eq!( + /// I128::from(-8).div_rem_floor_uint(&three), + /// (I128::from(-3), U128::ONE) + /// ); + /// ``` + pub fn div_rem_floor_uint(&self, rhs: &NonZero>) -> (Self, Uint) { + let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint(&rhs); + + // Increase the quotient by one when self is negative and there is a non-zero remainder. + let modify = remainder.is_nonzero().and(lhs_sgn); + let quotient = Uint::select("ient, "ient.wrapping_add(&Uint::ONE), modify); + + // Invert the remainder when self is negative and there is a non-zero remainder. + let remainder = Uint::select(&remainder, &rhs.wrapping_sub(&remainder), modify); + + // Negate if applicable + let quotient = Self(quotient).wrapping_neg_if(lhs_sgn); + + (quotient, remainder) + } + + /// Perform checked division. + /// Note: this operation rounds down. + /// + /// Example: + /// ``` + /// use crypto_bigint::{I128, U128}; + /// assert_eq!( + /// I128::from(8).div_floor_uint(&U128::from(3u32).to_nz().unwrap()), + /// I128::from(2) + /// ); + /// assert_eq!( + /// I128::from(-8).div_floor_uint(&U128::from(3u32).to_nz().unwrap()), + /// I128::from(-3) + /// ); + /// ``` + pub fn div_floor_uint(&self, rhs: &NonZero>) -> Self { + let (q, _) = self.div_rem_floor_uint(rhs); + q + } + + /// Compute `self % rhs` and return the result contained in the interval `[0, rhs)`. + /// + /// Example: + /// ``` + /// use crypto_bigint::{I128, U128}; + /// assert_eq!( + /// I128::from(8).normalized_rem(&U128::from(3u32).to_nz().unwrap()), + /// U128::from(2u32) + /// ); + /// assert_eq!( + /// I128::from(-8).normalized_rem(&U128::from(3u32).to_nz().unwrap()), + /// U128::ONE + /// ); + /// ``` + pub fn normalized_rem(&self, rhs: &NonZero>) -> Uint { + let (_, r) = self.div_rem_floor_uint(rhs); + r + } +} + +/// Vartime checked div-floor operations +impl Int { + /// Variable time equivalent of [Self::div_rem_floor_uint`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub fn div_rem_floor_uint_vartime( + &self, + rhs: &NonZero>, + ) -> (Self, Uint) { + let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint_vartime(&rhs); + + // Increase the quotient by one when self is negative and there is a non-zero remainder. + let modify = remainder.is_nonzero().and(lhs_sgn); + let quotient = Uint::select("ient, "ient.wrapping_add(&Uint::ONE), modify); + + // Invert the remainder when self is negative and there is a non-zero remainder. + let remainder = Uint::select(&remainder, &rhs.wrapping_sub(&remainder), modify); + + // Negate if applicable + let quotient = Self(quotient).wrapping_neg_if(lhs_sgn); + + (quotient, remainder) + } + + /// Variable time equivalent of [Self::div_floor_uint`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub fn div_floor_uint_vartime( + &self, + rhs: &NonZero>, + ) -> Self { + let (q, _) = self.div_rem_floor_uint_vartime(rhs); + q + } + + /// Variable time equivalent of [Self::normalized_rem`]. + /// + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub fn normalized_rem_vartime( + &self, + rhs: &NonZero>, + ) -> Uint { + let (_, r) = self.div_rem_floor_uint_vartime(rhs); + r + } +} + +impl Div<&NonZero>> for &Int { + type Output = Int; + + fn div(self, rhs: &NonZero>) -> Self::Output { + *self / *rhs + } +} + +impl Div<&NonZero>> for Int { + type Output = Int; + + fn div(self, rhs: &NonZero>) -> Self::Output { + self / *rhs + } +} + +impl Div>> for &Int { + type Output = Int; + + fn div(self, rhs: NonZero>) -> Self::Output { + *self / rhs + } +} + +impl Div>> for Int { + type Output = Int; + + fn div(self, rhs: NonZero>) -> Self::Output { + self.div_uint(&rhs) + } +} + +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; + } +} + +impl Div>> for Wrapping> { + type Output = Wrapping>; + + fn div(self, rhs: NonZero>) -> Self::Output { + Wrapping(self.0 / rhs) + } +} + +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); + } +} + +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_uint(&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 rand_core::OsRng; + + use crate::{NonZero, Random, I1024, I128, U128, U512}; + + #[test] + fn test_div_uint() { + // lhs = min + assert_eq!(I128::MIN / U128::ONE.to_nz().unwrap(), I128::MIN); + assert_eq!(I128::MIN / U128::MAX.to_nz().unwrap(), I128::ZERO); + + // lhs = -1 + assert_eq!( + I128::MINUS_ONE / U128::ONE.to_nz().unwrap(), + I128::MINUS_ONE + ); + assert_eq!(I128::MINUS_ONE / U128::MAX.to_nz().unwrap(), I128::ZERO); + + // lhs = 0 + assert_eq!(I128::ZERO / U128::ONE.to_nz().unwrap(), I128::ZERO); + assert_eq!(I128::ZERO / U128::MAX.to_nz().unwrap(), I128::ZERO); + + // lhs = 1 + assert_eq!(I128::ONE / U128::ONE.to_nz().unwrap(), I128::ONE); + assert_eq!(I128::ONE / U128::MAX.to_nz().unwrap(), I128::ZERO); + + // lhs = max + assert_eq!(I128::MAX / U128::ONE.to_nz().unwrap(), I128::MAX); + assert_eq!(I128::MAX / U128::MAX.to_nz().unwrap(), I128::ZERO); + } + + #[test] + fn test_div_ct_vs_vt() { + for _ in 0..50 { + let num = I1024::random(&mut OsRng); + let denom = NonZero::::random(&mut OsRng); + + assert_eq!( + num.div_uint(&denom.resize::<{ I1024::LIMBS }>().to_nz().unwrap()), + num.div_uint_vartime(&denom) + ) + } + } + + #[test] + fn test_div_rem_floor_uint() { + // lhs = min + assert_eq!( + I128::MIN.div_rem_floor_uint(&U128::ONE.to_nz().unwrap()), + (I128::MIN, U128::ZERO) + ); + assert_eq!( + I128::MIN.div_rem_floor_uint(&U128::MAX.to_nz().unwrap()), + ( + I128::MINUS_ONE, + I128::MIN.as_uint().wrapping_sub(&U128::ONE) + ) + ); + + // lhs = -1 + assert_eq!( + I128::MINUS_ONE.div_rem_floor_uint(&U128::ONE.to_nz().unwrap()), + (I128::MINUS_ONE, U128::ZERO) + ); + assert_eq!( + I128::MINUS_ONE.div_rem_floor_uint(&U128::MAX.to_nz().unwrap()), + (I128::MINUS_ONE, U128::MAX.wrapping_sub(&U128::ONE)) + ); + + // lhs = 0 + assert_eq!( + I128::ZERO.div_rem_floor_uint(&U128::ONE.to_nz().unwrap()), + (I128::ZERO, U128::ZERO) + ); + assert_eq!( + I128::ZERO.div_rem_floor_uint(&U128::MAX.to_nz().unwrap()), + (I128::ZERO, U128::ZERO) + ); + + // lhs = 1 + assert_eq!( + I128::ONE.div_rem_floor_uint(&U128::ONE.to_nz().unwrap()), + (I128::ONE, U128::ZERO) + ); + assert_eq!( + I128::ONE.div_rem_floor_uint(&U128::MAX.to_nz().unwrap()), + (I128::ZERO, U128::ONE) + ); + + // lhs = max + assert_eq!( + I128::MAX.div_rem_floor_uint(&U128::ONE.to_nz().unwrap()), + (I128::MAX, U128::ZERO) + ); + assert_eq!( + I128::MAX.div_rem_floor_uint(&U128::MAX.to_nz().unwrap()), + (I128::ZERO, *I128::MAX.as_uint()) + ); + } + + #[test] + fn test_div_floor_ct_vs_vt() { + for _ in 0..50 { + let num = I1024::random(&mut OsRng); + let denom = NonZero::::random(&mut OsRng); + + assert_eq!( + num.div_floor_uint(&denom.resize::<{ I1024::LIMBS }>().to_nz().unwrap()), + num.div_floor_uint_vartime(&denom) + ) + } + } +} From 3645a42f8f445629d4840f9c9b609fd30b1839a4 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Tue, 7 Jan 2025 17:35:43 +0100 Subject: [PATCH 2/7] Introduce `Int::mul_uint` --- src/int.rs | 1 + src/int/mul_uint.rs | 253 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 src/int/mul_uint.rs diff --git a/src/int.rs b/src/int.rs index 58603f3fd..4f3f349fe 100644 --- a/src/int.rs +++ b/src/int.rs @@ -22,6 +22,7 @@ mod div_uint; mod encoding; mod from; mod mul; +mod mul_uint; mod neg; mod resize; mod shl; diff --git a/src/int/mul_uint.rs b/src/int/mul_uint.rs new file mode 100644 index 000000000..483e48467 --- /dev/null +++ b/src/int/mul_uint.rs @@ -0,0 +1,253 @@ +use core::ops::Mul; + +use subtle::CtOption; + +use crate::{CheckedMul, ConcatMixed, ConstChoice, Int, Uint, Zero}; + +impl Int { + /// Compute "wide" multiplication between an [`Int`] and [`Uint`] as 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! + pub const fn split_mul_uint( + &self, + rhs: &Uint, + ) -> (Uint<{ LIMBS }>, Uint<{ RHS_LIMBS }>, ConstChoice) { + // Step 1. split self into its sign and magnitude. + let (lhs_abs, lhs_sgn) = self.abs_sign(); + + // Step 2. Multiply the magnitudes + let (lo, hi) = lhs_abs.split_mul(rhs); + + // Step 3. negate if and only if self has a negative sign. + (lo, hi, lhs_sgn) + } + + /// Compute "wide" multiplication between an [`Int`] and [`Uint`] as 3-tuple `(lo, hi, negate)`. + /// The `(lo, hi)` components contain the _magnitude of the product_, with sizes + /// corresponding to the sizes of the operands, in reversed order; `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! + pub const fn split_mul_uint_right( + &self, + rhs: &Uint, + ) -> (Uint<{ RHS_LIMBS }>, Uint<{ LIMBS }>, ConstChoice) { + let (lhs_abs, lhs_sgn) = self.abs_sign(); + let (lo, hi) = rhs.split_mul(&lhs_abs); + (lo, hi, lhs_sgn) + } + + /// Multiply `self` by [`Uint`] `rhs`, returning a concatenated "wide" result. + pub const fn widening_mul_uint( + &self, + rhs: &Uint, + ) -> Int + where + Uint: ConcatMixed, MixedOutput = Uint>, + { + let (lhs_abs, lhs_sign) = self.abs_sign(); + let product_abs = lhs_abs.widening_mul(rhs); + + // always fits + product_abs.wrapping_neg_if(lhs_sign).as_int() + } + + /// Checked multiplication of self with an `Uint`, where the result is to be stored + /// in an `Int`. + pub fn checked_mul_uint_right( + &self, + rhs: &Uint, + ) -> CtOption> { + let (lo, hi, is_negative) = self.split_mul_uint_right(rhs); + let val = Int::::new_from_abs_sign(lo, is_negative); + CtOption::from(val).and_then(|int| CtOption::new(int, hi.is_zero())) + } +} + +impl CheckedMul> for Int { + #[inline] + fn checked_mul(&self, rhs: &Uint) -> CtOption { + let (lo, hi, is_negative) = self.split_mul_uint(rhs); + let val = Self::new_from_abs_sign(lo, is_negative); + CtOption::from(val).and_then(|int| CtOption::new(int, hi.is_zero())) + } +} + +impl Mul> for Int { + type Output = Int; + + fn mul(self, rhs: Uint) -> Self { + self.mul(&rhs) + } +} + +impl Mul<&Uint> for Int { + type Output = Int; + + fn mul(self, rhs: &Uint) -> Self { + (&self).mul(rhs) + } +} + +impl Mul> for &Int { + type Output = Int; + + fn mul(self, rhs: Uint) -> Self::Output { + self.mul(&rhs) + } +} + +impl Mul<&Uint> for &Int { + type Output = Int; + + fn mul(self, rhs: &Uint) -> Self::Output { + self.checked_mul(rhs) + .expect("attempted to multiply with overflow") + } +} + +#[cfg(test)] +mod tests { + use crate::{CheckedMul, I128, I256, U128, U256}; + + #[test] + fn test_checked_mul_uint() { + // lhs = min + + let result = I128::MIN.checked_mul(&U128::ZERO); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::MIN.checked_mul(&U128::ONE); + assert_eq!(result.unwrap(), I128::MIN); + + let result = I128::MIN.checked_mul(&U128::MAX); + assert!(bool::from(result.is_none())); + + // lhs = -1 + + let result = I128::MINUS_ONE.checked_mul(&U128::ZERO); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::MINUS_ONE.checked_mul(&U128::ONE); + assert_eq!(result.unwrap(), I128::MINUS_ONE); + + let result = I128::MINUS_ONE.checked_mul(&U128::MAX); + assert!(bool::from(result.is_none())); + + // lhs = 0 + + let result = I128::ZERO.checked_mul(&U128::ZERO); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::ZERO.checked_mul(&U128::ONE); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::ZERO.checked_mul(&U128::MAX); + assert_eq!(result.unwrap(), I128::ZERO); + + // lhs = 1 + + let result = I128::ONE.checked_mul(&U128::ZERO); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::ONE.checked_mul(&U128::ONE); + assert_eq!(result.unwrap(), I128::ONE); + + let result = I128::ONE.checked_mul(&U128::MAX); + assert!(bool::from(result.is_none())); + + // lhs = max + + let result = I128::MAX.checked_mul(&U128::ZERO); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::MAX.checked_mul(&U128::ONE); + assert_eq!(result.unwrap(), I128::MAX); + + let result = I128::MAX.checked_mul(&U128::MAX); + assert!(bool::from(result.is_none())); + } + + #[test] + fn test_checked_mul_uint_right() { + // rhs = 0 + let result = I256::MIN.checked_mul_uint_right(&U128::ZERO); + assert!(bool::from(result.is_some())); + assert_eq!(result.unwrap(), I128::ZERO); + + let result = I128::MIN.checked_mul_uint_right(&U256::ZERO); + assert!(bool::from(result.is_some())); + assert_eq!(result.unwrap(), I256::ZERO); + + // rhs = 1 + let result = I256::MIN.checked_mul_uint_right(&U128::ONE); + assert!(bool::from(result.is_none())); + + let result = I128::MIN.checked_mul_uint_right(&U256::ONE); + assert!(bool::from(result.is_some())); + assert_eq!(result.unwrap(), I128::MIN.resize()); + + // rhs > 1 + let result = I256::ONE.checked_mul_uint_right(&I128::MAX.as_uint()); + assert!(bool::from(result.is_some())); + assert_eq!(result.unwrap(), I128::MAX.resize()); + + let result = I128::ONE.checked_mul_uint_right(&I256::MAX.as_uint()); + assert!(bool::from(result.is_some())); + assert_eq!(result.unwrap(), I256::MAX); + + // rhs = MAX + let result = I256::ONE.checked_mul_uint_right(&U128::MAX); + assert!(bool::from(result.is_none())); + + let result = I128::MIN.checked_mul_uint_right(&U256::MAX); + assert!(bool::from(result.is_none())); + } + + #[test] + fn test_widening_mul_uint() { + assert_eq!(I128::MIN.widening_mul_uint(&U128::ZERO), I256::ZERO); + assert_eq!( + I128::MIN.widening_mul_uint(&U128::ONE), + I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000000000000000000000000000000") + ); + assert_eq!( + I128::MIN.widening_mul_uint(&U128::MAX), + I256::from_be_hex("8000000000000000000000000000000080000000000000000000000000000000") + ); + + assert_eq!(I128::MINUS_ONE.widening_mul_uint(&U128::ZERO), I256::ZERO); + assert_eq!( + I128::MINUS_ONE.widening_mul_uint(&U128::ONE), + I256::MINUS_ONE + ); + assert_eq!( + I128::MINUS_ONE.widening_mul_uint(&U128::MAX), + I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000001") + ); + + assert_eq!(I128::ZERO.widening_mul_uint(&U128::ZERO), I256::ZERO); + assert_eq!(I128::ZERO.widening_mul_uint(&U128::ONE), I256::ZERO); + assert_eq!(I128::ZERO.widening_mul_uint(&U128::MAX), I256::ZERO); + + assert_eq!(I128::ONE.widening_mul_uint(&U128::ZERO), I256::ZERO); + assert_eq!(I128::ONE.widening_mul_uint(&U128::ONE), I256::ONE); + assert_eq!( + I128::ONE.widening_mul_uint(&U128::MAX), + I256::from_be_hex("00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + ); + + assert_eq!(I128::MAX.widening_mul_uint(&U128::ZERO), I256::ZERO); + assert_eq!( + I128::MAX.widening_mul_uint(&U128::ONE), + I256::from_be_hex("000000000000000000000000000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + ); + assert_eq!( + I128::MAX.widening_mul_uint(&U128::MAX), + I256::from_be_hex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE80000000000000000000000000000001") + ); + } +} From 00c660aae741d1fdaa67844b3c967f90c1e1c8d9 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Wed, 8 Jan 2025 09:45:27 +0100 Subject: [PATCH 3/7] Format `int::div_uint` --- src/int/div_uint.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs index 3e354d8e6..702ac3d84 100644 --- a/src/int/div_uint.rs +++ b/src/int/div_uint.rs @@ -136,7 +136,7 @@ impl Int { /// ); /// ``` pub fn div_rem_floor_uint(&self, rhs: &NonZero>) -> (Self, Uint) { - let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint(&rhs); + let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint(rhs); // Increase the quotient by one when self is negative and there is a non-zero remainder. let modify = remainder.is_nonzero().and(lhs_sgn); @@ -203,7 +203,7 @@ impl Int { &self, rhs: &NonZero>, ) -> (Self, Uint) { - let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint_vartime(&rhs); + let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint_vartime(rhs); // Increase the quotient by one when self is negative and there is a non-zero remainder. let modify = remainder.is_nonzero().and(lhs_sgn); @@ -427,7 +427,7 @@ impl RemAssign<&NonZero>> for Wrapping Date: Wed, 8 Jan 2025 09:46:57 +0100 Subject: [PATCH 4/7] fmt --- src/int.rs | 2 +- src/int/div_uint.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/int.rs b/src/int.rs index 4f3f349fe..1465c0629 100644 --- a/src/int.rs +++ b/src/int.rs @@ -7,9 +7,9 @@ use num_traits::ConstZero; use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use crate::{Bounded, Constants, ConstChoice, ConstCtOption, Limb, NonZero, Odd, Uint, Word}; #[cfg(feature = "serde")] use crate::Encoding; +use crate::{Bounded, ConstChoice, ConstCtOption, Constants, Limb, NonZero, Odd, Uint, Word}; mod add; mod bit_and; diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs index 702ac3d84..898e84095 100644 --- a/src/int/div_uint.rs +++ b/src/int/div_uint.rs @@ -427,7 +427,7 @@ impl RemAssign<&NonZero>> for Wrapping Date: Mon, 20 Jan 2025 10:34:40 +0100 Subject: [PATCH 5/7] Feature gate some `Int::div` tests --- src/int/div_uint.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs index 898e84095..9b8087e2c 100644 --- a/src/int/div_uint.rs +++ b/src/int/div_uint.rs @@ -425,9 +425,10 @@ impl RemAssign<&NonZero>> for Wrapping::random(&mut OsRng); + let denom = U1024::from(&U512::random(&mut OsRng)).to_nz().unwrap(); - assert_eq!( - num.div_uint(&denom.resize::<{ I1024::LIMBS }>().to_nz().unwrap()), - num.div_uint_vartime(&denom) - ) + assert_eq!(num.div_uint(&denom), num.div_uint_vartime(&denom)) } } @@ -524,14 +523,15 @@ mod tests { ); } + #[cfg(feature = "rand_core")] #[test] fn test_div_floor_ct_vs_vt() { for _ in 0..50 { let num = I1024::random(&mut OsRng); - let denom = NonZero::::random(&mut OsRng); + let denom = U1024::from(&U512::random(&mut OsRng)).to_nz().unwrap(); assert_eq!( - num.div_floor_uint(&denom.resize::<{ I1024::LIMBS }>().to_nz().unwrap()), + num.div_floor_uint(&denom), num.div_floor_uint_vartime(&denom) ) } From d0e1a21ffe7ac6422544f26f1e82ae1976fcfa7c Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 20 Jan 2025 10:38:08 +0100 Subject: [PATCH 6/7] Fix clippy --- src/int/div_uint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs index 9b8087e2c..da9554ddc 100644 --- a/src/int/div_uint.rs +++ b/src/int/div_uint.rs @@ -426,9 +426,9 @@ impl RemAssign<&NonZero>> for Wrapping Date: Mon, 20 Jan 2025 10:38:34 +0100 Subject: [PATCH 7/7] Fix fmt --- src/int/div_uint.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs index da9554ddc..dd379d1a3 100644 --- a/src/int/div_uint.rs +++ b/src/int/div_uint.rs @@ -426,7 +426,10 @@ impl RemAssign<&NonZero>> for Wrapping