use core::fmt;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Complex64 {
pub re: f64,
pub im: f64,
}
impl Complex64 {
pub const ZERO: Complex64 = Complex64::new(0.0, 0.0);
pub const ONE: Complex64 = Complex64::new(1.0, 0.0);
pub const I: Complex64 = Complex64::new(0.0, 1.0);
pub const fn new(re: f64, im: f64) -> Self {
Complex64 { re, im }
}
pub fn from_polar(r: f64, theta: f64) -> Self {
let (s, c) = theta.sin_cos();
Complex64::new(r * c, r * s)
}
pub fn cis(theta: f64) -> Self {
Complex64::from_polar(1.0, theta)
}
pub const fn conj(self) -> Self {
Complex64::new(self.re, -self.im)
}
pub fn norm(self) -> f64 {
self.re.hypot(self.im)
}
pub fn norm_sqr(self) -> f64 {
self.re * self.re + self.im * self.im
}
pub fn arg(self) -> f64 {
self.im.atan2(self.re)
}
pub fn to_polar(self) -> (f64, f64) {
(self.norm(), self.arg())
}
pub fn exp(self) -> Self {
Complex64::from_polar(self.re.exp(), self.im)
}
pub fn inv(self) -> Self {
Complex64::ONE / self
}
pub fn scale(self, k: f64) -> Self {
Complex64::new(self.re * k, self.im * k)
}
pub fn is_finite(self) -> bool {
self.re.is_finite() && self.im.is_finite()
}
pub fn is_nan(self) -> bool {
self.re.is_nan() || self.im.is_nan()
}
}
impl From<f64> for Complex64 {
fn from(re: f64) -> Self {
Complex64::new(re, 0.0)
}
}
impl fmt::Display for Complex64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.re, f)?;
let sign = if self.im.is_sign_negative() && !self.im.is_nan() {
'-'
} else {
'+'
};
let im = self.im.abs();
match f.precision() {
Some(p) => write!(f, "{sign}{im:.p$}j"),
None => write!(f, "{sign}{im}j"),
}
}
}
impl Neg for Complex64 {
type Output = Complex64;
fn neg(self) -> Complex64 {
Complex64::new(-self.re, -self.im)
}
}
impl Add for Complex64 {
type Output = Complex64;
fn add(self, o: Complex64) -> Complex64 {
Complex64::new(self.re + o.re, self.im + o.im)
}
}
impl Sub for Complex64 {
type Output = Complex64;
fn sub(self, o: Complex64) -> Complex64 {
Complex64::new(self.re - o.re, self.im - o.im)
}
}
impl Mul for Complex64 {
type Output = Complex64;
fn mul(self, o: Complex64) -> Complex64 {
Complex64::new(
self.re * o.re - self.im * o.im,
self.re * o.im + self.im * o.re,
)
}
}
impl Div for Complex64 {
type Output = Complex64;
fn div(self, o: Complex64) -> Complex64 {
let (a, b, c, d) = (self.re, self.im, o.re, o.im);
if c.abs() >= d.abs() {
let r = d / c;
let den = c + d * r;
Complex64::new((a + b * r) / den, (b - a * r) / den)
} else {
let r = c / d;
let den = c * r + d;
Complex64::new((a * r + b) / den, (b * r - a) / den)
}
}
}
impl Add<f64> for Complex64 {
type Output = Complex64;
fn add(self, o: f64) -> Complex64 {
Complex64::new(self.re + o, self.im)
}
}
impl Sub<f64> for Complex64 {
type Output = Complex64;
fn sub(self, o: f64) -> Complex64 {
Complex64::new(self.re - o, self.im)
}
}
impl Mul<f64> for Complex64 {
type Output = Complex64;
fn mul(self, o: f64) -> Complex64 {
self.scale(o)
}
}
impl Div<f64> for Complex64 {
type Output = Complex64;
fn div(self, o: f64) -> Complex64 {
Complex64::new(self.re / o, self.im / o)
}
}
impl Add<Complex64> for f64 {
type Output = Complex64;
fn add(self, o: Complex64) -> Complex64 {
Complex64::new(self + o.re, o.im)
}
}
impl Sub<Complex64> for f64 {
type Output = Complex64;
fn sub(self, o: Complex64) -> Complex64 {
Complex64::new(self - o.re, -o.im)
}
}
impl Mul<Complex64> for f64 {
type Output = Complex64;
fn mul(self, o: Complex64) -> Complex64 {
o.scale(self)
}
}
impl Div<Complex64> for f64 {
type Output = Complex64;
fn div(self, o: Complex64) -> Complex64 {
Complex64::from(self) / o
}
}
macro_rules! assign_ops {
($($tr:ident $m:ident $op:tt),*) => {$(
impl $tr for Complex64 {
fn $m(&mut self, o: Complex64) {
*self = *self $op o;
}
}
impl $tr<f64> for Complex64 {
fn $m(&mut self, o: f64) {
*self = *self $op o;
}
}
)*};
}
assign_ops!(
AddAssign add_assign +,
SubAssign sub_assign -,
MulAssign mul_assign *,
DivAssign div_assign /
);
impl Sum for Complex64 {
fn sum<I: Iterator<Item = Complex64>>(iter: I) -> Complex64 {
iter.fold(Complex64::ZERO, |a, b| a + b)
}
}
impl<'a> Sum<&'a Complex64> for Complex64 {
fn sum<I: Iterator<Item = &'a Complex64>>(iter: I) -> Complex64 {
iter.fold(Complex64::ZERO, |a, b| a + *b)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::f64::consts::{E, FRAC_PI_2, PI};
fn close(a: Complex64, b: Complex64) -> bool {
(a - b).norm() <= 1e-12 * (1.0 + b.norm())
}
#[test]
fn arithmetic() {
let a = Complex64::new(1.0, 2.0);
let b = Complex64::new(-3.0, 0.5);
assert_eq!(a + b, Complex64::new(-2.0, 2.5));
assert_eq!(a - b, Complex64::new(4.0, 1.5));
assert_eq!(a * b, Complex64::new(-4.0, -5.5));
assert!(close(a / b * b, a));
assert!(close(b / a * a, b));
assert!(close(
a / Complex64::new(0.5, -4.0) * Complex64::new(0.5, -4.0),
a
));
assert_eq!(-a, Complex64::new(-1.0, -2.0));
assert_eq!(Complex64::I * Complex64::I, -Complex64::ONE);
}
#[test]
fn mixed_with_f64() {
let a = Complex64::new(1.0, 2.0);
assert_eq!(a + 1.0, Complex64::new(2.0, 2.0));
assert_eq!(1.0 + a, Complex64::new(2.0, 2.0));
assert_eq!(a - 1.0, Complex64::new(0.0, 2.0));
assert_eq!(1.0 - a, Complex64::new(0.0, -2.0));
assert_eq!(a * 2.0, Complex64::new(2.0, 4.0));
assert_eq!(2.0 * a, Complex64::new(2.0, 4.0));
assert_eq!(a / 2.0, Complex64::new(0.5, 1.0));
assert!(close(1.0 / a, Complex64::new(0.2, -0.4)));
assert!(close(a.inv(), Complex64::new(0.2, -0.4)));
}
#[test]
fn assign_ops() {
let mut z = Complex64::new(1.0, 1.0);
z += Complex64::ONE;
z -= 0.5;
z *= Complex64::I;
z /= 2.0;
assert!(close(z, Complex64::new(-0.5, 0.75)));
z *= 4.0;
z /= Complex64::new(0.0, 1.0);
assert!(close(z, Complex64::new(3.0, 2.0)));
z -= Complex64::new(3.0, 2.0);
z += 1.0;
assert!(close(z, Complex64::ONE));
}
#[test]
fn division_extreme_scale() {
let big = Complex64::new(1e300, 1e300);
assert!(close(big / big, Complex64::ONE));
let tiny = Complex64::new(1e-300, -1e-300);
assert!(close(tiny / tiny, Complex64::ONE));
}
#[test]
fn polar() {
let z = Complex64::new(3.0, 4.0);
assert_eq!(z.norm(), 5.0);
assert_eq!(z.norm_sqr(), 25.0);
assert_eq!(z.conj(), Complex64::new(3.0, -4.0));
let (r, th) = z.to_polar();
assert!(close(Complex64::from_polar(r, th), z));
assert!((Complex64::I.arg() - FRAC_PI_2).abs() < 1e-15);
assert!((Complex64::new(-1.0, 0.0).arg() - PI).abs() < 1e-15);
assert!(close(Complex64::cis(PI), Complex64::new(-1.0, 0.0)));
assert!(close(
Complex64::new(1.0, PI).exp(),
Complex64::new(-E, 0.0)
));
assert!(close(z * z.conj(), Complex64::from(z.norm_sqr())));
}
#[test]
fn display() {
assert_eq!(Complex64::new(1.0, 2.0).to_string(), "1+2j");
assert_eq!(Complex64::new(1.0, -2.5).to_string(), "1-2.5j");
assert_eq!(Complex64::new(-0.5, 0.0).to_string(), "-0.5+0j");
assert_eq!(
format!("{:.2}", Complex64::new(1.0, -1.0 / 3.0)),
"1.00-0.33j"
);
}
#[test]
fn sum_and_default() {
let v = [Complex64::new(1.0, 1.0), Complex64::new(2.0, -3.0)];
let s: Complex64 = v.iter().sum();
assert_eq!(s, Complex64::new(3.0, -2.0));
let s2: Complex64 = v.into_iter().sum();
assert_eq!(s, s2);
assert_eq!(Complex64::default(), Complex64::ZERO);
assert!(Complex64::ONE.is_finite());
assert!(Complex64::new(f64::NAN, 0.0).is_nan());
}
}