Skip to main content

rustfft/sse/
sse_butterflies.rs

1use core::arch::x86_64::*;
2use num_complex::Complex;
3
4use crate::{common::FftNum, FftDirection};
5
6use crate::array_utils::DoubleBuf;
7use crate::twiddles;
8use crate::{Direction, Fft, Length};
9
10use super::sse_common::{assert_f32, assert_f64};
11use super::sse_utils::*;
12use super::sse_vector::{SseArrayMut, SseVector};
13
14#[inline(always)]
15unsafe fn pack_32(a: Complex<f32>, b: Complex<f32>) -> __m128 {
16    _mm_set_ps(b.im, b.re, a.im, a.re)
17}
18#[inline(always)]
19unsafe fn pack_64(a: Complex<f64>) -> __m128d {
20    _mm_set_pd(a.im, a.re)
21}
22
23#[allow(unused)]
24macro_rules! boilerplate_fft_sse_f32_butterfly {
25    ($struct_name:ident, $len:expr, $direction_fn:expr) => {
26        impl<T: FftNum> Fft<T> for $struct_name<T> {
27            fn process_immutable_with_scratch(
28                &self,
29                input: &[Complex<T>],
30                output: &mut [Complex<T>],
31                _scratch: &mut [Complex<T>],
32            ) {
33                unsafe {
34                    let simd_input = crate::array_utils::workaround_transmute(input);
35                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
36                    super::sse_common::sse_fft_helper_immut_unroll2x(
37                        simd_input,
38                        simd_output,
39                        self.len(),
40                        |input, output| {
41                            self.perform_parallel_fft_contiguous(DoubleBuf { input, output })
42                        },
43                        |input, output| self.perform_fft_contiguous(DoubleBuf { input, output }),
44                    );
45                }
46            }
47            fn process_outofplace_with_scratch(
48                &self,
49                input: &mut [Complex<T>],
50                output: &mut [Complex<T>],
51                _scratch: &mut [Complex<T>],
52            ) {
53                unsafe {
54                    let simd_input = crate::array_utils::workaround_transmute_mut(input);
55                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
56                    super::sse_common::sse_fft_helper_outofplace_unroll2x(
57                        simd_input,
58                        simd_output,
59                        self.len(),
60                        |input, output| {
61                            self.perform_parallel_fft_contiguous(DoubleBuf { input, output })
62                        },
63                        |input, output| self.perform_fft_contiguous(DoubleBuf { input, output }),
64                    );
65                }
66            }
67            fn process_with_scratch(&self, buffer: &mut [Complex<T>], _scratch: &mut [Complex<T>]) {
68                unsafe {
69                    let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
70                    super::sse_common::sse_fft_helper_inplace_unroll2x(
71                        simd_buffer,
72                        self.len(),
73                        |chunk| self.perform_parallel_fft_contiguous(chunk),
74                        |chunk| self.perform_fft_contiguous(chunk),
75                    )
76                }
77            }
78            #[inline(always)]
79            fn get_inplace_scratch_len(&self) -> usize {
80                0
81            }
82            #[inline(always)]
83            fn get_outofplace_scratch_len(&self) -> usize {
84                0
85            }
86            #[inline(always)]
87            fn get_immutable_scratch_len(&self) -> usize {
88                0
89            }
90        }
91        impl<T> Length for $struct_name<T> {
92            #[inline(always)]
93            fn len(&self) -> usize {
94                $len
95            }
96        }
97        impl<T> Direction for $struct_name<T> {
98            #[inline(always)]
99            fn fft_direction(&self) -> FftDirection {
100                $direction_fn(self)
101            }
102        }
103    };
104}
105
106macro_rules! boilerplate_fft_sse_f32_butterfly_noparallel {
107    ($struct_name:ident, $len:expr, $direction_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                unsafe {
116                    let simd_input = crate::array_utils::workaround_transmute(input);
117                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
118                    super::sse_common::sse_fft_helper_immut(
119                        simd_input,
120                        simd_output,
121                        &mut [],
122                        self.len(),
123                        0,
124                        |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
125                    );
126                }
127            }
128            fn process_outofplace_with_scratch(
129                &self,
130                input: &mut [Complex<T>],
131                output: &mut [Complex<T>],
132                _scratch: &mut [Complex<T>],
133            ) {
134                unsafe {
135                    let simd_input = crate::array_utils::workaround_transmute_mut(input);
136                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
137                    super::sse_common::sse_fft_helper_outofplace(
138                        simd_input,
139                        simd_output,
140                        &mut [],
141                        self.len(),
142                        0,
143                        |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
144                    );
145                }
146            }
147            fn process_with_scratch(&self, buffer: &mut [Complex<T>], _scratch: &mut [Complex<T>]) {
148                unsafe {
149                    let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
150                    super::sse_common::sse_fft_helper_inplace(
151                        simd_buffer,
152                        &mut [],
153                        self.len(),
154                        0,
155                        |chunk, _| self.perform_fft_contiguous(chunk),
156                    )
157                }
158            }
159            #[inline(always)]
160            fn get_inplace_scratch_len(&self) -> usize {
161                0
162            }
163            #[inline(always)]
164            fn get_outofplace_scratch_len(&self) -> usize {
165                0
166            }
167            #[inline(always)]
168            fn get_immutable_scratch_len(&self) -> usize {
169                0
170            }
171        }
172        impl<T> Length for $struct_name<T> {
173            #[inline(always)]
174            fn len(&self) -> usize {
175                $len
176            }
177        }
178        impl<T> Direction for $struct_name<T> {
179            #[inline(always)]
180            fn fft_direction(&self) -> FftDirection {
181                $direction_fn(self)
182            }
183        }
184    };
185}
186
187macro_rules! boilerplate_fft_sse_f64_butterfly {
188    ($struct_name:ident, $len:expr, $direction_fn:expr) => {
189        impl<T: FftNum> Fft<T> for $struct_name<T> {
190            fn process_immutable_with_scratch(
191                &self,
192                input: &[Complex<T>],
193                output: &mut [Complex<T>],
194                _scratch: &mut [Complex<T>],
195            ) {
196                unsafe {
197                    let simd_input = crate::array_utils::workaround_transmute(input);
198                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
199                    super::sse_common::sse_fft_helper_immut(
200                        simd_input,
201                        simd_output,
202                        &mut [],
203                        self.len(),
204                        0,
205                        |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
206                    );
207                }
208            }
209            fn process_outofplace_with_scratch(
210                &self,
211                input: &mut [Complex<T>],
212                output: &mut [Complex<T>],
213                _scratch: &mut [Complex<T>],
214            ) {
215                unsafe {
216                    let simd_input = crate::array_utils::workaround_transmute_mut(input);
217                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
218                    super::sse_common::sse_fft_helper_outofplace(
219                        simd_input,
220                        simd_output,
221                        &mut [],
222                        self.len(),
223                        0,
224                        |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
225                    );
226                }
227            }
228            fn process_with_scratch(&self, buffer: &mut [Complex<T>], _scratch: &mut [Complex<T>]) {
229                unsafe {
230                    let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
231                    super::sse_common::sse_fft_helper_inplace(
232                        simd_buffer,
233                        &mut [],
234                        self.len(),
235                        0,
236                        |chunk, _| self.perform_fft_contiguous(chunk),
237                    )
238                }
239            }
240            #[inline(always)]
241            fn get_inplace_scratch_len(&self) -> usize {
242                0
243            }
244            #[inline(always)]
245            fn get_outofplace_scratch_len(&self) -> usize {
246                0
247            }
248            #[inline(always)]
249            fn get_immutable_scratch_len(&self) -> usize {
250                0
251            }
252        }
253        impl<T> Length for $struct_name<T> {
254            #[inline(always)]
255            fn len(&self) -> usize {
256                $len
257            }
258        }
259        impl<T> Direction for $struct_name<T> {
260            #[inline(always)]
261            fn fft_direction(&self) -> FftDirection {
262                $direction_fn(self)
263            }
264        }
265    };
266}
267
268//   _            _________  _     _ _
269//  / |          |___ /___ \| |__ (_) |_
270//  | |   _____    |_ \ __) | '_ \| | __|
271//  | |  |_____|  ___) / __/| |_) | | |_
272//  |_|          |____/_____|_.__/|_|\__|
273//
274
275pub struct SseF32Butterfly1<T> {
276    direction: FftDirection,
277    _phantom: std::marker::PhantomData<T>,
278}
279
280boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly1, 1, |this: &SseF32Butterfly1<_>| this
281    .direction);
282impl<T: FftNum> SseF32Butterfly1<T> {
283    #[inline(always)]
284    pub fn new(direction: FftDirection) -> Self {
285        assert_f32::<T>();
286        Self {
287            direction,
288            _phantom: std::marker::PhantomData,
289        }
290    }
291    #[inline(always)]
292    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
293        let value = buffer.load_partial_lo_complex(0);
294        buffer.store_partial_lo_complex(value, 0);
295    }
296
297    #[inline(always)]
298    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
299        let value = buffer.load_complex(0);
300        buffer.store_complex(value, 0);
301    }
302}
303
304//   _             __   _  _   _     _ _
305//  / |           / /_ | || | | |__ (_) |_
306//  | |   _____  | '_ \| || |_| '_ \| | __|
307//  | |  |_____| | (_) |__   _| |_) | | |_
308//  |_|           \___/   |_| |_.__/|_|\__|
309//
310
311pub struct SseF64Butterfly1<T> {
312    direction: FftDirection,
313    _phantom: std::marker::PhantomData<T>,
314}
315
316boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly1, 1, |this: &SseF64Butterfly1<_>| this
317    .direction);
318impl<T: FftNum> SseF64Butterfly1<T> {
319    #[inline(always)]
320    pub fn new(direction: FftDirection) -> Self {
321        assert_f64::<T>();
322        Self {
323            direction,
324            _phantom: std::marker::PhantomData,
325        }
326    }
327    #[inline(always)]
328    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
329        let value = buffer.load_complex(0);
330        buffer.store_complex(value, 0);
331    }
332}
333
334//   ____            _________  _     _ _
335//  |___ \          |___ /___ \| |__ (_) |_
336//    __) |  _____    |_ \ __) | '_ \| | __|
337//   / __/  |_____|  ___) / __/| |_) | | |_
338//  |_____|         |____/_____|_.__/|_|\__|
339//
340
341pub struct SseF32Butterfly2<T> {
342    direction: FftDirection,
343    _phantom: std::marker::PhantomData<T>,
344}
345
346boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly2, 2, |this: &SseF32Butterfly2<_>| this
347    .direction);
348impl<T: FftNum> SseF32Butterfly2<T> {
349    #[inline(always)]
350    pub fn new(direction: FftDirection) -> Self {
351        assert_f32::<T>();
352        Self {
353            direction,
354            _phantom: std::marker::PhantomData,
355        }
356    }
357    #[inline(always)]
358    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
359        let values = buffer.load_complex(0);
360
361        let temp = self.perform_fft_direct(values);
362
363        buffer.store_complex(temp, 0);
364    }
365
366    #[inline(always)]
367    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
368        let values_a = buffer.load_complex(0);
369        let values_b = buffer.load_complex(2);
370
371        let out = self.perform_parallel_fft_direct(values_a, values_b);
372
373        let [out02, out13] = transpose_complex_2x2_f32(out[0], out[1]);
374
375        buffer.store_complex(out02, 0);
376        buffer.store_complex(out13, 2);
377    }
378
379    // length 2 fft of x, given as [x0, x1]
380    // result is [X0, X1]
381    #[inline(always)]
382    pub(crate) unsafe fn perform_fft_direct(&self, values: __m128) -> __m128 {
383        solo_fft2_f32(values)
384    }
385
386    // dual length 2 fft of x and y, given as [x0, x1], [y0, y1]
387    // result is [X0, Y0], [X1, Y1]
388    #[inline(always)]
389    pub(crate) unsafe fn perform_parallel_fft_direct(
390        &self,
391        values_x: __m128,
392        values_y: __m128,
393    ) -> [__m128; 2] {
394        parallel_fft2_contiguous_f32(values_x, values_y)
395    }
396}
397
398// double lenth 2 fft of a and b, given as [x0, y0], [x1, y1]
399// result is [X0, Y0], [X1, Y1]
400#[inline(always)]
401pub(crate) unsafe fn parallel_fft2_interleaved_f32(val02: __m128, val13: __m128) -> [__m128; 2] {
402    let temp0 = _mm_add_ps(val02, val13);
403    let temp1 = _mm_sub_ps(val02, val13);
404    [temp0, temp1]
405}
406
407// double lenth 2 fft of a and b, given as [x0, x1], [y0, y1]
408// result is [X0, Y0], [X1, Y1]
409#[inline(always)]
410unsafe fn parallel_fft2_contiguous_f32(left: __m128, right: __m128) -> [__m128; 2] {
411    let [temp02, temp13] = transpose_complex_2x2_f32(left, right);
412    parallel_fft2_interleaved_f32(temp02, temp13)
413}
414
415// length 2 fft of x, given as [x0, x1]
416// result is [X0, X1]
417#[inline(always)]
418unsafe fn solo_fft2_f32(values: __m128) -> __m128 {
419    let temp = reverse_complex_elements_f32(values);
420    let temp2 = negate_hi_f32(values);
421    _mm_add_ps(temp2, temp)
422}
423
424//   ____             __   _  _   _     _ _
425//  |___ \           / /_ | || | | |__ (_) |_
426//    __) |  _____  | '_ \| || |_| '_ \| | __|
427//   / __/  |_____| | (_) |__   _| |_) | | |_
428//  |_____|          \___/   |_| |_.__/|_|\__|
429//
430
431pub struct SseF64Butterfly2<T> {
432    direction: FftDirection,
433    _phantom: std::marker::PhantomData<T>,
434}
435
436boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly2, 2, |this: &SseF64Butterfly2<_>| this
437    .direction);
438impl<T: FftNum> SseF64Butterfly2<T> {
439    #[inline(always)]
440    pub fn new(direction: FftDirection) -> Self {
441        assert_f64::<T>();
442        Self {
443            direction,
444            _phantom: std::marker::PhantomData,
445        }
446    }
447
448    #[inline(always)]
449    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
450        let value0 = buffer.load_complex(0);
451        let value1 = buffer.load_complex(1);
452
453        let out = self.perform_fft_direct(value0, value1);
454
455        buffer.store_complex(out[0], 0);
456        buffer.store_complex(out[1], 1);
457    }
458
459    #[inline(always)]
460    pub(crate) unsafe fn perform_fft_direct(
461        &self,
462        value0: __m128d,
463        value1: __m128d,
464    ) -> [__m128d; 2] {
465        solo_fft2_f64(value0, value1)
466    }
467}
468
469#[inline(always)]
470pub(crate) unsafe fn solo_fft2_f64(left: __m128d, right: __m128d) -> [__m128d; 2] {
471    let temp0 = _mm_add_pd(left, right);
472    let temp1 = _mm_sub_pd(left, right);
473    [temp0, temp1]
474}
475
476//   _____            _________  _     _ _
477//  |___ /           |___ /___ \| |__ (_) |_
478//    |_ \    _____    |_ \ __) | '_ \| | __|
479//   ___) |  |_____|  ___) / __/| |_) | | |_
480//  |____/           |____/_____|_.__/|_|\__|
481//
482
483pub struct SseF32Butterfly3<T> {
484    direction: FftDirection,
485    _phantom: std::marker::PhantomData<T>,
486    rotate: Rotate90F32,
487    twiddle: __m128,
488    twiddle1re: __m128,
489    twiddle1im: __m128,
490}
491
492boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly3, 3, |this: &SseF32Butterfly3<_>| this
493    .direction);
494impl<T: FftNum> SseF32Butterfly3<T> {
495    #[inline(always)]
496    pub fn new(direction: FftDirection) -> Self {
497        assert_f32::<T>();
498        let rotate = Rotate90F32::new(true);
499        let tw1: Complex<f32> = twiddles::compute_twiddle(1, 3, direction);
500        let twiddle = unsafe { _mm_set_ps(-tw1.im, -tw1.im, tw1.re, tw1.re) };
501        let twiddle1re = unsafe { _mm_set_ps(tw1.re, tw1.re, tw1.re, tw1.re) };
502        let twiddle1im = unsafe { _mm_set_ps(tw1.im, tw1.im, tw1.im, tw1.im) };
503        Self {
504            direction,
505            _phantom: std::marker::PhantomData,
506            rotate,
507            twiddle,
508            twiddle1re,
509            twiddle1im,
510        }
511    }
512    #[inline(always)]
513    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
514        let value0x = buffer.load_partial_lo_complex(0);
515        let value12 = buffer.load_complex(1);
516
517        let out = self.perform_fft_direct(value0x, value12);
518
519        buffer.store_partial_lo_complex(out[0], 0);
520        buffer.store_complex(out[1], 1);
521    }
522
523    #[inline(always)]
524    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
525        let valuea0a1 = buffer.load_complex(0);
526        let valuea2b0 = buffer.load_complex(2);
527        let valueb1b2 = buffer.load_complex(4);
528
529        let value0 = extract_lo_hi_f32(valuea0a1, valuea2b0);
530        let value1 = extract_hi_lo_f32(valuea0a1, valueb1b2);
531        let value2 = extract_lo_hi_f32(valuea2b0, valueb1b2);
532
533        let out = self.perform_parallel_fft_direct(value0, value1, value2);
534
535        let out0 = extract_lo_lo_f32(out[0], out[1]);
536        let out1 = extract_lo_hi_f32(out[2], out[0]);
537        let out2 = extract_hi_hi_f32(out[1], out[2]);
538
539        buffer.store_complex(out0, 0);
540        buffer.store_complex(out1, 2);
541        buffer.store_complex(out2, 4);
542    }
543
544    // length 3 fft of a, given as [x0, 0.0], [x1, x2]
545    // result is [X0, Z], [X1, X2]
546    // The value Z should be discarded.
547    #[inline(always)]
548    pub(crate) unsafe fn perform_fft_direct(
549        &self,
550        value0x: __m128,
551        value12: __m128,
552    ) -> [__m128; 2] {
553        // This is a SSE translation of the scalar 3-point butterfly
554        let rev12 = negate_hi_f32(reverse_complex_elements_f32(value12));
555        let temp12pn = self.rotate.rotate_hi(_mm_add_ps(value12, rev12));
556        let twiddled = _mm_mul_ps(temp12pn, self.twiddle);
557        let temp = _mm_add_ps(value0x, twiddled);
558        let out12 = solo_fft2_f32(temp);
559        let out0x = _mm_add_ps(value0x, temp12pn);
560        [out0x, out12]
561    }
562
563    // length 3 dual fft of a, given as (x0, y0), (x1, y1), (x2, y2).
564    // result is [(X0, Y0), (X1, Y1), (X2, Y2)]
565    #[inline(always)]
566    pub(crate) unsafe fn perform_parallel_fft_direct(
567        &self,
568        value0: __m128,
569        value1: __m128,
570        value2: __m128,
571    ) -> [__m128; 3] {
572        // This is a SSE translation of the scalar 3-point butterfly
573        let x12p = _mm_add_ps(value1, value2);
574        let x12n = _mm_sub_ps(value1, value2);
575        let sum = _mm_add_ps(value0, x12p);
576
577        let temp_a = _mm_mul_ps(self.twiddle1re, x12p);
578        let temp_a = _mm_add_ps(temp_a, value0);
579
580        let n_rot = self.rotate.rotate_both(x12n);
581        let temp_b = _mm_mul_ps(self.twiddle1im, n_rot);
582
583        let x1 = _mm_add_ps(temp_a, temp_b);
584        let x2 = _mm_sub_ps(temp_a, temp_b);
585        [sum, x1, x2]
586    }
587}
588
589//   _____             __   _  _   _     _ _
590//  |___ /            / /_ | || | | |__ (_) |_
591//    |_ \    _____  | '_ \| || |_| '_ \| | __|
592//   ___) |  |_____| | (_) |__   _| |_) | | |_
593//  |____/            \___/   |_| |_.__/|_|\__|
594//
595
596pub struct SseF64Butterfly3<T> {
597    direction: FftDirection,
598    _phantom: std::marker::PhantomData<T>,
599    rotate: Rotate90F64,
600    twiddle1re: __m128d,
601    twiddle1im: __m128d,
602}
603
604boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly3, 3, |this: &SseF64Butterfly3<_>| this
605    .direction);
606impl<T: FftNum> SseF64Butterfly3<T> {
607    #[inline(always)]
608    pub fn new(direction: FftDirection) -> Self {
609        assert_f64::<T>();
610        let rotate = Rotate90F64::new(true);
611        let tw1: Complex<f64> = twiddles::compute_twiddle(1, 3, direction);
612        let twiddle1re = unsafe { _mm_set_pd(tw1.re, tw1.re) };
613        let twiddle1im = unsafe { _mm_set_pd(tw1.im, tw1.im) };
614
615        Self {
616            direction,
617            _phantom: std::marker::PhantomData,
618            rotate,
619            twiddle1re,
620            twiddle1im,
621        }
622    }
623
624    #[inline(always)]
625    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
626        let value0 = buffer.load_complex(0);
627        let value1 = buffer.load_complex(1);
628        let value2 = buffer.load_complex(2);
629
630        let out = self.perform_fft_direct(value0, value1, value2);
631
632        buffer.store_complex(out[0], 0);
633        buffer.store_complex(out[1], 1);
634        buffer.store_complex(out[2], 2);
635    }
636
637    // length 3 fft of x, given as x0, x1, x2.
638    // result is [X0, X1, X2]
639    #[inline(always)]
640    pub(crate) unsafe fn perform_fft_direct(
641        &self,
642        value0: __m128d,
643        value1: __m128d,
644        value2: __m128d,
645    ) -> [__m128d; 3] {
646        // This is a SSE translation of the scalar 3-point butterfly
647        let x12p = _mm_add_pd(value1, value2);
648        let x12n = _mm_sub_pd(value1, value2);
649        let sum = _mm_add_pd(value0, x12p);
650
651        let temp_a = _mm_mul_pd(self.twiddle1re, x12p);
652        let temp_a = _mm_add_pd(temp_a, value0);
653
654        let n_rot = self.rotate.rotate(x12n);
655        let temp_b = _mm_mul_pd(self.twiddle1im, n_rot);
656
657        let x1 = _mm_add_pd(temp_a, temp_b);
658        let x2 = _mm_sub_pd(temp_a, temp_b);
659        [sum, x1, x2]
660    }
661}
662
663//   _  _             _________  _     _ _
664//  | || |           |___ /___ \| |__ (_) |_
665//  | || |_   _____    |_ \ __) | '_ \| | __|
666//  |__   _| |_____|  ___) / __/| |_) | | |_
667//     |_|           |____/_____|_.__/|_|\__|
668//
669
670pub struct SseF32Butterfly4<T> {
671    direction: FftDirection,
672    _phantom: std::marker::PhantomData<T>,
673    rotate: Rotate90F32,
674}
675
676boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly4, 4, |this: &SseF32Butterfly4<_>| this
677    .direction);
678impl<T: FftNum> SseF32Butterfly4<T> {
679    #[inline(always)]
680    pub fn new(direction: FftDirection) -> Self {
681        assert_f32::<T>();
682        let rotate = if direction == FftDirection::Inverse {
683            Rotate90F32::new(true)
684        } else {
685            Rotate90F32::new(false)
686        };
687        Self {
688            direction,
689            _phantom: std::marker::PhantomData,
690            rotate,
691        }
692    }
693    #[inline(always)]
694    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
695        let value01 = buffer.load_complex(0);
696        let value23 = buffer.load_complex(2);
697
698        let out = self.perform_fft_direct(value01, value23);
699
700        buffer.store_complex(out[0], 0);
701        buffer.store_complex(out[1], 2);
702    }
703
704    #[inline(always)]
705    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
706        let value01a = buffer.load_complex(0);
707        let value23a = buffer.load_complex(2);
708        let value01b = buffer.load_complex(4);
709        let value23b = buffer.load_complex(6);
710
711        let [value0ab, value1ab] = transpose_complex_2x2_f32(value01a, value01b);
712        let [value2ab, value3ab] = transpose_complex_2x2_f32(value23a, value23b);
713
714        let out = self.perform_parallel_fft_direct([value0ab, value1ab, value2ab, value3ab]);
715
716        let [out0, out1] = transpose_complex_2x2_f32(out[0], out[1]);
717        let [out2, out3] = transpose_complex_2x2_f32(out[2], out[3]);
718
719        buffer.store_complex(out0, 0);
720        buffer.store_complex(out1, 4);
721        buffer.store_complex(out2, 2);
722        buffer.store_complex(out3, 6);
723    }
724
725    // length 4 fft of a, given as [x0, x1], [x2, x3]
726    // result is [[X0, X1], [X2, X3]]
727    #[inline(always)]
728    pub(crate) unsafe fn perform_fft_direct(
729        &self,
730        value01: __m128,
731        value23: __m128,
732    ) -> [__m128; 2] {
733        //we're going to hardcode a step of mixed radix
734        //aka we're going to do the six step algorithm
735
736        // step 1: transpose
737        // and
738        // step 2: column FFTs
739        let mut temp = parallel_fft2_interleaved_f32(value01, value23);
740
741        // step 3: apply twiddle factors (only one in this case, and it's either 0 + i or 0 - i)
742        temp[1] = self.rotate.rotate_hi(temp[1]);
743
744        // step 4: transpose, which we're skipping because we're the previous FFTs were non-contiguous
745
746        // step 5: row FFTs
747        // and
748        // step 6: transpose by swapping index 1 and 2
749        parallel_fft2_contiguous_f32(temp[0], temp[1])
750    }
751
752    #[inline(always)]
753    pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 4]) -> [__m128; 4] {
754        //we're going to hardcode a step of mixed radix
755        //aka we're going to do the six step algorithm
756
757        // step 1: transpose
758        // and
759        // step 2: column FFTs
760        let temp0 = parallel_fft2_interleaved_f32(values[0], values[2]);
761        let mut temp1 = parallel_fft2_interleaved_f32(values[1], values[3]);
762
763        // step 3: apply twiddle factors (only one in this case, and it's either 0 + i or 0 - i)
764        temp1[1] = self.rotate.rotate_both(temp1[1]);
765
766        // step 4: transpose, which we're skipping because we're the previous FFTs were non-contiguous
767
768        // step 5: row FFTs
769        let out0 = parallel_fft2_interleaved_f32(temp0[0], temp1[0]);
770        let out2 = parallel_fft2_interleaved_f32(temp0[1], temp1[1]);
771
772        // step 6: transpose by swapping index 1 and 2
773        [out0[0], out2[0], out0[1], out2[1]]
774    }
775}
776
777//   _  _              __   _  _   _     _ _
778//  | || |            / /_ | || | | |__ (_) |_
779//  | || |_   _____  | '_ \| || |_| '_ \| | __|
780//  |__   _| |_____| | (_) |__   _| |_) | | |_
781//     |_|            \___/   |_| |_.__/|_|\__|
782//
783
784pub struct SseF64Butterfly4<T> {
785    direction: FftDirection,
786    _phantom: std::marker::PhantomData<T>,
787    rotate: Rotate90F64,
788}
789
790boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly4, 4, |this: &SseF64Butterfly4<_>| this
791    .direction);
792impl<T: FftNum> SseF64Butterfly4<T> {
793    #[inline(always)]
794    pub fn new(direction: FftDirection) -> Self {
795        assert_f64::<T>();
796        let rotate = if direction == FftDirection::Inverse {
797            Rotate90F64::new(true)
798        } else {
799            Rotate90F64::new(false)
800        };
801
802        Self {
803            direction,
804            _phantom: std::marker::PhantomData,
805            rotate,
806        }
807    }
808
809    #[inline(always)]
810    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
811        let value0 = buffer.load_complex(0);
812        let value1 = buffer.load_complex(1);
813        let value2 = buffer.load_complex(2);
814        let value3 = buffer.load_complex(3);
815
816        let out = self.perform_fft_direct([value0, value1, value2, value3]);
817
818        buffer.store_complex(out[0], 0);
819        buffer.store_complex(out[1], 1);
820        buffer.store_complex(out[2], 2);
821        buffer.store_complex(out[3], 3);
822    }
823
824    #[inline(always)]
825    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 4]) -> [__m128d; 4] {
826        //we're going to hardcode a step of mixed radix
827        //aka we're going to do the six step algorithm
828
829        // step 1: transpose
830        // and
831        // step 2: column FFTs
832        let temp0 = solo_fft2_f64(values[0], values[2]);
833        let mut temp1 = solo_fft2_f64(values[1], values[3]);
834
835        // step 3: apply twiddle factors (only one in this case, and it's either 0 + i or 0 - i)
836        temp1[1] = self.rotate.rotate(temp1[1]);
837
838        // step 4: transpose, which we're skipping because we're the previous FFTs were non-contiguous
839
840        // step 5: row FFTs
841        let out0 = solo_fft2_f64(temp0[0], temp1[0]);
842        let out2 = solo_fft2_f64(temp0[1], temp1[1]);
843
844        // step 6: transpose by swapping index 1 and 2
845        [out0[0], out2[0], out0[1], out2[1]]
846    }
847}
848
849//   ____             _________  _     _ _
850//  | ___|           |___ /___ \| |__ (_) |_
851//  |___ \    _____    |_ \ __) | '_ \| | __|
852//   ___) |  |_____|  ___) / __/| |_) | | |_
853//  |____/           |____/_____|_.__/|_|\__|
854//
855
856pub struct SseF32Butterfly5<T> {
857    direction: FftDirection,
858    _phantom: std::marker::PhantomData<T>,
859    rotate: Rotate90F32,
860    twiddle12re: __m128,
861    twiddle21re: __m128,
862    twiddle12im: __m128,
863    twiddle21im: __m128,
864    twiddle1re: __m128,
865    twiddle1im: __m128,
866    twiddle2re: __m128,
867    twiddle2im: __m128,
868}
869
870boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly5, 5, |this: &SseF32Butterfly5<_>| this
871    .direction);
872impl<T: FftNum> SseF32Butterfly5<T> {
873    #[inline(always)]
874    pub fn new(direction: FftDirection) -> Self {
875        assert_f32::<T>();
876        let rotate = Rotate90F32::new(true);
877        let tw1: Complex<f32> = twiddles::compute_twiddle(1, 5, direction);
878        let tw2: Complex<f32> = twiddles::compute_twiddle(2, 5, direction);
879        let twiddle12re = unsafe { _mm_set_ps(tw2.re, tw2.re, tw1.re, tw1.re) };
880        let twiddle21re = unsafe { _mm_set_ps(tw1.re, tw1.re, tw2.re, tw2.re) };
881        let twiddle12im = unsafe { _mm_set_ps(tw2.im, tw2.im, tw1.im, tw1.im) };
882        let twiddle21im = unsafe { _mm_set_ps(-tw1.im, -tw1.im, tw2.im, tw2.im) };
883        let twiddle1re = unsafe { _mm_set_ps(tw1.re, tw1.re, tw1.re, tw1.re) };
884        let twiddle1im = unsafe { _mm_set_ps(tw1.im, tw1.im, tw1.im, tw1.im) };
885        let twiddle2re = unsafe { _mm_set_ps(tw2.re, tw2.re, tw2.re, tw2.re) };
886        let twiddle2im = unsafe { _mm_set_ps(tw2.im, tw2.im, tw2.im, tw2.im) };
887
888        Self {
889            direction,
890            _phantom: std::marker::PhantomData,
891            rotate,
892            twiddle12re,
893            twiddle21re,
894            twiddle12im,
895            twiddle21im,
896            twiddle1re,
897            twiddle1im,
898            twiddle2re,
899            twiddle2im,
900        }
901    }
902    #[inline(always)]
903    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
904        let value00 = buffer.load1_complex(0);
905        let value12 = buffer.load_complex(1);
906        let value34 = buffer.load_complex(3);
907
908        let out = self.perform_fft_direct(value00, value12, value34);
909
910        buffer.store_partial_lo_complex(out[0], 0);
911        buffer.store_complex(out[1], 1);
912        buffer.store_complex(out[2], 3);
913    }
914
915    #[inline(always)]
916    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
917        let input_packed = read_complex_to_array!(buffer, {0, 2, 4 ,6, 8});
918
919        let value0 = extract_lo_hi_f32(input_packed[0], input_packed[2]);
920        let value1 = extract_hi_lo_f32(input_packed[0], input_packed[3]);
921        let value2 = extract_lo_hi_f32(input_packed[1], input_packed[3]);
922        let value3 = extract_hi_lo_f32(input_packed[1], input_packed[4]);
923        let value4 = extract_lo_hi_f32(input_packed[2], input_packed[4]);
924
925        let out = self.perform_parallel_fft_direct(value0, value1, value2, value3, value4);
926
927        let out_packed = [
928            extract_lo_lo_f32(out[0], out[1]),
929            extract_lo_lo_f32(out[2], out[3]),
930            extract_lo_hi_f32(out[4], out[0]),
931            extract_hi_hi_f32(out[1], out[2]),
932            extract_hi_hi_f32(out[3], out[4]),
933        ];
934
935        write_complex_to_array_strided!(out_packed, buffer, 2, {0, 1, 2, 3, 4});
936    }
937
938    // length 5 fft of a, given as [x0, x0], [x1, x2], [x3, x4].
939    // result is [[X0, Z], [X1, X2], [X3, X4]]
940    // Note that Z should not be used.
941    #[inline(always)]
942    pub(crate) unsafe fn perform_fft_direct(
943        &self,
944        value00: __m128,
945        value12: __m128,
946        value34: __m128,
947    ) -> [__m128; 3] {
948        // This is a SSE translation of the scalar 5-point butterfly
949        let temp43 = reverse_complex_elements_f32(value34);
950        let x1423p = _mm_add_ps(value12, temp43);
951        let x1423n = _mm_sub_ps(value12, temp43);
952
953        let x1414p = duplicate_lo_f32(x1423p);
954        let x2323p = duplicate_hi_f32(x1423p);
955        let x1414n = duplicate_lo_f32(x1423n);
956        let x2323n = duplicate_hi_f32(x1423n);
957
958        let temp_a1 = _mm_mul_ps(self.twiddle12re, x1414p);
959        let temp_a2 = _mm_mul_ps(self.twiddle21re, x2323p);
960
961        let temp_b1 = _mm_mul_ps(self.twiddle12im, x1414n);
962        let temp_b2 = _mm_mul_ps(self.twiddle21im, x2323n);
963
964        let temp_a = _mm_add_ps(temp_a1, temp_a2);
965        let temp_a = _mm_add_ps(value00, temp_a);
966
967        let temp_b = _mm_add_ps(temp_b1, temp_b2);
968
969        let b_rot = self.rotate.rotate_both(temp_b);
970
971        let x00 = _mm_add_ps(value00, _mm_add_ps(x1414p, x2323p));
972
973        let x12 = _mm_add_ps(temp_a, b_rot);
974        let x34 = reverse_complex_elements_f32(_mm_sub_ps(temp_a, b_rot));
975        [x00, x12, x34]
976    }
977
978    // length 5 dual fft of x and y, given as (x0, y0), (x1, y1) ... (x4, y4).
979    // result is [(X0, Y0), (X1, Y1) ... (X2, Y2)]
980    #[inline(always)]
981    pub(crate) unsafe fn perform_parallel_fft_direct(
982        &self,
983        value0: __m128,
984        value1: __m128,
985        value2: __m128,
986        value3: __m128,
987        value4: __m128,
988    ) -> [__m128; 5] {
989        // This is a SSE translation of the scalar 3-point butterfly
990        let x14p = _mm_add_ps(value1, value4);
991        let x14n = _mm_sub_ps(value1, value4);
992        let x23p = _mm_add_ps(value2, value3);
993        let x23n = _mm_sub_ps(value2, value3);
994
995        let temp_a1_1 = _mm_mul_ps(self.twiddle1re, x14p);
996        let temp_a1_2 = _mm_mul_ps(self.twiddle2re, x23p);
997        let temp_b1_1 = _mm_mul_ps(self.twiddle1im, x14n);
998        let temp_b1_2 = _mm_mul_ps(self.twiddle2im, x23n);
999        let temp_a2_1 = _mm_mul_ps(self.twiddle1re, x23p);
1000        let temp_a2_2 = _mm_mul_ps(self.twiddle2re, x14p);
1001        let temp_b2_1 = _mm_mul_ps(self.twiddle2im, x14n);
1002        let temp_b2_2 = _mm_mul_ps(self.twiddle1im, x23n);
1003
1004        let temp_a1 = _mm_add_ps(value0, _mm_add_ps(temp_a1_1, temp_a1_2));
1005        let temp_b1 = _mm_add_ps(temp_b1_1, temp_b1_2);
1006        let temp_a2 = _mm_add_ps(value0, _mm_add_ps(temp_a2_1, temp_a2_2));
1007        let temp_b2 = _mm_sub_ps(temp_b2_1, temp_b2_2);
1008
1009        [
1010            _mm_add_ps(value0, _mm_add_ps(x14p, x23p)),
1011            _mm_add_ps(temp_a1, self.rotate.rotate_both(temp_b1)),
1012            _mm_add_ps(temp_a2, self.rotate.rotate_both(temp_b2)),
1013            _mm_sub_ps(temp_a2, self.rotate.rotate_both(temp_b2)),
1014            _mm_sub_ps(temp_a1, self.rotate.rotate_both(temp_b1)),
1015        ]
1016    }
1017}
1018
1019//   ____              __   _  _   _     _ _
1020//  | ___|            / /_ | || | | |__ (_) |_
1021//  |___ \    _____  | '_ \| || |_| '_ \| | __|
1022//   ___) |  |_____| | (_) |__   _| |_) | | |_
1023//  |____/            \___/   |_| |_.__/|_|\__|
1024//
1025
1026pub struct SseF64Butterfly5<T> {
1027    direction: FftDirection,
1028    _phantom: std::marker::PhantomData<T>,
1029    rotate: Rotate90F64,
1030    twiddle1re: __m128d,
1031    twiddle1im: __m128d,
1032    twiddle2re: __m128d,
1033    twiddle2im: __m128d,
1034}
1035
1036boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly5, 5, |this: &SseF64Butterfly5<_>| this
1037    .direction);
1038impl<T: FftNum> SseF64Butterfly5<T> {
1039    #[inline(always)]
1040    pub fn new(direction: FftDirection) -> Self {
1041        assert_f64::<T>();
1042        let rotate = Rotate90F64::new(true);
1043        let tw1: Complex<f64> = twiddles::compute_twiddle(1, 5, direction);
1044        let tw2: Complex<f64> = twiddles::compute_twiddle(2, 5, direction);
1045        let twiddle1re = unsafe { _mm_set_pd(tw1.re, tw1.re) };
1046        let twiddle1im = unsafe { _mm_set_pd(tw1.im, tw1.im) };
1047        let twiddle2re = unsafe { _mm_set_pd(tw2.re, tw2.re) };
1048        let twiddle2im = unsafe { _mm_set_pd(tw2.im, tw2.im) };
1049
1050        Self {
1051            direction,
1052            _phantom: std::marker::PhantomData,
1053            rotate,
1054            twiddle1re,
1055            twiddle1im,
1056            twiddle2re,
1057            twiddle2im,
1058        }
1059    }
1060
1061    #[inline(always)]
1062    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1063        let value0 = buffer.load_complex(0);
1064        let value1 = buffer.load_complex(1);
1065        let value2 = buffer.load_complex(2);
1066        let value3 = buffer.load_complex(3);
1067        let value4 = buffer.load_complex(4);
1068
1069        let out = self.perform_fft_direct(value0, value1, value2, value3, value4);
1070
1071        buffer.store_complex(out[0], 0);
1072        buffer.store_complex(out[1], 1);
1073        buffer.store_complex(out[2], 2);
1074        buffer.store_complex(out[3], 3);
1075        buffer.store_complex(out[4], 4);
1076    }
1077
1078    // length 5 fft of x, given as x0, x1, x2, x3, x4.
1079    // result is [X0, X1, X2, X3, X4]
1080    #[inline(always)]
1081    pub(crate) unsafe fn perform_fft_direct(
1082        &self,
1083        value0: __m128d,
1084        value1: __m128d,
1085        value2: __m128d,
1086        value3: __m128d,
1087        value4: __m128d,
1088    ) -> [__m128d; 5] {
1089        // This is a SSE translation of the scalar 5-point butterfly
1090        let x14p = _mm_add_pd(value1, value4);
1091        let x14n = _mm_sub_pd(value1, value4);
1092        let x23p = _mm_add_pd(value2, value3);
1093        let x23n = _mm_sub_pd(value2, value3);
1094
1095        let temp_a1_1 = _mm_mul_pd(self.twiddle1re, x14p);
1096        let temp_a1_2 = _mm_mul_pd(self.twiddle2re, x23p);
1097        let temp_a2_1 = _mm_mul_pd(self.twiddle2re, x14p);
1098        let temp_a2_2 = _mm_mul_pd(self.twiddle1re, x23p);
1099
1100        let temp_b1_1 = _mm_mul_pd(self.twiddle1im, x14n);
1101        let temp_b1_2 = _mm_mul_pd(self.twiddle2im, x23n);
1102        let temp_b2_1 = _mm_mul_pd(self.twiddle2im, x14n);
1103        let temp_b2_2 = _mm_mul_pd(self.twiddle1im, x23n);
1104
1105        let temp_a1 = _mm_add_pd(value0, _mm_add_pd(temp_a1_1, temp_a1_2));
1106        let temp_a2 = _mm_add_pd(value0, _mm_add_pd(temp_a2_1, temp_a2_2));
1107
1108        let temp_b1 = _mm_add_pd(temp_b1_1, temp_b1_2);
1109        let temp_b2 = _mm_sub_pd(temp_b2_1, temp_b2_2);
1110
1111        let temp_b1_rot = self.rotate.rotate(temp_b1);
1112        let temp_b2_rot = self.rotate.rotate(temp_b2);
1113        [
1114            _mm_add_pd(value0, _mm_add_pd(x14p, x23p)),
1115            _mm_add_pd(temp_a1, temp_b1_rot),
1116            _mm_add_pd(temp_a2, temp_b2_rot),
1117            _mm_sub_pd(temp_a2, temp_b2_rot),
1118            _mm_sub_pd(temp_a1, temp_b1_rot),
1119        ]
1120    }
1121}
1122
1123//    __             _________  _     _ _
1124//   / /_           |___ /___ \| |__ (_) |_
1125//  | '_ \   _____    |_ \ __) | '_ \| | __|
1126//  | (_) | |_____|  ___) / __/| |_) | | |_
1127//   \___/          |____/_____|_.__/|_|\__|
1128//
1129
1130pub struct SseF32Butterfly6<T> {
1131    direction: FftDirection,
1132    _phantom: std::marker::PhantomData<T>,
1133    bf3: SseF32Butterfly3<T>,
1134}
1135
1136boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly6, 6, |this: &SseF32Butterfly6<_>| this
1137    .direction);
1138impl<T: FftNum> SseF32Butterfly6<T> {
1139    #[inline(always)]
1140    pub fn new(direction: FftDirection) -> Self {
1141        assert_f32::<T>();
1142        let bf3 = SseF32Butterfly3::new(direction);
1143
1144        Self {
1145            direction,
1146            _phantom: std::marker::PhantomData,
1147            bf3,
1148        }
1149    }
1150
1151    #[inline(always)]
1152    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1153        let value01 = buffer.load_complex(0);
1154        let value23 = buffer.load_complex(2);
1155        let value45 = buffer.load_complex(4);
1156
1157        let out = self.perform_fft_direct(value01, value23, value45);
1158
1159        buffer.store_complex(out[0], 0);
1160        buffer.store_complex(out[1], 2);
1161        buffer.store_complex(out[2], 4);
1162    }
1163
1164    #[inline(always)]
1165    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1166        let input_packed = read_complex_to_array!(buffer,  {0, 2, 4, 6, 8, 10});
1167
1168        let values = interleave_complex_f32!(input_packed, 3, {0, 1, 2});
1169
1170        let out = self.perform_parallel_fft_direct(
1171            values[0], values[1], values[2], values[3], values[4], values[5],
1172        );
1173
1174        let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4});
1175        write_complex_to_array_strided!(out_sorted, buffer, 2, {0, 1, 2, 3, 4, 5});
1176    }
1177
1178    #[inline(always)]
1179    pub(crate) unsafe fn perform_fft_direct(
1180        &self,
1181        value01: __m128,
1182        value23: __m128,
1183        value45: __m128,
1184    ) -> [__m128; 3] {
1185        // Algorithm: 3x2 good-thomas
1186
1187        // Size-3 FFTs down the columns of our reordered array
1188        let reord0 = extract_lo_hi_f32(value01, value23);
1189        let reord1 = extract_lo_hi_f32(value23, value45);
1190        let reord2 = extract_lo_hi_f32(value45, value01);
1191
1192        let mid = self.bf3.perform_parallel_fft_direct(reord0, reord1, reord2);
1193
1194        // We normally would put twiddle factors right here, but since this is good-thomas algorithm, we don't need twiddle factors
1195
1196        // Transpose the data and do size-2 FFTs down the columns
1197        let [output0, output1] = parallel_fft2_contiguous_f32(mid[0], mid[1]);
1198        let output2 = solo_fft2_f32(mid[2]);
1199
1200        // Reorder into output
1201        [
1202            extract_lo_hi_f32(output0, output1),
1203            extract_lo_lo_f32(output2, output1),
1204            extract_hi_hi_f32(output0, output2),
1205        ]
1206    }
1207
1208    #[inline(always)]
1209    pub(crate) unsafe fn perform_parallel_fft_direct(
1210        &self,
1211        value0: __m128,
1212        value1: __m128,
1213        value2: __m128,
1214        value3: __m128,
1215        value4: __m128,
1216        value5: __m128,
1217    ) -> [__m128; 6] {
1218        // Algorithm: 3x2 good-thomas
1219
1220        // Size-3 FFTs down the columns of our reordered array
1221        let mid0 = self.bf3.perform_parallel_fft_direct(value0, value2, value4);
1222        let mid1 = self.bf3.perform_parallel_fft_direct(value3, value5, value1);
1223
1224        // We normally would put twiddle factors right here, but since this is good-thomas algorithm, we don't need twiddle factors
1225
1226        // Transpose the data and do size-2 FFTs down the columns
1227        let [output0, output1] = parallel_fft2_interleaved_f32(mid0[0], mid1[0]);
1228        let [output2, output3] = parallel_fft2_interleaved_f32(mid0[1], mid1[1]);
1229        let [output4, output5] = parallel_fft2_interleaved_f32(mid0[2], mid1[2]);
1230
1231        // Reorder into output
1232        [output0, output3, output4, output1, output2, output5]
1233    }
1234}
1235
1236//    __              __   _  _   _     _ _
1237//   / /_            / /_ | || | | |__ (_) |_
1238//  | '_ \   _____  | '_ \| || |_| '_ \| | __|
1239//  | (_) | |_____| | (_) |__   _| |_) | | |_
1240//   \___/           \___/   |_| |_.__/|_|\__|
1241//
1242
1243pub struct SseF64Butterfly6<T> {
1244    _phantom: std::marker::PhantomData<T>,
1245    bf3: SseF64Butterfly3<T>,
1246}
1247
1248boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly6, 6, |this: &SseF64Butterfly6<_>| this
1249    .bf3
1250    .direction);
1251impl<T: FftNum> SseF64Butterfly6<T> {
1252    #[inline(always)]
1253    pub fn new(direction: FftDirection) -> Self {
1254        assert_f64::<T>();
1255        let bf3 = SseF64Butterfly3::new(direction);
1256
1257        Self {
1258            _phantom: std::marker::PhantomData,
1259            bf3,
1260        }
1261    }
1262
1263    #[inline(always)]
1264    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1265        let value0 = buffer.load_complex(0);
1266        let value1 = buffer.load_complex(1);
1267        let value2 = buffer.load_complex(2);
1268        let value3 = buffer.load_complex(3);
1269        let value4 = buffer.load_complex(4);
1270        let value5 = buffer.load_complex(5);
1271
1272        let out = self.perform_fft_direct([value0, value1, value2, value3, value4, value5]);
1273
1274        buffer.store_complex(out[0], 0);
1275        buffer.store_complex(out[1], 1);
1276        buffer.store_complex(out[2], 2);
1277        buffer.store_complex(out[3], 3);
1278        buffer.store_complex(out[4], 4);
1279        buffer.store_complex(out[5], 5);
1280    }
1281
1282    #[inline(always)]
1283    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 6]) -> [__m128d; 6] {
1284        // Algorithm: 3x2 good-thomas
1285
1286        // Size-3 FFTs down the columns of our reordered array
1287        let mid0 = self.bf3.perform_fft_direct(values[0], values[2], values[4]);
1288        let mid1 = self.bf3.perform_fft_direct(values[3], values[5], values[1]);
1289
1290        // We normally would put twiddle factors right here, but since this is good-thomas algorithm, we don't need twiddle factors
1291
1292        // Transpose the data and do size-2 FFTs down the columns
1293        let [output0, output1] = solo_fft2_f64(mid0[0], mid1[0]);
1294        let [output2, output3] = solo_fft2_f64(mid0[1], mid1[1]);
1295        let [output4, output5] = solo_fft2_f64(mid0[2], mid1[2]);
1296
1297        // Reorder into output
1298        [output0, output3, output4, output1, output2, output5]
1299    }
1300}
1301
1302//    ___            _________  _     _ _
1303//   ( _ )          |___ /___ \| |__ (_) |_
1304//   / _ \   _____    |_ \ __) | '_ \| | __|
1305//  | (_) | |_____|  ___) / __/| |_) | | |_
1306//   \___/          |____/_____|_.__/|_|\__|
1307//
1308
1309pub struct SseF32Butterfly8<T> {
1310    root2: __m128,
1311    root2_dual: __m128,
1312    direction: FftDirection,
1313    bf4: SseF32Butterfly4<T>,
1314    rotate90: Rotate90F32,
1315}
1316
1317boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly8, 8, |this: &SseF32Butterfly8<_>| this
1318    .direction);
1319impl<T: FftNum> SseF32Butterfly8<T> {
1320    #[inline(always)]
1321    pub fn new(direction: FftDirection) -> Self {
1322        assert_f32::<T>();
1323        let bf4 = SseF32Butterfly4::new(direction);
1324        let root2 = unsafe { _mm_set_ps(0.5f32.sqrt(), 0.5f32.sqrt(), 1.0, 1.0) };
1325        let root2_dual = unsafe { _mm_load1_ps(&0.5f32.sqrt()) };
1326        let rotate90 = if direction == FftDirection::Inverse {
1327            Rotate90F32::new(true)
1328        } else {
1329            Rotate90F32::new(false)
1330        };
1331        Self {
1332            root2,
1333            root2_dual,
1334            direction,
1335            bf4,
1336            rotate90,
1337        }
1338    }
1339
1340    #[inline(always)]
1341    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1342        let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6});
1343
1344        let out = self.perform_fft_direct(input_packed);
1345
1346        write_complex_to_array_strided!(out, buffer, 2, {0,1,2,3});
1347    }
1348
1349    #[inline(always)]
1350    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1351        let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14});
1352
1353        let values = interleave_complex_f32!(input_packed, 4, {0, 1, 2, 3});
1354
1355        let out = self.perform_parallel_fft_direct(values);
1356
1357        let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4, 6});
1358
1359        write_complex_to_array_strided!(out_sorted, buffer, 2, {0,1,2,3,4,5,6,7});
1360    }
1361
1362    #[inline(always)]
1363    unsafe fn perform_fft_direct(&self, values: [__m128; 4]) -> [__m128; 4] {
1364        // we're going to hardcode a step of mixed radix
1365        // step 1: copy and reorder the input into the scratch
1366        let [in02, in13] = transpose_complex_2x2_f32(values[0], values[1]);
1367        let [in46, in57] = transpose_complex_2x2_f32(values[2], values[3]);
1368
1369        // step 2: column FFTs
1370        let val0 = self.bf4.perform_fft_direct(in02, in46);
1371        let mut val2 = self.bf4.perform_fft_direct(in13, in57);
1372
1373        // step 3: apply twiddle factors
1374        let val2b = self.rotate90.rotate_hi(val2[0]);
1375        let val2c = _mm_add_ps(val2b, val2[0]);
1376        let val2d = _mm_mul_ps(val2c, self.root2);
1377        val2[0] = extract_lo_hi_f32(val2[0], val2d);
1378
1379        let val3b = self.rotate90.rotate_both(val2[1]);
1380        let val3c = _mm_sub_ps(val3b, val2[1]);
1381        let val3d = _mm_mul_ps(val3c, self.root2);
1382        val2[1] = extract_lo_hi_f32(val3b, val3d);
1383
1384        // step 4: transpose -- skipped because we're going to do the next FFTs non-contiguously
1385
1386        // step 5: row FFTs
1387        let out0 = parallel_fft2_interleaved_f32(val0[0], val2[0]);
1388        let out1 = parallel_fft2_interleaved_f32(val0[1], val2[1]);
1389
1390        // step 6: rearrange and copy to buffer
1391        [out0[0], out1[0], out0[1], out1[1]]
1392    }
1393
1394    #[inline(always)]
1395    unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 8]) -> [__m128; 8] {
1396        // we're going to hardcode a step of mixed radix
1397        // step 1: copy and reorder the input into the scratch
1398        // and
1399        // step 2: column FFTs
1400        let val03 = self
1401            .bf4
1402            .perform_parallel_fft_direct([values[0], values[2], values[4], values[6]]);
1403        let mut val47 = self
1404            .bf4
1405            .perform_parallel_fft_direct([values[1], values[3], values[5], values[7]]);
1406
1407        // step 3: apply twiddle factors
1408        let val5b = self.rotate90.rotate_both(val47[1]);
1409        let val5c = _mm_add_ps(val5b, val47[1]);
1410        val47[1] = _mm_mul_ps(val5c, self.root2_dual);
1411        val47[2] = self.rotate90.rotate_both(val47[2]);
1412        let val7b = self.rotate90.rotate_both(val47[3]);
1413        let val7c = _mm_sub_ps(val7b, val47[3]);
1414        val47[3] = _mm_mul_ps(val7c, self.root2_dual);
1415
1416        // step 4: transpose -- skipped because we're going to do the next FFTs non-contiguously
1417
1418        // step 5: row FFTs
1419        let out0 = parallel_fft2_interleaved_f32(val03[0], val47[0]);
1420        let out1 = parallel_fft2_interleaved_f32(val03[1], val47[1]);
1421        let out2 = parallel_fft2_interleaved_f32(val03[2], val47[2]);
1422        let out3 = parallel_fft2_interleaved_f32(val03[3], val47[3]);
1423
1424        // step 6: rearrange and copy to buffer
1425        [
1426            out0[0], out1[0], out2[0], out3[0], out0[1], out1[1], out2[1], out3[1],
1427        ]
1428    }
1429}
1430
1431//    ___             __   _  _   _     _ _
1432//   ( _ )           / /_ | || | | |__ (_) |_
1433//   / _ \   _____  | '_ \| || |_| '_ \| | __|
1434//  | (_) | |_____| | (_) |__   _| |_) | | |_
1435//   \___/           \___/   |_| |_.__/|_|\__|
1436//
1437
1438pub struct SseF64Butterfly8<T> {
1439    bf4: SseF64Butterfly4<T>,
1440}
1441
1442boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly8, 8, |this: &SseF64Butterfly8<_>| this
1443    .bf4
1444    .direction);
1445impl<T: FftNum> SseF64Butterfly8<T> {
1446    #[inline(always)]
1447    pub fn new(direction: FftDirection) -> Self {
1448        assert_f64::<T>();
1449        Self {
1450            bf4: SseF64Butterfly4::new(direction),
1451        }
1452    }
1453
1454    #[inline(always)]
1455    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1456        let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7});
1457
1458        let out = self.perform_fft_direct(values);
1459
1460        write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7});
1461    }
1462
1463    #[inline(always)]
1464    unsafe fn perform_fft_direct(&self, values: [__m128d; 8]) -> [__m128d; 8] {
1465        // we're going to hardcode a step of mixed radix
1466        // step 1: copy and reorder the input into the scratch
1467        // and
1468        // step 2: column FFTs
1469        let val03 = self
1470            .bf4
1471            .perform_fft_direct([values[0], values[2], values[4], values[6]]);
1472        let mut val47 = self
1473            .bf4
1474            .perform_fft_direct([values[1], values[3], values[5], values[7]]);
1475
1476        // step 3: apply twiddle factors
1477        val47[1] = self.bf4.rotate.rotate_45(val47[1]);
1478        val47[2] = self.bf4.rotate.rotate(val47[2]);
1479        val47[3] = self.bf4.rotate.rotate_135(val47[3]);
1480
1481        // step 4: transpose -- skipped because we're going to do the next FFTs non-contiguously
1482
1483        // step 5: row FFTs
1484        let out0 = solo_fft2_f64(val03[0], val47[0]);
1485        let out1 = solo_fft2_f64(val03[1], val47[1]);
1486        let out2 = solo_fft2_f64(val03[2], val47[2]);
1487        let out3 = solo_fft2_f64(val03[3], val47[3]);
1488
1489        // step 6: rearrange and copy to buffer
1490        [
1491            out0[0], out1[0], out2[0], out3[0], out0[1], out1[1], out2[1], out3[1],
1492        ]
1493    }
1494}
1495
1496//    ___            _________  _     _ _
1497//   / _ \          |___ /___ \| |__ (_) |_
1498//  | (_) |  _____    |_ \ __) | '_ \| | __|
1499//   \__, | |_____|  ___) / __/| |_) | | |_
1500//     /_/          |____/_____|_.__/|_|\__|
1501//
1502pub struct SseF32Butterfly9<T> {
1503    direction: FftDirection,
1504    _phantom: std::marker::PhantomData<T>,
1505    bf3: SseF32Butterfly3<T>,
1506    twiddle1: __m128,
1507    twiddle2: __m128,
1508    twiddle4: __m128,
1509}
1510
1511boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly9, 9, |this: &SseF32Butterfly9<_>| this
1512    .direction);
1513impl<T: FftNum> SseF32Butterfly9<T> {
1514    #[inline(always)]
1515    pub fn new(direction: FftDirection) -> Self {
1516        assert_f32::<T>();
1517        let bf3 = SseF32Butterfly3::new(direction);
1518        let tw1: Complex<f32> = twiddles::compute_twiddle(1, 9, direction);
1519        let tw2: Complex<f32> = twiddles::compute_twiddle(2, 9, direction);
1520        let tw4: Complex<f32> = twiddles::compute_twiddle(4, 9, direction);
1521        let twiddle1 = unsafe { _mm_set_ps(tw1.im, tw1.re, tw1.im, tw1.re) };
1522        let twiddle2 = unsafe { _mm_set_ps(tw2.im, tw2.re, tw2.im, tw2.re) };
1523        let twiddle4 = unsafe { _mm_set_ps(tw4.im, tw4.re, tw4.im, tw4.re) };
1524        Self {
1525            direction,
1526            _phantom: std::marker::PhantomData,
1527            bf3,
1528            twiddle1,
1529            twiddle2,
1530            twiddle4,
1531        }
1532    }
1533
1534    #[inline(always)]
1535    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1536        // A single Sse 9-point will need a lot of shuffling, let's just reuse the dual one
1537        let values = read_partial1_complex_to_array!(buffer, {0,1,2,3,4,5,6,7,8});
1538
1539        let out = self.perform_parallel_fft_direct(values);
1540
1541        for n in 0..9 {
1542            buffer.store_partial_lo_complex(out[n], n);
1543        }
1544    }
1545
1546    #[inline(always)]
1547    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1548        let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16});
1549
1550        let values = [
1551            extract_lo_hi_f32(input_packed[0], input_packed[4]),
1552            extract_hi_lo_f32(input_packed[0], input_packed[5]),
1553            extract_lo_hi_f32(input_packed[1], input_packed[5]),
1554            extract_hi_lo_f32(input_packed[1], input_packed[6]),
1555            extract_lo_hi_f32(input_packed[2], input_packed[6]),
1556            extract_hi_lo_f32(input_packed[2], input_packed[7]),
1557            extract_lo_hi_f32(input_packed[3], input_packed[7]),
1558            extract_hi_lo_f32(input_packed[3], input_packed[8]),
1559            extract_lo_hi_f32(input_packed[4], input_packed[8]),
1560        ];
1561
1562        let out = self.perform_parallel_fft_direct(values);
1563
1564        let out_packed = [
1565            extract_lo_lo_f32(out[0], out[1]),
1566            extract_lo_lo_f32(out[2], out[3]),
1567            extract_lo_lo_f32(out[4], out[5]),
1568            extract_lo_lo_f32(out[6], out[7]),
1569            extract_lo_hi_f32(out[8], out[0]),
1570            extract_hi_hi_f32(out[1], out[2]),
1571            extract_hi_hi_f32(out[3], out[4]),
1572            extract_hi_hi_f32(out[5], out[6]),
1573            extract_hi_hi_f32(out[7], out[8]),
1574        ];
1575
1576        write_complex_to_array_strided!(out_packed, buffer, 2, {0,1,2,3,4,5,6,7,8});
1577    }
1578
1579    #[inline(always)]
1580    pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 9]) -> [__m128; 9] {
1581        // Algorithm: 3x3 mixed radix
1582
1583        // Size-3 FFTs down the columns
1584        let mid0 = self
1585            .bf3
1586            .perform_parallel_fft_direct(values[0], values[3], values[6]);
1587        let mut mid1 = self
1588            .bf3
1589            .perform_parallel_fft_direct(values[1], values[4], values[7]);
1590        let mut mid2 = self
1591            .bf3
1592            .perform_parallel_fft_direct(values[2], values[5], values[8]);
1593
1594        // Apply twiddle factors. Note that we're re-using twiddle2
1595        mid1[1] = SseVector::mul_complex(self.twiddle1, mid1[1]);
1596        mid1[2] = SseVector::mul_complex(self.twiddle2, mid1[2]);
1597        mid2[1] = SseVector::mul_complex(self.twiddle2, mid2[1]);
1598        mid2[2] = SseVector::mul_complex(self.twiddle4, mid2[2]);
1599
1600        let [output0, output1, output2] = self
1601            .bf3
1602            .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
1603        let [output3, output4, output5] = self
1604            .bf3
1605            .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
1606        let [output6, output7, output8] = self
1607            .bf3
1608            .perform_parallel_fft_direct(mid0[2], mid1[2], mid2[2]);
1609
1610        [
1611            output0, output3, output6, output1, output4, output7, output2, output5, output8,
1612        ]
1613    }
1614}
1615
1616//    ___             __   _  _   _     _ _
1617//   / _ \           / /_ | || | | |__ (_) |_
1618//  | (_) |  _____  | '_ \| || |_| '_ \| | __|
1619//   \__, | |_____| | (_) |__   _| |_) | | |_
1620//     /_/           \___/   |_| |_.__/|_|\__|
1621//
1622
1623pub struct SseF64Butterfly9<T> {
1624    _phantom: std::marker::PhantomData<T>,
1625    bf3: SseF64Butterfly3<T>,
1626    twiddle1: __m128d,
1627    twiddle2: __m128d,
1628    twiddle4: __m128d,
1629}
1630
1631boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly9, 9, |this: &SseF64Butterfly9<_>| this
1632    .bf3
1633    .direction);
1634impl<T: FftNum> SseF64Butterfly9<T> {
1635    #[inline(always)]
1636    pub fn new(direction: FftDirection) -> Self {
1637        assert_f64::<T>();
1638        let bf3 = SseF64Butterfly3::new(direction);
1639        let tw1: Complex<f64> = twiddles::compute_twiddle(1, 9, direction);
1640        let tw2: Complex<f64> = twiddles::compute_twiddle(2, 9, direction);
1641        let tw4: Complex<f64> = twiddles::compute_twiddle(4, 9, direction);
1642        let twiddle1 = unsafe { _mm_set_pd(tw1.im, tw1.re) };
1643        let twiddle2 = unsafe { _mm_set_pd(tw2.im, tw2.re) };
1644        let twiddle4 = unsafe { _mm_set_pd(tw4.im, tw4.re) };
1645        Self {
1646            _phantom: std::marker::PhantomData,
1647            bf3,
1648            twiddle1,
1649            twiddle2,
1650            twiddle4,
1651        }
1652    }
1653
1654    #[inline(always)]
1655    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1656        let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8});
1657
1658        let out = self.perform_fft_direct(values);
1659
1660        write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8});
1661    }
1662
1663    #[inline(always)]
1664    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 9]) -> [__m128d; 9] {
1665        // Algorithm: 3x3 mixed radix
1666
1667        // Size-3 FFTs down the columns
1668        let mid0 = self.bf3.perform_fft_direct(values[0], values[3], values[6]);
1669        let mut mid1 = self.bf3.perform_fft_direct(values[1], values[4], values[7]);
1670        let mut mid2 = self.bf3.perform_fft_direct(values[2], values[5], values[8]);
1671
1672        // Apply twiddle factors. Note that we're re-using twiddle2
1673        mid1[1] = SseVector::mul_complex(self.twiddle1, mid1[1]);
1674        mid1[2] = SseVector::mul_complex(self.twiddle2, mid1[2]);
1675        mid2[1] = SseVector::mul_complex(self.twiddle2, mid2[1]);
1676        mid2[2] = SseVector::mul_complex(self.twiddle4, mid2[2]);
1677
1678        let [output0, output1, output2] = self.bf3.perform_fft_direct(mid0[0], mid1[0], mid2[0]);
1679        let [output3, output4, output5] = self.bf3.perform_fft_direct(mid0[1], mid1[1], mid2[1]);
1680        let [output6, output7, output8] = self.bf3.perform_fft_direct(mid0[2], mid1[2], mid2[2]);
1681
1682        [
1683            output0, output3, output6, output1, output4, output7, output2, output5, output8,
1684        ]
1685    }
1686}
1687
1688//   _  ___            _________  _     _ _
1689//  / |/ _ \          |___ /___ \| |__ (_) |_
1690//  | | | | |  _____    |_ \ __) | '_ \| | __|
1691//  | | |_| | |_____|  ___) / __/| |_) | | |_
1692//  |_|\___/          |____/_____|_.__/|_|\__|
1693//
1694
1695pub struct SseF32Butterfly10<T> {
1696    direction: FftDirection,
1697    _phantom: std::marker::PhantomData<T>,
1698    bf5: SseF32Butterfly5<T>,
1699}
1700
1701boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly10, 10, |this: &SseF32Butterfly10<_>| this
1702    .direction);
1703impl<T: FftNum> SseF32Butterfly10<T> {
1704    #[inline(always)]
1705    pub fn new(direction: FftDirection) -> Self {
1706        assert_f32::<T>();
1707        let bf5 = SseF32Butterfly5::new(direction);
1708        Self {
1709            direction,
1710            _phantom: std::marker::PhantomData,
1711            bf5,
1712        }
1713    }
1714
1715    #[inline(always)]
1716    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1717        let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8});
1718
1719        let out = self.perform_fft_direct(input_packed);
1720
1721        write_complex_to_array_strided!(out, buffer, 2, {0,1,2,3,4});
1722    }
1723
1724    #[inline(always)]
1725    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1726        let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16, 18});
1727
1728        let values = interleave_complex_f32!(input_packed, 5, {0, 1, 2, 3, 4});
1729
1730        let out = self.perform_parallel_fft_direct(values);
1731
1732        let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4, 6, 8});
1733
1734        write_complex_to_array_strided!(out_sorted, buffer, 2, {0,1,2,3,4,5,6,7,8,9});
1735    }
1736
1737    #[inline(always)]
1738    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128; 5]) -> [__m128; 5] {
1739        // Algorithm: 5x2 good-thomas
1740        // Reorder and pack
1741        let reord0 = extract_lo_hi_f32(values[0], values[2]);
1742        let reord1 = extract_lo_hi_f32(values[1], values[3]);
1743        let reord2 = extract_lo_hi_f32(values[2], values[4]);
1744        let reord3 = extract_lo_hi_f32(values[3], values[0]);
1745        let reord4 = extract_lo_hi_f32(values[4], values[1]);
1746
1747        // Size-5 FFTs down the columns of our reordered array
1748        let mids = self
1749            .bf5
1750            .perform_parallel_fft_direct(reord0, reord1, reord2, reord3, reord4);
1751
1752        // Since this is good-thomas algorithm, we don't need twiddle factors
1753
1754        // Transpose the data and do size-2 FFTs down the columns
1755        let [temp01, temp23] = parallel_fft2_contiguous_f32(mids[0], mids[1]);
1756        let [temp45, temp67] = parallel_fft2_contiguous_f32(mids[2], mids[3]);
1757        let temp89 = solo_fft2_f32(mids[4]);
1758
1759        // Reorder
1760        let out01 = extract_lo_hi_f32(temp01, temp23);
1761        let out23 = extract_lo_hi_f32(temp45, temp67);
1762        let out45 = extract_lo_lo_f32(temp89, temp23);
1763        let out67 = extract_hi_lo_f32(temp01, temp67);
1764        let out89 = extract_hi_hi_f32(temp45, temp89);
1765
1766        [out01, out23, out45, out67, out89]
1767    }
1768
1769    #[inline(always)]
1770    pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 10]) -> [__m128; 10] {
1771        // Algorithm: 5x2 good-thomas
1772
1773        // Size-5 FFTs down the columns of our reordered array
1774        let mid0 = self
1775            .bf5
1776            .perform_parallel_fft_direct(values[0], values[2], values[4], values[6], values[8]);
1777        let mid1 = self
1778            .bf5
1779            .perform_parallel_fft_direct(values[5], values[7], values[9], values[1], values[3]);
1780
1781        // Since this is good-thomas algorithm, we don't need twiddle factors
1782
1783        // Transpose the data and do size-2 FFTs down the columns
1784        let [output0, output1] = parallel_fft2_interleaved_f32(mid0[0], mid1[0]);
1785        let [output2, output3] = parallel_fft2_interleaved_f32(mid0[1], mid1[1]);
1786        let [output4, output5] = parallel_fft2_interleaved_f32(mid0[2], mid1[2]);
1787        let [output6, output7] = parallel_fft2_interleaved_f32(mid0[3], mid1[3]);
1788        let [output8, output9] = parallel_fft2_interleaved_f32(mid0[4], mid1[4]);
1789
1790        // Reorder and return
1791        [
1792            output0, output3, output4, output7, output8, output1, output2, output5, output6,
1793            output9,
1794        ]
1795    }
1796}
1797
1798//   _  ___             __   _  _   _     _ _
1799//  / |/ _ \           / /_ | || | | |__ (_) |_
1800//  | | | | |  _____  | '_ \| || |_| '_ \| | __|
1801//  | | |_| | |_____| | (_) |__   _| |_) | | |_
1802//  |_|\___/           \___/   |_| |_.__/|_|\__|
1803//
1804
1805pub struct SseF64Butterfly10<T> {
1806    _phantom: std::marker::PhantomData<T>,
1807    bf2: SseF64Butterfly2<T>,
1808    bf5: SseF64Butterfly5<T>,
1809}
1810
1811boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly10, 10, |this: &SseF64Butterfly10<_>| this
1812    .bf5
1813    .direction);
1814impl<T: FftNum> SseF64Butterfly10<T> {
1815    #[inline(always)]
1816    pub fn new(direction: FftDirection) -> Self {
1817        assert_f64::<T>();
1818        let bf2 = SseF64Butterfly2::new(direction);
1819        let bf5 = SseF64Butterfly5::new(direction);
1820        Self {
1821            _phantom: std::marker::PhantomData,
1822            bf2,
1823            bf5,
1824        }
1825    }
1826
1827    #[inline(always)]
1828    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1829        let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
1830
1831        let out = self.perform_fft_direct(values);
1832
1833        write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
1834    }
1835
1836    #[inline(always)]
1837    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 10]) -> [__m128d; 10] {
1838        // Algorithm: 5x2 good-thomas
1839
1840        // Size-5 FFTs down the columns of our reordered array
1841        let mid0 = self
1842            .bf5
1843            .perform_fft_direct(values[0], values[2], values[4], values[6], values[8]);
1844        let mid1 = self
1845            .bf5
1846            .perform_fft_direct(values[5], values[7], values[9], values[1], values[3]);
1847
1848        // Since this is good-thomas algorithm, we don't need twiddle factors
1849
1850        // Transpose the data and do size-2 FFTs down the columns
1851        let [output0, output1] = self.bf2.perform_fft_direct(mid0[0], mid1[0]);
1852        let [output2, output3] = self.bf2.perform_fft_direct(mid0[1], mid1[1]);
1853        let [output4, output5] = self.bf2.perform_fft_direct(mid0[2], mid1[2]);
1854        let [output6, output7] = self.bf2.perform_fft_direct(mid0[3], mid1[3]);
1855        let [output8, output9] = self.bf2.perform_fft_direct(mid0[4], mid1[4]);
1856
1857        // Reorder and return
1858        [
1859            output0, output3, output4, output7, output8, output1, output2, output5, output6,
1860            output9,
1861        ]
1862    }
1863}
1864
1865//   _ ____            _________  _     _ _
1866//  / |___ \          |___ /___ \| |__ (_) |_
1867//  | | __) |  _____    |_ \ __) | '_ \| | __|
1868//  | |/ __/  |_____|  ___) / __/| |_) | | |_
1869//  |_|_____|         |____/_____|_.__/|_|\__|
1870//
1871
1872pub struct SseF32Butterfly12<T> {
1873    direction: FftDirection,
1874    _phantom: std::marker::PhantomData<T>,
1875    bf3: SseF32Butterfly3<T>,
1876    bf4: SseF32Butterfly4<T>,
1877}
1878
1879boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly12, 12, |this: &SseF32Butterfly12<_>| this
1880    .direction);
1881impl<T: FftNum> SseF32Butterfly12<T> {
1882    #[inline(always)]
1883    pub fn new(direction: FftDirection) -> Self {
1884        assert_f32::<T>();
1885        let bf3 = SseF32Butterfly3::new(direction);
1886        let bf4 = SseF32Butterfly4::new(direction);
1887        Self {
1888            direction,
1889            _phantom: std::marker::PhantomData,
1890            bf3,
1891            bf4,
1892        }
1893    }
1894
1895    #[inline(always)]
1896    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1897        let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10 });
1898
1899        let out = self.perform_fft_direct(input_packed);
1900
1901        write_complex_to_array_strided!(out, buffer, 2, {0,1,2,3,4,5});
1902    }
1903
1904    #[inline(always)]
1905    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1906        let input_packed =
1907            read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22});
1908
1909        let values = interleave_complex_f32!(input_packed, 6, {0, 1, 2, 3, 4, 5});
1910
1911        let out = self.perform_parallel_fft_direct(values);
1912
1913        let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4, 6, 8, 10});
1914
1915        write_complex_to_array_strided!(out_sorted, buffer, 2, {0,1,2,3,4,5,6,7,8,9, 10, 11});
1916    }
1917
1918    #[inline(always)]
1919    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128; 6]) -> [__m128; 6] {
1920        // Algorithm: 4x3 good-thomas
1921
1922        // Reorder and pack
1923        let packed03 = extract_lo_hi_f32(values[0], values[1]);
1924        let packed47 = extract_lo_hi_f32(values[2], values[3]);
1925        let packed69 = extract_lo_hi_f32(values[3], values[4]);
1926        let packed101 = extract_lo_hi_f32(values[5], values[0]);
1927        let packed811 = extract_lo_hi_f32(values[4], values[5]);
1928        let packed25 = extract_lo_hi_f32(values[1], values[2]);
1929
1930        // Size-4 FFTs down the columns of our reordered array
1931        let mid0 = self.bf4.perform_fft_direct(packed03, packed69);
1932        let mid1 = self.bf4.perform_fft_direct(packed47, packed101);
1933        let mid2 = self.bf4.perform_fft_direct(packed811, packed25);
1934
1935        // Since this is good-thomas algorithm, we don't need twiddle factors
1936
1937        // Transpose the data and do size-3 FFTs down the columns
1938        let [temp03, temp14, temp25] = self
1939            .bf3
1940            .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
1941        let [temp69, temp710, temp811] = self
1942            .bf3
1943            .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
1944
1945        // Reorder and return
1946        [
1947            extract_lo_hi_f32(temp03, temp14),
1948            extract_lo_hi_f32(temp811, temp69),
1949            extract_lo_hi_f32(temp14, temp25),
1950            extract_lo_hi_f32(temp69, temp710),
1951            extract_lo_hi_f32(temp25, temp03),
1952            extract_lo_hi_f32(temp710, temp811),
1953        ]
1954    }
1955
1956    #[inline(always)]
1957    pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 12]) -> [__m128; 12] {
1958        // Algorithm: 4x3 good-thomas
1959
1960        // Size-4 FFTs down the columns of our reordered array
1961        let mid0 = self
1962            .bf4
1963            .perform_parallel_fft_direct([values[0], values[3], values[6], values[9]]);
1964        let mid1 = self
1965            .bf4
1966            .perform_parallel_fft_direct([values[4], values[7], values[10], values[1]]);
1967        let mid2 = self
1968            .bf4
1969            .perform_parallel_fft_direct([values[8], values[11], values[2], values[5]]);
1970
1971        // Since this is good-thomas algorithm, we don't need twiddle factors
1972
1973        // Transpose the data and do size-3 FFTs down the columns
1974        let [output0, output1, output2] = self
1975            .bf3
1976            .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
1977        let [output3, output4, output5] = self
1978            .bf3
1979            .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
1980        let [output6, output7, output8] = self
1981            .bf3
1982            .perform_parallel_fft_direct(mid0[2], mid1[2], mid2[2]);
1983        let [output9, output10, output11] = self
1984            .bf3
1985            .perform_parallel_fft_direct(mid0[3], mid1[3], mid2[3]);
1986
1987        // Reorder and return
1988        [
1989            output0, output4, output8, output9, output1, output5, output6, output10, output2,
1990            output3, output7, output11,
1991        ]
1992    }
1993}
1994
1995//   _ ____             __   _  _   _     _ _
1996//  / |___ \           / /_ | || | | |__ (_) |_
1997//  | | __) |  _____  | '_ \| || |_| '_ \| | __|
1998//  | |/ __/  |_____| | (_) |__   _| |_) | | |_
1999//  |_|_____|          \___/   |_| |_.__/|_|\__|
2000//
2001
2002pub struct SseF64Butterfly12<T> {
2003    _phantom: std::marker::PhantomData<T>,
2004    bf3: SseF64Butterfly3<T>,
2005    bf4: SseF64Butterfly4<T>,
2006}
2007
2008boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly12, 12, |this: &SseF64Butterfly12<_>| this
2009    .bf3
2010    .direction);
2011impl<T: FftNum> SseF64Butterfly12<T> {
2012    #[inline(always)]
2013    pub fn new(direction: FftDirection) -> Self {
2014        assert_f64::<T>();
2015        let bf3 = SseF64Butterfly3::new(direction);
2016        let bf4 = SseF64Butterfly4::new(direction);
2017        Self {
2018            _phantom: std::marker::PhantomData,
2019            bf3,
2020            bf4,
2021        }
2022    }
2023
2024    #[inline(always)]
2025    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2026        let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
2027
2028        let out = self.perform_fft_direct(values);
2029
2030        write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
2031    }
2032
2033    #[inline(always)]
2034    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 12]) -> [__m128d; 12] {
2035        // Algorithm: 4x3 good-thomas
2036
2037        // Size-4 FFTs down the columns of our reordered array
2038        let mid0 = self
2039            .bf4
2040            .perform_fft_direct([values[0], values[3], values[6], values[9]]);
2041        let mid1 = self
2042            .bf4
2043            .perform_fft_direct([values[4], values[7], values[10], values[1]]);
2044        let mid2 = self
2045            .bf4
2046            .perform_fft_direct([values[8], values[11], values[2], values[5]]);
2047
2048        // Since this is good-thomas algorithm, we don't need twiddle factors
2049
2050        // Transpose the data and do size-3 FFTs down the columns
2051        let [output0, output1, output2] = self.bf3.perform_fft_direct(mid0[0], mid1[0], mid2[0]);
2052        let [output3, output4, output5] = self.bf3.perform_fft_direct(mid0[1], mid1[1], mid2[1]);
2053        let [output6, output7, output8] = self.bf3.perform_fft_direct(mid0[2], mid1[2], mid2[2]);
2054        let [output9, output10, output11] = self.bf3.perform_fft_direct(mid0[3], mid1[3], mid2[3]);
2055
2056        [
2057            output0, output4, output8, output9, output1, output5, output6, output10, output2,
2058            output3, output7, output11,
2059        ]
2060    }
2061}
2062
2063//   _ ____            _________  _     _ _
2064//  / | ___|          |___ /___ \| |__ (_) |_
2065//  | |___ \   _____    |_ \ __) | '_ \| | __|
2066//  | |___) | |_____|  ___) / __/| |_) | | |_
2067//  |_|____/          |____/_____|_.__/|_|\__|
2068//
2069pub struct SseF32Butterfly15<T> {
2070    direction: FftDirection,
2071    _phantom: std::marker::PhantomData<T>,
2072    bf3: SseF32Butterfly3<T>,
2073    bf5: SseF32Butterfly5<T>,
2074}
2075
2076boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly15, 15, |this: &SseF32Butterfly15<_>| this
2077    .direction);
2078impl<T: FftNum> SseF32Butterfly15<T> {
2079    #[inline(always)]
2080    pub fn new(direction: FftDirection) -> Self {
2081        assert_f32::<T>();
2082        let bf3 = SseF32Butterfly3::new(direction);
2083        let bf5 = SseF32Butterfly5::new(direction);
2084        Self {
2085            direction,
2086            _phantom: std::marker::PhantomData,
2087            bf3,
2088            bf5,
2089        }
2090    }
2091
2092    #[inline(always)]
2093    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2094        // A single Sse 15-point will need a lot of shuffling, let's just reuse the dual one
2095        let values = read_partial1_complex_to_array!(buffer, {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14});
2096
2097        let out = self.perform_parallel_fft_direct(values);
2098
2099        for n in 0..15 {
2100            buffer.store_partial_lo_complex(out[n], n);
2101        }
2102    }
2103
2104    #[inline(always)]
2105    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2106        let input_packed =
2107            read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28});
2108
2109        let values = [
2110            extract_lo_hi_f32(input_packed[0], input_packed[7]),
2111            extract_hi_lo_f32(input_packed[0], input_packed[8]),
2112            extract_lo_hi_f32(input_packed[1], input_packed[8]),
2113            extract_hi_lo_f32(input_packed[1], input_packed[9]),
2114            extract_lo_hi_f32(input_packed[2], input_packed[9]),
2115            extract_hi_lo_f32(input_packed[2], input_packed[10]),
2116            extract_lo_hi_f32(input_packed[3], input_packed[10]),
2117            extract_hi_lo_f32(input_packed[3], input_packed[11]),
2118            extract_lo_hi_f32(input_packed[4], input_packed[11]),
2119            extract_hi_lo_f32(input_packed[4], input_packed[12]),
2120            extract_lo_hi_f32(input_packed[5], input_packed[12]),
2121            extract_hi_lo_f32(input_packed[5], input_packed[13]),
2122            extract_lo_hi_f32(input_packed[6], input_packed[13]),
2123            extract_hi_lo_f32(input_packed[6], input_packed[14]),
2124            extract_lo_hi_f32(input_packed[7], input_packed[14]),
2125        ];
2126
2127        let out = self.perform_parallel_fft_direct(values);
2128
2129        let out_packed = [
2130            extract_lo_lo_f32(out[0], out[1]),
2131            extract_lo_lo_f32(out[2], out[3]),
2132            extract_lo_lo_f32(out[4], out[5]),
2133            extract_lo_lo_f32(out[6], out[7]),
2134            extract_lo_lo_f32(out[8], out[9]),
2135            extract_lo_lo_f32(out[10], out[11]),
2136            extract_lo_lo_f32(out[12], out[13]),
2137            extract_lo_hi_f32(out[14], out[0]),
2138            extract_hi_hi_f32(out[1], out[2]),
2139            extract_hi_hi_f32(out[3], out[4]),
2140            extract_hi_hi_f32(out[5], out[6]),
2141            extract_hi_hi_f32(out[7], out[8]),
2142            extract_hi_hi_f32(out[9], out[10]),
2143            extract_hi_hi_f32(out[11], out[12]),
2144            extract_hi_hi_f32(out[13], out[14]),
2145        ];
2146
2147        write_complex_to_array_strided!(out_packed, buffer, 2, {0,1,2,3,4,5,6,7,8,9, 10, 11, 12, 13, 14});
2148    }
2149
2150    #[inline(always)]
2151    pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 15]) -> [__m128; 15] {
2152        // Algorithm: 5x3 good-thomas
2153
2154        // Size-5 FFTs down the columns of our reordered array
2155        let mid0 = self
2156            .bf5
2157            .perform_parallel_fft_direct(values[0], values[3], values[6], values[9], values[12]);
2158        let mid1 = self
2159            .bf5
2160            .perform_parallel_fft_direct(values[5], values[8], values[11], values[14], values[2]);
2161        let mid2 = self
2162            .bf5
2163            .perform_parallel_fft_direct(values[10], values[13], values[1], values[4], values[7]);
2164
2165        // Since this is good-thomas algorithm, we don't need twiddle factors
2166
2167        // Transpose the data and do size-3 FFTs down the columns
2168        let [output0, output1, output2] = self
2169            .bf3
2170            .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
2171        let [output3, output4, output5] = self
2172            .bf3
2173            .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
2174        let [output6, output7, output8] = self
2175            .bf3
2176            .perform_parallel_fft_direct(mid0[2], mid1[2], mid2[2]);
2177        let [output9, output10, output11] = self
2178            .bf3
2179            .perform_parallel_fft_direct(mid0[3], mid1[3], mid2[3]);
2180        let [output12, output13, output14] = self
2181            .bf3
2182            .perform_parallel_fft_direct(mid0[4], mid1[4], mid2[4]);
2183
2184        [
2185            output0, output4, output8, output9, output13, output2, output3, output7, output11,
2186            output12, output1, output5, output6, output10, output14,
2187        ]
2188    }
2189}
2190
2191//   _ ____             __   _  _   _     _ _
2192//  / | ___|           / /_ | || | | |__ (_) |_
2193//  | |___ \   _____  | '_ \| || |_| '_ \| | __|
2194//  | |___) | |_____| | (_) |__   _| |_) | | |_
2195//  |_|____/           \___/   |_| |_.__/|_|\__|
2196//
2197
2198pub struct SseF64Butterfly15<T> {
2199    _phantom: std::marker::PhantomData<T>,
2200    bf3: SseF64Butterfly3<T>,
2201    bf5: SseF64Butterfly5<T>,
2202}
2203
2204boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly15, 15, |this: &SseF64Butterfly15<_>| this
2205    .bf3
2206    .direction);
2207impl<T: FftNum> SseF64Butterfly15<T> {
2208    #[inline(always)]
2209    pub fn new(direction: FftDirection) -> Self {
2210        assert_f64::<T>();
2211        let bf3 = SseF64Butterfly3::new(direction);
2212        let bf5 = SseF64Butterfly5::new(direction);
2213        Self {
2214            _phantom: std::marker::PhantomData,
2215            bf3,
2216            bf5,
2217        }
2218    }
2219
2220    #[inline(always)]
2221    pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2222        let values =
2223            read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14});
2224
2225        let out = self.perform_fft_direct(values);
2226
2227        write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14});
2228    }
2229
2230    #[inline(always)]
2231    pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 15]) -> [__m128d; 15] {
2232        // Algorithm: 5x3 good-thomas
2233
2234        // Size-5 FFTs down the columns of our reordered array
2235        let mid0 = self
2236            .bf5
2237            .perform_fft_direct(values[0], values[3], values[6], values[9], values[12]);
2238        let mid1 = self
2239            .bf5
2240            .perform_fft_direct(values[5], values[8], values[11], values[14], values[2]);
2241        let mid2 = self
2242            .bf5
2243            .perform_fft_direct(values[10], values[13], values[1], values[4], values[7]);
2244
2245        // Since this is good-thomas algorithm, we don't need twiddle factors
2246
2247        // Transpose the data and do size-3 FFTs down the columns
2248        let [output0, output1, output2] = self.bf3.perform_fft_direct(mid0[0], mid1[0], mid2[0]);
2249        let [output3, output4, output5] = self.bf3.perform_fft_direct(mid0[1], mid1[1], mid2[1]);
2250        let [output6, output7, output8] = self.bf3.perform_fft_direct(mid0[2], mid1[2], mid2[2]);
2251        let [output9, output10, output11] = self.bf3.perform_fft_direct(mid0[3], mid1[3], mid2[3]);
2252        let [output12, output13, output14] = self.bf3.perform_fft_direct(mid0[4], mid1[4], mid2[4]);
2253
2254        [
2255            output0, output4, output8, output9, output13, output2, output3, output7, output11,
2256            output12, output1, output5, output6, output10, output14,
2257        ]
2258    }
2259}
2260
2261//   _  __             _________  _     _ _
2262//  / |/ /_           |___ /___ \| |__ (_) |_
2263//  | | '_ \   _____    |_ \ __) | '_ \| | __|
2264//  | | (_) | |_____|  ___) / __/| |_) | | |_
2265//  |_|\___/          |____/_____|_.__/|_|\__|
2266//
2267
2268pub struct SseF32Butterfly16<T> {
2269    bf4: SseF32Butterfly4<T>,
2270    twiddles_packed: [__m128; 6],
2271    twiddle1: __m128,
2272    twiddle3: __m128,
2273    twiddle9: __m128,
2274}
2275
2276boilerplate_fft_sse_f32_butterfly_noparallel!(SseF32Butterfly16, 16, |this: &SseF32Butterfly16<
2277    _,
2278>| this.bf4.direction);
2279impl<T: FftNum> SseF32Butterfly16<T> {
2280    pub fn new(direction: FftDirection) -> Self {
2281        assert_f32::<T>();
2282        let tw0: Complex<f32> = Complex { re: 1.0, im: 0.0 };
2283        let tw1: Complex<f32> = twiddles::compute_twiddle(1, 16, direction);
2284        let tw2: Complex<f32> = twiddles::compute_twiddle(2, 16, direction);
2285        let tw3: Complex<f32> = twiddles::compute_twiddle(3, 16, direction);
2286        let tw4: Complex<f32> = twiddles::compute_twiddle(4, 16, direction);
2287        let tw6: Complex<f32> = twiddles::compute_twiddle(6, 16, direction);
2288        let tw9: Complex<f32> = twiddles::compute_twiddle(9, 16, direction);
2289
2290        unsafe {
2291            Self {
2292                bf4: SseF32Butterfly4::new(direction),
2293                twiddles_packed: [
2294                    pack_32(tw0, tw1),
2295                    pack_32(tw0, tw2),
2296                    pack_32(tw0, tw3),
2297                    pack_32(tw2, tw3),
2298                    pack_32(tw4, tw6),
2299                    pack_32(tw6, tw9),
2300                ],
2301                twiddle1: pack_32(tw1, tw1),
2302                twiddle3: pack_32(tw3, tw3),
2303                twiddle9: pack_32(tw9, tw9),
2304            }
2305        }
2306    }
2307
2308    #[inline(always)]
2309    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2310        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2311        // It's 4x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-4 FFTs again
2312        // But to reduce the number of times registers get spilled, we have these optimizations:
2313        // 1: Load data as late as possible, not upfront
2314        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2315        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2316        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2317        let load = |i| {
2318            [
2319                buffer.load_complex(i),
2320                buffer.load_complex(i + 4),
2321                buffer.load_complex(i + 8),
2322                buffer.load_complex(i + 12),
2323            ]
2324        };
2325
2326        // For each pair of columns: load the data, apply our size-4 FFT, apply twiddle factors, and transpose
2327        let mut tmp0 = self.bf4.perform_parallel_fft_direct(load(0));
2328        tmp0[1] = SseVector::mul_complex(tmp0[1], self.twiddles_packed[0]);
2329        tmp0[2] = SseVector::mul_complex(tmp0[2], self.twiddles_packed[1]);
2330        tmp0[3] = SseVector::mul_complex(tmp0[3], self.twiddles_packed[2]);
2331        let [mid0, mid1] = transpose_complex_2x2_f32(tmp0[0], tmp0[1]);
2332        let [mid4, mid5] = transpose_complex_2x2_f32(tmp0[2], tmp0[3]);
2333
2334        let mut tmp1 = self.bf4.perform_parallel_fft_direct(load(2));
2335        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddles_packed[3]);
2336        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddles_packed[4]);
2337        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddles_packed[5]);
2338        let [mid2, mid3] = transpose_complex_2x2_f32(tmp1[0], tmp1[1]);
2339        let [mid6, mid7] = transpose_complex_2x2_f32(tmp1[2], tmp1[3]);
2340
2341        ////////////////////////////////////////////////////////////
2342        let mut store = |i: usize, vectors: [__m128; 4]| {
2343            buffer.store_complex(vectors[0], i + 0);
2344            buffer.store_complex(vectors[1], i + 4);
2345            buffer.store_complex(vectors[2], i + 8);
2346            buffer.store_complex(vectors[3], i + 12);
2347        };
2348        // Size-4 FFTs down each pair of transposed columns, storing them as soon as we're done with them
2349        let out0 = self
2350            .bf4
2351            .perform_parallel_fft_direct([mid0, mid1, mid2, mid3]);
2352        store(0, out0);
2353
2354        let out1 = self
2355            .bf4
2356            .perform_parallel_fft_direct([mid4, mid5, mid6, mid7]);
2357        store(2, out1);
2358    }
2359
2360    // benchmarking shows it's faster to always use the nonparallel version, but this is kep around for reference
2361    #[allow(unused)]
2362    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2363        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2364        // It's 4x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-4 FFTs again
2365        // But to reduce the number of times registers get spilled, we have these optimizations:
2366        // 1: Load data as late as possible, not upfront
2367        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2368        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2369        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2370        let load = |i: usize| {
2371            let [a0, a1] =
2372                transpose_complex_2x2_f32(buffer.load_complex(i + 0), buffer.load_complex(i + 16));
2373            let [b0, b1] =
2374                transpose_complex_2x2_f32(buffer.load_complex(i + 4), buffer.load_complex(i + 20));
2375            let [c0, c1] =
2376                transpose_complex_2x2_f32(buffer.load_complex(i + 8), buffer.load_complex(i + 24));
2377            let [d0, d1] =
2378                transpose_complex_2x2_f32(buffer.load_complex(i + 12), buffer.load_complex(i + 28));
2379            [[a0, b0, c0, d0], [a1, b1, c1, d1]]
2380        };
2381
2382        // For each pair of columns: load the data, apply our size-4 FFT, apply twiddle factors
2383        let [in2, in3] = load(2);
2384        let mut tmp2 = self.bf4.perform_parallel_fft_direct(in2);
2385        let mut tmp3 = self.bf4.perform_parallel_fft_direct(in3);
2386        tmp2[1] = self.bf4.rotate.rotate_both_45(tmp2[1]);
2387        tmp2[2] = self.bf4.rotate.rotate_both(tmp2[2]);
2388        tmp2[3] = self.bf4.rotate.rotate_both_135(tmp2[3]);
2389        tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
2390        tmp3[2] = self.bf4.rotate.rotate_both_135(tmp3[2]);
2391        tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
2392
2393        // Do these last, because fewer twiddles means fewer temporaries forcing the above data to spill
2394        let [in0, in1] = load(0);
2395        let tmp0 = self.bf4.perform_parallel_fft_direct(in0);
2396        let mut tmp1 = self.bf4.perform_parallel_fft_direct(in1);
2397        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2398        tmp1[2] = self.bf4.rotate.rotate_both_45(tmp1[2]);
2399        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
2400
2401        ////////////////////////////////////////////////////////////
2402        let mut store = |i, values_a: [__m128; 4], values_b: [__m128; 4]| {
2403            for n in 0..4 {
2404                let [a, b] = transpose_complex_2x2_f32(values_a[n], values_b[n]);
2405                buffer.store_complex(a, i + n * 4);
2406                buffer.store_complex(b, i + n * 4 + 16);
2407            }
2408        };
2409        // Size-4 FFTs down each pair of transposed columns, storing them as soon as we're done with them
2410        let out0 = self
2411            .bf4
2412            .perform_parallel_fft_direct([tmp0[0], tmp1[0], tmp2[0], tmp3[0]]);
2413        let out1 = self
2414            .bf4
2415            .perform_parallel_fft_direct([tmp0[1], tmp1[1], tmp2[1], tmp3[1]]);
2416        store(0, out0, out1);
2417
2418        let out2 = self
2419            .bf4
2420            .perform_parallel_fft_direct([tmp0[2], tmp1[2], tmp2[2], tmp3[2]]);
2421        let out3 = self
2422            .bf4
2423            .perform_parallel_fft_direct([tmp0[3], tmp1[3], tmp2[3], tmp3[3]]);
2424        store(2, out2, out3);
2425    }
2426}
2427//   _  __              __   _  _   _     _ _
2428//  / |/ /_            / /_ | || | | |__ (_) |_
2429//  | | '_ \   _____  | '_ \| || |_| '_ \| | __|
2430//  | | (_) | |_____| | (_) |__   _| |_) | | |_
2431//  |_|\___/           \___/   |_| |_.__/|_|\__|
2432//
2433
2434pub struct SseF64Butterfly16<T> {
2435    bf4: SseF64Butterfly4<T>,
2436    twiddle1: __m128d,
2437    twiddle3: __m128d,
2438    twiddle9: __m128d,
2439}
2440
2441boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly16, 16, |this: &SseF64Butterfly16<_>| this
2442    .bf4
2443    .direction);
2444impl<T: FftNum> SseF64Butterfly16<T> {
2445    #[inline(always)]
2446    pub fn new(direction: FftDirection) -> Self {
2447        assert_f64::<T>();
2448        let tw1: Complex<f64> = twiddles::compute_twiddle(1, 16, direction);
2449        let tw3: Complex<f64> = twiddles::compute_twiddle(3, 16, direction);
2450        let tw9: Complex<f64> = twiddles::compute_twiddle(9, 16, direction);
2451
2452        unsafe {
2453            Self {
2454                bf4: SseF64Butterfly4::new(direction),
2455                twiddle1: pack_64(tw1),
2456                twiddle3: pack_64(tw3),
2457                twiddle9: pack_64(tw9),
2458            }
2459        }
2460    }
2461
2462    #[inline(always)]
2463    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2464        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2465        // It's 4x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-4 FFTs again
2466        // But to reduce the number of times registers get spilled, we have these optimizations:
2467        // 1: Load data as late as possible, not upfront
2468        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2469        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2470        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2471        let load = |i| {
2472            [
2473                buffer.load_complex(i),
2474                buffer.load_complex(i + 4),
2475                buffer.load_complex(i + 8),
2476                buffer.load_complex(i + 12),
2477            ]
2478        };
2479
2480        // For each column: load the data, apply our size-4 FFT, apply twiddle factors
2481        let mut tmp1 = self.bf4.perform_fft_direct(load(1));
2482        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2483        tmp1[2] = self.bf4.rotate.rotate_45(tmp1[2]);
2484        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
2485
2486        let mut tmp3 = self.bf4.perform_fft_direct(load(3));
2487        tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
2488        tmp3[2] = self.bf4.rotate.rotate_135(tmp3[2]);
2489        tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
2490
2491        let mut tmp2 = self.bf4.perform_fft_direct(load(2));
2492        tmp2[1] = self.bf4.rotate.rotate_45(tmp2[1]);
2493        tmp2[2] = self.bf4.rotate.rotate(tmp2[2]);
2494        tmp2[3] = self.bf4.rotate.rotate_135(tmp2[3]);
2495
2496        // Do the first column last, because no twiddles means fewer temporaries forcing the above data to spill
2497        let tmp0 = self.bf4.perform_fft_direct(load(0));
2498
2499        ////////////////////////////////////////////////////////////
2500        let mut store = |i: usize, vectors: [__m128d; 4]| {
2501            buffer.store_complex(vectors[0], i + 0);
2502            buffer.store_complex(vectors[1], i + 4);
2503            buffer.store_complex(vectors[2], i + 8);
2504            buffer.store_complex(vectors[3], i + 12);
2505        };
2506
2507        // Size-4 FFTs down each of our transposed columns, storing them as soon as we're done with them
2508        let out0 = self
2509            .bf4
2510            .perform_fft_direct([tmp0[0], tmp1[0], tmp2[0], tmp3[0]]);
2511        store(0, out0);
2512
2513        let out1 = self
2514            .bf4
2515            .perform_fft_direct([tmp0[1], tmp1[1], tmp2[1], tmp3[1]]);
2516        store(1, out1);
2517
2518        let out2 = self
2519            .bf4
2520            .perform_fft_direct([tmp0[2], tmp1[2], tmp2[2], tmp3[2]]);
2521        store(2, out2);
2522
2523        let out3 = self
2524            .bf4
2525            .perform_fft_direct([tmp0[3], tmp1[3], tmp2[3], tmp3[3]]);
2526        store(3, out3);
2527    }
2528}
2529
2530//    ___ _  _             _________  _     _ _
2531//   |__ \ || |           |___ /___ \| |__ (_) |_
2532//    __) ||| |_   _____    |_ \ __) | '_ \| | __|
2533//   / __/__   _| |_____|  ___) / __/| |_) | | |_
2534//  |____}  |_|           |____/_____|_.__/|_|\__|
2535//
2536
2537pub struct SseF32Butterfly24<T> {
2538    bf4: SseF32Butterfly4<T>,
2539    bf6: SseF32Butterfly6<T>,
2540    twiddles_packed: [__m128; 9],
2541    twiddle1: __m128,
2542    twiddle2: __m128,
2543    twiddle4: __m128,
2544    twiddle5: __m128,
2545    twiddle8: __m128,
2546    twiddle10: __m128,
2547}
2548
2549boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly24, 24, |this: &SseF32Butterfly24<_>| this
2550    .bf4
2551    .direction);
2552impl<T: FftNum> SseF32Butterfly24<T> {
2553    #[inline(always)]
2554    pub fn new(direction: FftDirection) -> Self {
2555        assert_f32::<T>();
2556        let tw0: Complex<f32> = Complex { re: 1.0, im: 0.0 };
2557        let tw1: Complex<f32> = twiddles::compute_twiddle(1, 24, direction);
2558        let tw2: Complex<f32> = twiddles::compute_twiddle(2, 24, direction);
2559        let tw3: Complex<f32> = twiddles::compute_twiddle(3, 24, direction);
2560        let tw4: Complex<f32> = twiddles::compute_twiddle(4, 24, direction);
2561        let tw5: Complex<f32> = twiddles::compute_twiddle(5, 24, direction);
2562        let tw6: Complex<f32> = twiddles::compute_twiddle(6, 24, direction);
2563        let tw8: Complex<f32> = twiddles::compute_twiddle(8, 24, direction);
2564        let tw9: Complex<f32> = twiddles::compute_twiddle(9, 24, direction);
2565        let tw10: Complex<f32> = twiddles::compute_twiddle(10, 24, direction);
2566        let tw12: Complex<f32> = twiddles::compute_twiddle(12, 24, direction);
2567        let tw15: Complex<f32> = twiddles::compute_twiddle(15, 24, direction);
2568        unsafe {
2569            Self {
2570                bf4: SseF32Butterfly4::new(direction),
2571                bf6: SseF32Butterfly6::new(direction),
2572                twiddles_packed: [
2573                    pack_32(tw0, tw1),
2574                    pack_32(tw0, tw2),
2575                    pack_32(tw0, tw3),
2576                    pack_32(tw2, tw3),
2577                    pack_32(tw4, tw6),
2578                    pack_32(tw6, tw9),
2579                    pack_32(tw4, tw5),
2580                    pack_32(tw8, tw10),
2581                    pack_32(tw12, tw15),
2582                ],
2583                twiddle1: pack_32(tw1, tw1),
2584                twiddle2: pack_32(tw2, tw2),
2585                twiddle4: pack_32(tw4, tw4),
2586                twiddle5: pack_32(tw5, tw5),
2587                twiddle8: pack_32(tw8, tw8),
2588                twiddle10: pack_32(tw10, tw10),
2589            }
2590        }
2591    }
2592
2593    #[inline(always)]
2594    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2595        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2596        // It's 6x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-6 FFTs
2597        // But to reduce the number of times registers get spilled, we have these optimizations:
2598        // 1: Load data as late as possible, not upfront
2599        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2600        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2601        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2602        let load = |i| {
2603            [
2604                buffer.load_complex(i),
2605                buffer.load_complex(i + 6),
2606                buffer.load_complex(i + 12),
2607                buffer.load_complex(i + 18),
2608            ]
2609        };
2610
2611        // For each pair of columns: load the data, apply our size-4 FFT, apply twiddle factors, transpose
2612        let mut tmp1 = self.bf4.perform_parallel_fft_direct(load(2));
2613        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddles_packed[3]);
2614        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddles_packed[4]);
2615        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddles_packed[5]);
2616        let [mid2, mid3] = transpose_complex_2x2_f32(tmp1[0], tmp1[1]);
2617        let [mid8, mid9] = transpose_complex_2x2_f32(tmp1[2], tmp1[3]);
2618
2619        let mut tmp2 = self.bf4.perform_parallel_fft_direct(load(4));
2620        tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddles_packed[6]);
2621        tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddles_packed[7]);
2622        tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddles_packed[8]);
2623        let [mid4, mid5] = transpose_complex_2x2_f32(tmp2[0], tmp2[1]);
2624        let [mid10, mid11] = transpose_complex_2x2_f32(tmp2[2], tmp2[3]);
2625
2626        let mut tmp0 = self.bf4.perform_parallel_fft_direct(load(0));
2627        tmp0[1] = SseVector::mul_complex(tmp0[1], self.twiddles_packed[0]);
2628        tmp0[2] = SseVector::mul_complex(tmp0[2], self.twiddles_packed[1]);
2629        tmp0[3] = SseVector::mul_complex(tmp0[3], self.twiddles_packed[2]);
2630        let [mid0, mid1] = transpose_complex_2x2_f32(tmp0[0], tmp0[1]);
2631        let [mid6, mid7] = transpose_complex_2x2_f32(tmp0[2], tmp0[3]);
2632
2633        ////////////////////////////////////////////////////////////
2634        let mut store = |i, vectors: [__m128; 6]| {
2635            buffer.store_complex(vectors[0], i);
2636            buffer.store_complex(vectors[1], i + 4);
2637            buffer.store_complex(vectors[2], i + 8);
2638            buffer.store_complex(vectors[3], i + 12);
2639            buffer.store_complex(vectors[4], i + 16);
2640            buffer.store_complex(vectors[5], i + 20);
2641        };
2642
2643        // Size-6 FFTs down each pair of transposed columns, storing them as soon as we're done with them
2644        let out0 = self
2645            .bf6
2646            .perform_parallel_fft_direct(mid0, mid1, mid2, mid3, mid4, mid5);
2647        store(0, out0);
2648
2649        let out1 = self
2650            .bf6
2651            .perform_parallel_fft_direct(mid6, mid7, mid8, mid9, mid10, mid11);
2652        store(2, out1);
2653    }
2654
2655    #[inline(always)]
2656    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2657        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2658        // It's 6x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-6 FFTs
2659        // But to reduce the number of times registers get spilled, we have these optimizations:
2660        // 1: Load data as late as possible, not upfront
2661        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2662        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2663        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2664        let load = |i: usize| {
2665            let [a0, a1] =
2666                transpose_complex_2x2_f32(buffer.load_complex(i + 0), buffer.load_complex(i + 24));
2667            let [b0, b1] =
2668                transpose_complex_2x2_f32(buffer.load_complex(i + 6), buffer.load_complex(i + 30));
2669            let [c0, c1] =
2670                transpose_complex_2x2_f32(buffer.load_complex(i + 12), buffer.load_complex(i + 36));
2671            let [d0, d1] =
2672                transpose_complex_2x2_f32(buffer.load_complex(i + 18), buffer.load_complex(i + 42));
2673            [[a0, b0, c0, d0], [a1, b1, c1, d1]]
2674        };
2675
2676        // For each pair of columns: load the data, apply our size-4 FFT, apply twiddle factors
2677        let [in0, in1] = load(0);
2678        let tmp0 = self.bf4.perform_parallel_fft_direct(in0);
2679        let mut tmp1 = self.bf4.perform_parallel_fft_direct(in1);
2680        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2681        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
2682        tmp1[3] = self.bf4.rotate.rotate_both_45(tmp1[3]);
2683
2684        let [in2, in3] = load(2);
2685        let mut tmp2 = self.bf4.perform_parallel_fft_direct(in2);
2686        let mut tmp3 = self.bf4.perform_parallel_fft_direct(in3);
2687        tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
2688        tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddle4);
2689        tmp2[3] = self.bf4.rotate.rotate_both(tmp2[3]);
2690        tmp3[1] = self.bf4.rotate.rotate_both_45(tmp3[1]);
2691        tmp3[2] = self.bf4.rotate.rotate_both(tmp3[2]);
2692        tmp3[3] = self.bf4.rotate.rotate_both_135(tmp3[3]);
2693
2694        let [in4, in5] = load(4);
2695        let mut tmp4 = self.bf4.perform_parallel_fft_direct(in4);
2696        let mut tmp5 = self.bf4.perform_parallel_fft_direct(in5);
2697        tmp4[1] = SseVector::mul_complex(tmp4[1], self.twiddle4);
2698        tmp4[2] = SseVector::mul_complex(tmp4[2], self.twiddle8);
2699        tmp4[3] = SseVector::neg(tmp4[3]);
2700        tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
2701        tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
2702        tmp5[3] = self.bf4.rotate.rotate_both_225(tmp5[3]);
2703
2704        ////////////////////////////////////////////////////////////
2705        let mut store = |i, vectors_a: [__m128; 6], vectors_b: [__m128; 6]| {
2706            for n in 0..6 {
2707                let [a, b] = transpose_complex_2x2_f32(vectors_a[n], vectors_b[n]);
2708                buffer.store_complex(a, i + n * 4);
2709                buffer.store_complex(b, i + n * 4 + 24);
2710            }
2711        };
2712
2713        // Size-6 FFTs down each pair of transposed columns, storing them as soon as we're done with them
2714        let out0 = self
2715            .bf6
2716            .perform_parallel_fft_direct(tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0]);
2717        let out1 = self
2718            .bf6
2719            .perform_parallel_fft_direct(tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1]);
2720        store(0, out0, out1);
2721
2722        let out2 = self
2723            .bf6
2724            .perform_parallel_fft_direct(tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2]);
2725        let out3 = self
2726            .bf6
2727            .perform_parallel_fft_direct(tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3]);
2728        store(2, out2, out3);
2729    }
2730}
2731
2732//    ___ _  _              __   _  _   _     _ _
2733//   |__ \ || |            / /_ | || | | |__ (_) |_
2734//    __) ||| |_   _____  | '_ \| || |_| '_ \| | __|
2735//   / __/__   _| |_____| | (_) |__   _| |_) | | |_
2736//  |____}  |_|            \___/   |_| |_.__/|_|\__|
2737//
2738
2739pub struct SseF64Butterfly24<T> {
2740    bf4: SseF64Butterfly4<T>,
2741    bf6: SseF64Butterfly6<T>,
2742    twiddle1: __m128d,
2743    twiddle2: __m128d,
2744    twiddle4: __m128d,
2745    twiddle5: __m128d,
2746    twiddle8: __m128d,
2747    twiddle10: __m128d,
2748}
2749
2750boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly24, 24, |this: &SseF64Butterfly24<_>| this
2751    .bf4
2752    .direction);
2753impl<T: FftNum> SseF64Butterfly24<T> {
2754    #[inline(always)]
2755    pub fn new(direction: FftDirection) -> Self {
2756        assert_f64::<T>();
2757        let tw1: Complex<f64> = twiddles::compute_twiddle(1, 24, direction);
2758        let tw2: Complex<f64> = twiddles::compute_twiddle(2, 24, direction);
2759        let tw4: Complex<f64> = twiddles::compute_twiddle(4, 24, direction);
2760        let tw5: Complex<f64> = twiddles::compute_twiddle(5, 24, direction);
2761        let tw8: Complex<f64> = twiddles::compute_twiddle(8, 24, direction);
2762        let tw10: Complex<f64> = twiddles::compute_twiddle(10, 24, direction);
2763
2764        unsafe {
2765            Self {
2766                bf4: SseF64Butterfly4::new(direction),
2767                bf6: SseF64Butterfly6::new(direction),
2768                twiddle1: pack_64(tw1),
2769                twiddle2: pack_64(tw2),
2770                twiddle4: pack_64(tw4),
2771                twiddle5: pack_64(tw5),
2772                twiddle8: pack_64(tw8),
2773                twiddle10: pack_64(tw10),
2774            }
2775        }
2776    }
2777
2778    #[inline(always)]
2779    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2780        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2781        // It's 6x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-6 FFTs
2782        // But to reduce the number of times registers get spilled, we have these optimizations:
2783        // 1: Load data as late as possible, not upfront
2784        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2785        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2786        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2787        let load = |i| {
2788            [
2789                buffer.load_complex(i),
2790                buffer.load_complex(i + 6),
2791                buffer.load_complex(i + 12),
2792                buffer.load_complex(i + 18),
2793            ]
2794        };
2795
2796        // For each column: load the data, apply our size-4 FFT, apply twiddle factors
2797        let mut tmp1 = self.bf4.perform_fft_direct(load(1));
2798        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2799        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
2800        tmp1[3] = self.bf4.rotate.rotate_45(tmp1[3]);
2801
2802        let mut tmp2 = self.bf4.perform_fft_direct(load(2));
2803        tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
2804        tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddle4);
2805        tmp2[3] = self.bf4.rotate.rotate(tmp2[3]);
2806
2807        let mut tmp4 = self.bf4.perform_fft_direct(load(4));
2808        tmp4[1] = SseVector::mul_complex(tmp4[1], self.twiddle4);
2809        tmp4[2] = SseVector::mul_complex(tmp4[2], self.twiddle8);
2810        tmp4[3] = SseVector::neg(tmp4[3]);
2811
2812        let mut tmp5 = self.bf4.perform_fft_direct(load(5));
2813        tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
2814        tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
2815        tmp5[3] = self.bf4.rotate.rotate_225(tmp5[3]);
2816
2817        let mut tmp3 = self.bf4.perform_fft_direct(load(3));
2818        tmp3[1] = self.bf4.rotate.rotate_45(tmp3[1]);
2819        tmp3[2] = self.bf4.rotate.rotate(tmp3[2]);
2820        tmp3[3] = self.bf4.rotate.rotate_135(tmp3[3]);
2821
2822        // Do the first column last, because no twiddles means fewer temporaries forcing the above data to spill
2823        let tmp0 = self.bf4.perform_fft_direct(load(0));
2824
2825        ////////////////////////////////////////////////////////////
2826        let mut store = |i, vectors: [__m128d; 6]| {
2827            buffer.store_complex(vectors[0], i);
2828            buffer.store_complex(vectors[1], i + 4);
2829            buffer.store_complex(vectors[2], i + 8);
2830            buffer.store_complex(vectors[3], i + 12);
2831            buffer.store_complex(vectors[4], i + 16);
2832            buffer.store_complex(vectors[5], i + 20);
2833        };
2834
2835        // Size-6 FFTs down each of our transposed columns, storing them as soon as we're done with them
2836        let out0 = self
2837            .bf6
2838            .perform_fft_direct([tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0]]);
2839        store(0, out0);
2840
2841        let out1 = self
2842            .bf6
2843            .perform_fft_direct([tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1]]);
2844        store(1, out1);
2845
2846        let out2 = self
2847            .bf6
2848            .perform_fft_direct([tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2]]);
2849        store(2, out2);
2850
2851        let out3 = self
2852            .bf6
2853            .perform_fft_direct([tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3]]);
2854        store(3, out3);
2855    }
2856}
2857
2858//   _________            _________  _     _ _
2859//  |___ /___ \          |___ /___ \| |__ (_) |_
2860//    |_ \ __) |  _____    |_ \ __) | '_ \| | __|
2861//   ___) / __/  |_____|  ___) / __/| |_) | | |_
2862//  |____/_____|         |____/_____|_.__/|_|\__|
2863//
2864
2865pub struct SseF32Butterfly32<T> {
2866    bf8: SseF32Butterfly8<T>,
2867    twiddles_packed: [__m128; 12],
2868    twiddle1: __m128,
2869    twiddle2: __m128,
2870    twiddle3: __m128,
2871    twiddle5: __m128,
2872    twiddle6: __m128,
2873    twiddle7: __m128,
2874    twiddle9: __m128,
2875    twiddle10: __m128,
2876    twiddle14: __m128,
2877    twiddle15: __m128,
2878    twiddle18: __m128,
2879    twiddle21: __m128,
2880}
2881
2882boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly32, 32, |this: &SseF32Butterfly32<_>| this
2883    .bf8
2884    .bf4
2885    .direction);
2886impl<T: FftNum> SseF32Butterfly32<T> {
2887    #[inline(always)]
2888    pub fn new(direction: FftDirection) -> Self {
2889        assert_f32::<T>();
2890        let tw0: Complex<f32> = Complex { re: 1.0, im: 0.0 };
2891        let tw1: Complex<f32> = twiddles::compute_twiddle(1, 32, direction);
2892        let tw2: Complex<f32> = twiddles::compute_twiddle(2, 32, direction);
2893        let tw3: Complex<f32> = twiddles::compute_twiddle(3, 32, direction);
2894        let tw4: Complex<f32> = twiddles::compute_twiddle(4, 32, direction);
2895        let tw5: Complex<f32> = twiddles::compute_twiddle(5, 32, direction);
2896        let tw6: Complex<f32> = twiddles::compute_twiddle(6, 32, direction);
2897        let tw7: Complex<f32> = twiddles::compute_twiddle(7, 32, direction);
2898        let tw8: Complex<f32> = twiddles::compute_twiddle(8, 32, direction);
2899        let tw9: Complex<f32> = twiddles::compute_twiddle(9, 32, direction);
2900        let tw10: Complex<f32> = twiddles::compute_twiddle(10, 32, direction);
2901        let tw12: Complex<f32> = twiddles::compute_twiddle(12, 32, direction);
2902        let tw14: Complex<f32> = twiddles::compute_twiddle(14, 32, direction);
2903        let tw15: Complex<f32> = twiddles::compute_twiddle(15, 32, direction);
2904        let tw18: Complex<f32> = twiddles::compute_twiddle(18, 32, direction);
2905        let tw21: Complex<f32> = twiddles::compute_twiddle(21, 32, direction);
2906        unsafe {
2907            Self {
2908                bf8: SseF32Butterfly8::new(direction),
2909                twiddles_packed: [
2910                    pack_32(tw0, tw1),
2911                    pack_32(tw0, tw2),
2912                    pack_32(tw0, tw3),
2913                    pack_32(tw2, tw3),
2914                    pack_32(tw4, tw6),
2915                    pack_32(tw6, tw9),
2916                    pack_32(tw4, tw5),
2917                    pack_32(tw8, tw10),
2918                    pack_32(tw12, tw15),
2919                    pack_32(tw6, tw7),
2920                    pack_32(tw12, tw14),
2921                    pack_32(tw18, tw21),
2922                ],
2923                twiddle1: pack_32(tw1, tw1),
2924                twiddle2: pack_32(tw2, tw2),
2925                twiddle3: pack_32(tw3, tw3),
2926                twiddle5: pack_32(tw5, tw5),
2927                twiddle6: pack_32(tw6, tw6),
2928                twiddle7: pack_32(tw7, tw7),
2929                twiddle9: pack_32(tw9, tw9),
2930                twiddle10: pack_32(tw10, tw10),
2931                twiddle14: pack_32(tw14, tw14),
2932                twiddle15: pack_32(tw15, tw15),
2933                twiddle18: pack_32(tw18, tw18),
2934                twiddle21: pack_32(tw21, tw21),
2935            }
2936        }
2937    }
2938
2939    #[inline(always)]
2940    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2941        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
2942        // It's 8x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-8 FFTs
2943        // But to reduce the number of times registers get spilled, we have these optimizations:
2944        // 1: Load data as late as possible, not upfront
2945        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
2946        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
2947        // 3: Store data as soon as we're finished with it, rather than waiting for the end
2948        let load = |i| {
2949            [
2950                buffer.load_complex(i),
2951                buffer.load_complex(i + 8),
2952                buffer.load_complex(i + 16),
2953                buffer.load_complex(i + 24),
2954            ]
2955        };
2956
2957        // For each pair of columns: load the data, apply our size-4 FFT, apply twiddle factors
2958        let mut tmp0 = self.bf8.bf4.perform_parallel_fft_direct(load(0));
2959        tmp0[1] = SseVector::mul_complex(tmp0[1], self.twiddles_packed[0]);
2960        tmp0[2] = SseVector::mul_complex(tmp0[2], self.twiddles_packed[1]);
2961        tmp0[3] = SseVector::mul_complex(tmp0[3], self.twiddles_packed[2]);
2962        let [mid0, mid1] = transpose_complex_2x2_f32(tmp0[0], tmp0[1]);
2963        let [mid8, mid9] = transpose_complex_2x2_f32(tmp0[2], tmp0[3]);
2964
2965        let mut tmp1 = self.bf8.bf4.perform_parallel_fft_direct(load(2));
2966        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddles_packed[3]);
2967        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddles_packed[4]);
2968        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddles_packed[5]);
2969        let [mid2, mid3] = transpose_complex_2x2_f32(tmp1[0], tmp1[1]);
2970        let [mid10, mid11] = transpose_complex_2x2_f32(tmp1[2], tmp1[3]);
2971
2972        let mut tmp2 = self.bf8.bf4.perform_parallel_fft_direct(load(4));
2973        tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddles_packed[6]);
2974        tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddles_packed[7]);
2975        tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddles_packed[8]);
2976        let [mid4, mid5] = transpose_complex_2x2_f32(tmp2[0], tmp2[1]);
2977        let [mid12, mid13] = transpose_complex_2x2_f32(tmp2[2], tmp2[3]);
2978
2979        let mut tmp3 = self.bf8.bf4.perform_parallel_fft_direct(load(6));
2980        tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddles_packed[9]);
2981        tmp3[2] = SseVector::mul_complex(tmp3[2], self.twiddles_packed[10]);
2982        tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddles_packed[11]);
2983        let [mid6, mid7] = transpose_complex_2x2_f32(tmp3[0], tmp3[1]);
2984        let [mid14, mid15] = transpose_complex_2x2_f32(tmp3[2], tmp3[3]);
2985
2986        ////////////////////////////////////////////////////////////
2987        let mut store = |i, vectors: [__m128; 8]| {
2988            buffer.store_complex(vectors[0], i);
2989            buffer.store_complex(vectors[1], i + 4);
2990            buffer.store_complex(vectors[2], i + 8);
2991            buffer.store_complex(vectors[3], i + 12);
2992            buffer.store_complex(vectors[4], i + 16);
2993            buffer.store_complex(vectors[5], i + 20);
2994            buffer.store_complex(vectors[6], i + 24);
2995            buffer.store_complex(vectors[7], i + 28);
2996        };
2997
2998        // Size-8 FFTs down each pair of transposed columns, storing them as soon as we're done with them
2999        let out0 = self
3000            .bf8
3001            .perform_parallel_fft_direct([mid0, mid1, mid2, mid3, mid4, mid5, mid6, mid7]);
3002        store(0, out0);
3003
3004        let out1 = self
3005            .bf8
3006            .perform_parallel_fft_direct([mid8, mid9, mid10, mid11, mid12, mid13, mid14, mid15]);
3007        store(2, out1);
3008    }
3009
3010    #[inline(always)]
3011    pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
3012        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
3013        // It's 8x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-8 FFTs
3014        // But to reduce the number of times registers get spilled, we have these optimizations:
3015        // 1: Load data as late as possible, not upfront
3016        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
3017        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
3018        // 3: Store data as soon as we're finished with it, rather than waiting for the end
3019        let load = |i: usize| {
3020            let [a0, a1] =
3021                transpose_complex_2x2_f32(buffer.load_complex(i + 0), buffer.load_complex(i + 32));
3022            let [b0, b1] =
3023                transpose_complex_2x2_f32(buffer.load_complex(i + 8), buffer.load_complex(i + 40));
3024            let [c0, c1] =
3025                transpose_complex_2x2_f32(buffer.load_complex(i + 16), buffer.load_complex(i + 48));
3026            let [d0, d1] =
3027                transpose_complex_2x2_f32(buffer.load_complex(i + 24), buffer.load_complex(i + 56));
3028            [[a0, b0, c0, d0], [a1, b1, c1, d1]]
3029        };
3030
3031        // For each pair of columns: load the data, apply our size-4 FFT, apply twiddle factors
3032        let [in0, in1] = load(0);
3033        let tmp0 = self.bf8.bf4.perform_parallel_fft_direct(in0);
3034        let mut tmp1 = self.bf8.bf4.perform_parallel_fft_direct(in1);
3035        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
3036        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
3037        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
3038
3039        let [in2, in3] = load(2);
3040        let mut tmp2 = self.bf8.bf4.perform_parallel_fft_direct(in2);
3041        let mut tmp3 = self.bf8.bf4.perform_parallel_fft_direct(in3);
3042        tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
3043        tmp2[2] = self.bf8.bf4.rotate.rotate_both_45(tmp2[2]);
3044        tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddle6);
3045        tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
3046        tmp3[2] = SseVector::mul_complex(tmp3[2], self.twiddle6);
3047        tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
3048
3049        let [in4, in5] = load(4);
3050        let mut tmp4 = self.bf8.bf4.perform_parallel_fft_direct(in4);
3051        let mut tmp5 = self.bf8.bf4.perform_parallel_fft_direct(in5);
3052        tmp4[1] = self.bf8.bf4.rotate.rotate_both_45(tmp4[1]);
3053        tmp4[2] = self.bf8.bf4.rotate.rotate_both(tmp4[2]);
3054        tmp4[3] = self.bf8.bf4.rotate.rotate_both_135(tmp4[3]);
3055        tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
3056        tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
3057        tmp5[3] = SseVector::mul_complex(tmp5[3], self.twiddle15);
3058
3059        let [in6, in7] = load(6);
3060        let mut tmp6 = self.bf8.bf4.perform_parallel_fft_direct(in6);
3061        let mut tmp7 = self.bf8.bf4.perform_parallel_fft_direct(in7);
3062        tmp6[1] = SseVector::mul_complex(tmp6[1], self.twiddle6);
3063        tmp6[2] = self.bf8.bf4.rotate.rotate_both_135(tmp6[2]);
3064        tmp6[3] = SseVector::mul_complex(tmp6[3], self.twiddle18);
3065        tmp7[1] = SseVector::mul_complex(tmp7[1], self.twiddle7);
3066        tmp7[2] = SseVector::mul_complex(tmp7[2], self.twiddle14);
3067        tmp7[3] = SseVector::mul_complex(tmp7[3], self.twiddle21);
3068
3069        ////////////////////////////////////////////////////////////
3070        let mut store = |i, vectors_a: [__m128; 8], vectors_b: [__m128; 8]| {
3071            for n in 0..8 {
3072                let [a, b] = transpose_complex_2x2_f32(vectors_a[n], vectors_b[n]);
3073                buffer.store_complex(a, i + n * 4);
3074                buffer.store_complex(b, i + n * 4 + 32);
3075            }
3076        };
3077
3078        // Size-8 FFTs down each pair of transposed columns, storing them as soon as we're done with them
3079        let out0 = self.bf8.perform_parallel_fft_direct([
3080            tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0], tmp6[0], tmp7[0],
3081        ]);
3082        let out1 = self.bf8.perform_parallel_fft_direct([
3083            tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1], tmp6[1], tmp7[1],
3084        ]);
3085        store(0, out0, out1);
3086
3087        let out2 = self.bf8.perform_parallel_fft_direct([
3088            tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2], tmp6[2], tmp7[2],
3089        ]);
3090        let out3 = self.bf8.perform_parallel_fft_direct([
3091            tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3], tmp6[3], tmp7[3],
3092        ]);
3093        store(2, out2, out3);
3094    }
3095}
3096
3097//   _________             __   _  _   _     _ _
3098//  |___ /___ \           / /_ | || | | |__ (_) |_
3099//    |_ \ __) |  _____  | '_ \| || |_| '_ \| | __|
3100//   ___) / __/  |_____| | (_) |__   _| |_) | | |_
3101//  |____/_____|          \___/   |_| |_.__/|_|\__|
3102//
3103
3104pub struct SseF64Butterfly32<T> {
3105    bf8: SseF64Butterfly8<T>,
3106    twiddle1: __m128d,
3107    twiddle2: __m128d,
3108    twiddle3: __m128d,
3109    twiddle5: __m128d,
3110    twiddle6: __m128d,
3111    twiddle7: __m128d,
3112    twiddle9: __m128d,
3113    twiddle10: __m128d,
3114    twiddle14: __m128d,
3115    twiddle15: __m128d,
3116    twiddle18: __m128d,
3117    twiddle21: __m128d,
3118}
3119
3120boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly32, 32, |this: &SseF64Butterfly32<_>| this
3121    .bf8
3122    .bf4
3123    .direction);
3124impl<T: FftNum> SseF64Butterfly32<T> {
3125    #[inline(always)]
3126    pub fn new(direction: FftDirection) -> Self {
3127        assert_f64::<T>();
3128        let tw1: Complex<f64> = twiddles::compute_twiddle(1, 32, direction);
3129        let tw2: Complex<f64> = twiddles::compute_twiddle(2, 32, direction);
3130        let tw3: Complex<f64> = twiddles::compute_twiddle(3, 32, direction);
3131        let tw5: Complex<f64> = twiddles::compute_twiddle(5, 32, direction);
3132        let tw6: Complex<f64> = twiddles::compute_twiddle(6, 32, direction);
3133        let tw7: Complex<f64> = twiddles::compute_twiddle(7, 32, direction);
3134        let tw9: Complex<f64> = twiddles::compute_twiddle(9, 32, direction);
3135        let tw10: Complex<f64> = twiddles::compute_twiddle(10, 32, direction);
3136        let tw14: Complex<f64> = twiddles::compute_twiddle(14, 32, direction);
3137        let tw15: Complex<f64> = twiddles::compute_twiddle(15, 32, direction);
3138        let tw18: Complex<f64> = twiddles::compute_twiddle(18, 32, direction);
3139        let tw21: Complex<f64> = twiddles::compute_twiddle(21, 32, direction);
3140
3141        unsafe {
3142            Self {
3143                bf8: SseF64Butterfly8::new(direction),
3144                twiddle1: pack_64(tw1),
3145                twiddle2: pack_64(tw2),
3146                twiddle3: pack_64(tw3),
3147                twiddle5: pack_64(tw5),
3148                twiddle6: pack_64(tw6),
3149                twiddle7: pack_64(tw7),
3150                twiddle9: pack_64(tw9),
3151                twiddle10: pack_64(tw10),
3152                twiddle14: pack_64(tw14),
3153                twiddle15: pack_64(tw15),
3154                twiddle18: pack_64(tw18),
3155                twiddle21: pack_64(tw21),
3156            }
3157        }
3158    }
3159
3160    #[inline(always)]
3161    unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
3162        // To make the best possible use of registers, we're going to write this algorithm in an unusual way
3163        // It's 8x4 mixed radix, so we're going to do the usual steps of size-4 FFTs down the columns, apply twiddle factors, then transpose and do size-8 FFTs
3164        // But to reduce the number of times registers get spilled, we have these optimizations:
3165        // 1: Load data as late as possible, not upfront
3166        // 2: Once we're working with a piece of data, make as much progress as possible before moving on
3167        //      IE, once we load a column, we should do the FFT down the column, do twiddle factors, and do the pieces of the transpose for that column, all before starting on the next column
3168        // 3: Store data as soon as we're finished with it, rather than waiting for the end
3169        let load = |i| {
3170            [
3171                buffer.load_complex(i),
3172                buffer.load_complex(i + 8),
3173                buffer.load_complex(i + 16),
3174                buffer.load_complex(i + 24),
3175            ]
3176        };
3177
3178        // For each column: load the data, apply our size-4 FFT, apply twiddle factors
3179        let mut tmp1 = self.bf8.bf4.perform_fft_direct(load(1));
3180        tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
3181        tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
3182        tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
3183
3184        let mut tmp2 = self.bf8.bf4.perform_fft_direct(load(2));
3185        tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
3186        tmp2[2] = self.bf8.bf4.rotate.rotate_45(tmp2[2]);
3187        tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddle6);
3188
3189        let mut tmp3 = self.bf8.bf4.perform_fft_direct(load(3));
3190        tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
3191        tmp3[2] = SseVector::mul_complex(tmp3[2], self.twiddle6);
3192        tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
3193
3194        let mut tmp5 = self.bf8.bf4.perform_fft_direct(load(5));
3195        tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
3196        tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
3197        tmp5[3] = SseVector::mul_complex(tmp5[3], self.twiddle15);
3198
3199        let mut tmp6 = self.bf8.bf4.perform_fft_direct(load(6));
3200        tmp6[1] = SseVector::mul_complex(tmp6[1], self.twiddle6);
3201        tmp6[2] = self.bf8.bf4.rotate.rotate_135(tmp6[2]);
3202        tmp6[3] = SseVector::mul_complex(tmp6[3], self.twiddle18);
3203
3204        let mut tmp7 = self.bf8.bf4.perform_fft_direct(load(7));
3205        tmp7[1] = SseVector::mul_complex(tmp7[1], self.twiddle7);
3206        tmp7[2] = SseVector::mul_complex(tmp7[2], self.twiddle14);
3207        tmp7[3] = SseVector::mul_complex(tmp7[3], self.twiddle21);
3208
3209        let mut tmp4 = self.bf8.bf4.perform_fft_direct(load(4));
3210        tmp4[1] = self.bf8.bf4.rotate.rotate_45(tmp4[1]);
3211        tmp4[2] = self.bf8.bf4.rotate.rotate(tmp4[2]);
3212        tmp4[3] = self.bf8.bf4.rotate.rotate_135(tmp4[3]);
3213
3214        // Do the first column last, because no twiddles means fewer temporaries forcing the above data to spill
3215        let tmp0 = self.bf8.bf4.perform_fft_direct(load(0));
3216
3217        ////////////////////////////////////////////////////////////
3218        let mut store = |i, vectors: [__m128d; 8]| {
3219            buffer.store_complex(vectors[0], i);
3220            buffer.store_complex(vectors[1], i + 4);
3221            buffer.store_complex(vectors[2], i + 8);
3222            buffer.store_complex(vectors[3], i + 12);
3223            buffer.store_complex(vectors[4], i + 16);
3224            buffer.store_complex(vectors[5], i + 20);
3225            buffer.store_complex(vectors[6], i + 24);
3226            buffer.store_complex(vectors[7], i + 28);
3227        };
3228
3229        // Size-8 FFTs down each of our transposed columns, storing them as soon as we're done with them
3230        let out0 = self.bf8.perform_fft_direct([
3231            tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0], tmp6[0], tmp7[0],
3232        ]);
3233        store(0, out0);
3234
3235        let out1 = self.bf8.perform_fft_direct([
3236            tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1], tmp6[1], tmp7[1],
3237        ]);
3238        store(1, out1);
3239
3240        let out2 = self.bf8.perform_fft_direct([
3241            tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2], tmp6[2], tmp7[2],
3242        ]);
3243        store(2, out2);
3244
3245        let out3 = self.bf8.perform_fft_direct([
3246            tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3], tmp6[3], tmp7[3],
3247        ]);
3248        store(3, out3);
3249    }
3250}
3251
3252#[cfg(test)]
3253mod unit_tests {
3254    use super::*;
3255    use crate::{
3256        algorithm::Dft,
3257        test_utils::{check_fft_algorithm, compare_vectors},
3258    };
3259
3260    //the tests for all butterflies will be identical except for the identifiers used and size
3261    //so it's ideal for a macro
3262    macro_rules! test_butterfly_32_func {
3263        ($test_name:ident, $struct_name:ident, $size:expr) => {
3264            #[test]
3265            fn $test_name() {
3266                let butterfly = $struct_name::new(FftDirection::Forward);
3267                check_fft_algorithm::<f32>(&butterfly, $size, FftDirection::Forward);
3268
3269                let butterfly_direction = $struct_name::new(FftDirection::Inverse);
3270                check_fft_algorithm::<f32>(&butterfly_direction, $size, FftDirection::Inverse);
3271            }
3272        };
3273    }
3274    test_butterfly_32_func!(test_ssef32_butterfly1, SseF32Butterfly1, 1);
3275    test_butterfly_32_func!(test_ssef32_butterfly2, SseF32Butterfly2, 2);
3276    test_butterfly_32_func!(test_ssef32_butterfly3, SseF32Butterfly3, 3);
3277    test_butterfly_32_func!(test_ssef32_butterfly4, SseF32Butterfly4, 4);
3278    test_butterfly_32_func!(test_ssef32_butterfly5, SseF32Butterfly5, 5);
3279    test_butterfly_32_func!(test_ssef32_butterfly6, SseF32Butterfly6, 6);
3280    test_butterfly_32_func!(test_ssef32_butterfly8, SseF32Butterfly8, 8);
3281    test_butterfly_32_func!(test_ssef32_butterfly9, SseF32Butterfly9, 9);
3282    test_butterfly_32_func!(test_ssef32_butterfly10, SseF32Butterfly10, 10);
3283    test_butterfly_32_func!(test_ssef32_butterfly12, SseF32Butterfly12, 12);
3284    test_butterfly_32_func!(test_ssef32_butterfly15, SseF32Butterfly15, 15);
3285    test_butterfly_32_func!(test_ssef32_butterfly16, SseF32Butterfly16, 16);
3286    test_butterfly_32_func!(test_ssef32_butterfly24, SseF32Butterfly24, 24);
3287    test_butterfly_32_func!(test_ssef32_butterfly32, SseF32Butterfly32, 32);
3288
3289    //the tests for all butterflies will be identical except for the identifiers used and size
3290    //so it's ideal for a macro
3291    macro_rules! test_butterfly_64_func {
3292        ($test_name:ident, $struct_name:ident, $size:expr) => {
3293            #[test]
3294            fn $test_name() {
3295                let butterfly = $struct_name::new(FftDirection::Forward);
3296                check_fft_algorithm::<f64>(&butterfly, $size, FftDirection::Forward);
3297
3298                let butterfly_direction = $struct_name::new(FftDirection::Inverse);
3299                check_fft_algorithm::<f64>(&butterfly_direction, $size, FftDirection::Inverse);
3300            }
3301        };
3302    }
3303    test_butterfly_64_func!(test_ssef64_butterfly1, SseF64Butterfly1, 1);
3304    test_butterfly_64_func!(test_ssef64_butterfly2, SseF64Butterfly2, 2);
3305    test_butterfly_64_func!(test_ssef64_butterfly3, SseF64Butterfly3, 3);
3306    test_butterfly_64_func!(test_ssef64_butterfly4, SseF64Butterfly4, 4);
3307    test_butterfly_64_func!(test_ssef64_butterfly5, SseF64Butterfly5, 5);
3308    test_butterfly_64_func!(test_ssef64_butterfly6, SseF64Butterfly6, 6);
3309    test_butterfly_64_func!(test_ssef64_butterfly8, SseF64Butterfly8, 8);
3310    test_butterfly_64_func!(test_ssef64_butterfly9, SseF64Butterfly9, 9);
3311    test_butterfly_64_func!(test_ssef64_butterfly10, SseF64Butterfly10, 10);
3312    test_butterfly_64_func!(test_ssef64_butterfly12, SseF64Butterfly12, 12);
3313    test_butterfly_64_func!(test_ssef64_butterfly15, SseF64Butterfly15, 15);
3314    test_butterfly_64_func!(test_ssef64_butterfly16, SseF64Butterfly16, 16);
3315    test_butterfly_64_func!(test_ssef64_butterfly24, SseF64Butterfly24, 24);
3316    test_butterfly_64_func!(test_ssef64_butterfly32, SseF64Butterfly32, 32);
3317
3318    #[test]
3319    fn test_parallel_fft4_32() {
3320        unsafe {
3321            let val_a1 = Complex::<f32>::new(1.0, 2.5);
3322            let val_a2 = Complex::<f32>::new(3.2, 4.2);
3323            let val_a3 = Complex::<f32>::new(5.6, 6.2);
3324            let val_a4 = Complex::<f32>::new(7.4, 8.3);
3325
3326            let val_b1 = Complex::<f32>::new(6.0, 24.5);
3327            let val_b2 = Complex::<f32>::new(4.2, 34.2);
3328            let val_b3 = Complex::<f32>::new(9.6, 61.2);
3329            let val_b4 = Complex::<f32>::new(17.4, 81.3);
3330
3331            let p1 = _mm_set_ps(val_b1.im, val_b1.re, val_a1.im, val_a1.re);
3332            let p2 = _mm_set_ps(val_b2.im, val_b2.re, val_a2.im, val_a2.re);
3333            let p3 = _mm_set_ps(val_b3.im, val_b3.re, val_a3.im, val_a3.re);
3334            let p4 = _mm_set_ps(val_b4.im, val_b4.re, val_a4.im, val_a4.re);
3335
3336            let mut val_a = vec![val_a1, val_a2, val_a3, val_a4];
3337            let mut val_b = vec![val_b1, val_b2, val_b3, val_b4];
3338
3339            let dft = Dft::new(4, FftDirection::Forward);
3340
3341            let bf4 = SseF32Butterfly4::<f32>::new(FftDirection::Forward);
3342
3343            dft.process(&mut val_a);
3344            dft.process(&mut val_b);
3345            let res_both = bf4.perform_parallel_fft_direct([p1, p2, p3, p4]);
3346
3347            let res = std::mem::transmute::<[__m128; 4], [Complex<f32>; 8]>(res_both);
3348            let sse_res_a = [res[0], res[2], res[4], res[6]];
3349            let sse_res_b = [res[1], res[3], res[5], res[7]];
3350            assert!(compare_vectors(&val_a, &sse_res_a));
3351            assert!(compare_vectors(&val_b, &sse_res_b));
3352        }
3353    }
3354
3355    #[test]
3356    fn test_pack() {
3357        unsafe {
3358            let nbr2 = _mm_set_ps(8.0, 7.0, 6.0, 5.0);
3359            let nbr1 = _mm_set_ps(4.0, 3.0, 2.0, 1.0);
3360            let first = extract_lo_lo_f32(nbr1, nbr2);
3361            let second = extract_hi_hi_f32(nbr1, nbr2);
3362            let first = std::mem::transmute::<__m128, [Complex<f32>; 2]>(first);
3363            let second = std::mem::transmute::<__m128, [Complex<f32>; 2]>(second);
3364            let first_expected = [Complex::new(1.0, 2.0), Complex::new(5.0, 6.0)];
3365            let second_expected = [Complex::new(3.0, 4.0), Complex::new(7.0, 8.0)];
3366            assert_eq!(first, first_expected);
3367            assert_eq!(second, second_expected);
3368        }
3369    }
3370}