mirror of
https://gitlab.com/artofrev/smallint.git
synced 2024-11-12 16:31:17 -05:00
Merge branch 'ops' into 'master'
Basic operations for smalluint See merge request artofrev/smallint!1
This commit is contained in:
commit
64ad3189bf
83
src/bigint.rs
Normal file
83
src/bigint.rs
Normal 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
155
src/convert.rs
Normal 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
15
src/error.rs
Normal 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.")
|
||||
}
|
||||
}
|
484
src/lib.rs
484
src/lib.rs
|
@ -9,483 +9,21 @@
|
|||
// 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;
|
||||
mod logic;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
let mut slice = ManuallyDrop::new(<Box<[u32]>>::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<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(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;
|
||||
|
|
219
src/logic.rs
Normal file
219
src/logic.rs
Normal file
|
@ -0,0 +1,219 @@
|
|||
use crate::SmallUint;
|
||||
use crate::smallint::SmallUintType;
|
||||
use core::ops::{BitAnd, BitOr, BitXor};
|
||||
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 bitand(a: &SmallUint, b: &SmallUint) -> SmallUint {
|
||||
match (&a.0, &b.0) {
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => {
|
||||
SmallUint(SmallUintType::Inline(i & j))
|
||||
},
|
||||
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) | (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) => {
|
||||
let slice = unsafe { core::slice::from_raw_parts(r, s) };
|
||||
let mut j = 0u128;
|
||||
for i in 0..4 {
|
||||
j <<= 32;
|
||||
|
||||
j |= slice[3 - i] as u128;
|
||||
|
||||
}
|
||||
SmallUint(SmallUintType::Inline(i & j))
|
||||
},
|
||||
|
||||
(&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 min = std::cmp::min(slice1.len(), slice2.len());
|
||||
|
||||
let mut res = Vec::with_capacity(min);
|
||||
|
||||
for l in 0..min {
|
||||
res.push(slice1[l] & slice2[l]);
|
||||
}
|
||||
|
||||
while res.len() != 1 && res[res.len() - 1] == 0 {
|
||||
res.pop();
|
||||
}
|
||||
|
||||
if res.len() <= 4 {
|
||||
let mut r = 0u128;
|
||||
for t in 0..res.len() {
|
||||
r <<= 32;
|
||||
r |= res[res.len() - 1 - t] as u128;
|
||||
}
|
||||
SmallUint(SmallUintType::Inline(r))
|
||||
} else {
|
||||
let mut slice = ManuallyDrop::new(res.into_boxed_slice());
|
||||
SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), slice.len())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
basic_op!(BitAnd, SmallUint, bitand);
|
||||
|
||||
fn bitor(a: &SmallUint, b: &SmallUint) -> SmallUint {
|
||||
match (&a.0, &b.0) {
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => {
|
||||
SmallUint(SmallUintType::Inline(i | j))
|
||||
},
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) | (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) => {
|
||||
let slice = unsafe { core::slice::from_raw_parts(r, s) };
|
||||
|
||||
let mut retvec = slice.to_vec();
|
||||
|
||||
let mut v = i;
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for r in 0..4 {
|
||||
retvec[r] |= v as u32;
|
||||
|
||||
v >>= 32;
|
||||
}
|
||||
|
||||
let mut retslice = ManuallyDrop::new(retvec.into_boxed_slice());
|
||||
|
||||
SmallUint(SmallUintType::Heap((retslice.as_mut_ptr(), retslice.len())))
|
||||
|
||||
}
|
||||
|
||||
(&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 m = std::cmp::min(slice1.len(), slice2.len());
|
||||
|
||||
let mut retvec;
|
||||
if slice1.len() > slice2.len() {
|
||||
retvec = slice1.to_vec();
|
||||
} else {
|
||||
retvec = slice2.to_vec();
|
||||
}
|
||||
|
||||
for t in 0..m {
|
||||
retvec[t] = slice1[t] | slice2[t];
|
||||
}
|
||||
|
||||
let mut slice = ManuallyDrop::new(retvec.into_boxed_slice());
|
||||
|
||||
SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), slice.len())))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
basic_op!(BitOr, SmallUint, bitor);
|
||||
|
||||
fn bitxor(a: &SmallUint, b: &SmallUint) -> SmallUint {
|
||||
match (&a.0, &b.0) {
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => {
|
||||
SmallUint(SmallUintType::Inline(i ^ j))
|
||||
}
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) | (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) => {
|
||||
let slice = unsafe { core::slice::from_raw_parts(r, s) };
|
||||
|
||||
let mut retvec = slice.to_vec();
|
||||
|
||||
let mut v = i;
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for r in 0..4 {
|
||||
retvec[r] ^= v as u32;
|
||||
|
||||
v >>= 32;
|
||||
}
|
||||
|
||||
let mut retslice = ManuallyDrop::new(retvec.into_boxed_slice());
|
||||
|
||||
SmallUint(SmallUintType::Heap((retslice.as_mut_ptr(), retslice.len())))
|
||||
|
||||
|
||||
},
|
||||
|
||||
(&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 m = std::cmp::min(slice1.len(), slice2.len());
|
||||
|
||||
let mut res;
|
||||
if slice1.len() > slice2.len() {
|
||||
res = slice1.to_vec();
|
||||
} else {
|
||||
res = slice2.to_vec();
|
||||
}
|
||||
|
||||
for t in 0..m {
|
||||
res[t] = slice1[t] ^ slice2[t];
|
||||
}
|
||||
|
||||
while res.len() != 1 && res[res.len() - 1] == 0 {
|
||||
res.pop();
|
||||
}
|
||||
|
||||
if res.len() <= 4 {
|
||||
let mut r = 0u128;
|
||||
for t in 0..res.len() {
|
||||
r <<= 32;
|
||||
r |= res[res.len() - 1 - t] as u128;
|
||||
}
|
||||
SmallUint(SmallUintType::Inline(r))
|
||||
} else {
|
||||
let mut slice = ManuallyDrop::new(res.into_boxed_slice());
|
||||
SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), slice.len())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
basic_op!(BitXor, SmallUint, bitxor);
|
451
src/ops.rs
Normal file
451
src/ops.rs
Normal file
|
@ -0,0 +1,451 @@
|
|||
use crate::SmallUint;
|
||||
use crate::smallint::SmallUintType;
|
||||
use core::ops::{Add, Sub, Mul};
|
||||
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.len() != 1 && 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);
|
||||
|
||||
fn sub_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec<u32> {
|
||||
let b = slice1.len();
|
||||
let s = slice2.len();
|
||||
|
||||
if b < s {
|
||||
panic!("First number is smaller than second.");
|
||||
}
|
||||
|
||||
let mut res = Vec::with_capacity(std::cmp::max(s, b));
|
||||
let mut borrow = false;
|
||||
|
||||
for i in 0..b {
|
||||
let mut value1 = slice1[i];
|
||||
|
||||
let value2 = if i < s {
|
||||
slice2[i]
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if borrow {
|
||||
let (temp, b) = value1.overflowing_sub(1);
|
||||
value1 = temp;
|
||||
borrow = b;
|
||||
}
|
||||
|
||||
if value2 > value1 {
|
||||
borrow = true;
|
||||
}
|
||||
|
||||
let val = value1.wrapping_sub(value2);
|
||||
res.push(val);
|
||||
|
||||
}
|
||||
|
||||
if borrow {
|
||||
panic!("First number is smaller than second. ");
|
||||
}
|
||||
|
||||
res
|
||||
|
||||
}
|
||||
|
||||
fn sub(a: &SmallUint, b: &SmallUint) -> SmallUint {
|
||||
|
||||
match (&a.0, &b.0) {
|
||||
(&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => {
|
||||
if let (t, false) = i.overflowing_sub(j) {
|
||||
SmallUint(SmallUintType::Inline(t))
|
||||
} else {
|
||||
panic!("First number is smaller than second. ");
|
||||
}
|
||||
},
|
||||
(&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) => {
|
||||
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 = sub_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::Inline(_), &SmallUintType::Heap((_, _))) => {
|
||||
panic!("First number is smaller than second. ");
|
||||
|
||||
},
|
||||
(&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 = sub_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!(Sub, SmallUint, sub);
|
||||
|
||||
// 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> {
|
||||
|
||||
// https://en.wikipedia.org/wiki/Karatsuba_algorithm
|
||||
|
||||
let l1 = slice1.len();
|
||||
let l2 = slice2.len();
|
||||
|
||||
|
||||
if l1 == 0 || l2 == 0 {
|
||||
return vec![];
|
||||
} else if l1 == 1 {
|
||||
let mut overflow = 0;
|
||||
let mut res: Vec<u32> = Vec::with_capacity(l2 + 1);
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..l2 {
|
||||
let mut r = (slice2[i] as u64) * (slice1[0] as u64);
|
||||
r += overflow as u64;
|
||||
let m = r as u32;
|
||||
overflow = (r >> 32) as u32;
|
||||
res.push(m);
|
||||
|
||||
}
|
||||
|
||||
if overflow != 0 {
|
||||
res.push(overflow);
|
||||
}
|
||||
|
||||
return res;
|
||||
} else if l2 == 1 {
|
||||
|
||||
let mut overflow = 0;
|
||||
let mut res: Vec<u32> = Vec::with_capacity(l2 + 1);
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..l1 {
|
||||
let mut r = (slice1[i] as u64) * (slice2[0] as u64);
|
||||
r += overflow as u64;
|
||||
let m = r as u32;
|
||||
overflow = (r >> 32) as u32;
|
||||
res.push(m);
|
||||
|
||||
}
|
||||
|
||||
if overflow != 0 {
|
||||
res.push(overflow);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
let m = std::cmp::min(l1, l2);
|
||||
let m2 = (m as u32) / 2;
|
||||
|
||||
let (low1, high1) = slice1.split_at(m2 as usize);
|
||||
let (low2, high2) = slice2.split_at(m2 as usize);
|
||||
|
||||
let z0 = mul_two_slices(low1, low2);
|
||||
let z1 = mul_two_slices(&add_two_slices(low1, high1), &add_two_slices(low2, high2));
|
||||
let z2 = mul_two_slices(high1, high2);
|
||||
|
||||
let mut op0 = z2.clone();
|
||||
|
||||
op0.reverse();
|
||||
|
||||
op0.resize(op0.len() + (m2 as usize * 2), 0);
|
||||
|
||||
op0.reverse();
|
||||
|
||||
|
||||
|
||||
let mut op1 = sub_two_slices(&sub_two_slices(&z1, &z2), &z0);
|
||||
|
||||
op1.reverse();
|
||||
|
||||
op1.resize(op1.len() + (m2 as usize), 0);
|
||||
|
||||
op1.reverse();
|
||||
|
||||
add_two_slices(&add_two_slices(&op0, &op1), &z0)
|
||||
|
||||
}
|
||||
|
||||
|
||||
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))) => {
|
||||
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 = mul_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 = mul_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!(Mul, SmallUint, mul);
|
||||
|
22
src/pretty.rs
Normal file
22
src/pretty.rs
Normal 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
43
src/smallint.rs
Normal 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)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
166
src/tests.rs
Normal file
166
src/tests.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
#![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, 3, 1]));
|
||||
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, 3, 1]) + BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "num-bigint")]
|
||||
fn test_op_mul_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(u32::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]) * u32::MAX);
|
||||
|
||||
let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
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, 3, 1]) * BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "num-bigint")]
|
||||
fn test_op_sub_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 k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(u128::MAX);
|
||||
let q = k - i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) - u128::MAX);
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
let q = k - i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) - BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "num-bigint")]
|
||||
fn test_op_and_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) & BigUint::from(u128::MAX));
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(u128::MAX);
|
||||
let q = k & i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3]) & BigUint::from(u128::MAX));
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
let q = k & i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) & BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "num-bigint")]
|
||||
fn test_op_or_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) | BigUint::from(u128::MAX));
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(u128::MAX);
|
||||
let q = k | i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) | BigUint::from(u128::MAX));
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
let q = k | i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) | BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "num-bigint")]
|
||||
fn test_op_xor_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) ^ BigUint::from(u128::MAX));
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(u128::MAX);
|
||||
let q = k ^ i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) ^ BigUint::from(u128::MAX));
|
||||
|
||||
let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81]));
|
||||
let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
let q = k ^ i;
|
||||
assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) ^ BigUint::new(vec![3, 9, 8, 3, 1]));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[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])
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user