use oxinum_core::OxiNumResult;
use oxinum_float::native::{BigFloat, RoundingMode};
use super::BigComplex;
const GUARD: u32 = 10;
#[inline]
fn mul_i(z: BigComplex) -> BigComplex {
BigComplex {
re: -&z.im,
im: z.re,
}
}
#[inline]
fn mul_neg_i(z: BigComplex) -> BigComplex {
BigComplex {
re: z.im,
im: -&z.re,
}
}
impl BigComplex {
pub fn asin(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
if self.is_zero() {
return Ok(BigComplex::zero(prec));
}
let guard = prec.saturating_add(GUARD);
let iz = mul_i(self.clone());
let z_sq = self.mul_core(self);
let one_c = BigComplex::one(guard, mode);
let one_minus_z2 = &one_c - &z_sq;
let sqrt_val = one_minus_z2.sqrt(guard, mode)?;
let arg = &iz + &sqrt_val;
let ln_val = arg.ln(guard, mode)?;
Ok(mul_neg_i(ln_val))
}
pub fn acos(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
let guard = prec.saturating_add(GUARD);
let z_sq = self.mul_core(self);
let one_c = BigComplex::one(guard, mode);
let one_minus_z2 = &one_c - &z_sq;
let sqrt_val = one_minus_z2.sqrt(guard, mode)?;
let i_sqrt = mul_i(sqrt_val);
let arg = self + &i_sqrt;
let ln_val = arg.ln(guard, mode)?;
Ok(mul_neg_i(ln_val))
}
pub fn atan(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
if self.is_zero() {
return Ok(BigComplex::zero(prec));
}
let guard = prec.saturating_add(GUARD);
let half = BigFloat::from_f64(0.5, guard)?;
let iz = mul_i(self.clone());
let one_c = BigComplex::one(guard, mode);
let ln_minus = (&one_c - &iz).ln(guard, mode)?;
let ln_plus = (&one_c + &iz).ln(guard, mode)?;
let diff = &ln_minus - &ln_plus;
let i_diff = mul_i(diff);
let re = (&i_diff.re * &half).with_precision(prec, mode);
let im = (&i_diff.im * &half).with_precision(prec, mode);
Ok(BigComplex { re, im })
}
pub fn asinh(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
if self.is_zero() {
return Ok(BigComplex::zero(prec));
}
let guard = prec.saturating_add(GUARD);
let one_c = BigComplex::one(guard, mode);
let z_sq_plus_one = &self.mul_core(self) + &one_c;
let sqrt_val = z_sq_plus_one.sqrt(guard, mode)?;
let arg = self + &sqrt_val;
arg.ln(guard, mode)
}
pub fn acosh(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
let guard = prec.saturating_add(GUARD);
let one_c = BigComplex::one(guard, mode);
let sq1 = (self - &one_c).sqrt(guard, mode)?;
let sq2 = (self + &one_c).sqrt(guard, mode)?;
let product = sq1.mul_core(&sq2);
let arg = self + &product;
arg.ln(guard, mode)
}
pub fn atanh(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
if self.is_zero() {
return Ok(BigComplex::zero(prec));
}
let guard = prec.saturating_add(GUARD);
let half = BigFloat::from_f64(0.5, guard)?;
let one_c = BigComplex::one(guard, mode);
let ln_plus = (&one_c + self).ln(guard, mode)?;
let ln_minus = (&one_c - self).ln(guard, mode)?;
let diff = &ln_plus - &ln_minus;
let re = (&diff.re * &half).with_precision(prec, mode);
let im = (&diff.im * &half).with_precision(prec, mode);
Ok(BigComplex { re, im })
}
}
#[cfg(test)]
mod tests {
use super::*;
const PREC: u32 = 80;
const MODE: RoundingMode = RoundingMode::HalfEven;
const TOL: f64 = 1e-9;
fn c(re: f64, im: f64) -> BigComplex {
BigComplex::from_f64(re, im, PREC).expect("finite parts")
}
#[test]
fn asin_zero_is_zero() {
let r = BigComplex::zero(PREC).asin(PREC, MODE).expect("asin");
assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn atan_zero_is_zero() {
let r = BigComplex::zero(PREC).atan(PREC, MODE).expect("atan");
assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn asinh_zero_is_zero() {
let r = BigComplex::zero(PREC).asinh(PREC, MODE).expect("asinh");
assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn atanh_zero_is_zero() {
let r = BigComplex::zero(PREC).atanh(PREC, MODE).expect("atanh");
assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn asin_one_is_half_pi() {
let r = c(1.0, 0.0).asin(PREC, MODE).expect("asin");
let half_pi = std::f64::consts::FRAC_PI_2;
assert!(
(r.re().to_f64() - half_pi).abs() < TOL,
"re = {}, expected π/2 ≈ {half_pi}",
r.re().to_f64()
);
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn atan_one_is_quarter_pi() {
let r = c(1.0, 0.0).atan(PREC, MODE).expect("atan");
let quarter_pi = std::f64::consts::FRAC_PI_4;
assert!(
(r.re().to_f64() - quarter_pi).abs() < TOL,
"re = {}, expected π/4 ≈ {quarter_pi}",
r.re().to_f64()
);
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn acosh_one_is_zero() {
let r = c(1.0, 0.0).acosh(PREC, MODE).expect("acosh");
assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
}
#[test]
fn sin_asin_roundtrip() {
let z = c(0.3, 0.4);
let asin_z = z.asin(PREC, MODE).expect("asin");
let r = asin_z.sin(PREC, MODE).expect("sin");
assert!(
(r.re().to_f64() - 0.3).abs() < TOL,
"re = {}",
r.re().to_f64()
);
assert!(
(r.im().to_f64() - 0.4).abs() < TOL,
"im = {}",
r.im().to_f64()
);
}
#[test]
fn cos_acos_roundtrip() {
let z = c(0.3, 0.4);
let acos_z = z.acos(PREC, MODE).expect("acos");
let r = acos_z.cos(PREC, MODE).expect("cos");
assert!(
(r.re().to_f64() - 0.3).abs() < TOL,
"re = {}",
r.re().to_f64()
);
assert!(
(r.im().to_f64() - 0.4).abs() < TOL,
"im = {}",
r.im().to_f64()
);
}
#[test]
fn tanh_atanh_roundtrip() {
let z = c(0.2, 0.1);
let atanh_z = z.atanh(PREC, MODE).expect("atanh");
let r = atanh_z.tanh(PREC, MODE).expect("tanh");
assert!(
(r.re().to_f64() - 0.2).abs() < TOL,
"re = {}",
r.re().to_f64()
);
assert!(
(r.im().to_f64() - 0.1).abs() < TOL,
"im = {}",
r.im().to_f64()
);
}
#[test]
fn sinh_asinh_roundtrip() {
let z = c(0.3, 0.4);
let asinh_z = z.asinh(PREC, MODE).expect("asinh");
let r = asinh_z.sinh(PREC, MODE).expect("sinh");
assert!(
(r.re().to_f64() - 0.3).abs() < TOL,
"re = {}",
r.re().to_f64()
);
assert!(
(r.im().to_f64() - 0.4).abs() < TOL,
"im = {}",
r.im().to_f64()
);
}
}