Skip to content

Commit

Permalink
Add truncate_* operations on BigInt and BigUint
Browse files Browse the repository at this point in the history
  • Loading branch information
digama0 committed Apr 11, 2021
1 parent 125fbbd commit eaeedfe
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod bits;
mod convert;
mod power;
mod shift;
mod truncate;

#[cfg(any(feature = "quickcheck", feature = "arbitrary"))]
mod arbitrary;
Expand Down
31 changes: 31 additions & 0 deletions src/bigint/truncate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use super::{BigInt, Sign};

macro_rules! impl_truncate_bigint {
($T:ty, $truncate_N:ident) => {
/// Returns the input reduced modulo 2 to the power of the size of the target type.
/// This is analogous to the behavior of `as` conversion on integral types.
#[inline]
pub fn $truncate_N(&self) -> $T {
match self.sign {
Sign::Minus => self.data.$truncate_N().wrapping_neg(),
Sign::NoSign => 0,
Sign::Plus => self.data.$truncate_N(),
}
}
};
}

impl BigInt {
impl_truncate_bigint!(u128, truncate_u128);
impl_truncate_bigint!(usize, truncate_usize);
impl_truncate_bigint!(u64, truncate_u64);
impl_truncate_bigint!(u32, truncate_u32);
impl_truncate_bigint!(u16, truncate_u16);
impl_truncate_bigint!(u8, truncate_u8);
impl_truncate_bigint!(i128, truncate_i128);
impl_truncate_bigint!(isize, truncate_isize);
impl_truncate_bigint!(i64, truncate_i64);
impl_truncate_bigint!(i32, truncate_i32);
impl_truncate_bigint!(i16, truncate_i16);
impl_truncate_bigint!(i8, truncate_i8);
}
1 change: 1 addition & 0 deletions src/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod iter;
mod monty;
mod power;
mod shift;
mod truncate;

#[cfg(any(feature = "quickcheck", feature = "arbitrary"))]
mod arbitrary;
Expand Down
70 changes: 70 additions & 0 deletions src/biguint/truncate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use super::{big_digit, BigUint};

macro_rules! impl_truncate_large {
($T:ty, $truncate_N:ident, $size:expr) => {
/// Returns the input reduced modulo 2 to the power of the size of the target type.
/// This is analogous to the behavior of `as` conversion on integral types.
#[inline]
pub fn $truncate_N(&self) -> $T {
let mut ret = 0;
let mut bits = 0;

for i in self.data.iter() {
if bits >= $size {
break;
}

ret += <$T>::from(*i) << bits;
bits += big_digit::BITS;
}
ret
}
};
}

macro_rules! impl_truncate_from {
($truncate_from:ident: $($T:ty, $truncate_to:ident);* $(;)?) => {
$(
#[inline]
pub fn $truncate_to(&self) -> $T {
self.$truncate_from() as $T
}
)*
};
}

impl BigUint {
#[cfg(not(u64_digit))]
impl_truncate_large!(u32, truncate_u32, 32);
impl_truncate_large!(u64, truncate_u64, 64);
impl_truncate_large!(u128, truncate_u128, 128);

#[cfg(not(u64_digit))]
impl_truncate_from! {
truncate_u32:
u16, truncate_u16;
u8, truncate_u8;
}

#[cfg(u64_digit)]
impl_truncate_from! {
truncate_u64:
u32, truncate_u32;
u16, truncate_u16;
u8, truncate_u8;
}

#[cfg(target_pointer_width = "64")]
impl_truncate_from!(truncate_u64: usize, truncate_usize);
#[cfg(target_pointer_width = "32")]
impl_truncate_from!(truncate_u32: usize, truncate_usize);
#[cfg(target_pointer_width = "16")]
impl_truncate_from!(truncate_u16: usize, truncate_usize);

impl_truncate_from!(truncate_u128: i128, truncate_i128);
impl_truncate_from!(truncate_usize: isize, truncate_isize);
impl_truncate_from!(truncate_u64: i64, truncate_i64);
impl_truncate_from!(truncate_u32: i32, truncate_i32);
impl_truncate_from!(truncate_u16: i16, truncate_i16);
impl_truncate_from!(truncate_u8: i8, truncate_i8);
}
39 changes: 39 additions & 0 deletions tests/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1404,3 +1404,42 @@ fn test_set_bit() {
x.set_bit(0, false);
assert_eq!(x, BigInt::from_biguint(Minus, BigUint::one() << 200));
}

#[test]
fn test_truncate() {
let x = BigInt::from(0xfedcba9012345678_u64);
assert_eq!(x.truncate_u8(), 0x78);
assert_eq!(x.truncate_u16(), 0x5678);
assert_eq!(x.truncate_u32(), 0x12345678);
assert_eq!(x.truncate_u64(), 0xfedcba9012345678);
assert_eq!(x.truncate_u128(), 0xfedcba9012345678);
assert_eq!(x.truncate_i8(), 0x78);
assert_eq!(x.truncate_i16(), 0x5678);
assert_eq!(x.truncate_i32(), 0x12345678);
assert_eq!(x.truncate_i64(), -0x123456fedcba988);
assert_eq!(x.truncate_i128(), 0xfedcba9012345678);

let x = BigInt::zero();
assert_eq!(x.truncate_u8(), 0);
assert_eq!(x.truncate_u16(), 0);
assert_eq!(x.truncate_u32(), 0);
assert_eq!(x.truncate_u64(), 0);
assert_eq!(x.truncate_u128(), 0);
assert_eq!(x.truncate_i8(), 0);
assert_eq!(x.truncate_i16(), 0);
assert_eq!(x.truncate_i32(), 0);
assert_eq!(x.truncate_i64(), 0);
assert_eq!(x.truncate_i128(), 0);

let x = -BigInt::from(500);
assert_eq!(x.truncate_u8(), 0x0c);
assert_eq!(x.truncate_u16(), 0xfe0c);
assert_eq!(x.truncate_u32(), 0xfffffe0c);
assert_eq!(x.truncate_u64(), 0xfffffffffffffe0c);
assert_eq!(x.truncate_u128(), 0xfffffffffffffffffffffffffffffe0c);
assert_eq!(x.truncate_i8(), 0x0c);
assert_eq!(x.truncate_i16(), -500);
assert_eq!(x.truncate_i32(), -500);
assert_eq!(x.truncate_i64(), -500);
assert_eq!(x.truncate_i128(), -500);
}
27 changes: 27 additions & 0 deletions tests/biguint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1838,3 +1838,30 @@ fn test_set_bit() {
x.set_bit(1, false);
assert_eq!(x, BigUint::zero());
}

#[test]
fn test_truncate() {
let x = BigUint::from(0xfedcba9012345678_u64);
assert_eq!(x.truncate_u8(), 0x78);
assert_eq!(x.truncate_u16(), 0x5678);
assert_eq!(x.truncate_u32(), 0x12345678);
assert_eq!(x.truncate_u64(), 0xfedcba9012345678);
assert_eq!(x.truncate_u128(), 0xfedcba9012345678);
assert_eq!(x.truncate_i8(), 0x78);
assert_eq!(x.truncate_i16(), 0x5678);
assert_eq!(x.truncate_i32(), 0x12345678);
assert_eq!(x.truncate_i64(), -0x123456fedcba988);
assert_eq!(x.truncate_i128(), 0xfedcba9012345678);

let x = BigUint::new(vec![0xffffffff, 0xfedcba90, 1, 1]);
assert_eq!(x.truncate_u8(), 0xff);
assert_eq!(x.truncate_u16(), 0xffff);
assert_eq!(x.truncate_u32(), 0xffffffff);
assert_eq!(x.truncate_u64(), 0xfedcba90ffffffff);
assert_eq!(x.truncate_u128(), 0x100000001fedcba90ffffffff);
assert_eq!(x.truncate_i8(), -1);
assert_eq!(x.truncate_i16(), -1);
assert_eq!(x.truncate_i32(), -1);
assert_eq!(x.truncate_i64(), -0x0123456f00000001);
assert_eq!(x.truncate_i128(), 0x100000001fedcba90ffffffff);
}

0 comments on commit eaeedfe

Please sign in to comment.