use crate::cast::cast;
use crate::integer::small::{Limbs, Mpz, LIMBS_IN_SMALL_INTEGER};
use crate::integer::ToSmall;
use crate::{Assign, Rational};
use gmp_mpfr_sys::gmp;
use std::mem;
use std::ops::Deref;
use std::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()
}
}
#[cfg(gmp_limb_bits_64)]
const LIMBS_ONE: Limbs = [1, 0];
#[cfg(gmp_limb_bits_32)]
const LIMBS_ONE: Limbs = [1, 0, 0, 0];
impl SmallRational {
#[inline]
pub fn new() -> Self {
SmallRational {
inner: Mpq {
num: Mpz {
alloc: cast(LIMBS_IN_SMALL_INTEGER),
size: 0,
d: Default::default(),
},
den: Mpz {
alloc: cast(LIMBS_IN_SMALL_INTEGER),
size: 1,
d: Default::default(),
},
},
first_limbs: [0; LIMBS_IN_SMALL_INTEGER],
last_limbs: LIMBS_ONE,
}
}
#[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, Den>(num: Num, den: Den) -> Self
where
Num: ToSmall,
Den: ToSmall,
{
let mut dst = SmallRational::default();
dst.assign_canonical(num, den);
dst
}
pub unsafe fn assign_canonical<Num, Den>(&mut self, num: Num, den: Den)
where
Num: ToSmall,
Den: ToSmall,
{
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);
}
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 *const gmp::limb_t as *mut gmp::limb_t;
let last =
&self.last_limbs[0] as *const gmp::limb_t 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> Assign<Num> for SmallRational
where
Num: ToSmall,
{
#[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] = 1;
}
}
impl<Num> From<Num> for SmallRational
where
Num: ToSmall,
{
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] = 1;
dst
}
}
impl<Num, Den> Assign<(Num, Den)> for SmallRational
where
Num: ToSmall,
Den: ToSmall,
{
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, Den> From<(Num, Den)> for SmallRational
where
Num: ToSmall,
Den: ToSmall,
{
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) {
mem::drop(mem::replace(self, other));
}
}
#[cfg(test)]
mod tests {
use crate::rational::SmallRational;
use crate::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));
}
}