use crate::cbig::CBig;
use crate::repr::{combine_parts, exact, riemann, CfpResult, Context};
use core::ops::{Div, DivAssign};
use dashu_base::{AbsOrd, Inverse};
use dashu_float::round::Round;
use dashu_float::{FBig, FpError};
use dashu_int::Word;
const DIV_GUARD: usize = 14;
impl<R: Round> Context<R> {
pub fn inv<const B: Word>(&self, z: &CBig<R, B>) -> CfpResult<R, B> {
if z.is_infinite() {
return Ok(exact(FBig::ZERO, FBig::ZERO)); }
if z.is_zero() {
return Ok(riemann(*self)); }
let gctx = self.guard(DIV_GUARD);
let p = self.precision();
let (x, y) = (z.re(), z.im());
let x2 = gctx.sqr(x)?.value();
let y2 = gctx.sqr(y)?.value();
let n = gctx.add(x2.repr(), y2.repr())?.value();
let re = gctx.div(x, n.repr())?.value().with_precision(p);
let neg_y = -y.clone();
let im = gctx.div(&neg_y, n.repr())?.value().with_precision(p);
Ok(combine_parts(re, im))
}
pub fn div<const B: Word>(&self, z: &CBig<R, B>, w: &CBig<R, B>) -> CfpResult<R, B> {
if let Some(special) = div_special(z, w) {
return special;
}
let gctx = self.guard(DIV_GUARD);
let p = self.precision();
let (x, y) = (z.re(), z.im());
let (u, v) = (w.re(), w.im());
let u_ge_v = {
let fu = FBig::from_repr(u.clone(), gctx);
let fv = FBig::from_repr(v.clone(), gctx);
fu.abs_cmp(&fv).is_ge()
};
let (r, d) = if u_ge_v {
let r = gctx.div(v, u)?.value();
let rv = gctx.mul(r.repr(), v)?.value();
let d = gctx.add(u, rv.repr())?.value();
(r, d)
} else {
let r = gctx.div(u, v)?.value();
let ru = gctx.mul(r.repr(), u)?.value();
let d = gctx.add(v, ru.repr())?.value();
(r, d)
};
let (re, im) = if u_ge_v {
let ry = gctx.mul(r.repr(), y)?.value();
let rx = gctx.mul(r.repr(), x)?.value();
let num_re = gctx.add(x, ry.repr())?.value();
let num_im = gctx.sub(y, rx.repr())?.value();
(
gctx.div(num_re.repr(), d.repr())?.value().with_precision(p),
gctx.div(num_im.repr(), d.repr())?.value().with_precision(p),
)
} else {
let rx = gctx.mul(r.repr(), x)?.value();
let ry = gctx.mul(r.repr(), y)?.value();
let num_re = gctx.add(rx.repr(), y)?.value();
let num_im = gctx.sub(ry.repr(), x)?.value();
(
gctx.div(num_re.repr(), d.repr())?.value().with_precision(p),
gctx.div(num_im.repr(), d.repr())?.value().with_precision(p),
)
};
Ok(combine_parts(re, im))
}
pub fn div_real<const B: Word>(&self, z: &CBig<R, B>, s: &FBig<R, B>) -> CfpResult<R, B> {
if z.is_infinite() || s.repr().is_infinite() {
if z.is_infinite() && s.repr().is_infinite() {
return Err(FpError::Indeterminate); }
if s.repr().is_infinite() {
return Ok(exact(FBig::ZERO, FBig::ZERO)); }
return Ok(riemann(*self));
}
if s.repr().is_pos_zero() || s.repr().is_neg_zero() {
if z.is_zero() {
return Err(FpError::Indeterminate); }
return Ok(riemann(*self)); }
let gctx = self.guard(DIV_GUARD);
let p = self.precision();
let re = gctx.div(z.re(), s.repr())?.value().with_precision(p);
let im = gctx.div(z.im(), s.repr())?.value().with_precision(p);
Ok(combine_parts(re, im))
}
}
fn div_special<R: Round, const B: Word>(z: &CBig<R, B>, w: &CBig<R, B>) -> Option<CfpResult<R, B>> {
let (zi, wi) = (z.is_infinite(), w.is_infinite());
let (zz, wz) = (z.is_zero(), w.is_zero());
let ctx = Context::max(z.context(), w.context());
if (zi && wi) || (zz && wz) {
Some(Err(FpError::Indeterminate)) } else if wi {
Some(Ok(exact(FBig::ZERO, FBig::ZERO))) } else if wz || zi {
Some(Ok(riemann(ctx))) } else if zz {
Some(Ok(exact(FBig::ZERO, FBig::ZERO))) } else {
None
}
}
impl<R: Round, const B: Word> Inverse for CBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn inv(self) -> Self::Output {
self.context().unwrap_cfp(self.context().inv(&self))
}
}
impl<R: Round, const B: Word> Inverse for &CBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn inv(self) -> Self::Output {
self.context().unwrap_cfp(self.context().inv(self))
}
}
crate::helper_macros::impl_cbig_binop!(Div, div, DivAssign, div_assign);
crate::helper_macros::impl_cbig_scalar_binop!(Div, div, div_real);
impl<R: Round, const B: Word> Div<&CBig<R, B>> for &FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn div(self, rhs: &CBig<R, B>) -> CBig<R, B> {
let s = CBig::from(self.clone());
let ctx = Context::max(s.context(), rhs.context());
ctx.unwrap_cfp(ctx.div(&s, rhs))
}
}
impl<R: Round, const B: Word> Div<CBig<R, B>> for &FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn div(self, rhs: CBig<R, B>) -> CBig<R, B> {
let this = self.clone();
let s = CBig::from(this);
let ctx = Context::max(s.context(), rhs.context());
ctx.unwrap_cfp(ctx.div(&s, &rhs))
}
}
impl<R: Round, const B: Word> Div<&CBig<R, B>> for FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn div(self, rhs: &CBig<R, B>) -> CBig<R, B> {
let s = CBig::from(self);
let ctx = Context::max(s.context(), rhs.context());
ctx.unwrap_cfp(ctx.div(&s, rhs))
}
}
impl<R: Round, const B: Word> Div<CBig<R, B>> for FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn div(self, rhs: CBig<R, B>) -> CBig<R, B> {
let s = CBig::from(self);
let ctx = Context::max(s.context(), rhs.context());
ctx.unwrap_cfp(ctx.div(&s, &rhs))
}
}
#[cfg(test)]
mod tests {
use super::*;
use dashu_float::round::mode;
type C = CBig<mode::HalfAway, 10>;
type F = FBig<mode::HalfAway, 10>;
fn c(re: i32, im: i32) -> C {
let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
C::from_parts(mk(re), mk(im))
}
#[test]
fn div_inverse() {
let z = c(3, 4);
let q = &z / &z;
assert_eq!(q.re().significand(), &1.into());
assert!(q.im().significand().is_zero());
}
#[test]
fn div_basic() {
let z = c(6, 8);
let w = c(3, 4);
let q = &z / &w;
assert_eq!(q.re().significand(), &2.into());
assert!(q.im().significand().is_zero());
}
#[test]
fn inv_basic() {
type F = FBig<mode::HalfAway, 10>;
let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
let z = C::from_parts(mk(3), mk(4));
let r = (&z).inv();
assert_eq!(r.context().precision(), 53);
let one = &z * &r;
assert_eq!(one.re().significand(), &1.into());
assert!(one.im().significand().is_zero());
}
#[test]
fn scalar_div_by_real() {
let z = c(6, 8);
let s = FBig::<mode::HalfAway, 10>::from(2);
let q = &z / &s;
assert_eq!(q.re().significand(), &3.into());
assert_eq!(q.im().significand(), &4.into());
}
#[test]
fn div_real_by_negative_zero_matches_positive_zero() {
use dashu_float::Repr;
let z = c(3, 4);
let neg_zero = F::from_repr(Repr::neg_zero(), z.context().float());
let q_neg = &z / &neg_zero;
let q_pos = &z / &F::ZERO;
assert!(q_neg.is_infinite());
assert!(q_neg == q_pos); }
}