use crate::types::F;
#[derive(Debug, Default, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct Complex {
pub re: F,
pub im: F,
}
impl Complex {
pub const ZERO: Self = Self { re: 0.0, im: 0.0 };
pub const ONE: Self = Self { re: 1.0, im: 0.0 };
pub const I: Self = Self { re: 0.0, im: 1.0 };
#[inline]
pub const fn new(re: F, im: F) -> Self {
Self { re, im }
}
#[inline]
pub fn from_polar(r: F, theta: F) -> Self {
Self::new(r * theta.cos(), r * theta.sin())
}
#[inline]
pub fn conj(self) -> Self {
Self::new(self.re, -self.im)
}
#[inline]
pub fn norm_sqr(self) -> F {
self.re * self.re + self.im * self.im
}
#[inline]
pub fn norm(self) -> F {
self.norm_sqr().sqrt()
}
#[inline]
pub fn scale(self, k: F) -> Self {
Self::new(self.re * k, self.im * k)
}
}
impl std::ops::Add for Complex {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self::new(self.re + rhs.re, self.im + rhs.im)
}
}
impl std::ops::AddAssign for Complex {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.re += rhs.re;
self.im += rhs.im;
}
}
impl std::ops::Sub for Complex {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self::new(self.re - rhs.re, self.im - rhs.im)
}
}
impl std::ops::Mul for Complex {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self::new(
self.re * rhs.re - self.im * rhs.im,
self.re * rhs.im + self.im * rhs.re,
)
}
}
impl std::ops::Mul<F> for Complex {
type Output = Self;
#[inline]
fn mul(self, rhs: F) -> Self {
self.scale(rhs)
}
}
impl std::ops::Neg for Complex {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self::new(-self.re, -self.im)
}
}
#[cfg(test)]
mod tests {
use super::*;
const TOL: F = 1e-12;
#[test]
fn polar_and_norm() {
let z = Complex::from_polar(2.0, std::f64::consts::FRAC_PI_4);
assert!((z.re - 2.0_f64.sqrt()).abs() < TOL);
assert!((z.im - 2.0_f64.sqrt()).abs() < TOL);
assert!((z.norm() - 2.0).abs() < TOL);
}
#[test]
fn conj_mul_gives_norm_sqr() {
let z = Complex::new(3.0, -4.0);
let n2 = (z * z.conj()).re;
assert!((n2 - 25.0).abs() < TOL);
assert!((z * z.conj()).im.abs() < TOL);
assert!((z.norm_sqr() - 25.0).abs() < TOL);
}
#[test]
fn euler_identity() {
let z = Complex::from_polar(1.0, std::f64::consts::PI);
assert!((z.re + 1.0).abs() < 1e-15);
assert!(z.im.abs() < 1e-15);
}
#[test]
fn arithmetic_ops() {
let a = Complex::new(1.0, 2.0);
let b = Complex::new(3.0, -1.0);
assert_eq!(a + b, Complex::new(4.0, 1.0));
assert_eq!(a - b, Complex::new(-2.0, 3.0));
assert_eq!(a * b, Complex::new(1.0 * 3.0 + 2.0, -1.0 + 2.0 * 3.0));
assert_eq!(-a, Complex::new(-1.0, -2.0));
}
}