use crate::{misc::NegAbs, Assign, Integer};
use core::{
cell::UnsafeCell,
mem::{self, MaybeUninit},
ops::Deref,
ptr::NonNull,
};
use gmp_mpfr_sys::gmp::{self, limb_t, mpz_t};
use libc::c_int;
pub const LIMBS_IN_SMALL: usize = (128 / gmp::LIMB_BITS) as usize;
pub type Limbs = [MaybeUninit<limb_t>; LIMBS_IN_SMALL];
#[derive(Clone)]
pub struct SmallInteger {
inner: Mpz,
limbs: Limbs,
}
#[repr(C)]
pub struct Mpz {
pub alloc: c_int,
pub size: c_int,
pub d: UnsafeCell<NonNull<limb_t>>,
}
impl Clone for Mpz {
fn clone(&self) -> Mpz {
Mpz {
alloc: self.alloc,
size: self.size,
d: UnsafeCell::new(unsafe { *self.d.get() }),
}
}
}
static_assert!(mem::size_of::<Limbs>() == 16);
static_assert_same_layout!(Mpz, mpz_t);
unsafe impl Send for SmallInteger {}
impl Default for SmallInteger {
#[inline]
fn default() -> Self {
SmallInteger::new()
}
}
impl SmallInteger {
#[inline]
pub const fn new() -> Self {
SmallInteger {
inner: Mpz {
alloc: LIMBS_IN_SMALL as c_int,
size: 0,
d: UnsafeCell::new(NonNull::dangling()),
},
limbs: small_limbs![0],
}
}
#[inline]
pub unsafe fn as_nonreallocating_integer(&mut self) -> &mut Integer {
self.update_d();
let ptr = cast_ptr_mut!(&mut self.inner, Integer);
&mut *ptr
}
#[inline]
fn update_d(&self) {
let d = NonNull::<[MaybeUninit<limb_t>]>::from(&self.limbs[..]);
unsafe {
*self.inner.d.get() = d.cast();
}
}
}
impl Deref for SmallInteger {
type Target = Integer;
#[inline]
fn deref(&self) -> &Integer {
self.update_d();
let ptr = cast_ptr!(&self.inner, Integer);
unsafe { &*ptr }
}
}
pub trait ToSmall: SealedToSmall {}
pub trait SealedToSmall: Sized {
fn copy(self, size: &mut c_int, limbs: &mut Limbs);
fn is_zero(&self) -> bool;
}
macro_rules! is_zero {
() => {
#[inline]
fn is_zero(&self) -> bool {
*self == 0
}
};
}
macro_rules! signed {
($($I:ty)*) => { $(
impl ToSmall for $I {}
impl SealedToSmall for $I {
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
let (neg, abs) = self.neg_abs();
abs.copy(size, limbs);
if neg {
*size = -*size;
}
}
is_zero! {}
}
)* };
}
macro_rules! one_limb {
($($U:ty)*) => { $(
impl ToSmall for $U {}
impl SealedToSmall for $U {
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
if self == 0 {
*size = 0;
} else {
*size = 1;
limbs[0] = MaybeUninit::new(self.into());
}
}
is_zero! {}
}
)* };
}
signed! { i8 i16 i32 i64 i128 isize }
impl ToSmall for bool {}
impl SealedToSmall for bool {
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
if !self {
*size = 0;
} else {
*size = 1;
limbs[0] = MaybeUninit::new(1);
}
}
#[inline]
fn is_zero(&self) -> bool {
!*self
}
}
one_limb! { u8 u16 u32 }
#[cfg(gmp_limb_bits_64)]
one_limb! { u64 }
#[cfg(gmp_limb_bits_32)]
impl ToSmall for u64 {}
#[cfg(gmp_limb_bits_32)]
impl SealedToSmall for u64 {
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
if self == 0 {
*size = 0;
} else if self <= 0xffff_ffff {
*size = 1;
limbs[0] = MaybeUninit::new(self as u32);
} else {
*size = 2;
limbs[0] = MaybeUninit::new(self as u32);
limbs[1] = MaybeUninit::new((self >> 32) as u32);
}
}
is_zero! {}
}
impl ToSmall for u128 {}
impl SealedToSmall for u128 {
#[cfg(gmp_limb_bits_64)]
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
if self == 0 {
*size = 0;
} else if self <= 0xffff_ffff_ffff_ffff {
*size = 1;
limbs[0] = MaybeUninit::new(self as u64);
} else {
*size = 2;
limbs[0] = MaybeUninit::new(self as u64);
limbs[1] = MaybeUninit::new((self >> 64) as u64);
}
}
#[cfg(gmp_limb_bits_32)]
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
if self == 0 {
*size = 0;
} else if self <= 0xffff_ffff {
*size = 1;
limbs[0] = MaybeUninit::new(self as u32);
} else if self <= 0xffff_ffff_ffff_ffff {
*size = 2;
limbs[0] = MaybeUninit::new(self as u32);
limbs[1] = MaybeUninit::new((self >> 32) as u32);
} else if self <= 0xffff_ffff_ffff_ffff_ffff_ffff {
*size = 3;
limbs[0] = MaybeUninit::new(self as u32);
limbs[1] = MaybeUninit::new((self >> 32) as u32);
limbs[2] = MaybeUninit::new((self >> 64) as u32);
} else {
*size = 4;
limbs[0] = MaybeUninit::new(self as u32);
limbs[1] = MaybeUninit::new((self >> 32) as u32);
limbs[2] = MaybeUninit::new((self >> 64) as u32);
limbs[3] = MaybeUninit::new((self >> 96) as u32);
}
}
is_zero! {}
}
impl ToSmall for usize {}
impl SealedToSmall for usize {
#[cfg(target_pointer_width = "32")]
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
(self as u32).copy(size, limbs);
}
#[cfg(target_pointer_width = "64")]
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
(self as u64).copy(size, limbs);
}
is_zero! {}
}
impl<T: ToSmall> Assign<T> for SmallInteger {
#[inline]
fn assign(&mut self, src: T) {
src.copy(&mut self.inner.size, &mut self.limbs);
}
}
impl<T: ToSmall> From<T> for SmallInteger {
#[inline]
fn from(src: T) -> Self {
let mut size = 0;
let mut limbs = small_limbs![0];
src.copy(&mut size, &mut limbs);
SmallInteger {
inner: Mpz {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: UnsafeCell::new(NonNull::dangling()),
},
limbs,
}
}
}
impl Assign<&Self> for SmallInteger {
#[inline]
fn assign(&mut self, other: &Self) {
self.clone_from(other);
}
}
impl Assign for SmallInteger {
#[inline]
fn assign(&mut self, other: Self) {
drop(mem::replace(self, other));
}
}
#[cfg(test)]
mod tests {
use crate::{integer::SmallInteger, Assign};
#[test]
fn check_assign() {
let mut i = SmallInteger::from(-1i32);
assert_eq!(*i, -1);
let other = SmallInteger::from(2i32);
i.assign(&other);
assert_eq!(*i, 2);
i.assign(6u8);
assert_eq!(*i, 6);
i.assign(-6i8);
assert_eq!(*i, -6);
i.assign(other);
assert_eq!(*i, 2);
i.assign(6u16);
assert_eq!(*i, 6);
i.assign(-6i16);
assert_eq!(*i, -6);
i.assign(6u32);
assert_eq!(*i, 6);
i.assign(-6i32);
assert_eq!(*i, -6);
i.assign(0xf_0000_0006u64);
assert_eq!(*i, 0xf_0000_0006u64);
i.assign(-0xf_0000_0006i64);
assert_eq!(*i, -0xf_0000_0006i64);
i.assign(6u128 << 64 | 7u128);
assert_eq!(*i, 6u128 << 64 | 7u128);
i.assign(-6i128 << 64 | 7i128);
assert_eq!(*i, -6i128 << 64 | 7i128);
i.assign(6usize);
assert_eq!(*i, 6);
i.assign(-6isize);
assert_eq!(*i, -6);
i.assign(0u32);
assert_eq!(*i, 0);
}
}