use crate::generic_singleton;
use realfft::ComplexToReal;
use realfft::RealFftPlanner;
use realfft::RealToComplex;
use rustfft::num_complex::Complex;
use rustfft::FftNum;
use std::cell::RefCell;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
pub trait RealFft<T, const SIZE: usize>
where
[T; SIZE / 2 + 1]: Sized,
{
fn real_fft(&self) -> RealDft<T, SIZE>;
}
pub trait RealIfft<T, const SIZE: usize> {
fn real_ifft(&self) -> [T; SIZE];
}
#[derive(Debug)]
pub struct RealDft<T, const SIZE: usize>
where
[T; SIZE / 2 + 1]: Sized,
{
inner: [Complex<T>; SIZE / 2 + 1],
}
impl<T, const SIZE: usize> Deref for RealDft<T, SIZE>
where
[T; SIZE / 2 + 1]: Sized,
{
type Target = [Complex<T>; SIZE / 2 + 1];
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T, const SIZE: usize> DerefMut for RealDft<T, SIZE>
where
[T; SIZE / 2 + 1]: Sized,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: FftNum + Default, const SIZE: usize> RealFft<T, SIZE> for [T; SIZE]
where
[T; SIZE / 2 + 1]: Sized,
{
fn real_fft(&self) -> RealDft<T, SIZE> {
let r2c = get_real_fft_algorithm::<T, SIZE>();
let mut output = [Complex::default(); SIZE / 2 + 1];
r2c.process(&mut self.clone(), &mut output).unwrap();
RealDft { inner: output }
}
}
impl<T: FftNum + Default, const SIZE: usize> RealIfft<T, SIZE> for RealDft<T, SIZE>
where
[T; SIZE / 2 + 1]: Sized,
{
fn real_ifft(&self) -> [T; SIZE] {
let c2r = get_inverse_real_fft_algorithm::<T, SIZE>();
let mut output = [T::default(); SIZE];
c2r.process(&mut (*self).clone(), &mut output).unwrap();
output
}
}
fn get_real_fft_algorithm<T: FftNum, const SIZE: usize>() -> Arc<dyn RealToComplex<T>> {
generic_singleton::get_or_init(|| RefCell::new(RealFftPlanner::new()))
.borrow_mut()
.plan_fft_forward(SIZE)
}
fn get_inverse_real_fft_algorithm<T: FftNum, const SIZE: usize>() -> Arc<dyn ComplexToReal<T>> {
generic_singleton::get_or_init(|| RefCell::new(RealFftPlanner::new()))
.borrow_mut()
.plan_fft_inverse(SIZE)
}
#[cfg(test)]
mod tests {
use super::*;
const ARBITRARY_EVEN_TEST_ARRAY: [f64; 6] = [1.5, 3.0, 2.1, 3.2, 2.2, 3.1];
const ARBITRARY_ODD_TEST_ARRAY: [f64; 7] = [1.5, 3.0, 2.1, 3.2, 2.2, 3.1, 1.2];
const ACCEPTABLE_ERROR: f64 = 0.00000000000001;
fn real_fft_and_real_ifft_are_inverse_operations<const SIZE: usize>(array: [f64; SIZE])
where
[f64; SIZE / 2 + 1]: Sized,
{
let converted: Vec<_> = array
.real_fft()
.real_ifft()
.iter_mut()
.map(|sample| *sample / array.len() as f64)
.collect();
assert_eq!(array.len(), converted.len());
for (converted, original) in converted.iter().zip(array.iter()) {
approx::assert_ulps_eq!(converted, original, epsilon = ACCEPTABLE_ERROR);
}
}
#[test]
fn real_fft_and_real_ifft_are_inverse_operations_even() {
real_fft_and_real_ifft_are_inverse_operations(ARBITRARY_EVEN_TEST_ARRAY);
}
#[test]
fn real_fft_and_real_ifft_are_inverse_operations_odd() {
real_fft_and_real_ifft_are_inverse_operations(ARBITRARY_ODD_TEST_ARRAY);
}
}