1use std::sync::Arc;
2
3use num_complex::Complex;
4
5use crate::array_utils::{factor_transpose, Load, LoadStore, TransposeFactor};
6use crate::common::RadixFactor;
7use crate::{common::FftNum, twiddles, FftDirection};
8use crate::{Direction, Fft, Length};
9
10use super::butterflies::{Butterfly2, Butterfly3, Butterfly4, Butterfly5, Butterfly6, Butterfly7};
11
12#[repr(u8)]
13enum InternalRadixFactor<T> {
14 Factor2(Butterfly2<T>),
15 Factor3(Butterfly3<T>),
16 Factor4(Butterfly4<T>),
17 Factor5(Butterfly5<T>),
18 Factor6(Butterfly6<T>),
19 Factor7(Butterfly7<T>),
20}
21impl<T> InternalRadixFactor<T> {
22 pub const fn radix(&self) -> usize {
23 match self {
25 InternalRadixFactor::Factor2(_) => 2,
26 InternalRadixFactor::Factor3(_) => 3,
27 InternalRadixFactor::Factor4(_) => 4,
28 InternalRadixFactor::Factor5(_) => 5,
29 InternalRadixFactor::Factor6(_) => 6,
30 InternalRadixFactor::Factor7(_) => 7,
31 }
32 }
33}
34
35pub(crate) struct RadixN<T> {
36 twiddles: Box<[Complex<T>]>,
37
38 base_fft: Arc<dyn Fft<T>>,
39 base_len: usize,
40
41 factors: Box<[TransposeFactor]>,
42 butterflies: Box<[InternalRadixFactor<T>]>,
43
44 len: usize,
45 direction: FftDirection,
46
47 inplace_scratch_len: usize,
48 outofplace_scratch_len: usize,
49 immut_scratch_len: usize,
50}
51
52impl<T: FftNum> RadixN<T> {
53 pub fn new(factors: &[RadixFactor], base_fft: Arc<dyn Fft<T>>) -> Self {
55 let base_len = base_fft.len();
56 let direction = base_fft.fft_direction();
57
58 let mut butterflies = Vec::with_capacity(factors.len());
60 let mut cross_fft_len = base_len;
61 let mut twiddle_count = 0;
62
63 for factor in factors {
64 let cross_fft_rows = factor.radix();
66 let cross_fft_columns = cross_fft_len;
67
68 twiddle_count += cross_fft_columns * (cross_fft_rows - 1);
69
70 let butterfly = match factor {
72 RadixFactor::Factor2 => InternalRadixFactor::Factor2(Butterfly2::new(direction)),
73 RadixFactor::Factor3 => InternalRadixFactor::Factor3(Butterfly3::new(direction)),
74 RadixFactor::Factor4 => InternalRadixFactor::Factor4(Butterfly4::new(direction)),
75 RadixFactor::Factor5 => InternalRadixFactor::Factor5(Butterfly5::new(direction)),
76 RadixFactor::Factor6 => InternalRadixFactor::Factor6(Butterfly6::new(direction)),
77 RadixFactor::Factor7 => InternalRadixFactor::Factor7(Butterfly7::new(direction)),
78 };
79 butterflies.push(butterfly);
80
81 cross_fft_len *= cross_fft_rows;
82 }
83 let len = cross_fft_len;
84
85 let mut transpose_factors: Vec<TransposeFactor> = Vec::with_capacity(factors.len());
89 for f in factors.iter().rev() {
90 let mut push_new = true;
92 if let Some(last) = transpose_factors.last_mut() {
93 if last.factor == *f {
94 last.count += 1;
95 push_new = false;
96 }
97 }
98 if push_new {
99 transpose_factors.push(TransposeFactor {
100 factor: *f,
101 count: 1,
102 });
103 }
104 }
105
106 let mut cross_fft_len = base_len;
111 let mut twiddle_factors = Vec::with_capacity(twiddle_count);
112
113 for factor in factors {
114 let cross_fft_columns = cross_fft_len;
116 cross_fft_len *= factor.radix();
117
118 for i in 0..cross_fft_columns {
119 for k in 1..factor.radix() {
120 let twiddle = twiddles::compute_twiddle(i * k, cross_fft_len, direction);
121 twiddle_factors.push(twiddle);
122 }
123 }
124 }
125
126 let base_inplace_scratch = base_fft.get_inplace_scratch_len();
128 let inplace_scratch_len = if base_inplace_scratch > len {
129 len + base_inplace_scratch
130 } else {
131 len
132 };
133 let outofplace_scratch_len = if base_inplace_scratch > len {
134 base_inplace_scratch
135 } else {
136 0
137 };
138
139 Self {
140 twiddles: twiddle_factors.into_boxed_slice(),
141
142 base_fft,
143 base_len,
144
145 factors: transpose_factors.into_boxed_slice(),
146 butterflies: butterflies.into_boxed_slice(),
147
148 len,
149 direction,
150
151 inplace_scratch_len,
152 outofplace_scratch_len,
153 immut_scratch_len: base_inplace_scratch,
154 }
155 }
156
157 fn inplace_scratch_len(&self) -> usize {
158 self.inplace_scratch_len
159 }
160 fn outofplace_scratch_len(&self) -> usize {
161 self.outofplace_scratch_len
162 }
163 fn immut_scratch_len(&self) -> usize {
164 self.immut_scratch_len
165 }
166
167 fn perform_fft_immut(
168 &self,
169 input: &[Complex<T>],
170 output: &mut [Complex<T>],
171 scratch: &mut [Complex<T>],
172 ) {
173 if let Some(unroll_factor) = self.factors.first() {
174 match unroll_factor.factor {
177 RadixFactor::Factor2 => {
178 factor_transpose::<Complex<T>, 2>(self.base_len, input, output, &self.factors)
179 }
180 RadixFactor::Factor3 => {
181 factor_transpose::<Complex<T>, 3>(self.base_len, input, output, &self.factors)
182 }
183 RadixFactor::Factor4 => {
184 factor_transpose::<Complex<T>, 4>(self.base_len, input, output, &self.factors)
185 }
186 RadixFactor::Factor5 => {
187 factor_transpose::<Complex<T>, 5>(self.base_len, input, output, &self.factors)
188 }
189 RadixFactor::Factor6 => {
190 factor_transpose::<Complex<T>, 6>(self.base_len, input, output, &self.factors)
191 }
192 RadixFactor::Factor7 => {
193 factor_transpose::<Complex<T>, 7>(self.base_len, input, output, &self.factors)
194 }
195 }
196 } else {
197 output.copy_from_slice(input);
199 }
200
201 self.base_fft.process_with_scratch(output, scratch);
203
204 let mut cross_fft_len = self.base_len;
205 let mut layer_twiddles: &[Complex<T>] = &self.twiddles;
206
207 for factor in self.butterflies.iter() {
208 let cross_fft_columns = cross_fft_len;
209 cross_fft_len *= factor.radix();
210
211 match factor {
212 InternalRadixFactor::Factor2(butterfly2) => {
213 for data in output.chunks_exact_mut(cross_fft_len) {
214 unsafe { butterfly_2(data, layer_twiddles, cross_fft_columns, butterfly2) }
215 }
216 }
217 InternalRadixFactor::Factor3(butterfly3) => {
218 for data in output.chunks_exact_mut(cross_fft_len) {
219 unsafe { butterfly_3(data, layer_twiddles, cross_fft_columns, butterfly3) }
220 }
221 }
222 InternalRadixFactor::Factor4(butterfly4) => {
223 for data in output.chunks_exact_mut(cross_fft_len) {
224 unsafe { butterfly_4(data, layer_twiddles, cross_fft_columns, butterfly4) }
225 }
226 }
227 InternalRadixFactor::Factor5(butterfly5) => {
228 for data in output.chunks_exact_mut(cross_fft_len) {
229 unsafe { butterfly_5(data, layer_twiddles, cross_fft_columns, butterfly5) }
230 }
231 }
232 InternalRadixFactor::Factor6(butterfly6) => {
233 for data in output.chunks_exact_mut(cross_fft_len) {
234 unsafe { butterfly_6(data, layer_twiddles, cross_fft_columns, butterfly6) }
235 }
236 }
237 InternalRadixFactor::Factor7(butterfly7) => {
238 for data in output.chunks_exact_mut(cross_fft_len) {
239 unsafe { butterfly_7(data, layer_twiddles, cross_fft_columns, butterfly7) }
240 }
241 }
242 }
243
244 let twiddle_offset = cross_fft_columns * (factor.radix() - 1);
246 layer_twiddles = &layer_twiddles[twiddle_offset..];
247 }
248 }
249
250 fn perform_fft_out_of_place(
251 &self,
252 input: &mut [Complex<T>],
253 output: &mut [Complex<T>],
254 scratch: &mut [Complex<T>],
255 ) {
256 if let Some(unroll_factor) = self.factors.first() {
257 match unroll_factor.factor {
260 RadixFactor::Factor2 => {
261 factor_transpose::<Complex<T>, 2>(self.base_len, input, output, &self.factors)
262 }
263 RadixFactor::Factor3 => {
264 factor_transpose::<Complex<T>, 3>(self.base_len, input, output, &self.factors)
265 }
266 RadixFactor::Factor4 => {
267 factor_transpose::<Complex<T>, 4>(self.base_len, input, output, &self.factors)
268 }
269 RadixFactor::Factor5 => {
270 factor_transpose::<Complex<T>, 5>(self.base_len, input, output, &self.factors)
271 }
272 RadixFactor::Factor6 => {
273 factor_transpose::<Complex<T>, 6>(self.base_len, input, output, &self.factors)
274 }
275 RadixFactor::Factor7 => {
276 factor_transpose::<Complex<T>, 7>(self.base_len, input, output, &self.factors)
277 }
278 }
279 } else {
280 output.copy_from_slice(input);
282 }
283
284 let base_scratch = if scratch.len() > 0 { scratch } else { input };
286 self.base_fft.process_with_scratch(output, base_scratch);
287
288 let mut cross_fft_len = self.base_len;
290 let mut layer_twiddles: &[Complex<T>] = &self.twiddles;
291
292 for factor in self.butterflies.iter() {
293 let cross_fft_columns = cross_fft_len;
294 cross_fft_len *= factor.radix();
295
296 match factor {
297 InternalRadixFactor::Factor2(butterfly2) => {
298 for data in output.chunks_exact_mut(cross_fft_len) {
299 unsafe { butterfly_2(data, layer_twiddles, cross_fft_columns, butterfly2) }
300 }
301 }
302 InternalRadixFactor::Factor3(butterfly3) => {
303 for data in output.chunks_exact_mut(cross_fft_len) {
304 unsafe { butterfly_3(data, layer_twiddles, cross_fft_columns, butterfly3) }
305 }
306 }
307 InternalRadixFactor::Factor4(butterfly4) => {
308 for data in output.chunks_exact_mut(cross_fft_len) {
309 unsafe { butterfly_4(data, layer_twiddles, cross_fft_columns, butterfly4) }
310 }
311 }
312 InternalRadixFactor::Factor5(butterfly5) => {
313 for data in output.chunks_exact_mut(cross_fft_len) {
314 unsafe { butterfly_5(data, layer_twiddles, cross_fft_columns, butterfly5) }
315 }
316 }
317 InternalRadixFactor::Factor6(butterfly6) => {
318 for data in output.chunks_exact_mut(cross_fft_len) {
319 unsafe { butterfly_6(data, layer_twiddles, cross_fft_columns, butterfly6) }
320 }
321 }
322 InternalRadixFactor::Factor7(butterfly7) => {
323 for data in output.chunks_exact_mut(cross_fft_len) {
324 unsafe { butterfly_7(data, layer_twiddles, cross_fft_columns, butterfly7) }
325 }
326 }
327 }
328
329 let twiddle_offset = cross_fft_columns * (factor.radix() - 1);
331 layer_twiddles = &layer_twiddles[twiddle_offset..];
332 }
333 }
334}
335boilerplate_fft_oop!(RadixN, |this: &RadixN<_>| this.len);
336
337#[inline(never)]
338pub(crate) unsafe fn butterfly_2<T: FftNum>(
339 mut data: impl LoadStore<T>,
340 twiddles: impl Load<T>,
341 num_columns: usize,
342 butterfly2: &Butterfly2<T>,
343) {
344 for idx in 0..num_columns {
345 let mut scratch = [
346 data.load(idx + 0 * num_columns),
347 data.load(idx + 1 * num_columns) * twiddles.load(idx),
348 ];
349
350 butterfly2.perform_fft_butterfly(&mut scratch);
351
352 data.store(scratch[0], idx + num_columns * 0);
353 data.store(scratch[1], idx + num_columns * 1);
354 }
355}
356
357#[inline(never)]
358pub(crate) unsafe fn butterfly_3<T: FftNum>(
359 mut data: impl LoadStore<T>,
360 twiddles: impl Load<T>,
361 num_columns: usize,
362 butterfly3: &Butterfly3<T>,
363) {
364 for idx in 0..num_columns {
365 let tw_idx = idx * 2;
366 let mut scratch = [
367 data.load(idx + 0 * num_columns),
368 data.load(idx + 1 * num_columns) * twiddles.load(tw_idx + 0),
369 data.load(idx + 2 * num_columns) * twiddles.load(tw_idx + 1),
370 ];
371
372 butterfly3.perform_fft_butterfly(&mut scratch);
373
374 data.store(scratch[0], idx + 0 * num_columns);
375 data.store(scratch[1], idx + 1 * num_columns);
376 data.store(scratch[2], idx + 2 * num_columns);
377 }
378}
379
380#[inline(never)]
381pub(crate) unsafe fn butterfly_4<T: FftNum>(
382 mut data: impl LoadStore<T>,
383 twiddles: impl Load<T>,
384 num_columns: usize,
385 butterfly4: &Butterfly4<T>,
386) {
387 for idx in 0..num_columns {
388 let tw_idx = idx * 3;
389 let mut scratch = [
390 data.load(idx + 0 * num_columns),
391 data.load(idx + 1 * num_columns) * twiddles.load(tw_idx + 0),
392 data.load(idx + 2 * num_columns) * twiddles.load(tw_idx + 1),
393 data.load(idx + 3 * num_columns) * twiddles.load(tw_idx + 2),
394 ];
395
396 butterfly4.perform_fft_butterfly(&mut scratch);
397
398 data.store(scratch[0], idx + 0 * num_columns);
399 data.store(scratch[1], idx + 1 * num_columns);
400 data.store(scratch[2], idx + 2 * num_columns);
401 data.store(scratch[3], idx + 3 * num_columns);
402 }
403}
404
405#[inline(never)]
406pub(crate) unsafe fn butterfly_5<T: FftNum>(
407 mut data: impl LoadStore<T>,
408 twiddles: impl Load<T>,
409 num_columns: usize,
410 butterfly5: &Butterfly5<T>,
411) {
412 for idx in 0..num_columns {
413 let tw_idx = idx * 4;
414 let mut scratch = [
415 data.load(idx + 0 * num_columns),
416 data.load(idx + 1 * num_columns) * twiddles.load(tw_idx + 0),
417 data.load(idx + 2 * num_columns) * twiddles.load(tw_idx + 1),
418 data.load(idx + 3 * num_columns) * twiddles.load(tw_idx + 2),
419 data.load(idx + 4 * num_columns) * twiddles.load(tw_idx + 3),
420 ];
421
422 butterfly5.perform_fft_butterfly(&mut scratch);
423
424 data.store(scratch[0], idx + 0 * num_columns);
425 data.store(scratch[1], idx + 1 * num_columns);
426 data.store(scratch[2], idx + 2 * num_columns);
427 data.store(scratch[3], idx + 3 * num_columns);
428 data.store(scratch[4], idx + 4 * num_columns);
429 }
430}
431
432#[inline(never)]
433pub(crate) unsafe fn butterfly_6<T: FftNum>(
434 mut data: impl LoadStore<T>,
435 twiddles: impl Load<T>,
436 num_columns: usize,
437 butterfly6: &Butterfly6<T>,
438) {
439 for idx in 0..num_columns {
440 let tw_idx = idx * 5;
441 let mut scratch = [
442 data.load(idx + 0 * num_columns),
443 data.load(idx + 1 * num_columns) * twiddles.load(tw_idx + 0),
444 data.load(idx + 2 * num_columns) * twiddles.load(tw_idx + 1),
445 data.load(idx + 3 * num_columns) * twiddles.load(tw_idx + 2),
446 data.load(idx + 4 * num_columns) * twiddles.load(tw_idx + 3),
447 data.load(idx + 5 * num_columns) * twiddles.load(tw_idx + 4),
448 ];
449
450 butterfly6.perform_fft_butterfly(&mut scratch);
451
452 data.store(scratch[0], idx + 0 * num_columns);
453 data.store(scratch[1], idx + 1 * num_columns);
454 data.store(scratch[2], idx + 2 * num_columns);
455 data.store(scratch[3], idx + 3 * num_columns);
456 data.store(scratch[4], idx + 4 * num_columns);
457 data.store(scratch[5], idx + 5 * num_columns);
458 }
459}
460
461#[inline(never)]
462pub(crate) unsafe fn butterfly_7<T: FftNum>(
463 mut data: impl LoadStore<T>,
464 twiddles: impl Load<T>,
465 num_columns: usize,
466 butterfly7: &Butterfly7<T>,
467) {
468 for idx in 0..num_columns {
469 let tw_idx = idx * 6;
470 let mut scratch = [
471 data.load(idx + 0 * num_columns),
472 data.load(idx + 1 * num_columns) * twiddles.load(tw_idx + 0),
473 data.load(idx + 2 * num_columns) * twiddles.load(tw_idx + 1),
474 data.load(idx + 3 * num_columns) * twiddles.load(tw_idx + 2),
475 data.load(idx + 4 * num_columns) * twiddles.load(tw_idx + 3),
476 data.load(idx + 5 * num_columns) * twiddles.load(tw_idx + 4),
477 data.load(idx + 6 * num_columns) * twiddles.load(tw_idx + 5),
478 ];
479
480 butterfly7.perform_fft_butterfly(&mut scratch);
481
482 data.store(scratch[0], idx + 0 * num_columns);
483 data.store(scratch[1], idx + 1 * num_columns);
484 data.store(scratch[2], idx + 2 * num_columns);
485 data.store(scratch[3], idx + 3 * num_columns);
486 data.store(scratch[4], idx + 4 * num_columns);
487 data.store(scratch[5], idx + 5 * num_columns);
488 data.store(scratch[6], idx + 6 * num_columns);
489 }
490}
491
492#[cfg(test)]
493mod unit_tests {
494 use super::*;
495 use crate::test_utils::{check_fft_algorithm, construct_base};
496
497 #[test]
498 fn test_scalar_radixn() {
499 let factor_list = &[
500 RadixFactor::Factor2,
501 RadixFactor::Factor3,
502 RadixFactor::Factor4,
503 RadixFactor::Factor5,
504 RadixFactor::Factor6,
505 RadixFactor::Factor7,
506 ];
507
508 for base in 1..7 {
509 let base_forward = construct_base(base, FftDirection::Forward);
510 let base_inverse = construct_base(base, FftDirection::Inverse);
511
512 test_radixn(&[], Arc::clone(&base_forward));
514 test_radixn(&[], Arc::clone(&base_inverse));
515
516 for factor_a in factor_list {
518 let factors = &[*factor_a];
519 test_radixn(factors, Arc::clone(&base_forward));
520 test_radixn(factors, Arc::clone(&base_inverse));
521 }
522
523 for factor_a in factor_list {
525 for factor_b in factor_list {
526 let factors = &[*factor_a, *factor_b];
527 test_radixn(factors, Arc::clone(&base_forward));
528 test_radixn(factors, Arc::clone(&base_inverse));
529 }
530 }
531 }
532 }
533
534 fn test_radixn(factors: &[RadixFactor], base_fft: Arc<dyn Fft<f64>>) {
535 let len = base_fft.len() * factors.iter().map(|f| f.radix()).product::<usize>();
536 let direction = base_fft.fft_direction();
537 let fft = RadixN::new(factors, base_fft);
538
539 check_fft_algorithm::<f64>(&fft, len, direction);
540 }
541}