use num_complex::Complex;
use num_traits::Float;
pub fn pulse<T: Float>(c: Complex<T>) -> T {
c.norm()
}
pub fn damp<T: Float>(c: Complex<T>) -> T {
let w = c.norm();
if w == T::zero() {
-T::one()
} else {
-c.re / w
}
}
pub(crate) fn compdiv<T: Float>(a: Complex<T>, b: Complex<T>) -> Complex<T> {
if b.im.abs() <= b.re.abs() {
let (e, f) = compdiv_impl(a.re, a.im, b.re, b.im);
Complex::new(e, f)
} else {
let (e, f) = compdiv_impl(a.im, a.re, b.im, b.re);
Complex::new(e, -f)
}
}
#[allow(clippy::many_single_char_names)]
fn compdiv_impl<T: Float>(a: T, b: T, c: T, d: T) -> (T, T) {
let r = d / c;
let t = (c + d * r).recip();
if r.is_zero() {
let e = (a + d * (b / c)) * t;
let f = (b - d * (a / c)) * t;
(e, f)
} else {
let e = (a + b * r) * t;
let f = (b - a * r) * t;
(e, f)
}
}
#[allow(clippy::many_single_char_names)]
pub(crate) fn compinv<T: Float>(n: Complex<T>) -> Complex<T> {
let c = n.re;
let d = n.im;
if d.abs() <= c.abs() {
let r = d / c;
let t = (c + d * r).recip();
let e = t;
let f = -r * t;
Complex::new(e, f)
} else {
let r = c / d;
let t = (c * r + d).recip();
let e = r * t;
let f = -t;
Complex::new(e, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
use std::str::FromStr;
#[test]
fn pulse_damp() {
let c = Complex::from_str("4+3i").unwrap();
assert_relative_eq!(5., pulse(c));
assert_relative_eq!(-0.8, damp(c));
let i = Complex::from_str("i").unwrap();
assert_relative_eq!(1., pulse(i));
assert_relative_eq!(0., damp(i));
let zero = Complex::from_str("0").unwrap();
assert_relative_eq!(0., pulse(zero));
assert_relative_eq!(-1., damp(zero));
}
fn p2(n: i32) -> f64 {
2.0_f64.powf(f64::from(n))
}
#[test]
fn complex_division_a() {
let a = compdiv(Complex::new(1., 1.), Complex::new(1., 1e307));
assert_eq!(
Complex::new(1.000_000_000_000_000_1e-307, -1.000_000_000_000_000_1e-307),
a
);
}
#[test]
fn complex_division_b() {
let b = compdiv(Complex::new(1., 1.), Complex::new(1e-307, 1e-307));
assert_eq!(Complex::new(1.000_000_000_000_000_1e307, 0.), b);
}
#[test]
fn complex_division_c() {
let c = compdiv(Complex::new(1e307, 1e-307), Complex::new(1e204, 1e-204));
assert_eq!(Complex::new(1e103, -1e-305), c);
}
#[test]
fn complex_division_1() {
let d1 = compdiv(Complex::new(1., 1.), Complex::new(1., p2(1023)));
assert_eq!(Complex::new(p2(-1023), -p2(-1023)), d1);
}
#[test]
fn complex_division_2() {
let d2 = compdiv(Complex::new(1., 1.), Complex::new(p2(-1023), p2(-1023)));
assert_eq!(Complex::new(p2(1023), 0.), d2);
}
#[test]
#[should_panic]
fn complex_division_2_fail() {
let d2 = Complex::new(1., 1.).fdiv(Complex::new(p2(-1023), p2(-1023)));
assert_eq!(Complex::new(p2(1023), 0.), d2);
}
#[test]
fn complex_division_3() {
let d3 = compdiv(
Complex::new(p2(1023), p2(-1023)),
Complex::new(p2(677), p2(-677)),
);
assert_eq!(Complex::new(p2(346), -p2(-1008)), d3);
}
#[test]
fn complex_division_5() {
let d5 = compdiv(
Complex::new(p2(1020), p2(-844)),
Complex::new(p2(656), p2(-780)),
);
assert_eq!(Complex::new(p2(364), -p2(-1072)), d5);
}
#[test]
fn complex_division_6() {
let d6 = compdiv(
Complex::new(p2(-71), p2(1021)),
Complex::new(p2(1001), p2(-323)),
);
assert_eq!(Complex::new(p2(-1072), p2(20)), d6);
}
#[test]
fn complex_division_limits() {
let c1 = Complex::new(1., 1.);
let c2 = Complex::new(1., -1.);
let c3 = Complex::new(-1., 1.);
let c4 = Complex::new(-1., -1.);
let zero = Complex::new(0., 0.);
assert!(compdiv(c1, zero).is_nan());
assert!(compdiv(c2, zero).is_nan());
assert!(compdiv(c3, zero).is_nan());
assert!(compdiv(c4, zero).is_nan());
}
proptest! {
#[test]
fn qc_complex_inversion_algorithm(a: f64, b: f64) {
prop_assume!(a != 0. && b != 0.);
let c = Complex::new(a, b);
assert_eq!(compdiv(Complex::new(1., 0.), c), compinv(c));
}
}
#[test]
fn complex_inversion_limits() {
assert!(compinv(Complex::new(0., 0.)).is_nan());
}
#[test]
fn complex_inversion_real() {
let n = 1234.45;
let inv = compinv(Complex::new(n, 0.));
assert_eq!(Complex::new(n.recip(), 0.), inv);
}
#[test]
fn complex_inversion_imaginary() {
let n = 1234.45;
let inv = compinv(Complex::new(0., n));
assert_eq!(-Complex::new(0., n.recip()), inv);
}
}