use crate::integer::BorrowInteger;
use crate::misc;
use crate::misc::NegAbs;
use crate::{Assign, Integer};
use az::{Az, Cast, WrappingCast};
use core::ffi::c_int;
use core::fmt::{
Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex,
};
use core::mem;
use core::mem::MaybeUninit;
#[allow(unused_imports)]
use core::ops::Deref;
use core::ptr::NonNull;
use gmp_mpfr_sys::gmp;
use gmp_mpfr_sys::gmp::{limb_t, mpz_t};
pub const LIMBS_IN_SMALL: usize = (128 / gmp::LIMB_BITS) as usize;
pub type Limbs = [MaybeUninit<limb_t>; LIMBS_IN_SMALL];
#[derive(Clone, Copy)]
pub struct MiniInteger {
pub(crate) inner: mpz_t,
pub(crate) limbs: Limbs,
}
static_assert!(mem::size_of::<Limbs>() == 16);
unsafe impl Send for MiniInteger {}
unsafe impl Sync for MiniInteger {}
impl Default for MiniInteger {
#[inline]
fn default() -> Self {
MiniInteger::new()
}
}
impl Display for MiniInteger {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Display::fmt(&*self.borrow(), f)
}
}
impl Debug for MiniInteger {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Debug::fmt(&*self.borrow(), f)
}
}
impl Binary for MiniInteger {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Binary::fmt(&*self.borrow(), f)
}
}
impl Octal for MiniInteger {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Octal::fmt(&*self.borrow(), f)
}
}
impl LowerHex for MiniInteger {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
LowerHex::fmt(&*self.borrow(), f)
}
}
impl UpperHex for MiniInteger {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
UpperHex::fmt(&*self.borrow(), f)
}
}
impl MiniInteger {
#[inline]
pub const fn new() -> Self {
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size: 0,
d: NonNull::dangling(),
},
limbs: small_limbs![],
}
}
#[inline]
pub const fn const_from_bool(val: bool) -> Self {
let (size, limbs) = from_bool(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_i8(val: i8) -> Self {
let (size, limbs) = from_i8(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_i16(val: i16) -> Self {
let (size, limbs) = from_i16(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_i32(val: i32) -> Self {
let (size, limbs) = from_i32(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_i64(val: i64) -> Self {
let (size, limbs) = from_i64(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_i128(val: i128) -> Self {
let (size, limbs) = from_i128(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_isize(val: isize) -> Self {
let (size, limbs) = from_isize(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_u8(val: u8) -> Self {
let (size, limbs) = from_u8(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_u16(val: u16) -> Self {
let (size, limbs) = from_u16(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_u32(val: u32) -> Self {
let (size, limbs) = from_u32(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_u64(val: u64) -> Self {
let (size, limbs) = from_u64(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_u128(val: u128) -> Self {
let (size, limbs) = from_u128(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub const fn const_from_usize(val: usize) -> Self {
let (size, limbs) = from_usize(val);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size,
d: NonNull::dangling(),
},
limbs,
}
}
#[inline]
pub unsafe fn as_nonreallocating_integer(&mut self) -> &mut Integer {
self.inner.d = NonNull::<[MaybeUninit<limb_t>]>::from(&self.limbs[..]).cast();
let ptr = misc::cast_ptr_mut(&mut self.inner);
unsafe { &mut *ptr }
}
#[inline]
pub const fn borrow(&self) -> BorrowInteger<'_> {
let d: *const Limbs = &self.limbs;
unsafe {
BorrowInteger::from_raw(mpz_t {
alloc: self.inner.alloc,
size: self.inner.size,
d: NonNull::new_unchecked(d.cast_mut().cast()),
})
}
}
#[inline]
pub fn borrow_excl(&mut self) -> &Integer {
unsafe { &*self.as_nonreallocating_integer() }
}
}
pub trait ToMini: SealedToMini {}
pub trait SealedToMini: 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, $fn:ident, $fnu:ident) => {
impl ToMini for $I {}
impl SealedToMini 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! {}
}
#[inline]
const fn $fn(val: $I) -> (c_int, Limbs) {
let unsigned_abs = val.unsigned_abs();
let (size, limbs) = $fnu(unsigned_abs);
let size = if val < 0 { -size } else { size };
(size, limbs)
}
};
}
macro_rules! one_limb {
($U:ty, $fn:ident) => {
impl ToMini for $U {}
impl SealedToMini 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! {}
}
#[inline]
const fn $fn(val: $U) -> (c_int, Limbs) {
if val == 0 {
(0, small_limbs![])
} else {
(1, small_limbs![val as limb_t])
}
}
};
}
signed! { i8, from_i8, from_u8 }
signed! { i16, from_i16, from_u16 }
signed! { i32, from_i32, from_u32 }
signed! { i64, from_i64, from_u64 }
signed! { i128, from_i128, from_u128 }
signed! { isize, from_isize, from_usize }
impl ToMini for bool {}
impl SealedToMini for bool {
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
if self {
*size = 1;
limbs[0] = MaybeUninit::new(1);
} else {
*size = 0;
}
}
#[inline]
fn is_zero(&self) -> bool {
!*self
}
}
#[inline]
const fn from_bool(val: bool) -> (c_int, Limbs) {
if val {
(1, small_limbs![1])
} else {
(0, small_limbs![])
}
}
one_limb! { u8, from_u8 }
one_limb! { u16, from_u16 }
one_limb! { u32, from_u32 }
#[cfg(gmp_limb_bits_64)]
one_limb! { u64, from_u64 }
#[cfg(gmp_limb_bits_32)]
impl ToMini for u64 {}
#[cfg(gmp_limb_bits_32)]
impl SealedToMini 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.wrapping_cast());
} else {
*size = 2;
limbs[0] = MaybeUninit::new(self.wrapping_cast());
limbs[1] = MaybeUninit::new((self >> 32).wrapping_cast());
}
}
is_zero! {}
}
#[cfg(gmp_limb_bits_32)]
#[inline]
const fn from_u64(val: u64) -> (c_int, Limbs) {
if val == 0 {
(0, small_limbs![])
} else if val <= 0xffff_ffff {
(1, small_limbs![val as limb_t])
} else {
(2, small_limbs![val as limb_t, (val >> 32) as limb_t])
}
}
impl ToMini for u128 {}
impl SealedToMini 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.wrapping_cast());
} else {
*size = 2;
limbs[0] = MaybeUninit::new(self.wrapping_cast());
limbs[1] = MaybeUninit::new((self >> 64).wrapping_cast());
}
}
#[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.wrapping_cast());
} else if self <= 0xffff_ffff_ffff_ffff {
*size = 2;
limbs[0] = MaybeUninit::new(self.wrapping_cast());
limbs[1] = MaybeUninit::new((self >> 32).wrapping_cast());
} else if self <= 0xffff_ffff_ffff_ffff_ffff_ffff {
*size = 3;
limbs[0] = MaybeUninit::new(self.wrapping_cast());
limbs[1] = MaybeUninit::new((self >> 32).wrapping_cast());
limbs[2] = MaybeUninit::new((self >> 64).wrapping_cast());
} else {
*size = 4;
limbs[0] = MaybeUninit::new(self.wrapping_cast());
limbs[1] = MaybeUninit::new((self >> 32).wrapping_cast());
limbs[2] = MaybeUninit::new((self >> 64).wrapping_cast());
limbs[3] = MaybeUninit::new((self >> 96).wrapping_cast());
}
}
is_zero! {}
}
#[cfg(gmp_limb_bits_64)]
#[inline]
const fn from_u128(val: u128) -> (c_int, Limbs) {
if val == 0 {
(0, small_limbs![])
} else if val <= 0xffff_ffff_ffff_ffff {
(1, small_limbs![val as limb_t])
} else {
(2, small_limbs![val as limb_t, (val >> 64) as limb_t])
}
}
#[cfg(gmp_limb_bits_32)]
#[inline]
const fn from_u128(val: u128) -> (c_int, Limbs) {
if val == 0 {
(0, small_limbs![])
} else if val <= 0xffff_ffff {
(1, small_limbs![val as limb_t])
} else if val <= 0xffff_ffff_ffff_ffff {
(2, small_limbs![val as limb_t, (val >> 32) as limb_t])
} else if val <= 0xffff_ffff_ffff_ffff_ffff_ffff {
(
3,
small_limbs![val as limb_t, (val >> 32) as limb_t, (val >> 64) as limb_t],
)
} else {
(
4,
small_limbs![
val as limb_t,
(val >> 32) as limb_t,
(val >> 64) as limb_t,
(val >> 96) as limb_t
],
)
}
}
impl ToMini for usize {}
impl SealedToMini for usize {
#[cfg(target_pointer_width = "32")]
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
self.az::<u32>().copy(size, limbs);
}
#[cfg(target_pointer_width = "64")]
#[inline]
fn copy(self, size: &mut c_int, limbs: &mut Limbs) {
self.az::<u64>().copy(size, limbs);
}
is_zero! {}
}
#[cfg(target_pointer_width = "32")]
#[inline]
const fn from_usize(val: usize) -> (c_int, Limbs) {
from_u32(val as u32)
}
#[cfg(target_pointer_width = "64")]
#[inline]
const fn from_usize(val: usize) -> (c_int, Limbs) {
from_u64(val as u64)
}
impl<T: ToMini> Assign<T> for MiniInteger {
#[inline]
fn assign(&mut self, src: T) {
src.copy(&mut self.inner.size, &mut self.limbs);
}
}
impl<T: ToMini> From<T> for MiniInteger {
#[inline]
fn from(src: T) -> Self {
let mut size = 0;
let mut limbs = small_limbs![];
src.copy(&mut size, &mut limbs);
MiniInteger {
inner: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size,
d: NonNull::dangling(),
},
limbs,
}
}
}
impl Assign<&Self> for MiniInteger {
#[inline]
fn assign(&mut self, other: &Self) {
self.clone_from(other);
}
}
impl Assign for MiniInteger {
#[inline]
fn assign(&mut self, other: Self) {
*self = other;
}
}
#[cfg(test)]
mod tests {
use crate::integer::MiniInteger;
use crate::{Assign, Integer};
#[test]
fn check_assign() {
let mut i = MiniInteger::from(-1i32);
assert_eq!(*i.borrow_excl(), -1);
let other = MiniInteger::from(2i32);
i.assign(&other);
assert_eq!(*i.borrow_excl(), 2);
i.assign(6u8);
assert_eq!(*i.borrow_excl(), 6);
i.assign(-6i8);
assert_eq!(*i.borrow_excl(), -6);
i.assign(other);
assert_eq!(*i.borrow_excl(), 2);
i.assign(6u16);
assert_eq!(*i.borrow_excl(), 6);
i.assign(-6i16);
assert_eq!(*i.borrow_excl(), -6);
i.assign(6u32);
assert_eq!(*i.borrow_excl(), 6);
i.assign(-6i32);
assert_eq!(*i.borrow_excl(), -6);
i.assign(0xf_0000_0006u64);
assert_eq!(*i.borrow_excl(), 0xf_0000_0006u64);
i.assign(-0xf_0000_0006i64);
assert_eq!(*i.borrow_excl(), -0xf_0000_0006i64);
i.assign((6u128 << 64) | 7u128);
assert_eq!(*i.borrow_excl(), (6u128 << 64) | 7u128);
i.assign((-6i128 << 64) | 7i128);
assert_eq!(*i.borrow_excl(), (-6i128 << 64) | 7i128);
i.assign(6usize);
assert_eq!(*i.borrow_excl(), 6);
i.assign(-6isize);
assert_eq!(*i.borrow_excl(), -6);
i.assign(0u32);
assert_eq!(*i.borrow_excl(), 0);
}
#[test]
fn check_traits() {
assert!(MiniInteger::default().borrow_excl().is_zero());
let mini = MiniInteger::from(-5);
let check = Integer::from(-5);
assert_eq!(format!("{mini}"), format!("{check}"));
assert_eq!(format!("{mini:?}"), format!("{check:?}"));
assert_eq!(format!("{mini:b}"), format!("{check:b}"));
assert_eq!(format!("{mini:o}"), format!("{check:o}"));
assert_eq!(format!("{mini:x}"), format!("{check:x}"));
assert_eq!(format!("{mini:X}"), format!("{check:X}"));
}
macro_rules! compare_conv {
($T:ident, $fn:ident, [$($val:expr),+] $( as $U:ident)?) => {
for &val in &[$($val),+] {
let a = MiniInteger::from(val);
let b = MiniInteger::$fn(val);
let mut c = MiniInteger::new();
c.assign(val);
assert_eq!(*a.borrow(), val $(as $U)?);
assert_eq!(*b.borrow(), val $(as $U)?);
assert_eq!(*c.borrow(), val $(as $U)?);
}
};
}
#[test]
fn check_equiv_convs() {
compare_conv!(bool, const_from_bool, [false, true] as u8);
compare_conv!(i8, const_from_i8, [i8::MIN, 0, i8::MAX]);
compare_conv!(i16, const_from_i16, [i16::MIN, 0, i16::MAX]);
compare_conv!(i32, const_from_i32, [i32::MIN, 0, i32::MAX]);
compare_conv!(i64, const_from_i64, [i64::MIN, 0, i64::MAX]);
compare_conv!(i128, const_from_i128, [i128::MIN, 0, i128::MAX]);
compare_conv!(isize, const_from_isize, [isize::MIN, 0, isize::MAX]);
compare_conv!(u8, const_from_u8, [0, u8::MAX]);
compare_conv!(u16, const_from_u16, [0, u16::MAX]);
compare_conv!(u32, const_from_u32, [0, u32::MAX]);
compare_conv!(u64, const_from_u64, [0, u64::MAX]);
compare_conv!(u128, const_from_u128, [0, u128::MAX]);
compare_conv!(usize, const_from_usize, [0, usize::MAX]);
}
}