use crate::{
cast,
integer::{small::Mpz, ToSmall},
misc::{Limbs, MaybeLimb, LIMBS_IN_SMALL},
Assign, Rational,
};
use gmp_mpfr_sys::gmp;
use std::{mem, ops::Deref, sync::atomic::Ordering};
#[repr(C)]
pub struct SmallRational {
inner: Mpq,
first_limbs: Limbs,
last_limbs: Limbs,
}
impl Clone for SmallRational {
#[inline]
fn clone(&self) -> SmallRational {
let (first_limbs, last_limbs) = if self.num_is_first() {
(&self.first_limbs, &self.last_limbs)
} else {
(&self.last_limbs, &self.first_limbs)
};
SmallRational {
inner: self.inner.clone(),
first_limbs: *first_limbs,
last_limbs: *last_limbs,
}
}
}
#[derive(Clone)]
#[repr(C)]
struct Mpq {
num: Mpz,
den: Mpz,
}
fn _static_assertions() {
static_assert_size!(Mpq, gmp::mpq_t);
}
impl Default for SmallRational {
#[inline]
fn default() -> Self {
SmallRational::new()
}
}
impl SmallRational {
#[inline]
pub fn new() -> Self {
SmallRational {
inner: Mpq {
num: Mpz {
alloc: cast::cast(LIMBS_IN_SMALL),
size: 0,
d: Default::default(),
},
den: Mpz {
alloc: cast::cast(LIMBS_IN_SMALL),
size: 1,
d: Default::default(),
},
},
first_limbs: small_limbs![0],
last_limbs: small_limbs![1],
}
}
#[inline]
pub unsafe fn as_nonreallocating_rational(&mut self) -> &mut Rational {
self.update_d();
let ptr = cast_ptr_mut!(&mut self.inner, Rational);
&mut *ptr
}
pub unsafe fn from_canonical<Num: ToSmall, Den: ToSmall>(num: Num, den: Den) -> Self {
let mut dst = SmallRational::default();
dst.assign_canonical(num, den);
dst
}
pub unsafe fn assign_canonical<Num: ToSmall, Den: ToSmall>(&mut self, num: Num, den: Den) {
let (num_limbs, den_limbs) = if self.num_is_first() {
(&mut self.first_limbs, &mut self.last_limbs)
} else {
(&mut self.last_limbs, &mut self.first_limbs)
};
num.copy(&mut self.inner.num.size, num_limbs);
den.copy(&mut self.inner.den.size, den_limbs);
}
#[inline]
fn num_is_first(&self) -> bool {
(self.inner.num.d.load(Ordering::Relaxed) as usize)
<= (self.inner.den.d.load(Ordering::Relaxed) as usize)
}
#[inline]
fn update_d(&self) {
let first = self.first_limbs[0].as_ptr() as *mut gmp::limb_t;
let last = self.last_limbs[0].as_ptr() as *mut gmp::limb_t;
let (num_d, den_d) = if self.num_is_first() {
(first, last)
} else {
(last, first)
};
self.inner.num.d.store(num_d, Ordering::Relaxed);
self.inner.den.d.store(den_d, Ordering::Relaxed);
}
}
impl Deref for SmallRational {
type Target = Rational;
#[inline]
fn deref(&self) -> &Rational {
self.update_d();
let ptr = cast_ptr!(&self.inner, Rational);
unsafe { &*ptr }
}
}
impl<Num: ToSmall> Assign<Num> for SmallRational {
#[inline]
fn assign(&mut self, src: Num) {
let (num_limbs, den_limbs) = if self.num_is_first() {
(&mut self.first_limbs, &mut self.last_limbs)
} else {
(&mut self.last_limbs, &mut self.first_limbs)
};
src.copy(&mut self.inner.num.size, num_limbs);
self.inner.den.size = 1;
den_limbs[0] = MaybeLimb::new(1);
}
}
impl<Num: ToSmall> From<Num> for SmallRational {
fn from(src: Num) -> Self {
let mut dst = SmallRational::default();
src.copy(&mut dst.inner.num.size, &mut dst.first_limbs);
dst.inner.den.size = 1;
dst.last_limbs[0] = MaybeLimb::new(1);
dst
}
}
impl<Num: ToSmall, Den: ToSmall> Assign<(Num, Den)> for SmallRational {
fn assign(&mut self, src: (Num, Den)) {
assert!(!src.1.is_zero(), "division by zero");
{
let (num_limbs, den_limbs) = if self.num_is_first() {
(&mut self.first_limbs, &mut self.last_limbs)
} else {
(&mut self.last_limbs, &mut self.first_limbs)
};
src.0.copy(&mut self.inner.num.size, num_limbs);
src.1.copy(&mut self.inner.den.size, den_limbs);
}
unsafe {
gmp::mpq_canonicalize(self.as_nonreallocating_rational().as_raw_mut());
}
}
}
impl<Num: ToSmall, Den: ToSmall> From<(Num, Den)> for SmallRational {
fn from(src: (Num, Den)) -> Self {
let mut dst = SmallRational::default();
dst.assign(src);
dst
}
}
impl Assign<&Self> for SmallRational {
#[inline]
fn assign(&mut self, other: &Self) {
self.clone_from(other);
}
}
impl Assign for SmallRational {
#[inline]
fn assign(&mut self, other: Self) {
drop(mem::replace(self, other));
}
}
#[cfg(test)]
mod tests {
use crate::{rational::SmallRational, Assign};
#[test]
fn check_assign() {
let mut r = SmallRational::from((1, 2));
assert_eq!(*r, (1, 2));
r.assign(3);
assert_eq!(*r, 3);
let other = SmallRational::from((4, 5));
r.assign(&other);
assert_eq!(*r, (4, 5));
r.assign((6, 7));
assert_eq!(*r, (6, 7));
r.assign(other);
assert_eq!(*r, (4, 5));
}
}