use std::sync::Arc;
use num_complex::Complex;
use num_traits::Zero;
use crate::common::FFTnum;
use crate::{Length, IsInverse, Fft};
pub struct BluesteinsAlgorithm<T> {
inner_fft: Arc<dyn Fft<T>>,
inner_fft_multiplier: Box<[Complex<T>]>,
twiddles: Box<[Complex<T>]>,
len: usize,
inverse: bool,
}
impl<T: FFTnum> BluesteinsAlgorithm<T> {
fn compute_bluesteins_twiddle(index: usize, size: usize, inverse: bool) -> Complex<T> {
let index_multiplier = core::f64::consts::PI / size as f64;
let index_float = index as f64;
let index_squared = index_float * index_float;
let theta = index_squared * index_multiplier;
let result = Complex::new(
T::from_f64(theta.cos()).unwrap(),
T::from_f64(theta.sin()).unwrap(),
);
if inverse {
result.conj()
} else {
result
}
}
pub fn new(len: usize, inner_fft: Arc<dyn Fft<T>>) -> Self {
let inner_fft_len = inner_fft.len();
assert!(len * 2 - 1 <= inner_fft_len, "Bluestein's algorithm requires inner_fft.len() >= self.len() * 2 - 1. Expected >= {}, got {}", len * 2 - 1, inner_fft_len);
let inner_len_float = T::from_usize(inner_fft_len).unwrap();
let inverse = inner_fft.is_inverse();
let mut inner_fft_input = vec![Complex::zero(); inner_fft_len];
for i in 0..len {
inner_fft_input[i] = Self::compute_bluesteins_twiddle(i, len, inverse) / inner_len_float;
}
for i in 1..len {
inner_fft_input[inner_fft_len - i] = inner_fft_input[i];
}
let mut inner_fft_scratch = vec![Complex::zero(); inner_fft.get_inplace_scratch_len()];
inner_fft.process_inplace_with_scratch(&mut inner_fft_input, &mut inner_fft_scratch);
let twiddles : Vec<_> = (0..len).map(|i| Self::compute_bluesteins_twiddle(i, len, !inverse)).collect();
Self {
inner_fft: inner_fft,
inner_fft_multiplier: inner_fft_input.into_boxed_slice(),
twiddles: twiddles.into_boxed_slice(),
len,
inverse,
}
}
fn perform_fft_inplace(&self, input: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
let (inner_input, inner_scratch) = scratch.split_at_mut(self.inner_fft_multiplier.len());
for ((buffer_entry, inner_entry), twiddle) in input.iter().zip(inner_input.iter_mut()).zip(self.twiddles.iter()) {
*inner_entry = *buffer_entry * *twiddle ;
}
for inner in inner_input.iter_mut().skip(input.len()) {
*inner = Complex::zero();
}
self.inner_fft.process_inplace_with_scratch(inner_input, inner_scratch);
for (inner, multiplier) in inner_input.iter_mut().zip(self.inner_fft_multiplier.iter()) {
*inner = (*inner * *multiplier).conj();
}
self.inner_fft.process_inplace_with_scratch(inner_input, inner_scratch);
for ((buffer_entry, inner_entry), twiddle) in input.iter_mut().zip(inner_input.iter()).zip(self.twiddles.iter()) {
*buffer_entry = inner_entry.conj() * twiddle;
}
}
fn perform_fft_out_of_place(&self, input: &mut [Complex<T>], output: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
let (inner_input, inner_scratch) = scratch.split_at_mut(self.inner_fft_multiplier.len());
for ((buffer_entry, inner_entry), twiddle) in input.iter().zip(inner_input.iter_mut()).zip(self.twiddles.iter()) {
*inner_entry = *buffer_entry * *twiddle ;
}
for inner in inner_input.iter_mut().skip(input.len()) {
*inner = Complex::zero();
}
self.inner_fft.process_inplace_with_scratch(inner_input, inner_scratch);
for (inner, multiplier) in inner_input.iter_mut().zip(self.inner_fft_multiplier.iter()) {
*inner = (*inner * *multiplier).conj();
}
self.inner_fft.process_inplace_with_scratch(inner_input, inner_scratch);
for ((buffer_entry, inner_entry), twiddle) in output.iter_mut().zip(inner_input.iter()).zip(self.twiddles.iter()) {
*buffer_entry = inner_entry.conj() * twiddle;
}
}
}
boilerplate_fft!(BluesteinsAlgorithm,
|this: &BluesteinsAlgorithm<_>| this.len, |this: &BluesteinsAlgorithm<_>| this.inner_fft_multiplier.len() + this.inner_fft.get_inplace_scratch_len(), |this: &BluesteinsAlgorithm<_>| this.inner_fft_multiplier.len() + this.inner_fft.get_inplace_scratch_len() );
#[cfg(test)]
mod unit_tests {
use super::*;
use std::sync::Arc;
use crate::test_utils::check_fft_algorithm;
use crate::algorithm::DFT;
#[test]
fn test_bluesteins() {
for &len in &[3,5,7,11,13] {
test_bluesteins_with_length(len, false);
test_bluesteins_with_length(len, true);
}
}
fn test_bluesteins_with_length(len: usize, inverse: bool) {
let inner_fft = Arc::new(DFT::new((len *2 - 1).checked_next_power_of_two().unwrap(), inverse));
let fft = BluesteinsAlgorithm::new(len, inner_fft);
check_fft_algorithm::<f32>(&fft, len, inverse);
}
}