use rayon::prelude::*;
use rustfft::num_complex::Complex;
use std::sync::Arc;
#[inline]
pub fn transpose_tiled<T: Copy>(src: &[T], dst: &mut [T], rows: usize, cols: usize) {
const TILE: usize = 8;
for r0 in (0..rows).step_by(TILE) {
let rend = (r0 + TILE).min(rows);
for c0 in (0..cols).step_by(TILE) {
let cend = (c0 + TILE).min(cols);
for r in r0..rend {
let src_base = r * cols;
for c in c0..cend {
dst[c * rows + r] = src[src_base + c];
}
}
}
}
}
pub fn fft_3d_c2c_scratch(
buf: &mut [Complex<f64>],
scratch: &mut [Complex<f64>],
shape: [usize; 3],
plans: &[Arc<dyn rustfft::Fft<f64>>; 3],
) {
let [nx, ny, nz] = shape;
buf.par_chunks_mut(nz).for_each(|row| {
plans[2].process(row);
});
scratch[..nx * ny * nz]
.par_chunks_mut(nz * ny)
.enumerate()
.for_each(|(ix, slab)| {
let src = &buf[ix * ny * nz..(ix + 1) * ny * nz];
transpose_tiled(src, slab, ny, nz);
});
scratch[..nx * nz * ny].par_chunks_mut(ny).for_each(|row| {
plans[1].process(row);
});
buf.par_chunks_mut(ny * nz)
.enumerate()
.for_each(|(ix, slab)| {
let src = &scratch[ix * nz * ny..(ix + 1) * nz * ny];
transpose_tiled(src, slab, nz, ny);
});
scratch[..nx * ny * nz]
.par_chunks_mut(nz * nx)
.enumerate()
.for_each(|(iy, slab)| {
for iz in 0..nz {
for ix in 0..nx {
slab[iz * nx + ix] = buf[ix * ny * nz + iy * nz + iz];
}
}
});
scratch[..ny * nz * nx].par_chunks_mut(nx).for_each(|row| {
plans[0].process(row);
});
buf.par_chunks_mut(ny * nz)
.enumerate()
.for_each(|(ix, slab)| {
for iy in 0..ny {
for iz in 0..nz {
slab[iy * nz + iz] = scratch[iy * nz * nx + iz * nx + ix];
}
}
});
}
pub fn fft_3d_forward_scratch(
data: &[f64],
scratch: &mut [Complex<f64>],
shape: [usize; 3],
plans: &[Arc<dyn rustfft::Fft<f64>>; 3],
) -> Vec<Complex<f64>> {
let n_total: usize = shape.iter().product();
assert_eq!(data.len(), n_total);
let mut buf: Vec<Complex<f64>> = data.iter().map(|&v| Complex::new(v, 0.0)).collect();
fft_3d_c2c_scratch(&mut buf, scratch, shape, plans);
buf
}
pub fn fft_3d_inverse_scratch(
data: &[Complex<f64>],
scratch: &mut [Complex<f64>],
shape: [usize; 3],
plans: &[Arc<dyn rustfft::Fft<f64>>; 3],
) -> Vec<f64> {
let n_total: usize = shape.iter().product();
assert_eq!(data.len(), n_total);
let scale = 1.0 / n_total as f64;
let mut buf = data.to_vec();
fft_3d_c2c_scratch(&mut buf, scratch, shape, plans);
buf.par_iter().map(|c| c.re * scale).collect()
}