1use num_traits::{FromPrimitive, Signed};
2use std::fmt::Debug;
3
4pub trait FftNum: Copy + FromPrimitive + Signed + Sync + Send + Debug + 'static {}
6
7impl<T> FftNum for T where T: Copy + FromPrimitive + Signed + Sync + Send + Debug + 'static {}
8
9#[cold]
12#[inline(never)]
13pub fn fft_error_inplace(
14 expected_len: usize,
15 actual_len: usize,
16 expected_scratch: usize,
17 actual_scratch: usize,
18) {
19 assert!(
20 actual_len >= expected_len,
21 "Provided FFT buffer was too small. Expected len = {}, got len = {}",
22 expected_len,
23 actual_len
24 );
25 assert_eq!(
26 actual_len % expected_len,
27 0,
28 "Input FFT buffer must be a multiple of FFT length. Expected multiple of {}, got len = {}",
29 expected_len,
30 actual_len
31 );
32 assert!(
33 actual_scratch >= expected_scratch,
34 "Not enough scratch space was provided. Expected scratch len >= {}, got scratch len = {}",
35 expected_scratch,
36 actual_scratch
37 );
38}
39
40#[cold]
43#[inline(never)]
44pub fn fft_error_outofplace(
45 expected_len: usize,
46 actual_input: usize,
47 actual_output: usize,
48 expected_scratch: usize,
49 actual_scratch: usize,
50) {
51 assert_eq!(actual_input, actual_output, "Provided FFT input buffer and output buffer must have the same length. Got input.len() = {}, output.len() = {}", actual_input, actual_output);
52 assert!(
53 actual_input >= expected_len,
54 "Provided FFT buffer was too small. Expected len = {}, got len = {}",
55 expected_len,
56 actual_input
57 );
58 assert_eq!(
59 actual_input % expected_len,
60 0,
61 "Input FFT buffer must be a multiple of FFT length. Expected multiple of {}, got len = {}",
62 expected_len,
63 actual_input
64 );
65 assert!(
66 actual_scratch >= expected_scratch,
67 "Not enough scratch space was provided. Expected scratch len >= {}, got scratch len = {}",
68 expected_scratch,
69 actual_scratch
70 );
71}
72
73macro_rules! boilerplate_fft_oop {
74 ($struct_name:ident, $len_fn:expr) => {
75 impl<T: FftNum> Fft<T> for $struct_name<T> {
76 fn process_outofplace_with_scratch(
77 &self,
78 input: &mut [Complex<T>],
79 output: &mut [Complex<T>],
80 scratch: &mut [Complex<T>],
81 ) {
82 if self.len() == 0 {
83 return;
84 }
85
86 let required_scratch = self.get_outofplace_scratch_len();
87 if input.len() < self.len()
88 || output.len() != input.len()
89 || scratch.len() < required_scratch
90 {
91 fft_error_outofplace(
93 self.len(),
94 input.len(),
95 output.len(),
96 required_scratch,
97 scratch.len(),
98 );
99 return; }
101
102 let result = array_utils::iter_chunks_zipped(
103 input,
104 output,
105 self.len(),
106 |in_chunk, out_chunk| {
107 self.perform_fft_out_of_place(in_chunk, out_chunk, scratch)
108 },
109 );
110
111 if result.is_err() {
112 fft_error_outofplace(self.len(), input.len(), output.len(), 0, 0);
115 }
116 }
117 fn process_with_scratch(&self, buffer: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
118 if self.len() == 0 {
119 return;
120 }
121
122 let required_scratch = self.get_inplace_scratch_len();
123 if scratch.len() < required_scratch || buffer.len() < self.len() {
124 fft_error_inplace(
126 self.len(),
127 buffer.len(),
128 self.get_inplace_scratch_len(),
129 scratch.len(),
130 );
131 return; }
133
134 let (scratch, extra_scratch) = scratch.split_at_mut(self.len());
135 let result = array_utils::iter_chunks(buffer, self.len(), |chunk| {
136 self.perform_fft_out_of_place(chunk, scratch, extra_scratch);
137 chunk.copy_from_slice(scratch);
138 });
139
140 if result.is_err() {
141 fft_error_inplace(
144 self.len(),
145 buffer.len(),
146 self.get_inplace_scratch_len(),
147 scratch.len(),
148 );
149 }
150 }
151 #[inline(always)]
152 fn get_inplace_scratch_len(&self) -> usize {
153 self.inplace_scratch_len()
154 }
155 #[inline(always)]
156 fn get_outofplace_scratch_len(&self) -> usize {
157 self.outofplace_scratch_len()
158 }
159 }
160 impl<T> Length for $struct_name<T> {
161 #[inline(always)]
162 fn len(&self) -> usize {
163 $len_fn(self)
164 }
165 }
166 impl<T> Direction for $struct_name<T> {
167 #[inline(always)]
168 fn fft_direction(&self) -> FftDirection {
169 self.direction
170 }
171 }
172 };
173}
174
175macro_rules! boilerplate_fft {
176 ($struct_name:ident, $len_fn:expr, $inplace_scratch_len_fn:expr, $out_of_place_scratch_len_fn:expr) => {
177 impl<T: FftNum> Fft<T> for $struct_name<T> {
178 fn process_outofplace_with_scratch(
179 &self,
180 input: &mut [Complex<T>],
181 output: &mut [Complex<T>],
182 scratch: &mut [Complex<T>],
183 ) {
184 if self.len() == 0 {
185 return;
186 }
187
188 let required_scratch = self.get_outofplace_scratch_len();
189 if scratch.len() < required_scratch
190 || input.len() < self.len()
191 || output.len() != input.len()
192 {
193 fft_error_outofplace(
195 self.len(),
196 input.len(),
197 output.len(),
198 self.get_outofplace_scratch_len(),
199 scratch.len(),
200 );
201 return; }
203
204 let scratch = &mut scratch[..required_scratch];
205 let result = array_utils::iter_chunks_zipped(
206 input,
207 output,
208 self.len(),
209 |in_chunk, out_chunk| {
210 self.perform_fft_out_of_place(in_chunk, out_chunk, scratch)
211 },
212 );
213
214 if result.is_err() {
215 fft_error_outofplace(
218 self.len(),
219 input.len(),
220 output.len(),
221 self.get_outofplace_scratch_len(),
222 scratch.len(),
223 );
224 }
225 }
226 fn process_with_scratch(&self, buffer: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
227 if self.len() == 0 {
228 return;
229 }
230
231 let required_scratch = self.get_inplace_scratch_len();
232 if scratch.len() < required_scratch || buffer.len() < self.len() {
233 fft_error_inplace(
235 self.len(),
236 buffer.len(),
237 self.get_inplace_scratch_len(),
238 scratch.len(),
239 );
240 return; }
242
243 let scratch = &mut scratch[..required_scratch];
244 let result = array_utils::iter_chunks(buffer, self.len(), |chunk| {
245 self.perform_fft_inplace(chunk, scratch)
246 });
247
248 if result.is_err() {
249 fft_error_inplace(
252 self.len(),
253 buffer.len(),
254 self.get_inplace_scratch_len(),
255 scratch.len(),
256 );
257 }
258 }
259 #[inline(always)]
260 fn get_inplace_scratch_len(&self) -> usize {
261 $inplace_scratch_len_fn(self)
262 }
263 #[inline(always)]
264 fn get_outofplace_scratch_len(&self) -> usize {
265 $out_of_place_scratch_len_fn(self)
266 }
267 }
268 impl<T: FftNum> Length for $struct_name<T> {
269 #[inline(always)]
270 fn len(&self) -> usize {
271 $len_fn(self)
272 }
273 }
274 impl<T: FftNum> Direction for $struct_name<T> {
275 #[inline(always)]
276 fn fft_direction(&self) -> FftDirection {
277 self.direction
278 }
279 }
280 };
281}
282
283#[non_exhaustive]
284#[repr(u8)]
285#[derive(Copy, Clone, Debug, PartialEq)]
286pub(crate) enum RadixFactor {
287 Factor2,
288 Factor3,
289 Factor4,
290 Factor5,
291 Factor6,
292 Factor7,
293}
294impl RadixFactor {
295 pub const fn radix(&self) -> usize {
296 match self {
298 RadixFactor::Factor2 => 2,
299 RadixFactor::Factor3 => 3,
300 RadixFactor::Factor4 => 4,
301 RadixFactor::Factor5 => 5,
302 RadixFactor::Factor6 => 6,
303 RadixFactor::Factor7 => 7,
304 }
305 }
306}