use {Assign, Rational};
use gmp_mpfr_sys::gmp;
use std::mem;
use std::ops::Deref;
use std::os::raw::c_int;
use std::sync::atomic::{AtomicPtr, Ordering};
#[repr(C)]
pub struct SmallRational {
num: Mpz,
den: Mpz,
num_limbs: [gmp::limb_t; LIMBS_IN_SMALL_INTEGER],
den_limbs: [gmp::limb_t; LIMBS_IN_SMALL_INTEGER],
}
#[cfg(gmp_limb_bits_64)]
const LIMBS_IN_SMALL_INTEGER: usize = 1;
#[cfg(gmp_limb_bits_32)]
const LIMBS_IN_SMALL_INTEGER: usize = 2;
#[repr(C)]
pub struct Mpz {
alloc: c_int,
size: c_int,
d: AtomicPtr<gmp::limb_t>,
}
impl Default for SmallRational {
#[inline]
fn default() -> SmallRational {
SmallRational::new()
}
}
impl SmallRational {
#[inline]
pub fn new() -> SmallRational {
let mut ret = SmallRational {
num: Mpz {
size: 0,
alloc: LIMBS_IN_SMALL_INTEGER as c_int,
d: Default::default(),
},
den: Mpz {
size: 1,
alloc: LIMBS_IN_SMALL_INTEGER as c_int,
d: Default::default(),
},
num_limbs: [0; LIMBS_IN_SMALL_INTEGER],
den_limbs: [0; LIMBS_IN_SMALL_INTEGER],
};
ret.den_limbs[0] = 1;
ret
}
#[inline]
pub unsafe fn from_canonicalized_32(
neg: bool,
num: u32,
den: u32,
) -> SmallRational {
let mut ret = SmallRational::new();
if num != 0 {
ret.assign_canonicalized_32(neg, num, den);
}
ret
}
#[inline]
pub unsafe fn from_canonicalized_64(
neg: bool,
num: u64,
den: u64,
) -> SmallRational {
let mut ret = SmallRational::new();
if num != 0 {
ret.assign_canonicalized_64(neg, num, den);
}
ret
}
#[inline]
pub unsafe fn assign_canonicalized_32(
&mut self,
neg: bool,
num: u32,
den: u32,
) {
self.num.size = if neg { -1 } else { 1 };
self.num_limbs[0] = num.into();
self.den.size = 1;
self.den_limbs[0] = den.into();
}
#[inline]
pub unsafe fn assign_canonicalized_64(
&mut self,
neg: bool,
num: u64,
den: u64,
) {
#[cfg(gmp_limb_bits_64)]
{
self.num.size = if neg { -1 } else { 1 };
self.num_limbs[0] = num as gmp::limb_t;
self.den.size = 1;
self.den_limbs[0] = den as gmp::limb_t;
}
#[cfg(gmp_limb_bits_32)]
{
if num <= 0xffff_ffff {
self.num.size = if neg { -1 } else { 1 };
self.num_limbs[0] = num as u32 as gmp::limb_t;
} else {
self.num.size = if neg { -2 } else { 2 };
self.num_limbs[0] = num as u32 as gmp::limb_t;
self.num_limbs[1] = (num >> 32) as u32 as gmp::limb_t;
}
if den <= 0xffff_ffff {
self.den.size = 1;
self.den_limbs[0] = den as u32 as gmp::limb_t;
} else {
self.den.size = 2;
self.den_limbs[0] = den as u32 as gmp::limb_t;
self.den_limbs[1] = (den >> 32) as u32 as gmp::limb_t;
}
}
}
#[inline]
fn set_num_den_32(&mut self, neg: bool, num: u32, den: u32) {
assert_ne!(den, 0, "division by zero");
if num == 0 {
self.num.size = 0;
self.den.size = 1;
self.den_limbs[0] = 1;
return;
}
unsafe {
self.assign_canonicalized_32(neg, num, den);
}
self.update_d();
unsafe {
gmp::mpq_canonicalize((&mut self.num) as *mut _ as *mut _);
}
}
#[inline]
fn set_num_den_64(&mut self, neg: bool, num: u64, den: u64) {
assert_ne!(den, 0, "division by zero");
if num == 0 {
self.num.size = 0;
self.den.size = 1;
self.den_limbs[0] = 1;
return;
}
unsafe {
self.assign_canonicalized_64(neg, num, den);
}
self.update_d();
unsafe {
gmp::mpq_canonicalize((&mut self.num) as *mut _ as *mut _);
}
}
#[inline]
fn update_d(&self) {
assert_eq!(mem::size_of::<Mpz>(), mem::size_of::<gmp::mpz_t>());
let num_d = &self.num_limbs[0] as *const _ as *mut _;
self.num.d.store(num_d, Ordering::Relaxed);
let den_d = &self.den_limbs[0] as *const _ as *mut _;
self.den.d.store(den_d, Ordering::Relaxed);
}
}
impl Deref for SmallRational {
type Target = Rational;
#[inline]
fn deref(&self) -> &Rational {
self.update_d();
let ptr = (&self.num) as *const _ as *const _;
unsafe { &*ptr }
}
}
impl<T> From<T> for SmallRational
where
SmallRational: Assign<T>,
{
#[inline]
fn from(val: T) -> SmallRational {
let mut ret = SmallRational::new();
ret.assign(val);
ret
}
}
impl Assign<i32> for SmallRational {
#[inline]
fn assign(&mut self, num: i32) {
unsafe {
self.assign_canonicalized_32(num < 0, num.wrapping_abs() as u32, 1);
}
}
}
impl Assign<i64> for SmallRational {
#[inline]
fn assign(&mut self, num: i64) {
unsafe {
self.assign_canonicalized_64(num < 0, num.wrapping_abs() as u64, 1);
}
}
}
impl Assign<u32> for SmallRational {
#[inline]
fn assign(&mut self, num: u32) {
unsafe {
self.assign_canonicalized_32(false, num, 1);
}
}
}
impl Assign<u64> for SmallRational {
#[inline]
fn assign(&mut self, num: u64) {
unsafe {
self.assign_canonicalized_64(false, num, 1);
}
}
}
impl Assign<(i32, i32)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (i32, i32)) {
let num_neg = num < 0;
let den_neg = den < 0;
self.set_num_den_32(
num_neg != den_neg,
num.wrapping_abs() as u32,
den.wrapping_abs() as u32,
);
}
}
impl Assign<(i64, i64)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (i64, i64)) {
let num_neg = num < 0;
let den_neg = den < 0;
self.set_num_den_64(
num_neg != den_neg,
num.wrapping_abs() as u64,
den.wrapping_abs() as u64,
);
}
}
impl Assign<(i32, u32)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (i32, u32)) {
self.set_num_den_32(num < 0, num.wrapping_abs() as u32, den);
}
}
impl Assign<(i64, u64)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (i64, u64)) {
self.set_num_den_64(num < 0, num.wrapping_abs() as u64, den);
}
}
impl Assign<(u32, i32)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (u32, i32)) {
self.set_num_den_32(den < 0, num, den.wrapping_abs() as u32);
}
}
impl Assign<(u64, i64)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (u64, i64)) {
self.set_num_den_64(den < 0, num, den.wrapping_abs() as u64);
}
}
impl Assign<(u32, u32)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (u32, u32)) {
self.set_num_den_32(false, num, den);
}
}
impl Assign<(u64, u64)> for SmallRational {
#[inline]
fn assign(&mut self, (num, den): (u64, u64)) {
self.set_num_den_64(false, num, den);
}
}