1#![allow(dead_code)]
4#![allow(unused_variables)]
5
6use std::{fmt, sync::Arc};
7use rustfft::{Fft, FftPlanner, FftDirection};
8use rustfft::num_complex::Complex;
9use rayon::prelude::*;
10
11#[derive(Clone)]
14pub struct PlannedFFT {
15 fft: Arc<dyn Fft<f64>>,
16 scratch_space: Vec<Complex<f64>>,
17}
18
19impl fmt::Debug for PlannedFFT {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 f.debug_struct("PreplannedFFT")
22 .field("scratch_space", &format!("Vec<Complex<f64>>, len: {}", self.scratch_space.len()))
23 .field("fft", &format!("Arc<dyn rustfft::Fft<f64>> => len: {}, direction: {}", self.fft.len(), self.fft.fft_direction()))
24 .finish()
25 }
26}
27
28impl PlannedFFT {
29 pub fn new(length: usize, inverse: bool) -> Self {
30 if length == 0 { panic!("PlannedFFT::new() - Provided length was 0. Length must be at least 1!"); }
31 let mut planner = FftPlanner::new();
32 let direction: FftDirection;
33 match inverse {
34 true => { direction = FftDirection::Inverse; }
35 false => { direction = FftDirection::Forward; }
36 }
37 let fft = planner.plan_fft(
38 length,
39 direction,
40 );
41 let scratch_space: Vec<Complex<f64>> = Vec::from_iter(std::iter::repeat(Complex::new(0.0, 0.0)).take(fft.get_inplace_scratch_len()));
42
43 PlannedFFT {
44 fft: fft,
45 scratch_space: scratch_space,
46 }
47 }
48
49 pub fn inverse(&self) -> bool {
50 match self.fft.fft_direction() {
51 FftDirection::Forward => { return false; }
52 FftDirection::Inverse => { return true; }
53 }
54 }
55
56 pub fn length(&self) -> usize {
57 self.fft.len()
58 }
59
60 pub fn transform(&mut self, data: &mut [Complex<f64>]) {
61 self.fft.process_with_scratch(data, &mut self.scratch_space);
62 if self.inverse() { let inverse_len = 1.0 / data.len() as f64;
64 for v in data.iter_mut() {
65 v.re *= inverse_len;
66 v.im *= inverse_len;
67 }
68 }
69 }
70}
71
72#[derive(Debug)]
75pub struct PlannedFFTND {
76 shape: Vec<usize>,
77 fft_instances: Vec<PlannedFFT>,
78 inverse: bool
79}
80
81impl PlannedFFTND {
82 pub fn new(shape: &[usize], inverse: bool) -> Self {
83 if shape.is_empty() { panic!("PlannedFFTND::new() - Provided shape was empty! Needs at least 1 dimension!"); }
84 let mut ffts: Vec<PlannedFFT> = Vec::with_capacity(shape.len());
85 for dim in shape {
86 ffts.push(PlannedFFT::new(*dim, inverse));
87 }
88 PlannedFFTND {
89 shape: shape.to_vec(),
90 fft_instances: ffts,
91 inverse: inverse,
92 }
93 }
94
95 pub fn shape(&self) -> &[usize] {
96 &self.shape
97 }
98
99 pub fn inverse(&self) -> bool {
100 self.inverse
101 }
102
103 pub fn transform(&mut self, data: &mut ndarray::ArrayD<Complex<f64>>) {
104 if data.shape() != self.shape { panic!("PlannedFFTND::transform() - shape of the data to be transformed does not agree with the shape that the fft can work on!"); }
105 let mut axis_iterator: Vec<usize> = Vec::with_capacity(self.shape.len());
106 if self.inverse() {
107 for i in (0..self.shape.len()).rev() {
108 axis_iterator.push(i);
109 }
110 }
111 else {
112 for i in 0..self.shape.len() {
113 axis_iterator.push(i);
114 }
115 }
116 for axis in axis_iterator {
117 for mut lane in data.lanes_mut(ndarray::Axis(axis)) {
118 let mut buf = lane.to_vec();
119 self.fft_instances[axis].transform(&mut buf);
120 for i in 0..lane.len() {
121 lane[i] = buf[i];
122 }
123 }
124 }
125 }
126}
127
128
129#[derive(Debug)]
131pub struct ParPlannedFFTND {
132 shape: Vec<usize>,
133 fft_instances: Vec<PlannedFFT>,
134 inverse: bool
135}
136
137impl ParPlannedFFTND {
138 pub fn new(shape: &[usize], inverse: bool) -> Self {
139 if shape.is_empty() { panic!("ParPlannedFFTND::new() - Provided shape was empty! Needs at least 1 dimension!"); }
140 let mut ffts: Vec<PlannedFFT> = Vec::with_capacity(shape.len());
141 for dim in shape {
142 ffts.push(PlannedFFT::new(*dim, inverse));
143 }
144 ParPlannedFFTND {
145 shape: shape.to_vec(),
146 fft_instances: ffts,
147 inverse: inverse,
148 }
149 }
150
151 pub fn shape(&self) -> &[usize] {
152 &self.shape
153 }
154
155 pub fn inverse(&self) -> bool {
156 self.inverse
157 }
158
159 pub fn transform(&mut self, data: &mut ndarray::ArrayD<Complex<f64>>) {
160 if data.shape() != self.shape { panic!("ParPlannedFFTND::transform() - shape of the data to be transformed does not agree with the shape that the fft can work on!"); }
161 let mut axis_iterator: Vec<usize> = Vec::with_capacity(self.shape.len());
162 if self.inverse() {
163 for i in (0..self.shape.len()).rev() {
164 axis_iterator.push(i);
165 }
166 }
167 else {
168 for i in 0..self.shape.len() {
169 axis_iterator.push(i);
170 }
171 }
172 for axis in axis_iterator {
173 let mut data_lane = data.lanes_mut(ndarray::Axis(axis));
174 let mut fft_lane = &mut self.fft_instances[axis];
175 ndarray::Zip::from(data_lane)
176 .into_par_iter()
177 .for_each_with(self.fft_instances[axis].clone(), |mut fft, mut row| {
178 let mut lane = row.0.to_vec();
179 fft.transform(&mut lane);
180 for i in 0..row.0.len() {
181 row.0[i] = lane[i];
182 }
183 });
184 }
185 }
186}