use crate::kernel::{Complex, Float};
#[inline]
pub fn notw_2<T: Float>(x: &mut [Complex<T>]) {
debug_assert!(x.len() >= 2);
let a = x[0];
let b = x[1];
x[0] = a + b;
x[1] = a - b;
}
#[inline]
pub fn notw_3<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 3);
let c = T::from_f64(-0.5);
let s = T::from_f64(0.866_025_403_784_438_6);
let x0 = x[0];
let x1 = x[1];
let x2 = x[2];
let t0 = x1 + x2;
let t1 = x0 + t0 * c;
let t2 = x1 - x2;
let t2_rot = if sign < 0 {
Complex::new(t2.im * s, -t2.re * s)
} else {
Complex::new(-t2.im * s, t2.re * s)
};
x[0] = x0 + t0; x[1] = t1 + t2_rot;
x[2] = t1 - t2_rot;
}
#[inline]
pub fn notw_5<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 5);
let c1 = T::from_f64(0.309_016_994_374_947_4); let c2 = T::from_f64(-0.809_016_994_374_947_4); let s1 = T::from_f64(0.951_056_516_295_153_5); let s2 = T::from_f64(0.587_785_252_292_473_1);
let x0 = x[0];
let x1 = x[1];
let x2 = x[2];
let x3 = x[3];
let x4 = x[4];
let a1 = x1 + x4; let a2 = x2 + x3; let b1 = x1 - x4; let b2 = x2 - x3;
let r1 = a1 * c1 + a2 * c2; let r2 = a1 * c2 + a2 * c1;
let t1 = b1 * s1 + b2 * s2;
let t2 = b1 * s2 - b2 * s1;
let (i1, i2) = if sign < 0 {
(Complex::new(t1.im, -t1.re), Complex::new(t2.im, -t2.re))
} else {
(Complex::new(-t1.im, t1.re), Complex::new(-t2.im, t2.re))
};
x[0] = x0 + a1 + a2;
x[1] = x0 + r1 + i1;
x[4] = x0 + r1 - i1;
x[2] = x0 + r2 + i2;
x[3] = x0 + r2 - i2;
}
#[inline]
pub fn notw_7<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 7);
let c1 = T::from_f64(0.623_489_801_858_734); let c2 = T::from_f64(-0.222_520_933_956_314); let c3 = T::from_f64(-0.900_968_867_902_419); let s1 = T::from_f64(0.781_831_482_468_03); let s2 = T::from_f64(0.974_927_912_181_824); let s3 = T::from_f64(0.433_883_739_117_558);
let x0 = x[0];
let x1 = x[1];
let x2 = x[2];
let x3 = x[3];
let x4 = x[4];
let x5 = x[5];
let x6 = x[6];
let a1 = x1 + x6;
let a2 = x2 + x5;
let a3 = x3 + x4;
let b1 = x1 - x6;
let b2 = x2 - x5;
let b3 = x3 - x4;
let r1 = a1 * c1 + a2 * c2 + a3 * c3;
let r2 = a1 * c2 + a2 * c3 + a3 * c1;
let r3 = a1 * c3 + a2 * c1 + a3 * c2;
let t1 = b1 * s1 + b2 * s2 + b3 * s3;
let t2 = b1 * s2 - b2 * s3 - b3 * s1;
let t3 = b1 * s3 - b2 * s1 + b3 * s2;
let (i1, i2, i3) = if sign < 0 {
(
Complex::new(t1.im, -t1.re),
Complex::new(t2.im, -t2.re),
Complex::new(t3.im, -t3.re),
)
} else {
(
Complex::new(-t1.im, t1.re),
Complex::new(-t2.im, t2.re),
Complex::new(-t3.im, t3.re),
)
};
x[0] = x0 + a1 + a2 + a3;
x[1] = x0 + r1 + i1;
x[6] = x0 + r1 - i1;
x[2] = x0 + r2 + i2;
x[5] = x0 + r2 - i2;
x[3] = x0 + r3 + i3;
x[4] = x0 + r3 - i3;
}
#[inline]
pub fn notw_4<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 4);
let x0 = x[0];
let x1 = x[1];
let x2 = x[2];
let x3 = x[3];
let t0 = x0 + x2;
let t1 = x0 - x2;
let t2 = x1 + x3;
let t3 = x1 - x3;
let t3_rot = if sign < 0 {
Complex::new(t3.im, -t3.re)
} else {
Complex::new(-t3.im, t3.re)
};
x[0] = t0 + t2;
x[2] = t0 - t2;
x[1] = t1 + t3_rot;
x[3] = t1 - t3_rot;
}
#[inline]
pub fn notw_8<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 8);
let sqrt2_2 = T::from_f64(core::f64::consts::FRAC_1_SQRT_2);
let x0 = x[0];
let x1 = x[1];
let x2 = x[2];
let x3 = x[3];
let x4 = x[4];
let x5 = x[5];
let x6 = x[6];
let x7 = x[7];
let t0 = x0 + x4;
let t1 = x0 - x4;
let t2 = x2 + x6;
let t3 = x2 - x6;
let t4 = x1 + x5;
let t5 = x1 - x5;
let t6 = x3 + x7;
let t7 = x3 - x7;
let t3_rot = if sign < 0 {
Complex::new(t3.im, -t3.re) } else {
Complex::new(-t3.im, t3.re) };
let t5_rot = if sign < 0 {
Complex::new((t5.re + t5.im) * sqrt2_2, (-t5.re + t5.im) * sqrt2_2)
} else {
Complex::new((t5.re - t5.im) * sqrt2_2, (t5.re + t5.im) * sqrt2_2)
};
let t7_rot = if sign < 0 {
Complex::new((-t7.re + t7.im) * sqrt2_2, (-t7.re - t7.im) * sqrt2_2)
} else {
Complex::new((-t7.re - t7.im) * sqrt2_2, (t7.re - t7.im) * sqrt2_2)
};
let u0 = t0 + t2;
let u1 = t0 - t2;
let u2 = t4 + t6;
let u3 = t4 - t6;
let u4 = t1 + t3_rot;
let u5 = t1 - t3_rot;
let u6 = t5_rot + t7_rot;
let u7 = t5_rot - t7_rot;
let u3_rot = if sign < 0 {
Complex::new(u3.im, -u3.re)
} else {
Complex::new(-u3.im, u3.re)
};
let u7_rot = if sign < 0 {
Complex::new(u7.im, -u7.re)
} else {
Complex::new(-u7.im, u7.re)
};
let y0 = u0 + u2; let y4 = u0 - u2; let y2 = u1 + u3_rot; let y6 = u1 - u3_rot; let y1 = u4 + u6; let y5 = u4 - u6; let y3 = u5 + u7_rot; let y7 = u5 - u7_rot;
x[0] = y0;
x[1] = y1;
x[2] = y2;
x[3] = y3;
x[4] = y4;
x[5] = y5;
x[6] = y6;
x[7] = y7;
}
#[inline]
pub fn notw_16<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 16);
let c1 = T::from_f64(0.923_879_532_511_286_7); let s1 = T::from_f64(0.382_683_432_365_089_8); let c2 = T::from_f64(core::f64::consts::FRAC_1_SQRT_2); let c3 = s1; let s3 = c1;
let mut a = [Complex::<T>::zero(); 16];
a[0] = x[0];
a[1] = x[8];
a[2] = x[4];
a[3] = x[12];
a[4] = x[2];
a[5] = x[10];
a[6] = x[6];
a[7] = x[14];
a[8] = x[1];
a[9] = x[9];
a[10] = x[5];
a[11] = x[13];
a[12] = x[3];
a[13] = x[11];
a[14] = x[7];
a[15] = x[15];
for i in (0..16).step_by(2) {
let t = a[i + 1];
a[i + 1] = a[i] - t;
a[i] = a[i] + t;
}
for group in (0..16).step_by(4) {
let t = a[group + 2];
a[group + 2] = a[group] - t;
a[group] = a[group] + t;
let t = a[group + 3];
let t_tw = if sign < 0 {
Complex::new(t.im, -t.re) } else {
Complex::new(-t.im, t.re) };
a[group + 3] = a[group + 1] - t_tw;
a[group + 1] = a[group + 1] + t_tw;
}
for group in (0..16).step_by(8) {
let t = a[group + 4];
a[group + 4] = a[group] - t;
a[group] = a[group] + t;
let t = a[group + 5];
let t_tw = if sign < 0 {
Complex::new((t.re + t.im) * c2, (t.im - t.re) * c2)
} else {
Complex::new((t.re - t.im) * c2, (t.im + t.re) * c2)
};
a[group + 5] = a[group + 1] - t_tw;
a[group + 1] = a[group + 1] + t_tw;
let t = a[group + 6];
let t_tw = if sign < 0 {
Complex::new(t.im, -t.re)
} else {
Complex::new(-t.im, t.re)
};
a[group + 6] = a[group + 2] - t_tw;
a[group + 2] = a[group + 2] + t_tw;
let t = a[group + 7];
let t_tw = if sign < 0 {
Complex::new((-t.re + t.im) * c2, (-t.im - t.re) * c2)
} else {
Complex::new((-t.re - t.im) * c2, (-t.im + t.re) * c2)
};
a[group + 7] = a[group + 3] - t_tw;
a[group + 3] = a[group + 3] + t_tw;
}
let t = a[8];
a[8] = a[0] - t;
a[0] = a[0] + t;
let t = a[9];
let t_tw = if sign < 0 {
Complex::new(t.re * c1 + t.im * s1, t.im * c1 - t.re * s1)
} else {
Complex::new(t.re * c1 - t.im * s1, t.im * c1 + t.re * s1)
};
a[9] = a[1] - t_tw;
a[1] = a[1] + t_tw;
let t = a[10];
let t_tw = if sign < 0 {
Complex::new((t.re + t.im) * c2, (t.im - t.re) * c2)
} else {
Complex::new((t.re - t.im) * c2, (t.im + t.re) * c2)
};
a[10] = a[2] - t_tw;
a[2] = a[2] + t_tw;
let t = a[11];
let t_tw = if sign < 0 {
Complex::new(t.re * c3 + t.im * s3, t.im * c3 - t.re * s3)
} else {
Complex::new(t.re * c3 - t.im * s3, t.im * c3 + t.re * s3)
};
a[11] = a[3] - t_tw;
a[3] = a[3] + t_tw;
let t = a[12];
let t_tw = if sign < 0 {
Complex::new(t.im, -t.re)
} else {
Complex::new(-t.im, t.re)
};
a[12] = a[4] - t_tw;
a[4] = a[4] + t_tw;
let t = a[13];
let t_tw = if sign < 0 {
Complex::new(-t.re * s1 + t.im * c1, -t.im * s1 - t.re * c1)
} else {
Complex::new(-t.re * s1 - t.im * c1, -t.im * s1 + t.re * c1)
};
a[13] = a[5] - t_tw;
a[5] = a[5] + t_tw;
let t = a[14];
let t_tw = if sign < 0 {
Complex::new((-t.re + t.im) * c2, (-t.im - t.re) * c2)
} else {
Complex::new((-t.re - t.im) * c2, (-t.im + t.re) * c2)
};
a[14] = a[6] - t_tw;
a[6] = a[6] + t_tw;
let t = a[15];
let t_tw = if sign < 0 {
Complex::new(-t.re * c1 + t.im * s1, -t.im * c1 - t.re * s1)
} else {
Complex::new(-t.re * c1 - t.im * s1, -t.im * c1 + t.re * s1)
};
a[15] = a[7] - t_tw;
a[7] = a[7] + t_tw;
x[0] = a[0];
x[1] = a[1];
x[2] = a[2];
x[3] = a[3];
x[4] = a[4];
x[5] = a[5];
x[6] = a[6];
x[7] = a[7];
x[8] = a[8];
x[9] = a[9];
x[10] = a[10];
x[11] = a[11];
x[12] = a[12];
x[13] = a[13];
x[14] = a[14];
x[15] = a[15];
}
#[inline]
pub fn notw_32<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 32);
let mut even = [Complex::<T>::zero(); 16];
let mut odd = [Complex::<T>::zero(); 16];
for i in 0..16 {
even[i] = x[2 * i];
odd[i] = x[2 * i + 1];
}
notw_16(&mut even, sign);
notw_16(&mut odd, sign);
let angle_step = if sign < 0 {
-<T as Float>::PI / T::from_usize(16)
} else {
<T as Float>::PI / T::from_usize(16)
};
let w_step = Complex::cis(angle_step);
let mut w = Complex::new(T::one(), T::zero());
for k in 0..16 {
let t = Complex::new(
odd[k].re * w.re - odd[k].im * w.im,
odd[k].im * w.re + odd[k].re * w.im,
);
x[k] = even[k] + t;
x[k + 16] = even[k] - t;
w = w * w_step;
}
}
#[inline]
pub fn notw_64<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 64);
let mut even = [Complex::<T>::zero(); 32];
let mut odd = [Complex::<T>::zero(); 32];
for i in 0..32 {
even[i] = x[2 * i];
odd[i] = x[2 * i + 1];
}
notw_32(&mut even, sign);
notw_32(&mut odd, sign);
let angle_step = if sign < 0 {
-<T as Float>::PI / T::from_usize(32)
} else {
<T as Float>::PI / T::from_usize(32)
};
let w_step = Complex::cis(angle_step);
let mut w = Complex::new(T::one(), T::zero());
for k in 0..32 {
let t = Complex::new(
odd[k].re * w.re - odd[k].im * w.im,
odd[k].im * w.re + odd[k].re * w.im,
);
x[k] = even[k] + t;
x[k + 32] = even[k] - t;
w = w * w_step;
}
}
#[inline]
pub fn notw_128<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 128);
let mut even = [Complex::<T>::zero(); 64];
let mut odd = [Complex::<T>::zero(); 64];
for i in 0..64 {
even[i] = x[2 * i];
odd[i] = x[2 * i + 1];
}
notw_64(&mut even, sign);
notw_64(&mut odd, sign);
let angle_step = if sign < 0 {
-<T as Float>::PI / T::from_usize(64)
} else {
<T as Float>::PI / T::from_usize(64)
};
let w_step = Complex::cis(angle_step);
let mut w = Complex::new(T::one(), T::zero());
for k in 0..64 {
let t = Complex::new(
odd[k].re * w.re - odd[k].im * w.im,
odd[k].im * w.re + odd[k].re * w.im,
);
x[k] = even[k] + t;
x[k + 64] = even[k] - t;
w = w * w_step;
}
}
#[inline]
pub fn notw_256<T: Float>(x: &mut [Complex<T>], sign: i32) {
debug_assert!(x.len() >= 256);
let mut even = [Complex::<T>::zero(); 128];
let mut odd = [Complex::<T>::zero(); 128];
for i in 0..128 {
even[i] = x[2 * i];
odd[i] = x[2 * i + 1];
}
notw_128(&mut even, sign);
notw_128(&mut odd, sign);
let angle_step = if sign < 0 {
-<T as Float>::PI / T::from_usize(128)
} else {
<T as Float>::PI / T::from_usize(128)
};
let w_step = Complex::cis(angle_step);
let mut w = Complex::new(T::one(), T::zero());
for k in 0..128 {
let t = Complex::new(
odd[k].re * w.re - odd[k].im * w.im,
odd[k].im * w.re + odd[k].re * w.im,
);
x[k] = even[k] + t;
x[k + 128] = even[k] - t;
w = w * w_step;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::fft;
fn complex_approx_eq(a: Complex<f64>, b: Complex<f64>, eps: f64) -> bool {
(a.re - b.re).abs() < eps && (a.im - b.im).abs() < eps
}
#[test]
fn test_notw_2() {
let mut x = [Complex::new(1.0_f64, 0.0), Complex::new(2.0, 0.0)];
notw_2(&mut x);
assert!((x[0].re - 3.0).abs() < 1e-10);
assert!((x[1].re - (-1.0)).abs() < 1e-10);
}
#[test]
fn test_notw_3() {
let input: Vec<Complex<f64>> = (0..3).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_3(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_3_roundtrip() {
let original: Vec<Complex<f64>> = (0..3)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_3(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_3(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 3.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_5() {
let input: Vec<Complex<f64>> = (0..5).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_5(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_5_roundtrip() {
let original: Vec<Complex<f64>> = (0..5)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_5(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_5(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 5.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_7() {
let input: Vec<Complex<f64>> = (0..7).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_7(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_7_roundtrip() {
let original: Vec<Complex<f64>> = (0..7)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_7(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_7(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 7.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_4() {
let mut x = [
Complex::new(1.0_f64, 0.0),
Complex::new(2.0, 0.0),
Complex::new(3.0, 0.0),
Complex::new(4.0, 0.0),
];
notw_4(&mut x, -1);
assert!((x[0].re - 10.0).abs() < 1e-10);
}
#[test]
fn test_notw_8() {
let input: Vec<Complex<f64>> = (0..8).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_8(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_8_roundtrip() {
let original: Vec<Complex<f64>> = (0..8)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_8(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_8(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 8.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-10),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_8_dc_component() {
let input: Vec<Complex<f64>> = vec![
Complex::new(1.0, 0.0),
Complex::new(2.0, 0.0),
Complex::new(3.0, 0.0),
Complex::new(4.0, 0.0),
Complex::new(5.0, 0.0),
Complex::new(6.0, 0.0),
Complex::new(7.0, 0.0),
Complex::new(8.0, 0.0),
];
let mut result = input;
notw_8(&mut result, -1);
assert!(
(result[0].re - 36.0).abs() < 1e-10,
"DC component should be 36, got {}",
result[0].re
);
assert!(
result[0].im.abs() < 1e-10,
"DC component should have no imaginary part"
);
}
#[test]
fn test_notw_16() {
let input: Vec<Complex<f64>> = (0..16).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_16(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-9),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_16_roundtrip() {
let original: Vec<Complex<f64>> = (0..16)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_16(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_16(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 16.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-9),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_16_dc_component() {
let input: Vec<Complex<f64>> = (1..=16).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let mut result = input;
notw_16(&mut result, -1);
assert!(
(result[0].re - 136.0).abs() < 1e-9,
"DC component should be 136, got {}",
result[0].re
);
assert!(
result[0].im.abs() < 1e-9,
"DC component should have no imaginary part"
);
}
#[test]
fn test_notw_32() {
let input: Vec<Complex<f64>> = (0..32).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_32(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-8),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_32_roundtrip() {
let original: Vec<Complex<f64>> = (0..32)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_32(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_32(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 32.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-9),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_32_dc_component() {
let input: Vec<Complex<f64>> = (1..=32).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let mut result = input;
notw_32(&mut result, -1);
assert!(
(result[0].re - 528.0).abs() < 1e-8,
"DC component should be 528, got {}",
result[0].re
);
assert!(
result[0].im.abs() < 1e-8,
"DC component should have no imaginary part"
);
}
#[test]
fn test_notw_64() {
let input: Vec<Complex<f64>> = (0..64).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_64(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-7),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_64_roundtrip() {
let original: Vec<Complex<f64>> = (0..64)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_64(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_64(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 64.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-9),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_64_dc_component() {
let input: Vec<Complex<f64>> = (1..=64).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let mut result = input;
notw_64(&mut result, -1);
assert!(
(result[0].re - 2080.0).abs() < 1e-7,
"DC component should be 2080, got {}",
result[0].re
);
assert!(
result[0].im.abs() < 1e-7,
"DC component should have no imaginary part"
);
}
#[test]
fn test_notw_128() {
let input: Vec<Complex<f64>> = (0..128).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_128(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-6),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_128_roundtrip() {
let original: Vec<Complex<f64>> = (0..128)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_128(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_128(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 128.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-8),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_128_dc_component() {
let input: Vec<Complex<f64>> = (1..=128).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let mut result = input;
notw_128(&mut result, -1);
assert!(
(result[0].re - 8256.0).abs() < 1e-5,
"DC component should be 8256, got {}",
result[0].re
);
assert!(
result[0].im.abs() < 1e-5,
"DC component should have no imaginary part"
);
}
#[test]
fn test_notw_256() {
let input: Vec<Complex<f64>> = (0..256).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let expected = fft(&input);
let mut actual = input;
notw_256(&mut actual, -1);
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-5),
"Mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_256_roundtrip() {
let original: Vec<Complex<f64>> = (0..256)
.map(|i| Complex::new(f64::from(i).sin(), f64::from(i).cos()))
.collect();
let mut transformed = original.clone();
notw_256(&mut transformed, -1);
let mut recovered = transformed.clone();
notw_256(&mut recovered, 1);
for x in &mut recovered {
*x = *x / 256.0;
}
for (i, (a, e)) in recovered.iter().zip(original.iter()).enumerate() {
assert!(
complex_approx_eq(*a, *e, 1e-8),
"Roundtrip mismatch at index {i}: got {a:?}, expected {e:?}"
);
}
}
#[test]
fn test_notw_256_dc_component() {
let input: Vec<Complex<f64>> = (1..=256).map(|i| Complex::new(f64::from(i), 0.0)).collect();
let mut result = input;
notw_256(&mut result, -1);
assert!(
(result[0].re - 32896.0).abs() < 1e-4,
"DC component should be 32896, got {}",
result[0].re
);
assert!(
result[0].im.abs() < 1e-4,
"DC component should have no imaginary part"
);
}
}