use crate::cbig::CBig;
use crate::repr::{combine_parts, exact, riemann, CfpResult, Context};
use core::ops::{Mul, MulAssign};
use dashu_float::round::Round;
use dashu_float::{FBig, FpError};
use dashu_int::Word;
const MUL_GUARD: usize = 10;
impl<R: Round> Context<R> {
pub fn sqr<const B: Word>(&self, z: &CBig<R, B>) -> CfpResult<R, B> {
if z.is_infinite() {
return Ok(riemann(*self)); }
if z.is_zero() {
return Ok(exact(FBig::ZERO, FBig::ZERO));
}
let gctx = self.guard(MUL_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 re = gctx.sub(x2.repr(), y2.repr())?.value().with_precision(p);
let xy = gctx.mul(x, y)?.value();
let im = gctx.add(xy.repr(), xy.repr())?.value().with_precision(p);
Ok(combine_parts(re, im))
}
pub fn mul<const B: Word>(&self, z: &CBig<R, B>, w: &CBig<R, B>) -> CfpResult<R, B> {
if z.is_infinite() || w.is_infinite() {
if z.is_zero() || w.is_zero() {
return Err(FpError::Indeterminate); }
return Ok(riemann(Context::max(z.context(), w.context()))); }
let gctx = self.guard(MUL_GUARD);
let p = self.precision();
let (x, y) = (z.re(), z.im());
let (u, v) = (w.re(), w.im());
let xu = gctx.mul(x, u)?.value();
let yv = gctx.mul(y, v)?.value();
let re = gctx.sub(xu.repr(), yv.repr())?.value().with_precision(p);
let xv = gctx.mul(x, v)?.value();
let yu = gctx.mul(y, u)?.value();
let im = gctx.add(xv.repr(), yu.repr())?.value().with_precision(p);
Ok(combine_parts(re, im))
}
pub fn mul_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_zero() || s.repr().is_pos_zero() || s.repr().is_neg_zero() {
return Err(FpError::Indeterminate); }
return Ok(riemann(*self));
}
let gctx = self.guard(MUL_GUARD);
let p = self.precision();
let re = gctx.mul(z.re(), s.repr())?.value().with_precision(p);
let im = gctx.mul(z.im(), s.repr())?.value().with_precision(p);
Ok(combine_parts(re, im))
}
}
impl<R: Round, const B: Word> CBig<R, B> {
#[inline]
pub fn sqr(&self) -> Self {
self.context().unwrap_cfp(self.context().sqr(self))
}
}
crate::helper_macros::impl_cbig_binop!(Mul, mul, MulAssign, mul_assign);
crate::helper_macros::impl_cbig_scalar_binop!(Mul, mul, mul_real);
impl<R: Round, const B: Word> Mul<&CBig<R, B>> for &FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn mul(self, rhs: &CBig<R, B>) -> CBig<R, B> {
rhs * self
}
}
impl<R: Round, const B: Word> Mul<CBig<R, B>> for &FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn mul(self, rhs: CBig<R, B>) -> CBig<R, B> {
&rhs * self
}
}
impl<R: Round, const B: Word> Mul<&CBig<R, B>> for FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn mul(self, rhs: &CBig<R, B>) -> CBig<R, B> {
rhs * &self
}
}
impl<R: Round, const B: Word> Mul<CBig<R, B>> for FBig<R, B> {
type Output = CBig<R, B>;
#[inline]
fn mul(self, rhs: CBig<R, B>) -> CBig<R, B> {
&rhs * &self
}
}
#[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 sqr_basic() {
let z = c(3, 4);
let s = z.sqr();
assert_eq!(s.re().significand(), &(-7i32).into());
assert_eq!(s.im().significand(), &24.into());
}
#[test]
fn mul_basic() {
let z = c(1, 2);
let w = c(3, 4);
let p = &z * &w;
assert!(p == c(-5, 10));
}
#[test]
fn mul_assign_val_and_ref() {
let z = c(1, 2);
let w = c(3, 4);
let mut acc = z.clone();
acc *= w.clone();
assert!(acc == c(-5, 10));
let mut acc = z.clone();
acc *= &w;
assert!(acc == c(-5, 10));
}
#[test]
fn mul_by_one_is_identity() {
let z = c(3, 4);
let p = &z * &CBig::ONE;
assert!(p == z);
}
#[test]
fn mul_by_conj_is_norm() {
let z = c(3, 4);
let p = &z * &z.conj();
assert!(p.im().is_pos_zero() || p.im().is_neg_zero());
assert_eq!(p.re().significand(), &25.into());
}
#[test]
fn scalar_mul_by_real() {
let z = c(3, 4);
let s = FBig::<mode::HalfAway, 10>::from(2);
let p = &z * &s;
assert_eq!(p.re().significand(), &6.into());
assert_eq!(p.im().significand(), &8.into());
let p2 = &s * &z;
assert_eq!(p2.re().significand(), &6.into());
}
}