use oxinum_float::{cos, cosh, sin, sinh};
use crate::{CBig, OxiNumResult};
const GUARD: usize = 10;
impl CBig {
pub fn sin(&self, precision: usize) -> OxiNumResult<CBig> {
let guard = precision.saturating_add(GUARD);
let a = &self.re;
let b = &self.im;
let sa = sin(a, guard)?;
let ca = cos(a, guard)?;
let shb = sinh(b, guard)?;
let chb = cosh(b, guard)?;
let re = &sa * &chb;
let im = &ca * &shb;
Ok(CBig::from_parts(re, im))
}
pub fn cos(&self, precision: usize) -> OxiNumResult<CBig> {
let guard = precision.saturating_add(GUARD);
let a = &self.re;
let b = &self.im;
let sa = sin(a, guard)?;
let ca = cos(a, guard)?;
let shb = sinh(b, guard)?;
let chb = cosh(b, guard)?;
let re = &ca * &chb;
let im = -(&sa * &shb);
Ok(CBig::from_parts(re, im))
}
pub fn sinh(&self, precision: usize) -> OxiNumResult<CBig> {
let guard = precision.saturating_add(GUARD);
let a = &self.re;
let b = &self.im;
let sha = sinh(a, guard)?;
let cha = cosh(a, guard)?;
let sb = sin(b, guard)?;
let cb = cos(b, guard)?;
let re = &sha * &cb;
let im = &cha * &sb;
Ok(CBig::from_parts(re, im))
}
pub fn cosh(&self, precision: usize) -> OxiNumResult<CBig> {
let guard = precision.saturating_add(GUARD);
let a = &self.re;
let b = &self.im;
let sha = sinh(a, guard)?;
let cha = cosh(a, guard)?;
let sb = sin(b, guard)?;
let cb = cos(b, guard)?;
let re = &cha * &cb;
let im = &sha * &sb;
Ok(CBig::from_parts(re, im))
}
pub fn tan(&self, precision: usize) -> OxiNumResult<CBig> {
self.sin(precision)?.checked_div(&self.cos(precision)?)
}
pub fn tanh(&self, precision: usize) -> OxiNumResult<CBig> {
self.sinh(precision)?.checked_div(&self.cosh(precision)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
const PREC: usize = 40;
fn c(re: f64, im: f64) -> CBig {
CBig::from_f64(re, im).expect("finite parts")
}
#[test]
fn sin_zero_is_zero() {
let s = CBig::zero().sin(PREC).expect("sin");
let (re, im) = s.to_f64_parts();
assert!(re.abs() < 1e-12, "re = {re}");
assert!(im.abs() < 1e-12, "im = {im}");
}
#[test]
fn cos_zero_is_one() {
let cz = CBig::zero().cos(PREC).expect("cos");
let (re, im) = cz.to_f64_parts();
assert!((re - 1.0).abs() < 1e-12, "re = {re}");
assert!(im.abs() < 1e-12, "im = {im}");
}
#[test]
fn sinh_zero_is_zero() {
let s = CBig::zero().sinh(PREC).expect("sinh");
let (re, im) = s.to_f64_parts();
assert!(re.abs() < 1e-12, "re = {re}");
assert!(im.abs() < 1e-12, "im = {im}");
}
#[test]
fn cosh_zero_is_one() {
let cz = CBig::zero().cosh(PREC).expect("cosh");
let (re, im) = cz.to_f64_parts();
assert!((re - 1.0).abs() < 1e-12, "re = {re}");
assert!(im.abs() < 1e-12, "im = {im}");
}
#[test]
fn pythagorean_identity_general() {
let z = c(0.5, 0.3);
let s = z.sin(PREC).expect("sin");
let co = z.cos(PREC).expect("cos");
let sum = &(&s * &s) + &(&co * &co);
let (re, im) = sum.to_f64_parts();
assert!((re - 1.0).abs() < 1e-9, "re(sum) = {re}");
assert!(im.abs() < 1e-9, "im(sum) = {im}");
}
#[test]
fn cosh_sq_minus_sinh_sq_is_one() {
let z = c(0.4, 0.7);
let ch = z.cosh(PREC).expect("cosh");
let sh = z.sinh(PREC).expect("sinh");
let diff = &(&ch * &ch) - &(&sh * &sh);
let (re, im) = diff.to_f64_parts();
assert!((re - 1.0).abs() < 1e-9, "re(diff) = {re}");
assert!(im.abs() < 1e-9, "im(diff) = {im}");
}
#[test]
fn tan_zero_is_zero() {
let t = CBig::zero().tan(PREC).expect("tan");
let (re, im) = t.to_f64_parts();
assert!(re.abs() < 1e-12, "re = {re}");
assert!(im.abs() < 1e-12, "im = {im}");
}
#[test]
fn tanh_zero_is_zero() {
let t = CBig::zero().tanh(PREC).expect("tanh");
let (re, im) = t.to_f64_parts();
assert!(re.abs() < 1e-12, "re = {re}");
assert!(im.abs() < 1e-12, "im = {im}");
}
#[test]
fn tan_matches_known_value() {
let z = c(0.5, 0.3);
let t = z.tan(PREC).expect("tan");
let (re, im) = t.to_f64_parts();
assert!((re - 0.487_592_316_492_138_74).abs() < 1e-9, "re = {re}");
assert!((im - 0.368_910_396_825_563_8).abs() < 1e-9, "im = {im}");
}
#[test]
fn tan_is_sin_over_cos() {
let z = c(0.5, 0.3);
let t = z.tan(PREC).expect("tan");
let q = z
.sin(PREC)
.expect("sin")
.checked_div(&z.cos(PREC).expect("cos"))
.expect("non-zero cos");
let (tre, tim) = t.to_f64_parts();
let (qre, qim) = q.to_f64_parts();
assert!((tre - qre).abs() < 1e-12, "re: {tre} vs {qre}");
assert!((tim - qim).abs() < 1e-12, "im: {tim} vs {qim}");
}
}