use crate::{
ext::xmpfr,
float::{self, small::Mpfr, ToSmall},
Assign, Complex,
};
use core::{
cell::UnsafeCell,
mem::{self, MaybeUninit},
ops::Deref,
ptr::NonNull,
};
use gmp_mpfr_sys::{
gmp::{self, limb_t},
mpc::mpc_t,
mpfr::{mpfr_t, prec_t},
};
const LIMBS_IN_SMALL: usize = (128 / gmp::LIMB_BITS) as usize;
type Limbs = [MaybeUninit<limb_t>; LIMBS_IN_SMALL];
#[derive(Clone)]
pub struct SmallComplex {
inner: Mpc,
first_limbs: Limbs,
last_limbs: Limbs,
}
unsafe impl Send for SmallComplex {}
impl Default for SmallComplex {
#[inline]
fn default() -> Self {
SmallComplex::new()
}
}
#[derive(Clone)]
#[repr(C)]
struct Mpc {
re: Mpfr,
im: Mpfr,
}
static_assert_same_layout!(Mpc, mpc_t);
impl SmallComplex {
#[inline]
pub const fn new() -> Self {
SmallComplex {
inner: Mpc {
re: Mpfr {
prec: float::prec_min() as prec_t,
sign: 1,
exp: xmpfr::EXP_ZERO,
d: UnsafeCell::new(NonNull::dangling()),
},
im: Mpfr {
prec: float::prec_min() as prec_t,
sign: 1,
exp: xmpfr::EXP_ZERO,
d: UnsafeCell::new(NonNull::dangling()),
},
},
first_limbs: small_limbs![],
last_limbs: small_limbs![],
}
}
#[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() <= *self.inner.im.d.get() }
}
#[inline]
fn update_d(&self) {
let first = NonNull::<[MaybeUninit<limb_t>]>::from(&self.first_limbs[..]);
let last = NonNull::<[MaybeUninit<limb_t>]>::from(&self.last_limbs[..]);
let (re_d, im_d) = if self.re_is_first() {
(first, last)
} else {
(last, first)
};
unsafe {
*self.inner.re.d.get() = re_d.cast();
*self.inner.im.d.get() = im_d.cast();
}
}
}
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),
cast_ptr_mut!(self.last_limbs.as_mut_ptr(), limb_t),
self.inner.re.prec,
);
}
}
}
impl<Re: ToSmall> From<Re> for SmallComplex {
fn from(src: Re) -> Self {
let mut inner = Mpc {
re: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(NonNull::dangling()),
},
im: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(NonNull::dangling()),
},
};
let mut re_limbs = small_limbs![];
let mut im_limbs = small_limbs![];
unsafe {
src.copy(&mut inner.re, &mut re_limbs);
xmpfr::custom_zero(
cast_ptr_mut!(&mut inner.im, mpfr_t),
cast_ptr_mut!(im_limbs.as_mut_ptr(), limb_t),
inner.re.prec,
);
}
if re_limbs.as_ptr() <= im_limbs.as_ptr() {
SmallComplex {
inner,
first_limbs: re_limbs,
last_limbs: im_limbs,
}
} else {
SmallComplex {
inner,
first_limbs: im_limbs,
last_limbs: re_limbs,
}
}
}
}
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 inner = Mpc {
re: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(NonNull::dangling()),
},
im: Mpfr {
prec: 0,
sign: 0,
exp: 0,
d: UnsafeCell::new(NonNull::dangling()),
},
};
let mut re_limbs = small_limbs![];
let mut im_limbs = small_limbs![];
unsafe {
src.0.copy(&mut inner.re, &mut re_limbs);
src.1.copy(&mut inner.im, &mut im_limbs);
}
if re_limbs.as_ptr() <= im_limbs.as_ptr() {
SmallComplex {
inner,
first_limbs: re_limbs,
last_limbs: im_limbs,
}
} else {
SmallComplex {
inner,
first_limbs: im_limbs,
last_limbs: re_limbs,
}
}
}
}
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 > im
}
}
#[test]
fn check_swapped_parts() {
let mut c = SmallComplex::from((1, 2));
assert_eq!(*c, (1, 2));
assert_eq!(*c.clone(), *c);
let mut orig_swapped_parts = swapped_parts(&c);
unsafe {
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert_eq!(*c, (-2, 1));
assert_eq!(*c.clone(), *c);
assert!(swapped_parts(&c) != orig_swapped_parts);
c.assign(12);
assert_eq!(*c, 12);
assert_eq!(*c.clone(), *c);
orig_swapped_parts = swapped_parts(&c);
unsafe {
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert_eq!(*c, (0, 12));
assert_eq!(*c.clone(), *c);
assert!(swapped_parts(&c) != orig_swapped_parts);
c.assign((4, 5));
assert_eq!(*c, (4, 5));
assert_eq!(*c.clone(), *c);
orig_swapped_parts = swapped_parts(&c);
unsafe {
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert_eq!(*c, (-5, 4));
assert_eq!(*c.clone(), *c);
assert!(swapped_parts(&c) != orig_swapped_parts);
}
}