From eaeedfedb7fce46ef9c40bd64398437f64005d47 Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Sun, 11 Apr 2021 05:39:31 -0400 Subject: [PATCH 1/3] Add `truncate_*` operations on `BigInt` and `BigUint` Fixes https://github.com/rust-num/num/issues/404 --- src/bigint.rs | 1 + src/bigint/truncate.rs | 31 ++++++++++++++++++ src/biguint.rs | 1 + src/biguint/truncate.rs | 70 +++++++++++++++++++++++++++++++++++++++++ tests/bigint.rs | 39 +++++++++++++++++++++++ tests/biguint.rs | 27 ++++++++++++++++ 6 files changed, 169 insertions(+) create mode 100644 src/bigint/truncate.rs create mode 100644 src/biguint/truncate.rs 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..19b97b19 --- /dev/null +++ b/src/bigint/truncate.rs @@ -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); +} diff --git a/src/biguint.rs b/src/biguint.rs index 271a8837..46091873 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; diff --git a/src/biguint/truncate.rs b/src/biguint/truncate.rs new file mode 100644 index 00000000..7519e47e --- /dev/null +++ b/src/biguint/truncate.rs @@ -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); +} 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); +} From b3be7099e2c37f21065446004a81dd4ff95ec9a6 Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Sun, 11 Apr 2021 05:42:51 -0400 Subject: [PATCH 2/3] fix --- src/biguint/truncate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biguint/truncate.rs b/src/biguint/truncate.rs index 7519e47e..f5a7b4ca 100644 --- a/src/biguint/truncate.rs +++ b/src/biguint/truncate.rs @@ -23,7 +23,7 @@ macro_rules! impl_truncate_large { } macro_rules! impl_truncate_from { - ($truncate_from:ident: $($T:ty, $truncate_to:ident);* $(;)?) => { + ($truncate_from:ident: $($T:ty, $truncate_to:ident);* $(;)*) => { $( #[inline] pub fn $truncate_to(&self) -> $T { From 69bdb4e4613a23d910a8bd62bb6b1ffa85628332 Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Wed, 26 May 2021 02:31:40 -0400 Subject: [PATCH 3/3] (WIP) trait version --- src/bigint/truncate.rs | 40 +++++----------- src/biguint.rs | 1 + src/biguint/truncate.rs | 104 +++++++++++++++++++++------------------- 3 files changed, 69 insertions(+), 76 deletions(-) diff --git a/src/bigint/truncate.rs b/src/bigint/truncate.rs index 19b97b19..5b427262 100644 --- a/src/bigint/truncate.rs +++ b/src/bigint/truncate.rs @@ -1,31 +1,17 @@ -use super::{BigInt, Sign}; +use num_traits::{WrappingNeg, Zero}; -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(), - } - } - }; -} +use super::{BigUint, BigInt, Sign}; +use crate::biguint::TruncateFrom; 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); + /// 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 46091873..b0a46faf 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -34,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 index f5a7b4ca..98811f64 100644 --- a/src/biguint/truncate.rs +++ b/src/biguint/truncate.rs @@ -1,70 +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, $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; + ($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 self.data.iter() { - if bits >= $size { - break; - } + for i in n.data.iter() { + if bits >= $size { + break; + } - ret += <$T>::from(*i) << bits; - bits += big_digit::BITS; + ret += Self::from(*i) << bits; + bits += big_digit::BITS; + } + ret } - ret } }; } macro_rules! impl_truncate_from { - ($truncate_from:ident: $($T:ty, $truncate_to:ident);* $(;)*) => { + ($from:ty: $($T:ty),* $(,)*) => { $( - #[inline] - pub fn $truncate_to(&self) -> $T { - self.$truncate_from() as $T + impl TruncateFrom for $T { + #[inline] + fn truncate_from(n: &BigUint) -> Self { + n.truncate::<$from>() as Self + } } )* }; } -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_large!(u32, 32); +impl_truncate_large!(u64, 64); +impl_truncate_large!(u128, 128); - #[cfg(not(u64_digit))] - impl_truncate_from! { - truncate_u32: - u16, truncate_u16; - u8, truncate_u8; - } +#[cfg(not(u64_digit))] +impl_truncate_from! { u32: u16, u8 } - #[cfg(u64_digit)] - impl_truncate_from! { - truncate_u64: - u32, truncate_u32; - u16, truncate_u16; - u8, truncate_u8; - } +#[cfg(u64_digit)] +impl_truncate_from! { u64: u32, u16, 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); +#[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!(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); -} +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);