#[derive(Copy, Clone)]
pub enum Direction {
Forward = 1,
Reverse = -1,
}
#[derive(Copy, Clone, Debug, Default)]
pub enum PlannerMode {
#[default]
Heuristic,
Tune,
}
macro_rules! impl_planner_dit_for {
($struct_name:ident, $precision:ident, $fft_func:path) => {
pub struct $struct_name {
/// Twiddles for each stage that needs them (stages with chunk_size > 64)
pub(crate) stage_twiddles: Vec<(Vec<$precision>, Vec<$precision>)>,
pub(crate) log_n: usize,
pub(crate) simd_level: fearless_simd::Level,
}
impl $struct_name {
pub fn new(num_points: usize) -> Self {
Self::with_mode(num_points, PlannerMode::Heuristic)
}
pub fn with_mode(num_points: usize, _mode: PlannerMode) -> Self {
assert!(num_points > 0 && num_points.is_power_of_two());
let simd_level = fearless_simd::Level::new();
let log_n = num_points.ilog2() as usize;
let mut stage_twiddles = Vec::new();
for stage in 0..log_n {
let dist = 1 << stage; let chunk_size = dist * 2;
if chunk_size > 64 {
let mut twiddles_re = vec![0.0 as $precision; dist];
let mut twiddles_im = vec![0.0 as $precision; dist];
let angle_mult =
-2.0 * std::$precision::consts::PI / chunk_size as $precision;
for k in 0..dist {
let angle = angle_mult * k as $precision;
twiddles_re[k] = angle.cos();
twiddles_im[k] = angle.sin();
}
stage_twiddles.push((twiddles_re, twiddles_im));
}
}
Self {
stage_twiddles,
log_n,
simd_level,
}
}
}
};
}
impl_planner_dit_for!(
PlannerDit64,
f64,
crate::algorithms::dit::fft_64_dit_with_planner_and_opts
);
impl_planner_dit_for!(
PlannerDit32,
f32,
crate::algorithms::dit::fft_32_dit_with_planner_and_opts
);
fn compute_r2c_twiddles_f64(n: usize) -> (Vec<f64>, Vec<f64>) {
let half = n / 2;
let mut w_re = vec![0.0f64; half];
let mut w_im = vec![0.0f64; half];
let angle_step = -std::f64::consts::PI / half as f64;
let (st, ct) = angle_step.sin_cos();
let (mut wr, mut wi) = (1.0f64, 0.0f64);
for k in 0..half {
w_re[k] = 0.5 * wr;
w_im[k] = 0.5 * wi;
let tmp = wr;
wr = tmp * ct - wi * st;
wi = tmp * st + wi * ct;
}
(w_re, w_im)
}
fn compute_r2c_twiddles_f32(n: usize) -> (Vec<f32>, Vec<f32>) {
let half = n / 2;
let mut w_re = vec![0.0f32; half];
let mut w_im = vec![0.0f32; half];
let angle_step = -std::f64::consts::PI / half as f64;
let (st, ct) = angle_step.sin_cos();
let (mut wr, mut wi) = (1.0f64, 0.0f64);
for k in 0..half {
w_re[k] = (0.5 * wr) as f32;
w_im[k] = (0.5 * wi) as f32;
let tmp = wr;
wr = tmp * ct - wi * st;
wi = tmp * st + wi * ct;
}
(w_re, w_im)
}
macro_rules! impl_planner_r2c_for {
($struct_name:ident, $precision:ident, $dit_planner:ident, $twiddle_fn:ident) => {
pub struct $struct_name {
/// Inner DIT planner for the N/2 complex FFT
pub(crate) dit_planner: $dit_planner,
pub(crate) w_re: Vec<$precision>,
pub(crate) w_im: Vec<$precision>,
pub(crate) n: usize,
}
impl $struct_name {
pub fn new(n: usize) -> Self {
assert!(n >= 4 && n.is_power_of_two(), "n must be a power of 2 >= 4");
let (w_re, w_im) = $twiddle_fn(n);
Self {
dit_planner: $dit_planner::new(n / 2),
w_re,
w_im,
n,
}
}
}
};
}
impl_planner_r2c_for!(PlannerR2c64, f64, PlannerDit64, compute_r2c_twiddles_f64);
impl_planner_r2c_for!(PlannerR2c32, f32, PlannerDit32, compute_r2c_twiddles_f32);