Compare commits

...

2 Commits

Author SHA1 Message Date
3510274fce mul 2022-08-05 07:45:20 -04:00
021454c438 fix add 2022-08-04 22:22:23 -04:00
8 changed files with 634 additions and 463 deletions

83
src/bigint.rs Normal file
View File

@ -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())
}
}
}
}

155
src/convert.rs Normal file
View File

@ -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<Self, Self::Error> {
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<Self, Self::Error> {
Ok(Self(SmallUintType::Inline(
u128::try_from(a).map_err(|_| SmallIntError::ConversionError)?,
)))
}
}
impl TryFrom<SmallUint> for $itype {
type Error = SmallIntError;
fn try_from(s: SmallUint) -> Result<Self, Self::Error> {
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<u128> 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<SmallInt> for u128 {
type Error = SmallIntError;
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
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<SmallUint> 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<SmallInt> for SmallUint {
type Error = SmallIntError;
fn try_from(value: SmallInt) -> Result<Self, Self::Error> {
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)?)))
}
}
}
}
}

15
src/error.rs Normal file
View File

@ -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.")
}
}

View File

@ -9,473 +9,20 @@
// Invariant: If a small integer is on the heap, the size is the minimum digits required to // Invariant: If a small integer is on the heap, the size is the minimum digits required to
// represent it. // represent it.
#[cfg(feature = "num-bigint")] mod smallint;
use num_bigint::BigInt;
#[cfg(feature = "num-bigint")] pub use crate::smallint::SmallInt;
use num_bigint::BigUint; pub use crate::smallint::SmallUint;
#[cfg(feature = "num-bigint")] mod error;
use num_bigint::Sign;
use core::mem::ManuallyDrop; pub use error::SmallIntError;
use core::ops::Add;
/// An error that occurred when processing a `SmallInt`. mod convert;
#[derive(Debug, Clone, PartialEq, Eq)] mod ops;
pub enum SmallIntError {
/// Conversion error when converting from SmallInt to other integer types.
ConversionError,
}
impl std::error::Error for SmallIntError {} mod bigint;
impl core::fmt::Display for SmallIntError { mod pretty;
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self::ConversionError = self;
write!(f, "Error converting integer to SmallInt.")
}
}
/// An integer-like type that will store small integers up to `i128` inline. Larger integers are mod tests;
/// represented as a slice to a sequence of base 2<sup>32</sup> 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 2<sup>32</sup> 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<u32> {
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<SmallUint> 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;
}
Self(SmallUintType::Heap((<Box<[u32]>>::from(res).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, 1];
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<Self, Self::Error> {
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<Self, Self::Error> {
Ok(Self(SmallUintType::Inline(
u128::try_from(a).map_err(|_| SmallIntError::ConversionError)?,
)))
}
}
impl TryFrom<SmallUint> for $itype {
type Error = SmallIntError;
fn try_from(s: SmallUint) -> Result<Self, Self::Error> {
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<u128> 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<SmallInt> for u128 {
type Error = SmallIntError;
fn try_from(s: SmallInt) -> Result<Self, Self::Error> {
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<SmallUint> 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<SmallInt> for SmallUint {
type Error = SmallIntError;
fn try_from(value: SmallInt) -> Result<Self, Self::Error> {
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(&BigUint::new(vec![5, 4, 9, 3, 2, 1, 5]));
let q = i + k;
println!("{:?}", BigUint::from(&q));
println!("{:?}", BigUint::new(vec![5, 4, 9, 3, 2, 1, 5]) + u128::MAX);
}
#[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])
);
}
}

243
src/ops.rs Normal file
View File

@ -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<u32> {
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(<Box<[u32]>>::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<u32> {
//
// 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))) => {
//
// }
// }
// }
//
//

22
src/pretty.rs Normal file
View File

@ -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))
}
}

43
src/smallint.rs Normal file
View File

@ -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 2<sup>32</sup> 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 2<sup>32</sup> 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)) }
}
}
}

63
src/tests.rs Normal file
View File

@ -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])
);
}