use crate::{
ext::xmpfr,
float::{small::Mpfr, ToSmall},
Assign, Complex,
};
use core::{
cell::UnsafeCell,
mem::{self, MaybeUninit},
ops::Deref,
ptr,
};
use gmp_mpfr_sys::{
gmp::{self, limb_t},
mpc::mpc_t,
mpfr::mpfr_t,
};
const LIMBS_IN_SMALL: usize = (128 / gmp::LIMB_BITS) as usize;
type Limbs = [MaybeUninit<limb_t>; LIMBS_IN_SMALL];
pub struct SmallComplex {
inner: Mpc,
first_limbs: Limbs,
last_limbs: Limbs,
}
unsafe impl Send for SmallComplex {}
impl Clone for SmallComplex {
#[inline]
fn clone(&self) -> SmallComplex {
let (first_limbs, last_limbs) = if self.re_is_first() {
(&self.first_limbs, &self.last_limbs)
} else {
(&self.last_limbs, &self.first_limbs)
};
SmallComplex {
inner: self.inner.clone(),
first_limbs: *first_limbs,
last_limbs: *last_limbs,
}
}
}
#[derive(Clone)]
#[repr(C)]
struct Mpc {
re: Mpfr,
im: Mpfr,
}
static_assert_same_layout!(Mpc, mpc_t);
impl SmallComplex {
#[inline]
pub unsafe fn as_nonreallocating_complex(&mut self) -> &mut Complex {
self.update_d();
let ptr = cast_ptr_mut!(&mut self.inner, Complex);
&mut *ptr
}
#[inline]
fn re_is_first(&self) -> bool {
unsafe { (*self.inner.re.d.get() as usize) <= (*self.inner.im.d.get() as usize) }
}
#[inline]
fn update_d(&self) {
let first = self.first_limbs[0].as_ptr() as *mut limb_t;
let last = self.last_limbs[0].as_ptr() as *mut limb_t;
let (re_d, im_d) = if self.re_is_first() {
(first, last)
} else {
(last, first)
};
unsafe {
*self.inner.re.d.get() = re_d;
*self.inner.im.d.get() = im_d;
}
}
}
impl Deref for SmallComplex {
type Target = Complex;
#[inline]
fn deref(&self) -> &Complex {
self.update_d();
let ptr = cast_ptr!(&self.inner, Complex);
unsafe { &*ptr }
}
}
impl<Re: ToSmall> Assign<Re> for SmallComplex {
fn assign(&mut self, src: Re) {
unsafe {
src.copy(&mut self.inner.re, &mut self.first_limbs);
xmpfr::custom_zero(
cast_ptr_mut!(&mut self.inner.im, mpfr_t),
self.last_limbs[0].as_mut_ptr(),
self.inner.re.prec,
);
}
}
}
impl<Re: ToSmall> From<Re> for SmallComplex {
fn from(src: Re) -> Self {
let mut dst = SmallComplex {
inner: Mpc {
re: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(ptr::null_mut()),
},
im: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(ptr::null_mut()),
},
},
first_limbs: small_limbs![],
last_limbs: small_limbs![],
};
unsafe {
src.copy(&mut dst.inner.re, &mut dst.first_limbs);
xmpfr::custom_zero(
cast_ptr_mut!(&mut dst.inner.im, mpfr_t),
dst.last_limbs[0].as_mut_ptr(),
dst.inner.re.prec,
);
}
dst
}
}
impl<Re: ToSmall, Im: ToSmall> Assign<(Re, Im)> for SmallComplex {
fn assign(&mut self, src: (Re, Im)) {
unsafe {
src.0.copy(&mut self.inner.re, &mut self.first_limbs);
src.1.copy(&mut self.inner.im, &mut self.last_limbs);
}
}
}
impl<Re: ToSmall, Im: ToSmall> From<(Re, Im)> for SmallComplex {
fn from(src: (Re, Im)) -> Self {
let mut dst = SmallComplex {
inner: Mpc {
re: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(ptr::null_mut()),
},
im: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(ptr::null_mut()),
},
},
first_limbs: small_limbs![],
last_limbs: small_limbs![],
};
unsafe {
src.0.copy(&mut dst.inner.re, &mut dst.first_limbs);
src.1.copy(&mut dst.inner.im, &mut dst.last_limbs);
}
dst
}
}
impl Assign<&Self> for SmallComplex {
#[inline]
fn assign(&mut self, other: &Self) {
self.clone_from(other);
}
}
impl Assign for SmallComplex {
#[inline]
fn assign(&mut self, other: Self) {
drop(mem::replace(self, other));
}
}
#[cfg(test)]
mod tests {
use crate::{
complex::SmallComplex,
float::{self, FreeCache},
Assign,
};
#[test]
fn check_assign() {
let mut c = SmallComplex::from((1.0, 2.0));
assert_eq!(*c, (1.0, 2.0));
c.assign(3.0);
assert_eq!(*c, (3.0, 0.0));
let other = SmallComplex::from((4.0, 5.0));
c.assign(&other);
assert_eq!(*c, (4.0, 5.0));
c.assign((6.0, 7.0));
assert_eq!(*c, (6.0, 7.0));
c.assign(other);
assert_eq!(*c, (4.0, 5.0));
float::free_cache(FreeCache::All);
}
fn swapped_parts(small: &SmallComplex) -> bool {
unsafe {
let re = (*small.real().as_raw()).d;
let im = (*small.imag().as_raw()).d;
(re as usize) > (im as usize)
}
}
#[test]
fn check_swapped_parts() {
let mut c = SmallComplex::from((1, 2));
assert!(!swapped_parts(&c));
assert_eq!(*c.clone(), *c);
unsafe {
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert!(swapped_parts(&c));
assert_eq!(*c, (-2, 1));
assert_eq!(*c.clone(), *c);
c.assign(12);
assert!(!swapped_parts(&c));
assert_eq!(*c, 12);
unsafe {
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert!(swapped_parts(&c));
c.assign((4, 5));
assert!(!swapped_parts(&c));
assert_eq!(*c, (4, 5));
}
}