use super::generated::types::Complex;
use crate::scalar::Float;
use std::ops::Div;
impl<T: Float> Complex<T> {
#[inline]
pub fn one() -> Self {
Self::new(T::one(), T::zero())
}
#[inline]
pub fn from_real(real: T) -> Self {
Self::new(real, T::zero())
}
#[inline]
pub fn from_imag(imag: T) -> Self {
Self::new(T::zero(), imag)
}
#[inline]
pub fn i() -> Self {
Self::new(T::zero(), T::one())
}
#[inline]
pub fn from_polar(r: T, theta: T) -> Self {
Self::new(r * theta.cos(), r * theta.sin())
}
#[inline]
pub fn conjugate(&self) -> Self {
Self::new(self.real(), -self.imag())
}
#[inline]
pub fn arg(&self) -> T {
self.imag().atan2(self.real())
}
#[inline]
pub fn exp(&self) -> Self {
let exp_real = self.real().exp();
Self::new(exp_real * self.imag().cos(), exp_real * self.imag().sin())
}
#[inline]
pub fn ln(&self) -> Self {
Self::new(self.norm().ln(), self.arg())
}
#[inline]
pub fn sin(&self) -> Self {
Self::new(
self.real().sin() * self.imag().cosh(),
self.real().cos() * self.imag().sinh(),
)
}
#[inline]
pub fn cos(&self) -> Self {
Self::new(
self.real().cos() * self.imag().cosh(),
-self.real().sin() * self.imag().sinh(),
)
}
#[inline]
pub fn sqrt(&self) -> Self {
let r = self.norm();
let theta = self.arg();
Self::from_polar(r.sqrt(), theta / T::TWO)
}
#[inline]
pub fn powi(&self, n: i32) -> Self {
if n == 0 {
return Self::one();
}
let mut result = Self::one();
let mut base = *self;
let mut exp = n.unsigned_abs();
while exp > 0 {
if exp & 1 == 1 {
result = result * base;
}
base = base * base;
exp >>= 1;
}
if n < 0 { Self::one() / result } else { result }
}
}
impl<T: Float> Div for Complex<T> {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self::Output {
let denom = other.norm_squared();
let conj = other.conjugate();
let numer = self * conj;
Self::new(numer.real() / denom, numer.imag() / denom)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::consts::{E, FRAC_PI_2, FRAC_PI_4, PI};
#[test]
fn test_one() {
let one = Complex::<f64>::one();
assert_eq!(one.real(), 1.0);
assert_eq!(one.imag(), 0.0);
}
#[test]
fn test_from_polar() {
let z = Complex::from_polar(2.0, FRAC_PI_4);
assert_relative_eq!(z.norm(), 2.0, epsilon = 1e-10);
assert_relative_eq!(z.arg(), FRAC_PI_4, epsilon = 1e-10);
}
#[test]
fn test_conjugate() {
let z = Complex::new(3.0, 4.0);
let conj = z.conjugate();
assert_eq!(conj.real(), 3.0);
assert_eq!(conj.imag(), -4.0);
}
#[test]
fn test_arg() {
let z = Complex::new(1.0, 1.0);
assert_relative_eq!(z.arg(), FRAC_PI_4, epsilon = 1e-10);
let z2 = Complex::new(-1.0, 0.0);
assert_relative_eq!(z2.arg(), PI, epsilon = 1e-10);
}
#[test]
fn test_exp_euler() {
let z = Complex::new(0.0, PI);
let result = z.exp();
assert_relative_eq!(result.real(), -1.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_exp_real() {
let z = Complex::new(1.0, 0.0);
let result = z.exp();
assert_relative_eq!(result.real(), E, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_ln() {
let z = Complex::new(E, 0.0);
let result = z.ln();
assert_relative_eq!(result.real(), 1.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_sin() {
let z = Complex::new(FRAC_PI_2, 0.0);
let result = z.sin();
assert_relative_eq!(result.real(), 1.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_cos() {
let z = Complex::new(0.0, 0.0);
let result = z.cos();
assert_relative_eq!(result.real(), 1.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_sqrt() {
let z = Complex::new(4.0, 0.0);
let result = z.sqrt();
assert_relative_eq!(result.real(), 2.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_sqrt_negative() {
let z = Complex::new(-1.0, 0.0);
let result = z.sqrt();
assert_relative_eq!(result.real(), 0.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 1.0, epsilon = 1e-10);
}
#[test]
fn test_powi() {
let z = Complex::new(0.0, 1.0); let result = z.powi(2);
assert_relative_eq!(result.real(), -1.0, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.0, epsilon = 1e-10);
let result4 = z.powi(4);
assert_relative_eq!(result4.real(), 1.0, epsilon = 1e-10);
assert_relative_eq!(result4.imag(), 0.0, epsilon = 1e-10);
}
#[test]
fn test_division() {
let z1 = Complex::new(2.0, 3.0);
let z2 = Complex::new(1.0, 1.0);
let result = z1 / z2;
assert_relative_eq!(result.real(), 2.5, epsilon = 1e-10);
assert_relative_eq!(result.imag(), 0.5, epsilon = 1e-10);
}
}