rustfft/
common.rs

1use num_traits::{FromPrimitive, Signed};
2use std::fmt::Debug;
3
4/// Generic floating point number, implemented for f32 and f64
5pub 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// Prints an error raised by an in-place FFT algorithm's `process_inplace` method
10// Marked cold and inline never to keep all formatting code out of the many monomorphized process_inplace methods
11#[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// Prints an error raised by an in-place FFT algorithm's `process_inplace` method
41// Marked cold and inline never to keep all formatting code out of the many monomorphized process_inplace methods
42#[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
73// Prints an error raised by an in-place FFT algorithm's `process_inplace` method
74// Marked cold and inline never to keep all formatting code out of the many monomorphized process_inplace methods
75#[cold]
76#[inline(never)]
77pub fn fft_error_immut(
78    expected_len: usize,
79    actual_input: usize,
80    actual_output: usize,
81    expected_scratch: usize,
82    actual_scratch: usize,
83) {
84    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);
85    assert!(
86        actual_input >= expected_len,
87        "Provided FFT buffer was too small. Expected len = {}, got len = {}",
88        expected_len,
89        actual_input
90    );
91    assert_eq!(
92        actual_input % expected_len,
93        0,
94        "Input FFT buffer must be a multiple of FFT length. Expected multiple of {}, got len = {}",
95        expected_len,
96        actual_input
97    );
98    assert!(
99        actual_scratch >= expected_scratch,
100        "Not enough scratch space was provided. Expected scratch len >= {}, got scratch len = {}",
101        expected_scratch,
102        actual_scratch
103    );
104}
105
106macro_rules! boilerplate_fft_oop {
107    ($struct_name:ident, $len_fn:expr) => {
108        impl<T: FftNum> Fft<T> for $struct_name<T> {
109            fn process_immutable_with_scratch(
110                &self,
111                input: &[Complex<T>],
112                output: &mut [Complex<T>],
113                scratch: &mut [Complex<T>],
114            ) {
115                crate::fft_helper::fft_helper_immut(
116                    input,
117                    output,
118                    scratch,
119                    self.len(),
120                    self.get_immutable_scratch_len(),
121                    |in_chunk, out_chunk, scratch| {
122                        self.perform_fft_immut(in_chunk, out_chunk, scratch)
123                    },
124                );
125            }
126            fn process_outofplace_with_scratch(
127                &self,
128                input: &mut [Complex<T>],
129                output: &mut [Complex<T>],
130                scratch: &mut [Complex<T>],
131            ) {
132                crate::fft_helper::fft_helper_outofplace(
133                    input,
134                    output,
135                    scratch,
136                    self.len(),
137                    self.get_outofplace_scratch_len(),
138                    |in_chunk, out_chunk, scratch| {
139                        self.perform_fft_out_of_place(in_chunk, out_chunk, scratch)
140                    },
141                );
142            }
143            fn process_with_scratch(&self, buffer: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
144                crate::fft_helper::fft_helper_inplace(
145                    buffer,
146                    scratch,
147                    self.len(),
148                    self.get_inplace_scratch_len(),
149                    |chunk, scratch| {
150                        let (self_scratch, inner_scratch) = scratch.split_at_mut(self.len());
151                        self.perform_fft_out_of_place(chunk, self_scratch, inner_scratch);
152                        chunk.copy_from_slice(self_scratch);
153                    },
154                );
155            }
156            #[inline(always)]
157            fn get_inplace_scratch_len(&self) -> usize {
158                self.inplace_scratch_len()
159            }
160            #[inline(always)]
161            fn get_outofplace_scratch_len(&self) -> usize {
162                self.outofplace_scratch_len()
163            }
164            #[inline(always)]
165            fn get_immutable_scratch_len(&self) -> usize {
166                self.immut_scratch_len()
167            }
168        }
169        impl<T> Length for $struct_name<T> {
170            #[inline(always)]
171            fn len(&self) -> usize {
172                $len_fn(self)
173            }
174        }
175        impl<T> Direction for $struct_name<T> {
176            #[inline(always)]
177            fn fft_direction(&self) -> FftDirection {
178                self.direction
179            }
180        }
181    };
182}
183
184macro_rules! boilerplate_fft {
185    ($struct_name:ident, $len_fn:expr, $inplace_scratch_len_fn:expr, $out_of_place_scratch_len_fn:expr, $immut_scratch_len:expr) => {
186        impl<T: FftNum> Fft<T> for $struct_name<T> {
187            fn process_immutable_with_scratch(
188                &self,
189                input: &[Complex<T>],
190                output: &mut [Complex<T>],
191                scratch: &mut [Complex<T>],
192            ) {
193                crate::fft_helper::fft_helper_immut(
194                    input,
195                    output,
196                    scratch,
197                    self.len(),
198                    self.get_immutable_scratch_len(),
199                    |in_chunk, out_chunk, scratch| {
200                        self.perform_fft_immut(in_chunk, out_chunk, scratch)
201                    },
202                );
203            }
204
205            fn process_outofplace_with_scratch(
206                &self,
207                input: &mut [Complex<T>],
208                output: &mut [Complex<T>],
209                scratch: &mut [Complex<T>],
210            ) {
211                crate::fft_helper::fft_helper_outofplace(
212                    input,
213                    output,
214                    scratch,
215                    self.len(),
216                    self.get_outofplace_scratch_len(),
217                    |in_chunk, out_chunk, scratch| {
218                        self.perform_fft_out_of_place(in_chunk, out_chunk, scratch)
219                    },
220                );
221            }
222            fn process_with_scratch(&self, buffer: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
223                crate::fft_helper::fft_helper_inplace(
224                    buffer,
225                    scratch,
226                    self.len(),
227                    self.get_inplace_scratch_len(),
228                    |chunk, scratch| {
229                        self.perform_fft_inplace(chunk, scratch);
230                    },
231                );
232            }
233            #[inline(always)]
234            fn get_inplace_scratch_len(&self) -> usize {
235                $inplace_scratch_len_fn(self)
236            }
237            #[inline(always)]
238            fn get_outofplace_scratch_len(&self) -> usize {
239                $out_of_place_scratch_len_fn(self)
240            }
241            #[inline(always)]
242            fn get_immutable_scratch_len(&self) -> usize {
243                $immut_scratch_len(self)
244            }
245        }
246        impl<T: FftNum> Length for $struct_name<T> {
247            #[inline(always)]
248            fn len(&self) -> usize {
249                $len_fn(self)
250            }
251        }
252        impl<T: FftNum> Direction for $struct_name<T> {
253            #[inline(always)]
254            fn fft_direction(&self) -> FftDirection {
255                self.direction
256            }
257        }
258    };
259}
260
261#[non_exhaustive]
262#[repr(u8)]
263#[derive(Copy, Clone, Debug, PartialEq)]
264pub(crate) enum RadixFactor {
265    Factor2,
266    Factor3,
267    Factor4,
268    Factor5,
269    Factor6,
270    Factor7,
271}
272impl RadixFactor {
273    pub const fn radix(&self) -> usize {
274        // note: if we had rustc 1.66, we could just turn these values explicit discriminators on the enum
275        match self {
276            RadixFactor::Factor2 => 2,
277            RadixFactor::Factor3 => 3,
278            RadixFactor::Factor4 => 4,
279            RadixFactor::Factor5 => 5,
280            RadixFactor::Factor6 => 6,
281            RadixFactor::Factor7 => 7,
282        }
283    }
284}