Skip to content

Commit 0761be3

Browse files
authored
Add a RemMixed trait and impl for Uints (#746)
1 parent 7fbf363 commit 0761be3

File tree

6 files changed

+64
-5
lines changed

6 files changed

+64
-5
lines changed

src/traits.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,12 @@ pub trait DivRemLimb: Sized {
626626
fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb);
627627
}
628628

629+
/// Support for calculating the remainder of two differently sized integers.
630+
pub trait RemMixed<Reductor>: Sized {
631+
/// Calculate the remainder of `self` by the `reductor`.
632+
fn rem_mixed(&self, reductor: &NonZero<Reductor>) -> Reductor;
633+
}
634+
629635
/// Support for optimized division by a single limb.
630636
pub trait RemLimb: Sized {
631637
/// Computes `self % rhs` using a pre-made reciprocal.

src/uint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ impl_uint_concat_split_even! {
439439
U16384,
440440
}
441441

442-
// Implement mixed concat and split for combinations not implemented by
442+
// Implement mixed concat, split and reduce for combinations not implemented by
443443
// impl_uint_concat_split_even. The numbers represent the size of each
444444
// component Uint in multiple of 64 bits. For example,
445445
// (U256, [1, 3]) will allow splitting U256 into (U64, U192) as well as

src/uint/boxed/div.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
div_limb::{div2by1, div3by2},
77
},
88
BoxedUint, CheckedDiv, ConstChoice, ConstantTimeSelect, DivRemLimb, Limb, NonZero, Reciprocal,
9-
RemLimb, Wrapping,
9+
RemLimb, RemMixed, Wrapping,
1010
};
1111
use core::ops::{Div, DivAssign, Rem, RemAssign};
1212
use subtle::CtOption;
@@ -391,6 +391,12 @@ impl RemLimb for BoxedUint {
391391
}
392392
}
393393

394+
impl RemMixed<BoxedUint> for BoxedUint {
395+
fn rem_mixed(&self, reductor: &NonZero<BoxedUint>) -> BoxedUint {
396+
Self::div_rem_vartime(self, reductor).1
397+
}
398+
}
399+
394400
/// Computes `limbs << shift` inplace, where `0 <= shift < Limb::BITS`, returning the carry.
395401
fn shl_limb_vartime(limbs: &mut [Limb], shift: u32) -> Limb {
396402
if shift == 0 {

src/uint/div.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ impl<const LIMBS: usize> RemLimb for Uint<LIMBS> {
953953

954954
#[cfg(test)]
955955
mod tests {
956-
use crate::{Limb, NonZero, Uint, Word, U128, U256, U64};
956+
use crate::{Limb, NonZero, RemMixed, Uint, Word, Zero, U1024, U128, U256, U64, U896};
957957

958958
#[cfg(feature = "rand")]
959959
use {
@@ -1145,4 +1145,44 @@ mod tests {
11451145
assert_eq!(&a % b, c);
11461146
assert_eq!(&a % &b, c);
11471147
}
1148+
1149+
#[test]
1150+
fn rem_mixed() {
1151+
let x = U1024::from_be_hex("3740C11DB8F260753BC6B97DD2B8746D3E2694412772AC6ABD975119EE0A6190F27F6F0969BCA069D8D151031AF83EE2283CC2E3E4FADBBDB9EEDBF0B8F4C1FD51912C0D329FDC37D49176DB0A1A2D17E5E6D4F9F6B217FE9412EAA2F881F7027A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A");
1152+
let y = U128::from_u64(1234567890987654321);
1153+
let rem = x.rem_mixed(&y.to_nz().unwrap());
1154+
1155+
let y2: U1024 = U128::concat_mixed(&y, &U896::ZERO);
1156+
let rem_control = x.rem(&NonZero::new(y2).unwrap());
1157+
1158+
assert_eq!(rem.bits(), rem_control.bits());
1159+
assert_eq!(rem.as_words(), &rem_control.as_words()[0..U128::LIMBS]);
1160+
assert!(rem_control.as_words()[U128::LIMBS..]
1161+
.iter()
1162+
.all(|w| *w == 0));
1163+
}
1164+
1165+
#[test]
1166+
fn rem_mixed_through_traits() {
1167+
struct A<T, U> {
1168+
t: T,
1169+
u: U,
1170+
}
1171+
impl<T, U> A<T, U>
1172+
where
1173+
T: RemMixed<U>,
1174+
U: Clone + Zero,
1175+
{
1176+
fn reduce_t_by_u(&self) -> U {
1177+
let rhs = &NonZero::new(self.u.clone()).unwrap();
1178+
self.t.rem_mixed(rhs)
1179+
}
1180+
}
1181+
1182+
let a = A {
1183+
t: U1024::from(1234567890u64),
1184+
u: U128::from(456u64),
1185+
};
1186+
assert_eq!(a.reduce_t_by_u(), U128::from(330u64));
1187+
}
11481188
}

src/uint/macros.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ macro_rules! impl_uint_concat_split_mixed {
9797
self.split_mixed()
9898
}
9999
}
100+
101+
impl $crate::traits::RemMixed<Uint<{ U64::LIMBS * $size }>> for $name
102+
{
103+
fn rem_mixed(&self, reductor: &NonZero<Uint<{ U64::LIMBS * $size }>>) -> Uint<{ U64::LIMBS * $size }> {
104+
self.div_rem_vartime(reductor).1
105+
}
106+
}
100107
};
101108
($name:ident, [ $($size:literal),+ ]) => {
102109
$(

src/uint/mul.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ pub(crate) const fn schoolbook_squaring(limbs: &[Limb], lo: &mut [Limb], hi: &mu
9494
let mut carry = Limb::ZERO;
9595
let mut i = 0;
9696
while i < limbs.len() {
97-
(lo[i].0, carry) = (lo[i].0 << 1 | carry.0, lo[i].shr(Limb::BITS - 1));
97+
(lo[i].0, carry) = ((lo[i].0 << 1) | carry.0, lo[i].shr(Limb::BITS - 1));
9898
i += 1;
9999
}
100100

101101
let mut i = 0;
102102
while i < limbs.len() - 1 {
103-
(hi[i].0, carry) = (hi[i].0 << 1 | carry.0, hi[i].shr(Limb::BITS - 1));
103+
(hi[i].0, carry) = ((hi[i].0 << 1) | carry.0, hi[i].shr(Limb::BITS - 1));
104104
i += 1;
105105
}
106106
hi[limbs.len() - 1] = carry;

0 commit comments

Comments
 (0)