use crate::error::SolMathError;
use crate::arithmetic::{fp_mul_i, fp_div_i, fp_sqrt};
use crate::transcendental::exp_fixed_i;
use crate::trig::{cos_fixed, sin_fixed};
#[derive(Clone, Copy)]
pub struct Complex {
pub re: i128,
pub im: i128,
}
impl Complex {
pub fn new(re: i128, im: i128) -> Self {
Self { re, im }
}
}
pub fn complex_mul(a: Complex, b: Complex) -> Result<Complex, SolMathError> {
Ok(Complex::new(
fp_mul_i(a.re, b.re)?.checked_sub(fp_mul_i(a.im, b.im)?).ok_or(SolMathError::Overflow)?,
fp_mul_i(a.re, b.im)?.checked_add(fp_mul_i(a.im, b.re)?).ok_or(SolMathError::Overflow)?,
))
}
pub fn complex_div(a: Complex, b: Complex) -> Result<Complex, SolMathError> {
let b_re_sq = fp_mul_i(b.re, b.re)?;
let b_im_sq = fp_mul_i(b.im, b.im)?;
let denom = b_re_sq.checked_add(b_im_sq).ok_or(SolMathError::Overflow)?;
if denom == 0 {
return Err(SolMathError::DivisionByZero);
}
Ok(Complex::new(
fp_div_i(fp_mul_i(a.re, b.re)?.checked_add(fp_mul_i(a.im, b.im)?).ok_or(SolMathError::Overflow)?, denom)?,
fp_div_i(fp_mul_i(a.im, b.re)?.checked_sub(fp_mul_i(a.re, b.im)?).ok_or(SolMathError::Overflow)?, denom)?,
))
}
pub fn complex_exp(z: Complex) -> Result<Complex, SolMathError> {
let e = exp_fixed_i(z.re)?;
Ok(Complex::new(fp_mul_i(e, cos_fixed(z.im)?)?, fp_mul_i(e, sin_fixed(z.im)?)?))
}
pub fn complex_sqrt(z: Complex) -> Result<Complex, SolMathError> {
let a_sq = fp_mul_i(z.re, z.re)?;
let b_sq = fp_mul_i(z.im, z.im)?;
let norm_sq = a_sq.checked_add(b_sq).ok_or(SolMathError::Overflow)?;
if norm_sq == 0 {
return Ok(Complex::new(0, 0));
}
let modz = fp_sqrt(norm_sq as u128)? as i128;
let re_arg = (modz + z.re) / 2;
let re = if re_arg > 0 {
fp_sqrt(re_arg as u128)? as i128
} else {
0
};
if re == 0 {
let im_arg = (modz - z.re) / 2;
let im = fp_sqrt(im_arg as u128)? as i128;
return Ok(Complex::new(0, if z.im < 0 { -im } else { im }));
}
let im = fp_div_i(z.im, 2 * re)?;
Ok(Complex::new(re, im))
}