autd3-core 38.1.0

AUTD3 core traits and types
Documentation
/// a complex number
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Complex {
    pub re: f32,
    pub im: f32,
}

impl Complex {
    pub const ZERO: Self = Self { re: 0.0, im: 0.0 };

    #[inline]
    #[must_use]
    pub const fn new(re: f32, im: f32) -> Self {
        Self { re, im }
    }

    #[inline]
    #[must_use]
    pub const fn conj(&self) -> Self {
        Self {
            re: self.re,
            im: -self.im,
        }
    }

    #[inline]
    #[must_use]
    pub fn exp(&self) -> Self {
        let exp_re = self.re.exp();
        Self {
            re: exp_re * self.im.cos(),
            im: exp_re * self.im.sin(),
        }
    }

    #[inline]
    #[must_use]
    pub fn arg(&self) -> f32 {
        self.im.atan2(self.re)
    }

    #[inline]
    #[must_use]
    pub const fn norm_sqr(&self) -> f32 {
        self.re * self.re + self.im * self.im
    }

    #[inline]
    #[must_use]
    pub fn norm(&self) -> f32 {
        self.re.hypot(self.im)
    }
}

impl core::ops::Add<&Complex> for Complex {
    type Output = Complex;

    #[inline]
    fn add(self, rhs: &Complex) -> Self::Output {
        Complex {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

impl core::ops::Add for Complex {
    type Output = Complex;

    #[inline]
    fn add(self, rhs: Complex) -> Self::Output {
        Complex {
            re: self.re + rhs.re,
            im: self.im + rhs.im,
        }
    }
}

impl core::ops::AddAssign for Complex {
    #[inline]
    fn add_assign(&mut self, rhs: Complex) {
        self.re += rhs.re;
        self.im += rhs.im;
    }
}

impl core::ops::Mul<f32> for Complex {
    type Output = Complex;

    #[inline]
    fn mul(self, rhs: f32) -> Self::Output {
        Complex {
            re: self.re * rhs,
            im: self.im * rhs,
        }
    }
}

impl core::ops::Mul<&Complex> for &Complex {
    type Output = Complex;

    #[inline]
    fn mul(self, rhs: &Complex) -> Self::Output {
        Complex {
            re: self.re * rhs.re - self.im * rhs.im,
            im: self.re * rhs.im + self.im * rhs.re,
        }
    }
}

impl core::ops::Mul<Complex> for &Complex {
    type Output = Complex;

    #[inline]
    fn mul(self, rhs: Complex) -> Self::Output {
        Complex {
            re: self.re * rhs.re - self.im * rhs.im,
            im: self.re * rhs.im + self.im * rhs.re,
        }
    }
}

impl core::ops::Mul for Complex {
    type Output = Complex;

    #[inline]
    fn mul(self, rhs: Complex) -> Self::Output {
        Complex {
            re: self.re * rhs.re - self.im * rhs.im,
            im: self.re * rhs.im + self.im * rhs.re,
        }
    }
}

impl core::ops::MulAssign for Complex {
    #[inline]
    fn mul_assign(&mut self, rhs: Complex) {
        *self = *self * rhs;
    }
}

impl core::ops::Div<f32> for Complex {
    type Output = Complex;

    #[inline]
    fn div(self, rhs: f32) -> Self::Output {
        Complex {
            re: self.re / rhs,
            im: self.im / rhs,
        }
    }
}

impl core::iter::Sum for Complex {
    #[inline]
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
        iter.fold(Complex::ZERO, |a, b| a + b)
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::op_ref)]

    use super::*;

    #[test]
    fn new() {
        let c = Complex::new(3.0, 4.0);
        assert_eq!(c.re, 3.0);
        assert_eq!(c.im, 4.0);
    }

    #[test]
    fn zero() {
        let c = Complex::ZERO;
        assert_eq!(c.re, 0.0);
        assert_eq!(c.im, 0.0);
    }

    #[test]
    fn conj() {
        let c = Complex::new(3.0, 4.0);
        let conj = c.conj();
        assert_eq!(conj.re, 3.0);
        assert_eq!(conj.im, -4.0);
    }

    #[rstest::rstest]
    #[case(Complex::new(1.0, 0.0), Complex::new(0.0, 0.0))]
    #[case(
        Complex::new(0.0, 1.0),
        Complex::new(0.0, core::f32::consts::FRAC_PI_2)
    )]
    #[case(Complex::new(-1.0, 0.0), Complex::new(0.0, core::f32::consts::PI))]
    #[case(Complex::new(0.0, -1.0), Complex::new(0.0, -core::f32::consts::FRAC_PI_2))]
    fn exp(#[case] expect: Complex, #[case] value: Complex) {
        let exp = value.exp();
        assert!((exp.re - expect.re).abs() < 1e-6);
        assert!((exp.im - expect.im).abs() < 1e-6);
    }

    #[test]
    fn arg() {
        let c = Complex::new(1.0, 1.0);
        let arg = c.arg();
        assert!((arg - core::f32::consts::PI / 4.0).abs() < 1e-6);
    }

    #[test]
    fn norm() {
        let c = Complex::new(3.0, 4.0);
        assert!((c.norm() - 5.0) < 1e-6);
    }

    #[test]
    fn norm_sqr() {
        let c = Complex::new(3.0, 4.0);
        assert!((c.norm_sqr() - 25.0) < 1e-6);
    }

    #[test]
    fn add() {
        let c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        let result = c1 + c2;
        assert_eq!(result.re, 1.0 + 3.0);
        assert_eq!(result.im, 2.0 + 4.0);
    }

    #[test]
    fn add_ref() {
        let c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        let result = c1 + &c2;
        assert_eq!(result.re, 1.0 + 3.0);
        assert_eq!(result.im, 2.0 + 4.0);
    }

    #[test]
    fn add_assign() {
        let mut c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        c1 += c2;
        assert_eq!(c1.re, 1.0 + 3.0);
        assert_eq!(c1.im, 2.0 + 4.0);
    }

    #[test]
    fn mul_scalar() {
        let c = Complex::new(2.0, 3.0);
        let result = c * 2.0;
        assert_eq!(result.re, 2.0 * 2.0);
        assert_eq!(result.im, 3.0 * 2.0);
    }

    #[test]
    fn mul() {
        let c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        let result = c1 * c2;
        assert_eq!(result.re, 1.0 * 3.0 - 2.0 * 4.0);
        assert_eq!(result.im, 1.0 * 4.0 + 2.0 * 3.0);
    }

    #[test]
    fn mul_ref() {
        let c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        let result = &c1 * &c2;
        assert_eq!(result.re, 1.0 * 3.0 - 2.0 * 4.0);
        assert_eq!(result.im, 1.0 * 4.0 + 2.0 * 3.0);
    }

    #[test]
    fn div_scalar() {
        let c = Complex::new(4.0, 6.0);
        let result = c / 2.0;
        assert_eq!(result.re, 4.0 / 2.0);
        assert_eq!(result.im, 6.0 / 2.0);
    }

    #[test]
    fn mul_assign() {
        let mut c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        c1 *= c2;
        assert_eq!(c1.re, 1.0 * 3.0 - 2.0 * 4.0);
        assert_eq!(c1.im, 1.0 * 4.0 + 2.0 * 3.0);
    }

    #[test]
    fn sum() {
        let c1 = Complex::new(1.0, 2.0);
        let c2 = Complex::new(3.0, 4.0);
        let c3 = Complex::new(5.0, 6.0);
        let sum: Complex = [c1, c2, c3].iter().copied().sum();
        assert_eq!(sum.re, 1.0 + 3.0 + 5.0);
        assert_eq!(sum.im, 2.0 + 4.0 + 6.0);
    }
}