use crate::complex::BorrowComplex;
use crate::ext::xmpfr;
use crate::float;
use crate::float::{MiniFloat, ToMini};
use crate::misc;
use crate::{Assign, Complex};
use core::fmt::{
Binary, Debug, Display, Formatter, LowerExp, LowerHex, Octal, Result as FmtResult, UpperExp,
UpperHex,
};
use core::mem;
use core::mem::MaybeUninit;
#[allow(unused_imports)]
use core::ops::Deref;
use core::ptr::NonNull;
use gmp_mpfr_sys::gmp;
use gmp_mpfr_sys::gmp::limb_t;
use gmp_mpfr_sys::mpc::mpc_t;
use gmp_mpfr_sys::mpfr::{mpfr_t, prec_t};
#[cfg(feature = "num-complex")]
use num_complex::Complex as NumComplex;
const LIMBS_IN_SMALL: usize = (128 / gmp::LIMB_BITS) as usize;
type Limbs = [MaybeUninit<limb_t>; LIMBS_IN_SMALL];
#[derive(Clone, Copy)]
pub struct MiniComplex {
inner: mpc_t,
first_limbs: Limbs,
last_limbs: Limbs,
}
static_assert!(mem::size_of::<Limbs>() == 16);
unsafe impl Send for MiniComplex {}
unsafe impl Sync for MiniComplex {}
impl Default for MiniComplex {
#[inline]
fn default() -> Self {
MiniComplex::new()
}
}
impl Display for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Display::fmt(&*self.borrow(), f)
}
}
impl Debug for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Debug::fmt(&*self.borrow(), f)
}
}
impl LowerExp for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
LowerExp::fmt(&*self.borrow(), f)
}
}
impl UpperExp for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
UpperExp::fmt(&*self.borrow(), f)
}
}
impl Binary for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Binary::fmt(&*self.borrow(), f)
}
}
impl Octal for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Octal::fmt(&*self.borrow(), f)
}
}
impl LowerHex for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
LowerHex::fmt(&*self.borrow(), f)
}
}
impl UpperHex for MiniComplex {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
UpperHex::fmt(&*self.borrow(), f)
}
}
impl MiniComplex {
#[inline]
pub const fn new() -> Self {
let d = NonNull::dangling();
MiniComplex {
inner: mpc_t {
re: mpfr_t {
prec: float::prec_min() as prec_t,
sign: 1,
exp: xmpfr::EXP_ZERO,
d,
},
im: mpfr_t {
prec: float::prec_min() as prec_t,
sign: 1,
exp: xmpfr::EXP_ZERO,
d,
},
},
first_limbs: small_limbs![],
last_limbs: small_limbs![],
}
}
#[inline]
pub const fn const_from_real(real: MiniFloat) -> Self {
let MiniFloat { inner, limbs } = real;
MiniComplex {
inner: mpc_t {
re: inner,
im: mpfr_t {
prec: inner.prec,
sign: 1,
exp: xmpfr::EXP_ZERO,
d: inner.d,
},
},
first_limbs: limbs,
last_limbs: small_limbs![],
}
}
#[inline]
pub const fn const_from_parts(real: MiniFloat, imag: MiniFloat) -> Self {
let MiniFloat {
inner: mut re_inner,
limbs: re_limbs,
} = real;
let MiniFloat {
inner: mut im_inner,
limbs: im_limbs,
} = imag;
let d = NonNull::dangling();
re_inner.d = d;
im_inner.d = d;
MiniComplex {
inner: mpc_t {
re: re_inner,
im: im_inner,
},
first_limbs: re_limbs,
last_limbs: im_limbs,
}
}
#[inline]
pub unsafe fn as_nonreallocating_complex(&mut self) -> &mut Complex {
let first = NonNull::<[MaybeUninit<limb_t>]>::from(&self.first_limbs[..]).cast();
let last = NonNull::<[MaybeUninit<limb_t>]>::from(&self.last_limbs[..]).cast();
let (re_d, im_d) = if self.re_is_first() {
(first, last)
} else {
(last, first)
};
self.inner.re.d = re_d;
self.inner.im.d = im_d;
let ptr = misc::cast_ptr_mut(&mut self.inner);
unsafe { &mut *ptr }
}
#[inline]
pub const fn borrow(&self) -> BorrowComplex<'_> {
let first_d: *const Limbs = &self.first_limbs;
let last_d: *const Limbs = &self.last_limbs;
let (re_d, im_d) = if self.re_is_first() {
(first_d, last_d)
} else {
(last_d, first_d)
};
unsafe {
BorrowComplex::from_raw(mpc_t {
re: mpfr_t {
prec: self.inner.re.prec,
sign: self.inner.re.sign,
exp: self.inner.re.exp,
d: NonNull::new_unchecked(re_d.cast_mut().cast()),
},
im: mpfr_t {
prec: self.inner.im.prec,
sign: self.inner.im.sign,
exp: self.inner.im.exp,
d: NonNull::new_unchecked(im_d.cast_mut().cast()),
},
})
}
}
#[inline]
pub fn borrow_excl(&mut self) -> &Complex {
unsafe { &*self.as_nonreallocating_complex() }
}
#[inline]
const fn re_is_first(&self) -> bool {
let re_ptr = self.inner.re.d.as_ptr();
let im_ptr = self.inner.im.d.as_ptr();
unsafe { re_ptr.offset_from(im_ptr) <= 0 }
}
}
impl Assign<MiniFloat> for MiniComplex {
#[inline]
fn assign(&mut self, src: MiniFloat) {
self.inner.im.d = self.inner.re.d;
self.inner.re.prec = src.inner.prec;
self.inner.re.sign = src.inner.sign;
self.inner.re.exp = src.inner.exp;
self.inner.im.prec = src.inner.prec;
self.inner.im.sign = 1;
self.inner.im.exp = xmpfr::EXP_ZERO;
self.first_limbs = src.limbs;
}
}
impl From<MiniFloat> for MiniComplex {
#[inline]
fn from(src: MiniFloat) -> Self {
MiniComplex::const_from_real(src)
}
}
impl Assign<(MiniFloat, MiniFloat)> for MiniComplex {
#[inline]
fn assign(&mut self, src: (MiniFloat, MiniFloat)) {
self.inner.im.d = self.inner.re.d;
self.inner.re.prec = src.0.inner.prec;
self.inner.re.sign = src.0.inner.sign;
self.inner.re.exp = src.0.inner.exp;
self.inner.im.prec = src.1.inner.prec;
self.inner.im.sign = src.1.inner.sign;
self.inner.im.exp = src.1.inner.exp;
self.first_limbs = src.0.limbs;
self.last_limbs = src.1.limbs;
}
}
impl From<(MiniFloat, MiniFloat)> for MiniComplex {
#[inline]
fn from(src: (MiniFloat, MiniFloat)) -> Self {
MiniComplex::const_from_parts(src.0, src.1)
}
}
impl<Re: ToMini> Assign<Re> for MiniComplex {
fn assign(&mut self, src: Re) {
self.inner.im.d = self.inner.re.d;
src.copy(&mut self.inner.re, &mut self.first_limbs);
self.inner.im.prec = self.inner.re.prec;
self.inner.im.sign = 1;
self.inner.im.exp = xmpfr::EXP_ZERO;
}
}
impl<Re: ToMini> From<Re> for MiniComplex {
fn from(src: Re) -> Self {
let re = MiniFloat::from(src);
MiniComplex::const_from_real(re)
}
}
impl<Re: ToMini, Im: ToMini> Assign<(Re, Im)> for MiniComplex {
fn assign(&mut self, src: (Re, Im)) {
self.inner.im.d = self.inner.re.d;
src.0.copy(&mut self.inner.re, &mut self.first_limbs);
src.1.copy(&mut self.inner.im, &mut self.last_limbs);
}
}
impl<Re: ToMini, Im: ToMini> From<(Re, Im)> for MiniComplex {
#[inline]
fn from(src: (Re, Im)) -> Self {
let re = MiniFloat::from(src.0);
let im = MiniFloat::from(src.1);
MiniComplex::const_from_parts(re, im)
}
}
#[cfg(feature = "num-complex")]
impl<T: ToMini> Assign<NumComplex<T>> for MiniComplex {
fn assign(&mut self, src: NumComplex<T>) {
self.inner.im.d = self.inner.re.d;
src.re.copy(&mut self.inner.re, &mut self.first_limbs);
src.im.copy(&mut self.inner.im, &mut self.last_limbs);
}
}
#[cfg(feature = "num-complex")]
impl<T: ToMini> From<NumComplex<T>> for MiniComplex {
#[inline]
fn from(src: NumComplex<T>) -> Self {
let re = MiniFloat::from(src.re);
let im = MiniFloat::from(src.im);
MiniComplex::const_from_parts(re, im)
}
}
impl Assign<&Self> for MiniComplex {
#[inline]
fn assign(&mut self, other: &Self) {
self.clone_from(other);
}
}
impl Assign for MiniComplex {
#[inline]
fn assign(&mut self, other: Self) {
*self = other;
}
}
#[cfg(test)]
mod tests {
use crate::complex::MiniComplex;
use crate::float;
use crate::float::{FreeCache, MiniFloat, Special};
use crate::{Assign, Complex};
#[test]
fn check_assign() {
let mut c = MiniComplex::from((1.0, 2.0));
assert_eq!(*c.borrow_excl(), (1.0, 2.0));
c.assign(3.0);
assert_eq!(*c.borrow_excl(), (3.0, 0.0));
let other = MiniComplex::from((4.0, 5.0));
c.assign(&other);
assert_eq!(*c.borrow_excl(), (4.0, 5.0));
c.assign((6.0, 7.0));
assert_eq!(*c.borrow_excl(), (6.0, 7.0));
c.assign(other);
assert_eq!(*c.borrow_excl(), (4.0, 5.0));
float::free_cache(FreeCache::All);
}
fn swapped_parts(small: &MiniComplex) -> bool {
unsafe {
let borrow = small.borrow();
let re = (*borrow.real().as_raw()).d;
let im = (*borrow.imag().as_raw()).d;
re > im
}
}
#[test]
fn check_swapped_parts() {
let mut c = MiniComplex::from((1, 2));
assert_eq!(*c.borrow_excl(), (1, 2));
assert_eq!(*c.clone().borrow_excl(), c);
let mut orig_swapped_parts = swapped_parts(&c);
unsafe {
assert_eq!(c.borrow_excl().real().prec(), c.borrow_excl().imag().prec());
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert_eq!(*c.borrow_excl(), (-2, 1));
assert_eq!(*c.clone().borrow_excl(), c);
assert!(swapped_parts(&c) != orig_swapped_parts);
c.assign(12);
assert_eq!(*c.borrow_excl(), 12);
assert_eq!(*c.clone().borrow_excl(), c);
orig_swapped_parts = swapped_parts(&c);
unsafe {
assert_eq!(c.borrow_excl().real().prec(), c.borrow_excl().imag().prec());
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert_eq!(*c.borrow_excl(), (0, 12));
assert_eq!(*c.clone().borrow_excl(), c);
assert!(swapped_parts(&c) != orig_swapped_parts);
c.assign((4, 5));
assert_eq!(*c.borrow_excl(), (4, 5));
assert_eq!(*c.clone().borrow_excl(), c);
orig_swapped_parts = swapped_parts(&c);
unsafe {
assert_eq!(c.borrow_excl().real().prec(), c.borrow_excl().imag().prec());
c.as_nonreallocating_complex().mul_i_mut(false);
}
assert_eq!(*c.borrow_excl(), (-5, 4));
assert_eq!(*c.clone().borrow_excl(), c);
assert!(swapped_parts(&c) != orig_swapped_parts);
}
#[test]
fn check_traits() {
assert!(MiniComplex::default().borrow_excl().is_zero());
let mini = MiniComplex::from((-5.2f64, 4u128));
let check = Complex::with_val((53, 128), (-5.2f64, 4u128));
assert_eq!(format!("{mini}"), format!("{check}"));
assert_eq!(format!("{mini:?}"), format!("{check:?}"));
assert_eq!(format!("{mini:e}"), format!("{check:e}"));
assert_eq!(format!("{mini:E}"), format!("{check:E}"));
assert_eq!(format!("{mini:b}"), format!("{check:b}"));
assert_eq!(format!("{mini:o}"), format!("{check:o}"));
assert_eq!(format!("{mini:x}"), format!("{check:x}"));
assert_eq!(format!("{mini:X}"), format!("{check:X}"));
}
macro_rules! compare_conv {
($T:ident, $prec:expr, [$($val:expr),+] $( as $U:ident)?) => {
for &val in &[$($val),+] {
let float = MiniFloat::from(val);
let a = MiniComplex::from(float);
let b = MiniComplex::const_from_real(float);
let mut c = MiniComplex::new();
c.assign(float);
assert_eq!(*a.borrow(), val $(as $U)?);
assert_eq!(*b.borrow(), val $(as $U)?);
assert_eq!(*c.borrow(), val $(as $U)?);
assert_eq!(a.borrow().prec(), ($prec, $prec));
assert_eq!(b.borrow().prec(), ($prec, $prec));
assert_eq!(c.borrow().prec(), ($prec, $prec));
let one = MiniFloat::from(true);
let a = MiniComplex::from((float, one));
let b = MiniComplex::const_from_parts(float, one);
let mut c = MiniComplex::new();
c.assign((float, one));
assert_eq!(*a.borrow(), (val $(as $U)?, 1));
assert_eq!(*b.borrow(), (val $(as $U)?, 1));
assert_eq!(*c.borrow(), (val $(as $U)?, 1));
assert_eq!(a.borrow().prec(), ($prec, 1));
assert_eq!(b.borrow().prec(), ($prec, 1));
assert_eq!(c.borrow().prec(), ($prec, 1));
let a = MiniComplex::from((one, float));
let b = MiniComplex::const_from_parts(one, float);
let mut c = MiniComplex::new();
c.assign((one, float));
assert_eq!(*a.borrow(), (1, val $(as $U)?));
assert_eq!(*b.borrow(), (1, val $(as $U)?));
assert_eq!(*c.borrow(), (1, val $(as $U)?));
assert_eq!(a.borrow().prec(), (1, $prec));
assert_eq!(b.borrow().prec(), (1, $prec));
assert_eq!(c.borrow().prec(), (1, $prec));
}
};
}
#[test]
fn check_equiv_convs() {
compare_conv!(bool, 1, [false, true] as u8);
compare_conv!(i8, i8::BITS, [i8::MIN, 0, i8::MAX]);
compare_conv!(i16, i16::BITS, [i16::MIN, 0, i16::MAX]);
compare_conv!(i32, i32::BITS, [i32::MIN, 0, i32::MAX]);
compare_conv!(i64, i64::BITS, [i64::MIN, 0, i64::MAX]);
compare_conv!(i128, i128::BITS, [i128::MIN, 0, i128::MAX]);
compare_conv!(isize, isize::BITS, [isize::MIN, 0, isize::MAX]);
compare_conv!(u8, u8::BITS, [0, u8::MAX]);
compare_conv!(u16, u16::BITS, [0, u16::MAX]);
compare_conv!(u32, u32::BITS, [0, u32::MAX]);
compare_conv!(u64, u64::BITS, [0, u64::MAX]);
compare_conv!(u128, u128::BITS, [0, u128::MAX]);
compare_conv!(usize, usize::BITS, [0, usize::MAX]);
compare_conv!(
f32,
f32::MANTISSA_DIGITS,
[f32::MIN, 0.0, f32::MAX, f32::INFINITY]
);
compare_conv!(
f64,
f64::MANTISSA_DIGITS,
[f64::MIN, 0.0, f64::MAX, f64::INFINITY]
);
compare_conv!(Special, 1, [Special::NegZero, Special::Infinity]);
}
}