From 3510274fce84de98c23d671488718773b8fd9ff1 Mon Sep 17 00:00:00 2001 From: EvilMuffinHa Date: Fri, 5 Aug 2022 07:45:20 -0400 Subject: [PATCH] mul --- src/bigint.rs | 83 +++++++++ src/convert.rs | 155 ++++++++++++++++ src/error.rs | 15 ++ src/lib.rs | 483 +----------------------------------------------- src/ops.rs | 243 ++++++++++++++++++++++++ src/pretty.rs | 22 +++ src/smallint.rs | 43 +++++ src/tests.rs | 63 +++++++ 8 files changed, 634 insertions(+), 473 deletions(-) create mode 100644 src/bigint.rs create mode 100644 src/convert.rs create mode 100644 src/error.rs create mode 100644 src/ops.rs create mode 100644 src/pretty.rs create mode 100644 src/smallint.rs create mode 100644 src/tests.rs diff --git a/src/bigint.rs b/src/bigint.rs new file mode 100644 index 0000000..7725d46 --- /dev/null +++ b/src/bigint.rs @@ -0,0 +1,83 @@ +#![cfg(feature = "num-bigint")] + +use num_bigint::BigInt; +use num_bigint::BigUint; +use num_bigint::Sign; + +use core::mem::ManuallyDrop; + +use crate::SmallInt; +use crate::SmallUint; + +use crate::smallint::SmallIntType; +use crate::smallint::SmallUintType; + +impl From<&BigInt> for SmallInt { + fn from(b: &BigInt) -> Self { + match b.try_into() { + Ok(i) => Self(SmallIntType::Inline(i)), + Err(_) => { + let (sign, vec) = b.to_u32_digits(); + let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); + let size = match sign { + Sign::Minus => -isize::try_from(slice.len()).unwrap(), + Sign::NoSign => panic!( + "Shouldn't happen; BigInts which store zero should convert to inline." + ), + Sign::Plus => isize::try_from(slice.len()).unwrap(), + }; + Self(SmallIntType::Heap((slice.as_mut_ptr(), size))) + } + } + } +} + +impl From<&SmallInt> for BigInt { + fn from(s: &SmallInt) -> Self { + match s.0 { + SmallIntType::Inline(i) => Self::from(i), + SmallIntType::Heap((r, s)) => { + let size = usize::try_from(s.abs()).unwrap(); + let sign = s.signum(); + let slice = unsafe { core::slice::from_raw_parts(r, size) }; + let bs = match sign { + 0 => Sign::NoSign, + -1 => Sign::Minus, + 1 => Sign::Plus, + _ => panic!("This shouldn't happen; There are only 3 signums."), + }; + + BigInt::new(bs, slice.to_vec()) + } + } + } +} + +impl From<&BigUint> for SmallUint { + fn from(b: &BigUint) -> Self { + match b.try_into() { + Ok(i) => Self(SmallUintType::Inline(i)), + Err(_) => { + let vec = b.to_u32_digits(); + let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); + let size = slice.len(); + Self(SmallUintType::Heap((slice.as_mut_ptr(), size))) + } + } + } +} + +impl From<&SmallUint> for BigUint { + fn from(s: &SmallUint) -> Self { + match s.0 { + SmallUintType::Inline(i) => Self::from(i), + SmallUintType::Heap((r, s)) => { + let slice = unsafe { core::slice::from_raw_parts(r, s) }; + + BigUint::new(slice.to_vec()) + } + } + } +} + + diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 0000000..f64b7dc --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,155 @@ +use crate::SmallInt; +use crate::SmallUint; +use crate::SmallIntError; +use crate::smallint::{SmallIntType, SmallUintType}; +use core::mem::ManuallyDrop; + +macro_rules! int_impl { + ($itype:ty, $rt:tt, $rtt:tt, $n:tt) => { + impl From<$itype> for $rt { + fn from(a: $itype) -> Self { + Self(<$rtt>::Inline($n::from(a))) + } + } + + impl TryFrom<$rt> for $itype { + type Error = SmallIntError; + + fn try_from(s: $rt) -> Result { + match s.0 { + $rtt::Inline(i) => { + <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError) + } + $rtt::Heap((_, _)) => Err(SmallIntError::ConversionError), + } + } + } + }; +} + +int_impl!(u8, SmallInt, SmallIntType, i128); +int_impl!(u16, SmallInt, SmallIntType, i128); +int_impl!(u32, SmallInt, SmallIntType, i128); +int_impl!(u64, SmallInt, SmallIntType, i128); +int_impl!(i8, SmallInt, SmallIntType, i128); +int_impl!(i16, SmallInt, SmallIntType, i128); +int_impl!(i32, SmallInt, SmallIntType, i128); +int_impl!(i64, SmallInt, SmallIntType, i128); +int_impl!(i128, SmallInt, SmallIntType, i128); + +int_impl!(u8, SmallUint, SmallUintType, u128); +int_impl!(u16, SmallUint, SmallUintType, u128); +int_impl!(u32, SmallUint, SmallUintType, u128); +int_impl!(u64, SmallUint, SmallUintType, u128); +int_impl!(u128, SmallUint, SmallUintType, u128); + +macro_rules! try_from_itou { + ($itype:ty) => { + impl TryFrom<$itype> for SmallUint { + type Error = SmallIntError; + + fn try_from(a: $itype) -> Result { + Ok(Self(SmallUintType::Inline( + u128::try_from(a).map_err(|_| SmallIntError::ConversionError)?, + ))) + } + } + + impl TryFrom for $itype { + type Error = SmallIntError; + + fn try_from(s: SmallUint) -> Result { + match s.0 { + SmallUintType::Inline(i) => { + <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError) + } + SmallUintType::Heap((_, _)) => Err(SmallIntError::ConversionError), + } + } + } + }; +} + +try_from_itou!(i8); +try_from_itou!(i16); +try_from_itou!(i32); +try_from_itou!(i64); +try_from_itou!(i128); + +impl From for SmallInt { + fn from(a: u128) -> Self { + match i128::try_from(a) { + Ok(i) => Self(SmallIntType::Inline(i)), + Err(_) => { + let mut v = a; + let mut vec = Vec::with_capacity(4); + while v != 0 { + vec.push(v as u32); + + v >>= 32; + } + let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); + Self(SmallIntType::Heap(( + slice.as_mut_ptr(), + isize::try_from(slice.len()).unwrap(), + ))) + } + } + } +} + +impl TryFrom for u128 { + type Error = SmallIntError; + + fn try_from(s: SmallInt) -> Result { + match s.0 { + SmallIntType::Inline(i) => { + u128::try_from(i).map_err(|_| SmallIntError::ConversionError) + } + SmallIntType::Heap((r, s)) => { + let mut ret: u128 = 0; + let mut bits = 0; + let size = usize::try_from(s.abs()).unwrap(); + let slice = unsafe { core::slice::from_raw_parts(r, size) }; + for i in slice { + if bits >= 128 { + return Err(SmallIntError::ConversionError); + } + ret |= u128::from(*i) << bits; + bits += 32; + } + + Ok(ret) + } + } + } +} + +impl From for SmallInt { + fn from(s: SmallUint) -> Self { + match s.0 { + SmallUintType::Inline(i) => SmallInt::from(i), + SmallUintType::Heap((r, s)) => { + SmallInt(SmallIntType::Heap((r, isize::try_from(s).unwrap()))) + } + } + } +} + +impl TryFrom for SmallUint { + type Error = SmallIntError; + + fn try_from(value: SmallInt) -> Result { + match value.0 { + SmallIntType::Inline(i) => Self::try_from(i), + SmallIntType::Heap((r, s)) => { + let size = usize::try_from(s).map_err(|_| SmallIntError::ConversionError)?; + if size > 4 { + Ok(Self(SmallUintType::Heap((r, size)))) + } else { + Ok(Self(SmallUintType::Inline(u128::try_from(value)?))) + } + } + } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..f6d48d4 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,15 @@ +/// An error that occurred when processing a `SmallInt`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SmallIntError { + /// Conversion error when converting from SmallInt to other integer types. + ConversionError, +} + +impl std::error::Error for SmallIntError {} + +impl core::fmt::Display for SmallIntError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let Self::ConversionError = self; + write!(f, "Error converting integer to SmallInt.") + } +} diff --git a/src/lib.rs b/src/lib.rs index 4c7289a..56b9710 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,483 +9,20 @@ // Invariant: If a small integer is on the heap, the size is the minimum digits required to // represent it. -#[cfg(feature = "num-bigint")] -use num_bigint::BigInt; +mod smallint; -#[cfg(feature = "num-bigint")] -use num_bigint::BigUint; +pub use crate::smallint::SmallInt; +pub use crate::smallint::SmallUint; -#[cfg(feature = "num-bigint")] -use num_bigint::Sign; +mod error; -use core::mem::ManuallyDrop; -use core::ops::Add; +pub use error::SmallIntError; -/// An error that occurred when processing a `SmallInt`. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SmallIntError { - /// Conversion error when converting from SmallInt to other integer types. - ConversionError, -} +mod convert; +mod ops; -impl std::error::Error for SmallIntError {} +mod bigint; -impl core::fmt::Display for SmallIntError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let Self::ConversionError = self; - write!(f, "Error converting integer to SmallInt.") - } -} +mod pretty; -/// An integer-like type that will store small integers up to `i128` inline. Larger integers are -/// represented as a slice to a sequence of base 232 digits represented as a `*mut u32`. -#[derive(Clone, PartialEq, Eq)] -pub struct SmallInt(SmallIntType); - -/// An integer-like type that will store small integers up to `u128` inline. Larger integers are -/// represented as a slice to a sequence of base 232 digits represented as a `*mut u32`. -#[derive(Clone, PartialEq, Eq)] -pub struct SmallUint(SmallUintType); - -#[derive(Clone, PartialEq, Eq)] -enum SmallIntType { - Inline(i128), - Heap((*mut u32, isize)), -} - -#[derive(Clone, PartialEq, Eq)] -enum SmallUintType { - Inline(u128), - Heap((*mut u32, usize)), -} - -// TODO: Need to reimplement Debug, to_str_radix, and from_str_radix without num-bigint feature for -// SmallInt and SmallUint - -// TODO: add native operations for SmallInt and SmallUint - -#[cfg(feature = "num-bigint")] -impl core::fmt::Debug for SmallInt { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", BigInt::from(self)) - } -} - -#[cfg(feature = "num-bigint")] -impl core::fmt::Debug for SmallUint { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", BigUint::from(self)) - } -} - -fn add_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec { - - let s = slice1.len(); - let j = slice2.len(); - - let larger = std::cmp::max(s, j); - let mut res = Vec::with_capacity(larger + 1); - let mut carry = false; - - for t in 0..larger { - - let value1 = if t < s { - slice1[t] - } else { - 0 - }; - - let value2 = if t < j { - slice2[t] - } else { - 0 - }; - - let (val, overflow) = value1.overflowing_add(value2); - let (cval, coverflow) = val.overflowing_add(carry as u32); - res.push(cval); - carry = overflow | coverflow; - - } - - if carry { - res.push(1); - } - while res[res.len() - 1] == 0 { - res.pop(); - } - res -} - -impl Add for SmallUint { - type Output = SmallUint; - - fn add(self, rhs: SmallUint) -> Self::Output { - match (&self.0, &rhs.0) { - (&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => { - match i.overflowing_add(j) { - (t, false) => Self(SmallUintType::Inline(t)), - (t, true) => { - let mut res = [0, 0, 0, 0, 1]; - - let mut v = t; - #[allow(clippy::needless_range_loop)] - for r in 0..4 { - res[r] = v as u32; - - v >>= 32; - } - - let mut slice = ManuallyDrop::new(>::from(res)); - - Self(SmallUintType::Heap((slice.as_mut_ptr(), 5))) - - } - } - }, - (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) | (&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) => { - let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; - - let mut res = [0, 0, 0, 0]; - - let mut v = i; - #[allow(clippy::needless_range_loop)] - for r in 0..4 { - res[r] = v as u32; - - v >>= 32; - } - - let result = add_two_slices(slice1, &res[..]); - let size = result.len(); - - let mut slice = ManuallyDrop::new(result.into_boxed_slice()); - - Self(SmallUintType::Heap((slice.as_mut_ptr(), size))) - - - }, - (&SmallUintType::Heap((r, s)), &SmallUintType::Heap((i, j))) => { - - let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; - let slice2 = unsafe { core::slice::from_raw_parts(i, j) }; - - let res = add_two_slices(slice1, slice2); - let size = res.len(); - - let mut slice = ManuallyDrop::new(res.into_boxed_slice()); - - Self(SmallUintType::Heap((slice.as_mut_ptr(), size))) - - } - } - } -} - - -impl Drop for SmallInt { - fn drop(&mut self) { - if let Self(SmallIntType::Heap((r, s))) = self { - let size = usize::try_from(s.abs()).unwrap(); - let slice = unsafe { core::slice::from_raw_parts_mut(*r, size) }; - unsafe { std::mem::drop(Box::from_raw(slice)) } - } - } -} - -impl Drop for SmallUint { - fn drop(&mut self) { - if let Self(SmallUintType::Heap((r, s))) = self { - let slice = unsafe { core::slice::from_raw_parts_mut(*r, *s) }; - unsafe { std::mem::drop(Box::from_raw(slice)) } - } - } -} - -macro_rules! int_impl { - ($itype:ty, $rt:tt, $rtt:tt, $n:tt) => { - impl From<$itype> for $rt { - fn from(a: $itype) -> Self { - Self(<$rtt>::Inline($n::from(a))) - } - } - - impl TryFrom<$rt> for $itype { - type Error = SmallIntError; - - fn try_from(s: $rt) -> Result { - match s.0 { - $rtt::Inline(i) => { - <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError) - } - $rtt::Heap((_, _)) => Err(SmallIntError::ConversionError), - } - } - } - }; -} - -int_impl!(u8, SmallInt, SmallIntType, i128); -int_impl!(u16, SmallInt, SmallIntType, i128); -int_impl!(u32, SmallInt, SmallIntType, i128); -int_impl!(u64, SmallInt, SmallIntType, i128); -int_impl!(i8, SmallInt, SmallIntType, i128); -int_impl!(i16, SmallInt, SmallIntType, i128); -int_impl!(i32, SmallInt, SmallIntType, i128); -int_impl!(i64, SmallInt, SmallIntType, i128); -int_impl!(i128, SmallInt, SmallIntType, i128); - -int_impl!(u8, SmallUint, SmallUintType, u128); -int_impl!(u16, SmallUint, SmallUintType, u128); -int_impl!(u32, SmallUint, SmallUintType, u128); -int_impl!(u64, SmallUint, SmallUintType, u128); -int_impl!(u128, SmallUint, SmallUintType, u128); - -macro_rules! try_from_itou { - ($itype:ty) => { - impl TryFrom<$itype> for SmallUint { - type Error = SmallIntError; - - fn try_from(a: $itype) -> Result { - Ok(Self(SmallUintType::Inline( - u128::try_from(a).map_err(|_| SmallIntError::ConversionError)?, - ))) - } - } - - impl TryFrom for $itype { - type Error = SmallIntError; - - fn try_from(s: SmallUint) -> Result { - match s.0 { - SmallUintType::Inline(i) => { - <$itype>::try_from(i).map_err(|_| SmallIntError::ConversionError) - } - SmallUintType::Heap((_, _)) => Err(SmallIntError::ConversionError), - } - } - } - }; -} - -try_from_itou!(i8); -try_from_itou!(i16); -try_from_itou!(i32); -try_from_itou!(i64); -try_from_itou!(i128); - -impl From for SmallInt { - fn from(a: u128) -> Self { - match i128::try_from(a) { - Ok(i) => Self(SmallIntType::Inline(i)), - Err(_) => { - let mut v = a; - let mut vec = Vec::with_capacity(4); - while v != 0 { - vec.push(v as u32); - - v >>= 32; - } - let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); - Self(SmallIntType::Heap(( - slice.as_mut_ptr(), - isize::try_from(slice.len()).unwrap(), - ))) - } - } - } -} - -impl TryFrom for u128 { - type Error = SmallIntError; - - fn try_from(s: SmallInt) -> Result { - match s.0 { - SmallIntType::Inline(i) => { - u128::try_from(i).map_err(|_| SmallIntError::ConversionError) - } - SmallIntType::Heap((r, s)) => { - let mut ret: u128 = 0; - let mut bits = 0; - let size = usize::try_from(s.abs()).unwrap(); - let slice = unsafe { core::slice::from_raw_parts(r, size) }; - for i in slice { - if bits >= 128 { - return Err(SmallIntError::ConversionError); - } - ret |= u128::from(*i) << bits; - bits += 32; - } - - Ok(ret) - } - } - } -} - -impl From for SmallInt { - fn from(s: SmallUint) -> Self { - match s.0 { - SmallUintType::Inline(i) => SmallInt::from(i), - SmallUintType::Heap((r, s)) => { - SmallInt(SmallIntType::Heap((r, isize::try_from(s).unwrap()))) - } - } - } -} - -impl TryFrom for SmallUint { - type Error = SmallIntError; - - fn try_from(value: SmallInt) -> Result { - match value.0 { - SmallIntType::Inline(i) => Self::try_from(i), - SmallIntType::Heap((r, s)) => { - let size = usize::try_from(s).map_err(|_| SmallIntError::ConversionError)?; - if size > 4 { - Ok(Self(SmallUintType::Heap((r, size)))) - } else { - Ok(Self(SmallUintType::Inline(u128::try_from(value)?))) - } - } - } - } -} - -#[cfg(feature = "num-bigint")] -impl From<&BigInt> for SmallInt { - fn from(b: &BigInt) -> Self { - match b.try_into() { - Ok(i) => Self(SmallIntType::Inline(i)), - Err(_) => { - let (sign, vec) = b.to_u32_digits(); - let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); - let size = match sign { - Sign::Minus => -isize::try_from(slice.len()).unwrap(), - Sign::NoSign => panic!( - "Shouldn't happen; BigInts which store zero should convert to inline." - ), - Sign::Plus => isize::try_from(slice.len()).unwrap(), - }; - Self(SmallIntType::Heap((slice.as_mut_ptr(), size))) - } - } - } -} - -#[cfg(feature = "num-bigint")] -impl From<&SmallInt> for BigInt { - fn from(s: &SmallInt) -> Self { - match s.0 { - SmallIntType::Inline(i) => Self::from(i), - SmallIntType::Heap((r, s)) => { - let size = usize::try_from(s.abs()).unwrap(); - let sign = s.signum(); - let slice = unsafe { core::slice::from_raw_parts(r, size) }; - let bs = match sign { - 0 => Sign::NoSign, - -1 => Sign::Minus, - 1 => Sign::Plus, - _ => panic!("This shouldn't happen; There are only 3 signums."), - }; - - BigInt::new(bs, slice.to_vec()) - } - } - } -} - -#[cfg(feature = "num-bigint")] -impl From<&BigUint> for SmallUint { - fn from(b: &BigUint) -> Self { - match b.try_into() { - Ok(i) => Self(SmallUintType::Inline(i)), - Err(_) => { - let vec = b.to_u32_digits(); - let mut slice = ManuallyDrop::new(vec.into_boxed_slice()); - let size = slice.len(); - Self(SmallUintType::Heap((slice.as_mut_ptr(), size))) - } - } - } -} - -#[cfg(feature = "num-bigint")] -impl From<&SmallUint> for BigUint { - fn from(s: &SmallUint) -> Self { - match s.0 { - SmallUintType::Inline(i) => Self::from(i), - SmallUintType::Heap((r, s)) => { - let slice = unsafe { core::slice::from_raw_parts(r, s) }; - - BigUint::new(slice.to_vec()) - } - } - } -} - -#[cfg(test)] -mod conversion_tests { - - use crate::SmallInt; - use crate::SmallUint; - - #[cfg(feature = "num-bigint")] - use num_bigint::{BigInt, Sign, BigUint}; - - macro_rules! conversion_tests { - ($t:ty, $i:ident) => { - #[test] - fn $i() { - let i = <$t>::MAX; - let s = SmallInt::from(i); - assert_eq!(<$t>::try_from(s).unwrap(), i); - - let i = <$t>::MIN; - let s = SmallInt::from(i); - assert_eq!(<$t>::try_from(s).unwrap(), i); - } - }; - } - - conversion_tests!(u8, test_u8); - conversion_tests!(i8, test_i8); - conversion_tests!(u16, test_u16); - conversion_tests!(i16, test_i16); - conversion_tests!(u32, test_u32); - conversion_tests!(i32, test_i32); - conversion_tests!(u64, test_u64); - conversion_tests!(i64, test_i64); - conversion_tests!(u128, test_u128); - conversion_tests!(i128, test_i128); - - #[test] - #[cfg(feature = "num-bigint")] - fn test_op_add_u_u() { - let i = SmallUint::from(u128::MAX); - let k = SmallUint::from(u128::MAX); - let q = i + k; - assert_eq!(BigUint::from(&q), BigUint::from(u128::MAX) + u128::MAX); - - let i = SmallUint::from(u128::MAX); - let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); - let q = i + k; - assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) + u128::MAX); - - let i = SmallUint::from(&BigUint::new(vec![3, 9, 8])); - let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); - let q = i + k; - assert_eq!(BigUint::from(&q), BigUint::new(vec![3, 9, 8]) + BigUint::new(vec![5, 4, 9, 3, 1, 81])); - } - - #[test] - #[cfg(feature = "num-bigint")] - fn test_bigint() { - let i = BigInt::new(Sign::Plus, vec![5, 4, 8, 3, 2, 9, 3]); - let s = SmallInt::from(&i); - assert_eq!( - BigInt::from(&s).to_u32_digits(), - (Sign::Plus, vec![5, 4, 8, 3, 2, 9, 3]) - ); - } -} +mod tests; diff --git a/src/ops.rs b/src/ops.rs new file mode 100644 index 0000000..cb50c2e --- /dev/null +++ b/src/ops.rs @@ -0,0 +1,243 @@ +use crate::SmallUint; +use crate::smallint::SmallUintType; +use core::ops::Add; +use core::mem::ManuallyDrop; + +macro_rules! basic_op { + ($imp:ident, $typ:ty, $fun:ident) => { + + impl<'a, 'b> $imp<&'a $typ> for &'b $typ { + + type Output = $typ; + + fn $fun(self, rhs: &$typ) -> Self::Output { + $fun(self, rhs) + } + + } + + impl<'a> $imp<$typ> for &'a $typ { + + type Output = $typ; + + fn $fun(self, rhs: $typ) -> Self::Output { + self.$fun(&rhs) + } + + } + + impl<'a> $imp<&'a $typ> for $typ { + + type Output = $typ; + + fn $fun(self, rhs: &$typ) -> Self::Output { + (&self).$fun(rhs) + } + + } + + + impl $imp<$typ> for $typ { + + type Output = $typ; + + fn $fun(self, rhs: $typ) -> Self::Output { + (&self).$fun(&rhs) + } + + } + } +} + + +fn add_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec { + + let s = slice1.len(); + let j = slice2.len(); + + let larger = std::cmp::max(s, j); + let mut res = Vec::with_capacity(larger + 1); + let mut carry = false; + + for t in 0..larger { + + let value1 = if t < s { + slice1[t] + } else { + 0 + }; + + let value2 = if t < j { + slice2[t] + } else { + 0 + }; + + let (val, overflow) = value1.overflowing_add(value2); + let (cval, coverflow) = val.overflowing_add(carry as u32); + res.push(cval); + carry = overflow | coverflow; + + } + + if carry { + res.push(1); + } + while res[res.len() - 1] == 0 { + res.pop(); + } + res +} + +fn add(a: &SmallUint, b: &SmallUint) -> SmallUint { + + match (&a.0, &b.0) { + (&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => { + match i.overflowing_add(j) { + (t, false) => SmallUint(SmallUintType::Inline(t)), + (t, true) => { + let mut res = [0, 0, 0, 0, 1]; + + let mut v = t; + #[allow(clippy::needless_range_loop)] + for r in 0..4 { + res[r] = v as u32; + + v >>= 32; + } + + let mut slice = ManuallyDrop::new(>::from(res)); + + SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), 5))) + + } + } + }, + (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) | (&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) => { + let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; + + let mut res = [0, 0, 0, 0]; + + let mut v = i; + #[allow(clippy::needless_range_loop)] + for r in 0..4 { + res[r] = v as u32; + + v >>= 32; + } + + let result = add_two_slices(slice1, &res[..]); + let size = result.len(); + + let mut slice = ManuallyDrop::new(result.into_boxed_slice()); + + SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) + + + }, + (&SmallUintType::Heap((r, s)), &SmallUintType::Heap((i, j))) => { + + let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; + let slice2 = unsafe { core::slice::from_raw_parts(i, j) }; + + let res = add_two_slices(slice1, slice2); + let size = res.len(); + + let mut slice = ManuallyDrop::new(res.into_boxed_slice()); + + SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) + + } + } +} + +basic_op!(Add, SmallUint, add); + +// Taken from https://github.com/rust-lang/rust/issues/85532#issuecomment-916309635. Credit to +// AaronKutch. +const fn carrying_mul_u128(lhs: u128, rhs: u128, carry: u128) -> (u128, u128) { + // [rhs_hi] [rhs_lo] + // [lhs_hi] [lhs_lo] + // X___________________ + // [------tmp0------] + // [------tmp1------] + // [------tmp2------] + // [------tmp3------] + // [-------add------] + // +_______________________________________ + // [------sum0------] + // [------sum1------] + + let lhs_lo = lhs as u64; + let rhs_lo = rhs as u64; + let lhs_hi = (lhs.wrapping_shr(64)) as u64; + let rhs_hi = (rhs.wrapping_shr(64)) as u64; + let tmp0 = (lhs_lo as u128).wrapping_mul(rhs_lo as u128); + let tmp1 = (lhs_lo as u128).wrapping_mul(rhs_hi as u128); + let tmp2 = (lhs_hi as u128).wrapping_mul(rhs_lo as u128); + let tmp3 = (lhs_hi as u128).wrapping_mul(rhs_hi as u128); + // tmp1 and tmp2 straddle the boundary. We have to handle three carries + let (sum0, carry0) = tmp0.overflowing_add(tmp1.wrapping_shl(64)); + let (sum0, carry1) = sum0.overflowing_add(tmp2.wrapping_shl(64)); + let (sum0, carry2) = sum0.overflowing_add(carry); + let sum1 = tmp3 + .wrapping_add(tmp1.wrapping_shr(64)) + .wrapping_add(tmp2.wrapping_shr(64)) + .wrapping_add(carry0 as u128) + .wrapping_add(carry1 as u128) + .wrapping_add(carry2 as u128); + (sum0, sum1) +} + +// fn mul_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec { +// +// let l1 = slice1.len(); +// let l2 = slice2.len(); +// +// +// } +// +// +// fn mul(a: &SmallUint, b: &SmallUint) -> SmallUint { +// match (&a.0, &b.0) { +// (&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => { +// match carrying_mul_u128(i, j, 0) { +// (t, 0) => SmallUint(SmallUintType::Inline(t)), +// (t, o) => { +// let mut res = Vec::with_capacity(8); +// +// let mut v = t; +// #[allow(clippy::needless_range_loop)] +// for _ in 0..4 { +// res.push(v as u32); +// +// v >>= 32; +// } +// +// let mut v = o; +// for _ in 4..8 { +// res.push(v as u32); +// +// v >>= 32; +// } +// +// while res[res.len() - 1] == 0 { +// res.pop(); +// } +// +// let size = res.len(); +// +// let mut slice = ManuallyDrop::new(res.into_boxed_slice()); +// +// SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) +// +// } +// } +// }, +// (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) | (&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) => { +// +// } +// } +// } +// +// diff --git a/src/pretty.rs b/src/pretty.rs new file mode 100644 index 0000000..be22ad9 --- /dev/null +++ b/src/pretty.rs @@ -0,0 +1,22 @@ +use crate::SmallInt; +use crate::SmallUint; + + +#[cfg(feature= "num-bigint")] +use num_bigint::{BigInt, BigUint}; + + +#[cfg(feature = "num-bigint")] +impl core::fmt::Debug for SmallInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", BigInt::from(self)) + } +} + +#[cfg(feature = "num-bigint")] +impl core::fmt::Debug for SmallUint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", BigUint::from(self)) + } +} + diff --git a/src/smallint.rs b/src/smallint.rs new file mode 100644 index 0000000..dabff1e --- /dev/null +++ b/src/smallint.rs @@ -0,0 +1,43 @@ + +/// An integer-like type that will store small integers up to `i128` inline. Larger integers are +/// represented as a slice to a sequence of base 232 digits represented as a `*mut u32`. +#[derive(Clone, PartialEq, Eq)] +pub struct SmallInt(pub(crate) SmallIntType); + +/// An integer-like type that will store small integers up to `u128` inline. Larger integers are +/// represented as a slice to a sequence of base 232 digits represented as a `*mut u32`. +#[derive(Clone, PartialEq, Eq)] +pub struct SmallUint(pub(crate) SmallUintType); + +#[derive(Clone, PartialEq, Eq)] +pub enum SmallIntType { + Inline(i128), + Heap((*mut u32, isize)), +} + +#[derive(Clone, PartialEq, Eq)] +pub enum SmallUintType { + Inline(u128), + Heap((*mut u32, usize)), +} + + +impl Drop for SmallInt { + fn drop(&mut self) { + if let Self(SmallIntType::Heap((r, s))) = self { + let size = usize::try_from(s.abs()).unwrap(); + let slice = unsafe { core::slice::from_raw_parts_mut(*r, size) }; + unsafe { std::mem::drop(Box::from_raw(slice)) } + } + } +} + +impl Drop for SmallUint { + fn drop(&mut self) { + if let Self(SmallUintType::Heap((r, s))) = self { + let slice = unsafe { core::slice::from_raw_parts_mut(*r, *s) }; + unsafe { std::mem::drop(Box::from_raw(slice)) } + } + } +} + diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..fc95bc9 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,63 @@ +#![cfg(test)] + +use crate::SmallInt; +use crate::SmallUint; + +#[cfg(feature = "num-bigint")] +use num_bigint::{BigInt, Sign, BigUint}; + +macro_rules! conversion_tests { + ($t:ty, $i:ident) => { + #[test] + fn $i() { + let i = <$t>::MAX; + let s = SmallInt::from(i); + assert_eq!(<$t>::try_from(s).unwrap(), i); + + let i = <$t>::MIN; + let s = SmallInt::from(i); + assert_eq!(<$t>::try_from(s).unwrap(), i); + } + }; +} + +conversion_tests!(u8, test_u8); +conversion_tests!(i8, test_i8); +conversion_tests!(u16, test_u16); +conversion_tests!(i16, test_i16); +conversion_tests!(u32, test_u32); +conversion_tests!(i32, test_i32); +conversion_tests!(u64, test_u64); +conversion_tests!(i64, test_i64); +conversion_tests!(u128, test_u128); +conversion_tests!(i128, test_i128); + +#[test] +#[cfg(feature = "num-bigint")] +fn test_op_add_u_u() { + let i = SmallUint::from(u128::MAX); + let k = SmallUint::from(u128::MAX); + let q = i + k; + assert_eq!(BigUint::from(&q), BigUint::from(u128::MAX) + u128::MAX); + + let i = SmallUint::from(u128::MAX); + let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); + let q = i + k; + assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) + u128::MAX); + + let i = SmallUint::from(&BigUint::new(vec![3, 9, 8])); + let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); + let q = i + k; + assert_eq!(BigUint::from(&q), BigUint::new(vec![3, 9, 8]) + BigUint::new(vec![5, 4, 9, 3, 1, 81])); +} + +#[test] +#[cfg(feature = "num-bigint")] +fn test_bigint() { + let i = BigInt::new(Sign::Plus, vec![5, 4, 8, 3, 2, 9, 3]); + let s = SmallInt::from(&i); + assert_eq!( + BigInt::from(&s).to_u32_digits(), + (Sign::Plus, vec![5, 4, 8, 3, 2, 9, 3]) + ); +}