From 3bcc7e3aa79263cb708aaec50a9829310dd6114e Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:10:10 +0200 Subject: [PATCH 1/7] Implement `div_rem` for smaller sized denominators --- src/uint/div.rs | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/uint/div.rs b/src/uint/div.rs index d7bee114..781b4b16 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -38,14 +38,18 @@ impl Uint { /// Computes `self` / `rhs`, returns the quotient (q) and the remainder (r) /// /// This function is constant-time with respect to both `self` and `rhs`. - pub const fn div_rem(&self, rhs: &NonZero) -> (Self, Self) { + pub const fn div_rem( + &self, + rhs: &NonZero>, + ) -> (Self, Uint) { + assert!(RHS_LIMBS <= LIMBS); // Based on Section 4.3.1, of The Art of Computer Programming, Volume 2, by Donald E. Knuth. // Further explanation at https://janmr.com/blog/2014/04/basic-multiple-precision-long-division/ // Statically determined short circuit for Uint<1> - if LIMBS == 1 { + if RHS_LIMBS == 1 { let (quo, rem_limb) = self.div_rem_limb(rhs.0.limbs[0].to_nz().expect("zero divisor")); - let mut rem = Self::ZERO; + let mut rem = Uint::::ZERO; rem.limbs[0] = rem_limb; return (quo, rem); } @@ -56,7 +60,7 @@ impl Uint { let lshift = (Limb::BITS - (dbits % Limb::BITS)) % Limb::BITS; // Shift entire divisor such that the high bit is set - let mut y = rhs.0.shl(Self::BITS - dbits).to_limbs(); + let mut y = rhs.0.shl(Uint::::BITS - dbits).to_limbs(); // Shift the dividend to align the words let (x, mut x_hi) = self.shl_limb(lshift); let mut x = x.to_limbs(); @@ -65,11 +69,11 @@ impl Uint { let mut i; let mut carry; - let reciprocal = Reciprocal::new(y[LIMBS - 1].to_nz().expect("zero divisor")); + let reciprocal = Reciprocal::new(y[RHS_LIMBS - 1].to_nz().expect("zero divisor")); while xi > 0 { // Divide high dividend words by the high divisor word to estimate the quotient word - let mut quo = div3by2(x_hi.0, x_lo.0, x[xi - 1].0, &reciprocal, y[LIMBS - 2].0); + let mut quo = div3by2(x_hi.0, x_lo.0, x[xi - 1].0, &reciprocal, y[RHS_LIMBS - 2].0); // This loop is a no-op once xi is smaller than the number of words in the divisor let done = ConstChoice::from_u32_lt(xi as u32, dwords - 1); @@ -79,9 +83,9 @@ impl Uint { carry = Limb::ZERO; let mut borrow = Limb::ZERO; let mut tmp; - i = 0; + i = (xi + 1).saturating_sub(RHS_LIMBS); while i <= xi { - (tmp, carry) = Limb::ZERO.mac(y[LIMBS - xi + i - 1], Limb(quo), carry); + (tmp, carry) = Limb::ZERO.mac(y[RHS_LIMBS + i - xi - 1], Limb(quo), carry); (x[i], borrow) = x[i].sbb(tmp, borrow); i += 1; } @@ -91,10 +95,10 @@ impl Uint { // The probability of this being needed is very low, about 2/(Limb::MAX+1) let ct_borrow = ConstChoice::from_word_mask(borrow.0); carry = Limb::ZERO; - i = 0; + i = (xi + 1).saturating_sub(RHS_LIMBS); while i <= xi { (x[i], carry) = x[i].adc( - Limb::select(Limb::ZERO, y[LIMBS - xi + i - 1], ct_borrow), + Limb::select(Limb::ZERO, y[RHS_LIMBS + i - xi - 1], ct_borrow), carry, ); i += 1; @@ -124,7 +128,7 @@ impl Uint { // Copy out the remainder y[0] = Limb::select(x[0], Limb(rem2), limb_div); i = 1; - while i < LIMBS { + while i < RHS_LIMBS { y[i] = Limb::select(Limb::ZERO, x[i], ConstChoice::from_u32_lt(i as u32, dwords)); y[i] = Limb::select(y[i], x_hi, ConstChoice::from_u32_eq(i as u32, dwords - 1)); i += 1; @@ -1049,6 +1053,23 @@ mod tests { assert_eq!(r5.resize(), expect.1); } + #[test] + #[should_panic] + fn div_rem_denominator_too_large() { + U128::MAX.div_rem(&NonZero::::ONE); + } + + #[test] + fn div_rem_larger_numerator() { + let denom = U128::from_be_hex("AAAA0000FFFF11117777333344449999"); + let (full_q, full_r) = + U1024::MAX.div_rem(&denom.resize::<{ U1024::LIMBS }>().to_nz().unwrap()); + + let (q, r) = U1024::MAX.div_rem(&denom.to_nz().unwrap()); + assert_eq!(full_q, q); + assert_eq!(full_r.resize::<{ U128::LIMBS }>(), r); + } + #[test] fn reduce_one() { let r = U256::from(10u8).rem_vartime(&NonZero::new(U256::ONE).unwrap()); From dac7678b6601d73ce533e658758a97f3781767dc Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:19:48 +0200 Subject: [PATCH 2/7] Implement `div_rem` for larger denominators --- src/uint/div.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/uint/div.rs b/src/uint/div.rs index 781b4b16..2d8c6e59 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -42,7 +42,6 @@ impl Uint { &self, rhs: &NonZero>, ) -> (Self, Uint) { - assert!(RHS_LIMBS <= LIMBS); // Based on Section 4.3.1, of The Art of Computer Programming, Volume 2, by Donald E. Knuth. // Further explanation at https://janmr.com/blog/2014/04/basic-multiple-precision-long-division/ @@ -128,11 +127,17 @@ impl Uint { // Copy out the remainder y[0] = Limb::select(x[0], Limb(rem2), limb_div); i = 1; - while i < RHS_LIMBS { + + let min = if LIMBS < RHS_LIMBS { LIMBS } else { RHS_LIMBS }; + while i < min { y[i] = Limb::select(Limb::ZERO, x[i], ConstChoice::from_u32_lt(i as u32, dwords)); y[i] = Limb::select(y[i], x_hi, ConstChoice::from_u32_eq(i as u32, dwords - 1)); i += 1; } + while i < RHS_LIMBS { + y[i] = Limb::ZERO; + i += 1; + } ( Uint::new(x).shr((dwords - 1) * Limb::BITS), @@ -957,7 +962,7 @@ impl RemLimb for Uint { #[cfg(test)] mod tests { - use crate::{Limb, NonZero, RemMixed, U64, U128, U256, U896, U1024, Uint, Word, Zero}; + use crate::{Limb, NonZero, RemMixed, U64, U128, U256, U512, U896, U1024, Uint, Word, Zero}; #[cfg(feature = "rand")] use { @@ -1054,9 +1059,13 @@ mod tests { } #[test] - #[should_panic] - fn div_rem_denominator_too_large() { - U128::MAX.div_rem(&NonZero::::ONE); + fn div_rem_larger_denominator() { + let denom = U128::from_be_hex("AAAA0000FFFF11117777333344449999"); + let (full_q, full_r) = U256::MAX.div_rem(&denom.to_nz().unwrap()); + let (q, r) = U256::MAX.div_rem(&denom.resize::<{ U512::LIMBS }>().to_nz().unwrap()); + + assert_eq!(full_q, q); + assert_eq!(full_r.resize(), r); } #[test] @@ -1067,7 +1076,7 @@ mod tests { let (q, r) = U1024::MAX.div_rem(&denom.to_nz().unwrap()); assert_eq!(full_q, q); - assert_eq!(full_r.resize::<{ U128::LIMBS }>(), r); + assert_eq!(full_r.resize(), r); } #[test] From 41c328bf9c4a1698c767d32a6c46d5ae681e8821 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:21:03 +0200 Subject: [PATCH 3/7] Clean up `Uint::rem_mixed` test --- src/uint/div.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/uint/div.rs b/src/uint/div.rs index 2d8c6e59..07373bf9 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -1178,9 +1178,12 @@ mod tests { #[test] fn rem_mixed() { - let x = U1024::from_be_hex( - "3740C11DB8F260753BC6B97DD2B8746D3E2694412772AC6ABD975119EE0A6190F27F6F0969BCA069D8D151031AF83EE2283CC2E3E4FADBBDB9EEDBF0B8F4C1FD51912C0D329FDC37D49176DB0A1A2D17E5E6D4F9F6B217FE9412EAA2F881F7027A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A", - ); + let x = U1024::from_be_hex(concat![ + "3740C11DB8F260753BC6B97DD2B8746D3E2694412772AC6ABD975119EE0A6190", + "F27F6F0969BCA069D8D151031AF83EE2283CC2E3E4FADBBDB9EEDBF0B8F4C1FD", + "51912C0D329FDC37D49176DB0A1A2D17E5E6D4F9F6B217FE9412EAA2F881F702", + "7A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A", + ]); let y = U128::from_u64(1234567890987654321); let rem = x.rem_mixed(&y.to_nz().unwrap()); From 0883c6359b0e3f6ee30e87ee476b465dfbdb748f Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:36:50 +0200 Subject: [PATCH 4/7] Implement `Uint::div` traits --- src/modular/monty_form.rs | 4 +- src/uint/div.rs | 144 ++++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 60 deletions(-) diff --git a/src/modular/monty_form.rs b/src/modular/monty_form.rs index 7e3f8905..ebd49121 100644 --- a/src/modular/monty_form.rs +++ b/src/modular/monty_form.rs @@ -45,7 +45,9 @@ where pub const fn new(modulus: Odd>) -> Self { // `R mod modulus` where `R = 2^BITS`. // Represents 1 in Montgomery form. - let one = Uint::MAX.rem(modulus.as_nz_ref()).wrapping_add(&Uint::ONE); + let one = Uint::::MAX + .rem(modulus.as_nz_ref()) + .wrapping_add(&Uint::ONE); // `R^2 mod modulus`, used to convert integers to Montgomery form. let r2 = one diff --git a/src/uint/div.rs b/src/uint/div.rs index 07373bf9..42f5bf4e 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -308,7 +308,10 @@ impl Uint { } /// Computes `self` % `rhs`, returns the remainder. - pub const fn rem(&self, rhs: &NonZero) -> Self { + pub const fn rem( + &self, + rhs: &NonZero>, + ) -> Uint { self.div_rem(rhs).1 } @@ -495,7 +498,7 @@ impl Uint { /// let zero = U448::from(0_u64); /// assert!(bool::from(a.checked_div(&zero).is_none()), "Should be None for division by zero"); /// ``` - pub fn checked_div(&self, rhs: &Self) -> CtOption { + pub fn checked_div(&self, rhs: &Uint) -> CtOption { NonZero::new(*rhs).map(|rhs| { let (q, _r) = self.div_rem(&rhs); q @@ -538,7 +541,10 @@ impl Uint { /// /// assert!(bool::from(a.checked_rem(&zero).is_none()), "Should be None for reduction by zero"); /// ``` - pub fn checked_rem(&self, rhs: &Self) -> CtOption { + pub fn checked_rem( + &self, + rhs: &Uint, + ) -> CtOption> { NonZero::new(*rhs).map(|rhs| self.rem(&rhs)) } } @@ -729,40 +735,40 @@ impl RemAssign<&NonZero> for Wrapping> { // Division by an Uint // -impl CheckedDiv for Uint { - fn checked_div(&self, rhs: &Uint) -> CtOption { +impl CheckedDiv> for Uint { + fn checked_div(&self, rhs: &Uint) -> CtOption { self.checked_div(rhs) } } -impl Div<&NonZero>> for &Uint { +impl Div<&NonZero>> for &Uint { type Output = Uint; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { *self / *rhs } } -impl Div<&NonZero>> for Uint { +impl Div<&NonZero>> for Uint { type Output = Uint; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { self / *rhs } } -impl Div>> for &Uint { +impl Div>> for &Uint { type Output = Uint; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { *self / rhs } } -impl Div>> for Uint { +impl Div>> for Uint { type Output = Uint; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { let (q, _) = self.div_rem(&rhs); q } @@ -780,114 +786,126 @@ impl DivAssign>> for Uint { } } -impl Div>> for Wrapping> { +impl Div>> + for Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { Wrapping(self.0 / rhs) } } -impl Div>> for &Wrapping> { +impl Div>> + for &Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { *self / rhs } } -impl Div<&NonZero>> for &Wrapping> { +impl Div<&NonZero>> + for &Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { *self / *rhs } } -impl Div<&NonZero>> for Wrapping> { +impl Div<&NonZero>> + for Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { self / *rhs } } -impl Div> for &Uint { +impl Div> for &Uint { type Output = Uint; #[inline] - fn div(self, rhs: Uint) -> Self::Output { + fn div(self, rhs: Uint) -> Self::Output { self / NonZero::new(rhs).expect("attempt to divide with a divisor of zero") } } -impl Div> for Uint { +impl Div> for Uint { type Output = Uint; #[inline] - fn div(self, rhs: Uint) -> Self::Output { + fn div(self, rhs: Uint) -> Self::Output { &self / rhs } } -impl DivAssign<&NonZero>> for Wrapping> { - fn div_assign(&mut self, rhs: &NonZero>) { +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>) { +impl DivAssign>> + for Wrapping> +{ + fn div_assign(&mut self, rhs: NonZero>) { *self /= &rhs; } } -impl Rem<&NonZero>> for &Uint { - type Output = Uint; +impl Rem<&NonZero>> for &Uint { + type Output = Uint; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { *self % *rhs } } -impl Rem<&NonZero>> for Uint { - type Output = Uint; +impl Rem<&NonZero>> for Uint { + type Output = Uint; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { self % *rhs } } -impl Rem>> for &Uint { - type Output = Uint; +impl Rem>> for &Uint { + type Output = Uint; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { *self % rhs } } -impl Rem>> for Uint { - type Output = Uint; +impl Rem>> for Uint { + type Output = Uint; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { Self::rem(&self, &rhs) } } -impl Rem> for &Uint { - type Output = Uint; +impl Rem> for &Uint { + type Output = Uint; #[inline] - fn rem(self, rhs: Uint) -> Self::Output { + fn rem(self, rhs: Uint) -> Self::Output { self % NonZero::new(rhs).expect("attempt to calculate the remainder with a divisor of zero") } } -impl Rem> for Uint { - type Output = Uint; +impl Rem> for Uint { + type Output = Uint; #[inline] - fn rem(self, rhs: Uint) -> Self::Output { + fn rem(self, rhs: Uint) -> Self::Output { &self % rhs } } @@ -904,34 +922,42 @@ impl RemAssign>> for Uint { } } -impl Rem>> for Wrapping> { - type Output = Wrapping>; +impl Rem>> + for Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { Wrapping(self.0 % rhs) } } -impl Rem>> for &Wrapping> { - type Output = Wrapping>; +impl Rem>> + for &Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { *self % rhs } } -impl Rem<&NonZero>> for &Wrapping> { - type Output = Wrapping>; +impl Rem<&NonZero>> + for &Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { *self % *rhs } } -impl Rem<&NonZero>> for Wrapping> { - type Output = Wrapping>; +impl Rem<&NonZero>> + for Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { self % *rhs } } From a147ed94c12b903eda8b99b6cc4afeb3860d3cb8 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:43:03 +0200 Subject: [PATCH 5/7] Implement `Int::div` traits --- src/int/div.rs | 128 ++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 50 deletions(-) diff --git a/src/int/div.rs b/src/int/div.rs index cc2a1bc9..56bdfc60 100644 --- a/src/int/div.rs +++ b/src/int/div.rs @@ -13,10 +13,10 @@ impl Int { /// /// Computes the quotient and remainder of `self / rhs`. /// Furthermore, returns the signs of `self` and `rhs`. - const fn div_rem_base( + const fn div_rem_base( &self, - rhs: &NonZero, - ) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice, ConstChoice) { + rhs: &NonZero>, + ) -> (Uint, Uint, ConstChoice, ConstChoice) { // Step 1: split operands into signs and magnitudes. let (lhs_mag, lhs_sgn) = self.abs_sign(); let (rhs_mag, rhs_sgn) = rhs.abs_sign(); @@ -52,7 +52,10 @@ impl Int { /// assert_eq!(quotient.unwrap(), I128::from(2)); /// assert_eq!(remainder, I128::from(-2)); /// ``` - pub const fn checked_div_rem(&self, rhs: &NonZero) -> (ConstCtOption, Self) { + pub const fn checked_div_rem( + &self, + rhs: &NonZero>, + ) -> (ConstCtOption, Int) { let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(rhs); let opposing_signs = lhs_sgn.ne(rhs_sgn); ( @@ -66,12 +69,15 @@ impl Int { /// - `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 { + pub fn checked_div(&self, rhs: &Int) -> CtOption { NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem(&rhs).0.into()) } /// Computes `self` % `rhs`, returns the remainder. - pub const fn rem(&self, rhs: &NonZero) -> Self { + pub const fn rem( + &self, + rhs: &NonZero>, + ) -> Int { self.checked_div_rem(rhs).1 } } @@ -223,7 +229,10 @@ impl Int { /// I128::from(2) /// ) /// ``` - pub fn checked_div_floor(&self, rhs: &Self) -> CtOption { + pub fn checked_div_floor( + &self, + rhs: &Int, + ) -> CtOption { NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_floor(&rhs).0.into()) } @@ -257,7 +266,10 @@ impl Int { /// assert_eq!(quotient.unwrap(), I128::from(2)); /// assert_eq!(remainder, I128::from(2)); /// ``` - pub const fn checked_div_rem_floor(&self, rhs: &NonZero) -> (ConstCtOption, Self) { + pub const fn checked_div_rem_floor( + &self, + rhs: &NonZero>, + ) -> (ConstCtOption, Int) { 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); @@ -283,40 +295,40 @@ impl Int { } } -impl CheckedDiv for Int { - fn checked_div(&self, rhs: &Int) -> CtOption { +impl CheckedDiv> for Int { + fn checked_div(&self, rhs: &Int) -> CtOption { self.checked_div(rhs) } } -impl Div<&NonZero>> for &Int { +impl Div<&NonZero>> for &Int { type Output = CtOption>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { *self / *rhs } } -impl Div<&NonZero>> for Int { +impl Div<&NonZero>> for Int { type Output = CtOption>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { self / *rhs } } -impl Div>> for &Int { +impl Div>> for &Int { type Output = CtOption>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { *self / rhs } } -impl Div>> for Int { +impl Div>> for Int { type Output = CtOption>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { self.checked_div(&rhs) } } @@ -333,34 +345,42 @@ impl DivAssign>> for Int { } } -impl Div>> for Wrapping> { +impl Div>> + for Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: NonZero>) -> Self::Output { + 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> { +impl Div>> + for &Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { *self / rhs } } -impl Div<&NonZero>> for &Wrapping> { +impl Div<&NonZero>> + for &Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { *self / *rhs } } -impl Div<&NonZero>> for Wrapping> { +impl Div<&NonZero>> + for Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { self / *rhs } } @@ -379,34 +399,34 @@ impl DivAssign>> for Wrapping> } } -impl Rem<&NonZero>> for &Int { - type Output = Int; +impl Rem<&NonZero>> for &Int { + type Output = Int; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { *self % *rhs } } -impl Rem<&NonZero>> for Int { - type Output = Int; +impl Rem<&NonZero>> for Int { + type Output = Int; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { self % *rhs } } -impl Rem>> for &Int { - type Output = Int; +impl Rem>> for &Int { + type Output = Int; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { *self % rhs } } -impl Rem>> for Int { - type Output = Int; +impl Rem>> for Int { + type Output = Int; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { Self::rem(&self, &rhs) } } @@ -423,34 +443,42 @@ impl RemAssign>> for Int { } } -impl Rem>> for Wrapping> { - type Output = Wrapping>; +impl Rem>> + for Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { Wrapping(self.0 % rhs) } } -impl Rem>> for &Wrapping> { - type Output = Wrapping>; +impl Rem>> + for &Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { *self % rhs } } -impl Rem<&NonZero>> for &Wrapping> { - type Output = Wrapping>; +impl Rem<&NonZero>> + for &Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { *self % *rhs } } -impl Rem<&NonZero>> for Wrapping> { - type Output = Wrapping>; +impl Rem<&NonZero>> + for Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { self % *rhs } } From 10eec6773a77f34cfb33d055c1b9558d576ca8fa Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:47:57 +0200 Subject: [PATCH 6/7] Implement `Int::div_uint` traits --- src/int/div_uint.rs | 128 +++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 50 deletions(-) diff --git a/src/int/div_uint.rs b/src/int/div_uint.rs index 98768eff..21525ea8 100644 --- a/src/int/div_uint.rs +++ b/src/int/div_uint.rs @@ -9,10 +9,10 @@ impl Int { /// 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( + const fn div_rem_base_uint( &self, - rhs: &NonZero>, - ) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice) { + rhs: &NonZero>, + ) -> (Uint, Uint, ConstChoice) { let (lhs_mag, lhs_sgn) = self.abs_sign(); let (quotient, remainder) = lhs_mag.div_rem(rhs); (quotient, remainder, lhs_sgn) @@ -32,23 +32,29 @@ impl Int { /// assert_eq!(quotient, I128::from(-2)); /// assert_eq!(remainder, I128::from(-2)); /// ``` - pub const fn div_rem_uint(&self, rhs: &NonZero>) -> (Self, Self) { + pub const fn div_rem_uint( + &self, + rhs: &NonZero>, + ) -> (Self, Int) { 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), + Int::new_from_abs_sign(remainder, lhs_sgn).expect("no overflow; always fits"), ) } /// 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 { + 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 { + pub const fn rem_uint( + &self, + rhs: &NonZero>, + ) -> Int { self.div_rem_uint(rhs).1 } } @@ -135,7 +141,10 @@ impl Int { /// (I128::from(-3), U128::ONE) /// ); /// ``` - pub fn div_rem_floor_uint(&self, rhs: &NonZero>) -> (Self, Uint) { + 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. @@ -166,7 +175,7 @@ impl Int { /// I128::from(-3) /// ); /// ``` - pub fn div_floor_uint(&self, rhs: &NonZero>) -> Self { + pub fn div_floor_uint(&self, rhs: &NonZero>) -> Self { let (q, _) = self.div_rem_floor_uint(rhs); q } @@ -185,7 +194,10 @@ impl Int { /// U128::ONE /// ); /// ``` - pub fn normalized_rem(&self, rhs: &NonZero>) -> Uint { + pub fn normalized_rem( + &self, + rhs: &NonZero>, + ) -> Uint { let (_, r) = self.div_rem_floor_uint(rhs); r } @@ -247,34 +259,34 @@ impl Int { } } -impl Div<&NonZero>> for &Int { +impl Div<&NonZero>> for &Int { type Output = Int; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { *self / *rhs } } -impl Div<&NonZero>> for Int { +impl Div<&NonZero>> for Int { type Output = Int; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { self / *rhs } } -impl Div>> for &Int { +impl Div>> for &Int { type Output = Int; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { *self / rhs } } -impl Div>> for Int { +impl Div>> for Int { type Output = Int; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { self.div_uint(&rhs) } } @@ -291,34 +303,42 @@ impl DivAssign>> for Int { } } -impl Div>> for Wrapping> { +impl Div>> + for Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { Wrapping(self.0 / rhs) } } -impl Div>> for &Wrapping> { +impl Div>> + for &Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: NonZero>) -> Self::Output { + fn div(self, rhs: NonZero>) -> Self::Output { *self / rhs } } -impl Div<&NonZero>> for &Wrapping> { +impl Div<&NonZero>> + for &Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { *self / *rhs } } -impl Div<&NonZero>> for Wrapping> { +impl Div<&NonZero>> + for Wrapping> +{ type Output = Wrapping>; - fn div(self, rhs: &NonZero>) -> Self::Output { + fn div(self, rhs: &NonZero>) -> Self::Output { self / *rhs } } @@ -335,34 +355,34 @@ impl DivAssign>> for Wrapping } } -impl Rem<&NonZero>> for &Int { - type Output = Int; +impl Rem<&NonZero>> for &Int { + type Output = Int; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { *self % *rhs } } -impl Rem<&NonZero>> for Int { - type Output = Int; +impl Rem<&NonZero>> for Int { + type Output = Int; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { self % *rhs } } -impl Rem>> for &Int { - type Output = Int; +impl Rem>> for &Int { + type Output = Int; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { *self % rhs } } -impl Rem>> for Int { - type Output = Int; +impl Rem>> for Int { + type Output = Int; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { Self::rem_uint(&self, &rhs) } } @@ -379,34 +399,42 @@ impl RemAssign>> for Int { } } -impl Rem>> for Wrapping> { - type Output = Wrapping>; +impl Rem>> + for Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { Wrapping(self.0 % rhs) } } -impl Rem>> for &Wrapping> { - type Output = Wrapping>; +impl Rem>> + for &Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: NonZero>) -> Self::Output { + fn rem(self, rhs: NonZero>) -> Self::Output { *self % rhs } } -impl Rem<&NonZero>> for &Wrapping> { - type Output = Wrapping>; +impl Rem<&NonZero>> + for &Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { *self % *rhs } } -impl Rem<&NonZero>> for Wrapping> { - type Output = Wrapping>; +impl Rem<&NonZero>> + for Wrapping> +{ + type Output = Wrapping>; - fn rem(self, rhs: &NonZero>) -> Self::Output { + fn rem(self, rhs: &NonZero>) -> Self::Output { self % *rhs } } From 6b004a44fa2572904725ab85edbea6ba88489d57 Mon Sep 17 00:00:00 2001 From: Erik Takke Date: Mon, 31 Mar 2025 12:56:52 +0200 Subject: [PATCH 7/7] Update `Uint::div` benchmarks --- benches/uint.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/benches/uint.rs b/benches/uint.rs index 7bbc9e17..aaa1c723 100644 --- a/benches/uint.rs +++ b/benches/uint.rs @@ -219,7 +219,19 @@ fn bench_division(c: &mut Criterion) { let mut rng = make_rng(); let mut group = c.benchmark_group("wrapping ops"); - group.bench_function("div/rem, U256/U128, full size", |b| { + group.bench_function("div/rem, U256/U128", |b| { + b.iter_batched( + || { + let x = U256::random(&mut rng); + let y = U128::random(&mut rng); + (x, NonZero::new(y).unwrap()) + }, + |(x, y)| black_box(x.div_rem(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("div/rem, U256/U128 (in U256)", |b| { b.iter_batched( || { let x = U256::random(&mut rng); @@ -232,6 +244,18 @@ fn bench_division(c: &mut Criterion) { ) }); + group.bench_function("div/rem, U256/U128 (in U512)", |b| { + b.iter_batched( + || { + let x = U256::random(&mut rng); + let y: U512 = U128::random(&mut rng).resize(); + (x, NonZero::new(y).unwrap()) + }, + |(x, y)| black_box(x.div_rem(&y)), + BatchSize::SmallInput, + ) + }); + group.bench_function("div/rem_vartime, U256/U128, full size", |b| { b.iter_batched( || { @@ -244,12 +268,35 @@ fn bench_division(c: &mut Criterion) { ) }); - group.bench_function("rem, U256/U128, full size", |b| { + group.bench_function("rem, U256/U128", |b| { b.iter_batched( || { let x = U256::random(&mut rng); - let y_half = U128::random(&mut rng); - let y: U256 = (y_half, U128::ZERO).into(); + let y = U128::random(&mut rng); + (x, NonZero::new(y).unwrap()) + }, + |(x, y)| black_box(x.rem(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("rem, U256/U128 (in U256)", |b| { + b.iter_batched( + || { + let x = U256::random(&mut rng); + let y: U256 = U128::random(&mut rng).resize(); + (x, NonZero::new(y).unwrap()) + }, + |(x, y)| black_box(x.rem(&y)), + BatchSize::SmallInput, + ) + }); + + group.bench_function("rem, U256/U128 (in U512)", |b| { + b.iter_batched( + || { + let x = U256::random(&mut rng); + let y: U512 = U128::random(&mut rng).resize(); (x, NonZero::new(y).unwrap()) }, |(x, y)| black_box(x.rem(&y)),