diff --git a/src/bigint.rs b/src/bigint.rs index 891eeb46..b18cbd5e 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -29,6 +29,7 @@ mod bits; mod convert; mod power; mod shift; +mod truncate; #[cfg(any(feature = "quickcheck", feature = "arbitrary"))] mod arbitrary; diff --git a/src/bigint/truncate.rs b/src/bigint/truncate.rs new file mode 100644 index 00000000..5b427262 --- /dev/null +++ b/src/bigint/truncate.rs @@ -0,0 +1,17 @@ +use num_traits::{WrappingNeg, Zero}; + +use super::{BigUint, BigInt, Sign}; +use crate::biguint::TruncateFrom; + +impl BigInt { + /// 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 + Zero + WrappingNeg>(&self) -> T { + match self.sign { + Sign::Minus => self.data.truncate::().wrapping_neg(), + Sign::NoSign => T::zero(), + Sign::Plus => self.data.truncate(), + } + } +} diff --git a/src/biguint.rs b/src/biguint.rs index 271a8837..b0a46faf 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -24,6 +24,7 @@ mod iter; mod monty; mod power; mod shift; +mod truncate; #[cfg(any(feature = "quickcheck", feature = "arbitrary"))] mod arbitrary; @@ -33,6 +34,7 @@ mod serde; pub(crate) use self::convert::to_str_radix_reversed; pub use self::iter::{U32Digits, U64Digits}; +pub use truncate::TruncateFrom; /// A big unsigned integer type. pub struct BigUint { diff --git a/src/biguint/truncate.rs b/src/biguint/truncate.rs new file mode 100644 index 00000000..98811f64 --- /dev/null +++ b/src/biguint/truncate.rs @@ -0,0 +1,76 @@ +use super::{big_digit, BigUint}; + +pub trait TruncateFrom { + /// 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. + fn truncate_from(_: &S) -> Self; +} + +impl BigUint { + /// 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>(&self) -> T { + TruncateFrom::truncate_from(self) + } +} + +macro_rules! impl_truncate_large { + ($T:ty, $size:expr) => { + impl TruncateFrom for $T { + #[inline] + fn truncate_from(n: &BigUint) -> Self { + let mut ret = 0; + let mut bits = 0; + + for i in n.data.iter() { + if bits >= $size { + break; + } + + ret += Self::from(*i) << bits; + bits += big_digit::BITS; + } + ret + } + } + }; +} + +macro_rules! impl_truncate_from { + ($from:ty: $($T:ty),* $(,)*) => { + $( + impl TruncateFrom for $T { + #[inline] + fn truncate_from(n: &BigUint) -> Self { + n.truncate::<$from>() as Self + } + } + )* + }; +} + +#[cfg(not(u64_digit))] +impl_truncate_large!(u32, 32); +impl_truncate_large!(u64, 64); +impl_truncate_large!(u128, 128); + +#[cfg(not(u64_digit))] +impl_truncate_from! { u32: u16, u8 } + +#[cfg(u64_digit)] +impl_truncate_from! { u64: u32, u16, u8 } + +#[cfg(target_pointer_width = "64")] +impl_truncate_from!(u64: usize); +#[cfg(target_pointer_width = "32")] +impl_truncate_from!(u32: usize); +#[cfg(target_pointer_width = "16")] +impl_truncate_from!(u16: usize); + +impl_truncate_from!(u128: i128); +impl_truncate_from!(usize: isize); +impl_truncate_from!(u64: i64); +impl_truncate_from!(u32: i32); +impl_truncate_from!(u16: i16); +impl_truncate_from!(u8: i8); diff --git a/tests/bigint.rs b/tests/bigint.rs index f244bc4b..c7adace5 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -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); +} diff --git a/tests/biguint.rs b/tests/biguint.rs index 716bcc81..43601cd6 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -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); +}