use crate::ext::xmpq;
use crate::integer::{MiniInteger, ToMini};
use crate::misc;
use crate::rational::BorrowRational;
use crate::{Assign, Rational};
use az::Cast;
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, mpq_t, mpz_t};
const LIMBS_IN_SMALL: usize = (128 / gmp::LIMB_BITS) as usize;
type Limbs = [MaybeUninit<limb_t>; LIMBS_IN_SMALL];
#[derive(Clone, Copy)]
pub struct MiniRational {
inner: mpq_t,
first_limbs: Limbs,
last_limbs: Limbs,
}
static_assert!(mem::size_of::<Limbs>() == 16);
unsafe impl Send for MiniRational {}
unsafe impl Sync for MiniRational {}
impl Default for MiniRational {
#[inline]
fn default() -> Self {
MiniRational::new()
}
}
impl Display for MiniRational {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Display::fmt(&*self.borrow(), f)
}
}
impl Debug for MiniRational {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Debug::fmt(&*self.borrow(), f)
}
}
impl Binary for MiniRational {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Binary::fmt(&*self.borrow(), f)
}
}
impl Octal for MiniRational {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Octal::fmt(&*self.borrow(), f)
}
}
impl LowerHex for MiniRational {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
LowerHex::fmt(&*self.borrow(), f)
}
}
impl UpperHex for MiniRational {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
UpperHex::fmt(&*self.borrow(), f)
}
}
impl MiniRational {
#[inline]
pub const fn new() -> Self {
let d = NonNull::dangling();
MiniRational {
inner: mpq_t {
num: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size: 0,
d,
},
den: mpz_t {
alloc: LIMBS_IN_SMALL as c_int,
size: 1,
d,
},
},
first_limbs: small_limbs![],
last_limbs: small_limbs![1],
}
}
#[inline]
pub const fn const_from_integer(val: MiniInteger) -> Self {
let MiniInteger {
inner: mut num_inner,
limbs: num_limbs,
} = val;
let MiniInteger {
inner: mut den_inner,
limbs: den_limbs,
} = MiniInteger::const_from_u8(1);
let d = NonNull::dangling();
num_inner.d = d;
den_inner.d = d;
MiniRational {
inner: mpq_t {
num: num_inner,
den: den_inner,
},
first_limbs: num_limbs,
last_limbs: den_limbs,
}
}
#[inline]
pub unsafe fn as_nonreallocating_rational(&mut self) -> &mut Rational {
let first = NonNull::<[MaybeUninit<limb_t>]>::from(&self.first_limbs[..]).cast();
let last = NonNull::<[MaybeUninit<limb_t>]>::from(&self.last_limbs[..]).cast();
let (num_d, den_d) = if self.num_is_first() {
(first, last)
} else {
(last, first)
};
self.inner.num.d = num_d;
self.inner.den.d = den_d;
let ptr = misc::cast_ptr_mut(&mut self.inner);
unsafe { &mut *ptr }
}
#[inline]
pub const fn borrow(&self) -> BorrowRational<'_> {
let first_d: *const Limbs = &self.first_limbs;
let last_d: *const Limbs = &self.last_limbs;
let (num_d, den_d) = if self.num_is_first() {
(first_d, last_d)
} else {
(last_d, first_d)
};
unsafe {
BorrowRational::from_raw(mpq_t {
num: mpz_t {
alloc: self.inner.num.alloc,
size: self.inner.num.size,
d: NonNull::new_unchecked(num_d.cast_mut().cast()),
},
den: mpz_t {
alloc: self.inner.den.alloc,
size: self.inner.den.size,
d: NonNull::new_unchecked(den_d.cast_mut().cast()),
},
})
}
}
#[inline]
pub fn borrow_excl(&mut self) -> &Rational {
unsafe { &*self.as_nonreallocating_rational() }
}
pub unsafe fn from_canonical<Num: ToMini, Den: ToMini>(num: Num, den: Den) -> Self {
let mut num_size = 0;
let mut den_size = 0;
let mut num_limbs: Limbs = small_limbs![];
let mut den_limbs: Limbs = small_limbs![];
num.copy(&mut num_size, &mut num_limbs);
den.copy(&mut den_size, &mut den_limbs);
let d = NonNull::dangling();
MiniRational {
inner: mpq_t {
num: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size: num_size,
d,
},
den: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size: den_size,
d,
},
},
first_limbs: num_limbs,
last_limbs: den_limbs,
}
}
pub unsafe fn assign_canonical<Num: ToMini, Den: ToMini>(&mut self, num: Num, den: Den) {
self.inner.den.d = self.inner.num.d;
num.copy(&mut self.inner.num.size, &mut self.first_limbs);
den.copy(&mut self.inner.den.size, &mut self.last_limbs);
}
#[inline]
const fn num_is_first(&self) -> bool {
let num_ptr = self.inner.num.d.as_ptr();
let den_ptr = self.inner.den.d.as_ptr();
unsafe { num_ptr.offset_from(den_ptr) <= 0 }
}
}
impl Assign<MiniInteger> for MiniRational {
#[inline]
fn assign(&mut self, src: MiniInteger) {
self.inner.den.d = self.inner.num.d;
self.inner.num.size = src.inner.size;
self.first_limbs = src.limbs;
self.inner.den.size = 1;
self.last_limbs[0] = MaybeUninit::new(1);
}
}
impl From<MiniInteger> for MiniRational {
#[inline]
fn from(src: MiniInteger) -> Self {
MiniRational::const_from_integer(src)
}
}
impl<Num: ToMini> Assign<Num> for MiniRational {
#[inline]
fn assign(&mut self, src: Num) {
self.inner.den.d = self.inner.num.d;
src.copy(&mut self.inner.num.size, &mut self.first_limbs);
self.inner.den.size = 1;
self.last_limbs[0] = MaybeUninit::new(1);
}
}
impl<Num: ToMini> From<Num> for MiniRational {
fn from(src: Num) -> Self {
let mut num_size = 0;
let mut num_limbs = small_limbs![];
src.copy(&mut num_size, &mut num_limbs);
let d = NonNull::dangling();
MiniRational {
inner: mpq_t {
num: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size: num_size,
d,
},
den: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size: 1,
d,
},
},
first_limbs: num_limbs,
last_limbs: small_limbs![1],
}
}
}
impl<Num: ToMini, Den: ToMini> Assign<(Num, Den)> for MiniRational {
fn assign(&mut self, src: (Num, Den)) {
assert!(!src.1.is_zero(), "division by zero");
self.inner.den.d = self.inner.num.d;
src.0.copy(&mut self.inner.num.size, &mut self.first_limbs);
src.1.copy(&mut self.inner.den.size, &mut self.last_limbs);
xmpq::canonicalize(unsafe { self.as_nonreallocating_rational() });
}
}
impl<Num: ToMini, Den: ToMini> From<(Num, Den)> for MiniRational {
fn from(src: (Num, Den)) -> Self {
assert!(!src.1.is_zero(), "division by zero");
let d = NonNull::dangling();
let mut ret = MiniRational {
inner: mpq_t {
num: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size: 0,
d,
},
den: mpz_t {
alloc: LIMBS_IN_SMALL.cast(),
size: 0,
d,
},
},
first_limbs: small_limbs![],
last_limbs: small_limbs![],
};
src.0.copy(&mut ret.inner.num.size, &mut ret.first_limbs);
src.1.copy(&mut ret.inner.den.size, &mut ret.last_limbs);
xmpq::canonicalize(unsafe { ret.as_nonreallocating_rational() });
ret
}
}
impl Assign<&Self> for MiniRational {
#[inline]
fn assign(&mut self, other: &Self) {
self.clone_from(other);
}
}
impl Assign for MiniRational {
#[inline]
fn assign(&mut self, other: Self) {
*self = other;
}
}
#[cfg(test)]
mod tests {
use crate::integer::MiniInteger;
use crate::rational::MiniRational;
use crate::{Assign, Rational};
#[test]
fn check_assign() {
let mut r = MiniRational::from((1, 2));
assert_eq!(*r.borrow_excl(), MiniRational::from((1, 2)));
r.assign(3);
assert_eq!(*r.borrow_excl(), 3);
let other = MiniRational::from((4, 5));
r.assign(&other);
assert_eq!(*r.borrow_excl(), MiniRational::from((4, 5)));
r.assign((6, 7));
assert_eq!(*r.borrow_excl(), MiniRational::from((6, 7)));
r.assign(other);
assert_eq!(*r.borrow_excl(), MiniRational::from((4, 5)));
}
fn swapped_parts(small: &MiniRational) -> bool {
unsafe {
let borrow = small.borrow();
let num = (*borrow.numer().as_raw()).d;
let den = (*borrow.denom().as_raw()).d;
num > den
}
}
#[test]
fn check_swapped_parts() {
let mut r = MiniRational::from((2, 3));
assert_eq!(*r.borrow_excl(), MiniRational::from((2, 3)));
assert_eq!(*r.clone().borrow_excl(), r);
let mut orig_swapped_parts = swapped_parts(&r);
unsafe {
r.as_nonreallocating_rational().recip_mut();
}
assert_eq!(*r.borrow_excl(), MiniRational::from((3, 2)));
assert_eq!(*r.clone().borrow_excl(), r);
assert!(swapped_parts(&r) != orig_swapped_parts);
unsafe {
r.assign_canonical(5, 7);
}
assert_eq!(*r.borrow_excl(), MiniRational::from((5, 7)));
assert_eq!(*r.clone().borrow_excl(), r);
orig_swapped_parts = swapped_parts(&r);
unsafe {
r.as_nonreallocating_rational().recip_mut();
}
assert_eq!(*r.borrow_excl(), MiniRational::from((7, 5)));
assert_eq!(*r.clone().borrow_excl(), r);
assert!(swapped_parts(&r) != orig_swapped_parts);
r.assign(2);
assert_eq!(*r.borrow_excl(), 2);
assert_eq!(*r.clone().borrow_excl(), r);
orig_swapped_parts = swapped_parts(&r);
unsafe {
r.as_nonreallocating_rational().recip_mut();
}
assert_eq!(*r.borrow_excl(), MiniRational::from((1, 2)));
assert_eq!(*r.clone().borrow_excl(), r);
assert!(swapped_parts(&r) != orig_swapped_parts);
r.assign((3, -5));
assert_eq!(*r.borrow_excl(), MiniRational::from((-3, 5)));
assert_eq!(*r.clone().borrow_excl(), r);
orig_swapped_parts = swapped_parts(&r);
unsafe {
r.as_nonreallocating_rational().recip_mut();
}
assert_eq!(*r.borrow_excl(), MiniRational::from((-5, 3)));
assert_eq!(*r.clone().borrow_excl(), r);
assert!(swapped_parts(&r) != orig_swapped_parts);
}
#[test]
fn check_traits() {
assert!(MiniRational::default().borrow_excl().is_zero());
let mini = MiniRational::from((14, -10));
let check = Rational::from((14, -10));
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, [$($val:expr),+] $( as $U:ident)?) => {
for &val in &[$($val),+] {
let integer = MiniInteger::from(val);
let a = MiniRational::from(integer);
let b = MiniRational::const_from_integer(integer);
let mut c = MiniInteger::new();
c.assign(integer);
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, [false, true] as u8);
compare_conv!(i8, [i8::MIN, 0, i8::MAX]);
compare_conv!(i16, [i16::MIN, 0, i16::MAX]);
compare_conv!(i32, [i32::MIN, 0, i32::MAX]);
compare_conv!(i64, [i64::MIN, 0, i64::MAX]);
compare_conv!(i128, [i128::MIN, 0, i128::MAX]);
compare_conv!(isize, [isize::MIN, 0, isize::MAX]);
compare_conv!(u8, [0, u8::MAX]);
compare_conv!(u16, [0, u16::MAX]);
compare_conv!(u32, [0, u32::MAX]);
compare_conv!(u64, [0, u64::MAX]);
compare_conv!(u128, [0, u128::MAX]);
compare_conv!(usize, [0, usize::MAX]);
}
}