diff --git a/src/convert.rs b/src/convert.rs index 46c4f6d..d3098e7 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -153,3 +153,26 @@ impl TryFrom for SmallUint { } } } + +impl SmallUint { + /// Converts a `SmallInt` into a `SmallUint` and drops the sign instead of throwing an error. + pub fn from_smallint_unsigned(value: SmallInt) -> Self { + match value.0 { + SmallIntType::Inline(i) => Self::try_from(i.abs()).unwrap(), + SmallIntType::Heap((r, s)) => { + let size = usize::try_from(s.abs()).unwrap(); + if size > 4 { + let slice = unsafe { core::slice::from_raw_parts(r, size) }; + let mut ret = vec![0; size]; + ret.clone_from_slice(slice); + let mut val = ManuallyDrop::new(ret.into_boxed_slice()); + Self(SmallUintType::Heap((val.as_mut_ptr(), size))) + } else if s >= 0 { + Self(SmallUintType::Inline(u128::try_from(value).unwrap())) + } else { + Self(SmallUintType::Inline(u128::try_from(-value).unwrap())) + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 3dc823e..c6bc011 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,8 @@ mod error; pub use error::SmallIntError; mod convert; +mod ord; + mod logic; mod ops; diff --git a/src/ops.rs b/src/ops.rs index 5ca074d..738047e 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -1,14 +1,32 @@ -use crate::smallint::SmallUintType; -use crate::SmallUint; +use crate::smallint::{SmallIntType, SmallUintType}; +use crate::{SmallInt, SmallUint}; use core::mem::ManuallyDrop; -use core::ops::{Add, Mul, Sub}; +use core::ops::{Add, Mul, Neg, Sub}; + +impl Neg for SmallInt { + type Output = Self; + + fn neg(self) -> Self::Output { + match self.0 { + SmallIntType::Inline(i) => SmallInt(SmallIntType::Inline(-i)), + SmallIntType::Heap((r, s)) => { + let size = usize::try_from(s.abs()).unwrap(); + let slice = unsafe { core::slice::from_raw_parts(r, size) }; + let mut ret = vec![0; size]; + ret.clone_from_slice(slice); + let mut val = ManuallyDrop::new(ret.into_boxed_slice()); + SmallInt(SmallIntType::Heap((val.as_mut_ptr(), -s))) + } + } + } +} macro_rules! basic_op { - ($imp:ident, $typ:ty, $fun:ident) => { + ($imp:ident, $lower:ident, $typ:ty, $fun:ident) => { impl<'a, 'b> $imp<&'a $typ> for &'b $typ { type Output = $typ; - fn $fun(self, rhs: &$typ) -> Self::Output { + fn $lower(self, rhs: &$typ) -> Self::Output { $fun(self, rhs) } } @@ -16,24 +34,24 @@ macro_rules! basic_op { impl<'a> $imp<$typ> for &'a $typ { type Output = $typ; - fn $fun(self, rhs: $typ) -> Self::Output { - self.$fun(&rhs) + fn $lower(self, rhs: $typ) -> Self::Output { + self.$lower(&rhs) } } impl<'a> $imp<&'a $typ> for $typ { type Output = $typ; - fn $fun(self, rhs: &$typ) -> Self::Output { - (&self).$fun(rhs) + fn $lower(self, rhs: &$typ) -> Self::Output { + (&self).$lower(rhs) } } impl $imp<$typ> for $typ { type Output = $typ; - fn $fun(self, rhs: $typ) -> Self::Output { - (&self).$fun(&rhs) + fn $lower(self, rhs: $typ) -> Self::Output { + (&self).$lower(&rhs) } } }; @@ -122,7 +140,57 @@ fn add(a: &SmallUint, b: &SmallUint) -> SmallUint { } } -basic_op!(Add, SmallUint, add); +basic_op!(Add, add, SmallUint, add); + +fn add_signed(a: &SmallInt, b: &SmallInt) -> SmallInt { + let a_sign; + match &a.0 { + SmallIntType::Inline(i) => a_sign = i.signum() as i8, + SmallIntType::Heap((_, s)) => a_sign = s.signum() as i8, + } + + let b_sign; + match &b.0 { + SmallIntType::Inline(i) => b_sign = i.signum() as i8, + SmallIntType::Heap((_, s)) => b_sign = s.signum() as i8, + } + + match (a_sign, b_sign) { + x if (x.0 >= 0 && x.1 >= 0) => SmallInt::from( + SmallUint::from_smallint_unsigned(a.clone()) + + SmallUint::from_smallint_unsigned(b.clone()), + ), + x if (x.0 < 0 && x.1 < 0) => -SmallInt::from( + SmallUint::from_smallint_unsigned(a.clone()) + + SmallUint::from_smallint_unsigned(b.clone()), + ), + + x if (x.0 >= 0 && x.1 < 0) => { + let s = SmallUint::from_smallint_unsigned(a.clone()); + let b = SmallUint::from_smallint_unsigned(b.clone()); + if b <= s { + SmallInt::from(s - b) + } else { + -SmallInt::from(b - s) + } + } + + x if (x.0 < 0 && x.1 >= 0) => { + let s = SmallUint::from_smallint_unsigned(a.clone()); + let b = SmallUint::from_smallint_unsigned(b.clone()); + if s <= b { + SmallInt::from(b - s) + } else { + -SmallInt::from(s - b) + } + } + (_, _) => { + panic!("This shouldn't happen. "); + } + } +} + +basic_op!(Add, add, SmallInt, add_signed); fn sub_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec { let b = slice1.len(); @@ -207,7 +275,7 @@ fn sub(a: &SmallUint, b: &SmallUint) -> SmallUint { } } -basic_op!(Sub, SmallUint, sub); +basic_op!(Sub, sub, SmallUint, sub); // Taken from https://github.com/rust-lang/rust/issues/85532#issuecomment-916309635. Credit to // AaronKutch. @@ -392,4 +460,4 @@ fn mul(a: &SmallUint, b: &SmallUint) -> SmallUint { } } -basic_op!(Mul, SmallUint, mul); +basic_op!(Mul, mul, SmallUint, mul); diff --git a/src/ord.rs b/src/ord.rs new file mode 100644 index 0000000..18fc508 --- /dev/null +++ b/src/ord.rs @@ -0,0 +1,40 @@ +use crate::smallint::SmallIntType; +use crate::SmallInt; +use crate::SmallUint; +use std::cmp::Ordering; + +impl PartialOrd for SmallInt { + fn partial_cmp(&self, other: &Self) -> Option { + let a_sign; + match &self.0 { + SmallIntType::Inline(i) => a_sign = i.signum() as i8, + SmallIntType::Heap((_, s)) => a_sign = s.signum() as i8, + } + + let b_sign; + match &other.0 { + SmallIntType::Inline(i) => b_sign = i.signum() as i8, + SmallIntType::Heap((_, s)) => b_sign = s.signum() as i8, + } + + match (a_sign, b_sign) { + x if (x.0 >= 0 && x.1 < 0) => Some(Ordering::Greater), + + x if (x.0 < 0 && x.1 >= 0) => Some(Ordering::Less), + + x if (x.0 >= 0 && x.1 >= 0) => SmallUint::from_smallint_unsigned(self.clone()) + .partial_cmp(&SmallUint::from_smallint_unsigned(other.clone())), + + x if (x.0 < 0 && x.1 < 0) => SmallUint::from_smallint_unsigned(other.clone()) + .partial_cmp(&SmallUint::from_smallint_unsigned(self.clone())), + + (_, _) => None, + } + } +} + +impl Ord for SmallInt { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).expect("This should not happen.") + } +} diff --git a/src/smallint.rs b/src/smallint.rs index 74078d6..0897f87 100644 --- a/src/smallint.rs +++ b/src/smallint.rs @@ -5,7 +5,7 @@ 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)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct SmallUint(pub(crate) SmallUintType); #[derive(Clone, PartialEq, Eq)] @@ -14,7 +14,7 @@ pub enum SmallIntType { Heap((*mut u32, isize)), } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum SmallUintType { Inline(u128), Heap((*mut u32, usize)), diff --git a/src/tests.rs b/src/tests.rs index cac0569..5ffb10d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -191,6 +191,26 @@ fn test_op_xor_u_u() { ); } +#[test] +#[cfg(feature = "num-bigint")] +fn test_op_neg() { + let i = SmallInt::from(u128::MAX); + let q = -i; + assert_eq!(BigInt::from(&q), -BigInt::from(u128::MAX)); + + let i = SmallInt::from(i64::MIN); + let q = -i; + assert_eq!(BigInt::from(&q), -BigInt::from(i64::MIN)); +} + +#[test] +#[cfg(feature = "num-bigint")] +fn test_conversion_sign_drop() { + let si = SmallInt::from(i128::MIN + 1); + let i = SmallUint::from_smallint_unsigned(si); + assert_eq!(BigUint::from(&i), BigUint::from(-(i128::MIN + 1) as u128)) +} + #[test] #[cfg(feature = "num-bigint")] fn test_bigint() {