use crate::repr::Context;
use dashu_base::Sign;
use dashu_float::round::{mode, Round};
use dashu_float::{FBig, Repr};
use dashu_int::Word;
pub struct CBig<R: Round = mode::Zero, const B: Word = 2> {
pub(crate) re: Repr<B>,
pub(crate) im: Repr<B>,
pub(crate) context: Context<R>,
}
impl<R: Round, const B: Word> CBig<R, B> {
#[inline]
pub const fn new(re: Repr<B>, im: Repr<B>, context: Context<R>) -> Self {
Self { re, im, context }
}
#[inline]
pub fn from_parts(re: FBig<R, B>, im: FBig<R, B>) -> Self {
let fctx = dashu_float::Context::max(re.context(), im.context());
Self {
re: re.into_repr(),
im: im.into_repr(),
context: Context(fctx),
}
}
pub const ZERO: Self = Self::new(Repr::zero(), Repr::zero(), Context::new(0));
pub const ONE: Self = Self::new(Repr::one(), Repr::zero(), Context::new(0));
pub const NEG_ONE: Self = Self::new(Repr::neg_one(), Repr::zero(), Context::new(0));
pub const I: Self = Self::new(Repr::zero(), Repr::one(), Context::new(0));
#[inline]
pub const fn context(&self) -> Context<R> {
self.context
}
#[inline]
pub const fn precision(&self) -> usize {
self.context.precision()
}
#[inline]
pub const fn re(&self) -> &Repr<B> {
&self.re
}
#[inline]
pub const fn im(&self) -> &Repr<B> {
&self.im
}
#[inline]
pub fn into_parts(self) -> (FBig<R, B>, FBig<R, B>) {
let fctx = self.context.float();
(FBig::from_repr(self.re, fctx), FBig::from_repr(self.im, fctx))
}
#[inline]
pub fn is_zero(&self) -> bool {
(self.re.is_pos_zero() || self.re.is_neg_zero())
&& (self.im.is_pos_zero() || self.im.is_neg_zero())
}
#[inline]
pub fn is_infinite(&self) -> bool {
self.re.is_infinite() || self.im.is_infinite()
}
#[inline]
pub fn is_finite(&self) -> bool {
!self.is_infinite()
}
#[inline]
pub(crate) fn overflow(context: &Context<R>, _sign: Sign) -> Self {
Self::new(Repr::infinity(), Repr::zero(), *context)
}
#[inline]
pub(crate) fn underflow(context: &Context<R>, sign: Sign) -> Self {
let re = match sign {
Sign::Positive => Repr::zero(),
Sign::Negative => Repr::neg_zero(),
};
Self::new(re, Repr::zero(), *context)
}
}
impl<R: Round, const B: Word> Clone for CBig<R, B> {
#[inline]
fn clone(&self) -> Self {
Self {
re: self.re.clone(),
im: self.im.clone(),
context: self.context,
}
}
}
impl<R: Round, const B: Word> Default for CBig<R, B> {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
#[cfg(test)]
mod tests {
use super::*;
type C = CBig<mode::HalfAway, 10>;
#[test]
fn constants() {
assert!(C::ZERO.is_zero());
assert!(!C::ONE.is_zero());
assert!(!C::NEG_ONE.is_zero());
assert!(C::NEG_ONE != C::ONE);
assert!(!C::I.is_zero());
let (re, im) = C::I.into_parts();
assert!(re.repr().is_pos_zero());
assert!(im.repr().is_one());
}
#[test]
fn from_parts_reconciles_precision() {
type F = FBig<mode::HalfAway, 10>;
let re = F::from_parts(3.into(), 0); let im = F::from_parts(4.into(), 0); let z = CBig::from_parts(re, im);
assert_eq!(z.precision(), 1);
assert_eq!(z.re().significand(), &3.into());
assert_eq!(z.im().significand(), &4.into());
}
#[test]
fn predicates() {
let inf = C::new(Repr::infinity(), Repr::zero(), Context::new(0));
assert!(inf.is_infinite());
assert!(!inf.is_finite());
assert!(!inf.is_zero());
let z = C::from_parts(FBig::from(3), FBig::from(0));
assert!(!z.is_infinite());
assert!(z.is_finite());
assert!(!z.is_zero());
let neg_zero = C::new(Repr::neg_zero(), Repr::zero(), Context::new(0));
assert!(neg_zero.is_zero());
}
}