1#![allow(
30 clippy::assign_op_pattern,
31 clippy::only_used_in_recursion,
32 clippy::too_many_arguments,
33 clippy::type_complexity,
34 clippy::modulo_one
35)]
36#![cfg_attr(docsrs, feature(doc_cfg))]
37#![cfg_attr(
38 all(feature = "fcma", target_arch = "aarch64"),
39 feature(stdarch_neon_fcma)
40)]
41#[cfg(all(target_arch = "x86_64", feature = "avx"))]
42mod avx;
43mod bluestein;
44mod butterflies;
45mod complex_fma;
46mod dft;
47mod err;
48mod factory;
49mod factory64;
50mod fast_divider;
51mod good_thomas;
52mod good_thomas_small;
53mod mixed_radix;
54mod mla;
55#[cfg(all(target_arch = "aarch64", feature = "neon"))]
56mod neon;
57mod prime_factors;
58mod r2c;
59mod raders;
60mod radix10;
61mod radix11;
62mod radix13;
63mod radix3;
64mod radix4;
65mod radix5;
66mod radix6;
67mod radix7;
68mod spectrum_arithmetic;
69mod store;
70mod td;
71mod traits;
72mod transpose;
73mod transpose_arbitrary;
74mod util;
75
76#[allow(unused_imports)]
77use radix3::Radix3;
78#[allow(unused_imports)]
79use radix4::Radix4;
80#[allow(unused_imports)]
81use radix5::Radix5;
82#[allow(unused_imports)]
83use radix6::Radix6;
84#[allow(unused_imports)]
85use radix7::Radix7;
86#[allow(unused_imports)]
87use radix10::Radix10;
88#[allow(unused_imports)]
89use radix11::Radix11;
90#[allow(unused_imports)]
91use radix13::Radix13;
92use std::collections::HashMap;
93
94use crate::factory::AlgorithmFactory;
95use crate::prime_factors::{
96 PrimeFactors, can_be_two_factors, split_factors_closest, try_greedy_pure_power_split,
97};
98use crate::r2c::{
99 C2RFftEvenInterceptor, C2RFftOddInterceptor, OneSizedRealFft, R2CTwiddlesFactory,
100 R2cAlgorithmFactory, strategy_r2c,
101};
102use crate::spectrum_arithmetic::ComplexArithFactory;
103use crate::td::{TwoDimensionalC2C, TwoDimensionalC2R, TwoDimensionalR2C};
104use crate::traits::FftTrigonometry;
105use crate::transpose::TransposeFactory;
106use crate::util::{
107 ALWAYS_BLUESTEIN_1000, ALWAYS_BLUESTEIN_2000, ALWAYS_BLUESTEIN_3000, ALWAYS_BLUESTEIN_4000,
108 ALWAYS_BLUESTEIN_5000, ALWAYS_BLUESTEIN_6000,
109};
110pub use err::ZaftError;
111use num_complex::Complex;
112use num_traits::{AsPrimitive, Float, MulAdd};
113pub use r2c::{C2RFftExecutor, R2CFftExecutor};
114use std::fmt::{Debug, Display, Formatter};
115use std::sync::{Arc, OnceLock, RwLock};
116pub use td::{TwoDimensionalExecutorC2R, TwoDimensionalExecutorR2C, TwoDimensionalFftExecutor};
117
118pub(crate) trait FftSample:
119 AlgorithmFactory<Self>
120 + FftTrigonometry
121 + Float
122 + 'static
123 + Send
124 + Sync
125 + MulAdd<Self, Output = Self>
126 + ComplexArithFactory<Self>
127 + TransposeFactory<Self>
128 + Copy
129 + Display
130 + FftPrimeCache<Self>
131 + Default
132 + Debug
133 + R2cAlgorithmFactory<Self>
134 + R2CTwiddlesFactory<Self>
135{
136 const HALF: Self;
137 const SQRT_3_OVER_2: Self;
138}
139
140impl FftSample for f64 {
141 const HALF: Self = 0.5;
142 const SQRT_3_OVER_2: Self = f64::from_bits(0x3febb67ae8584caa);
161}
162impl FftSample for f32 {
163 const HALF: Self = 0.5;
164 const SQRT_3_OVER_2: Self = f32::from_bits(0x3f5db3d7);
183}
184
185pub trait FftExecutor<T> {
186 fn execute(&self, in_place: &mut [Complex<T>]) -> Result<(), ZaftError>;
200 fn execute_with_scratch(
205 &self,
206 in_place: &mut [Complex<T>],
207 scratch: &mut [Complex<T>],
208 ) -> Result<(), ZaftError>;
209 fn execute_out_of_place(
214 &self,
215 src: &[Complex<T>],
216 dst: &mut [Complex<T>],
217 ) -> Result<(), ZaftError>;
218 fn execute_out_of_place_with_scratch(
223 &self,
224 src: &[Complex<T>],
225 dst: &mut [Complex<T>],
226 scratch: &mut [Complex<T>],
227 ) -> Result<(), ZaftError>;
228 fn execute_destructive_with_scratch(
236 &self,
237 src: &mut [Complex<T>],
238 dst: &mut [Complex<T>],
239 scratch: &mut [Complex<T>],
240 ) -> Result<(), ZaftError>;
241 fn direction(&self) -> FftDirection;
246 fn length(&self) -> usize;
250 fn scratch_length(&self) -> usize;
256 fn out_of_place_scratch_length(&self) -> usize;
262 fn destructive_scratch_length(&self) -> usize;
268}
269
270static PRIME_CACHE_F: OnceLock<RwLock<HashMap<usize, Arc<dyn FftExecutor<f32> + Send + Sync>>>> =
271 OnceLock::new();
272
273static PRIME_CACHE_B: OnceLock<RwLock<HashMap<usize, Arc<dyn FftExecutor<f32> + Send + Sync>>>> =
274 OnceLock::new();
275
276static PRIME_CACHE_DF: OnceLock<RwLock<HashMap<usize, Arc<dyn FftExecutor<f64> + Send + Sync>>>> =
277 OnceLock::new();
278
279static PRIME_CACHE_DB: OnceLock<RwLock<HashMap<usize, Arc<dyn FftExecutor<f64> + Send + Sync>>>> =
280 OnceLock::new();
281
282pub(crate) trait FftPrimeCache<T> {
283 fn has_cached_prime(
284 n: usize,
285 fft_direction: FftDirection,
286 ) -> Option<Arc<dyn FftExecutor<T> + Send + Sync>>;
287 fn put_prime_to_cache(fft_direction: FftDirection, fft: Arc<dyn FftExecutor<T> + Send + Sync>);
288}
289
290impl FftPrimeCache<f32> for f32 {
291 fn has_cached_prime(
292 n: usize,
293 fft_direction: FftDirection,
294 ) -> Option<Arc<dyn FftExecutor<f32> + Send + Sync>> {
295 if n >= 4000 {
296 return None;
297 }
298 let cache = (match fft_direction {
299 FftDirection::Forward => &PRIME_CACHE_F,
300 FftDirection::Inverse => &PRIME_CACHE_B,
301 })
302 .get_or_init(|| RwLock::new(HashMap::new()));
303 cache.read().ok()?.get(&n).cloned()
304 }
305
306 fn put_prime_to_cache(
307 fft_direction: FftDirection,
308 fft: Arc<dyn FftExecutor<f32> + Send + Sync>,
309 ) {
310 let length = fft.length();
311 if length > 4000 {
312 return;
313 }
314 let cache = (match fft_direction {
315 FftDirection::Forward => &PRIME_CACHE_F,
316 FftDirection::Inverse => &PRIME_CACHE_B,
317 })
318 .get_or_init(|| RwLock::new(HashMap::new()));
319 _ = cache.write().ok().and_then(|mut x| x.insert(length, fft));
320 }
321}
322
323impl FftPrimeCache<f64> for f64 {
324 fn has_cached_prime(
325 n: usize,
326 fft_direction: FftDirection,
327 ) -> Option<Arc<dyn FftExecutor<f64> + Send + Sync>> {
328 if n >= 4000 {
329 return None;
330 }
331 let cache = (match fft_direction {
332 FftDirection::Forward => &PRIME_CACHE_DF,
333 FftDirection::Inverse => &PRIME_CACHE_DB,
334 })
335 .get_or_init(|| RwLock::new(HashMap::new()));
336 cache.read().ok()?.get(&n).cloned()
337 }
338
339 fn put_prime_to_cache(
340 fft_direction: FftDirection,
341 fft: Arc<dyn FftExecutor<f64> + Send + Sync>,
342 ) {
343 let length = fft.length();
344 if length > 4000 {
345 return;
346 }
347 let cache = (match fft_direction {
348 FftDirection::Forward => &PRIME_CACHE_DF,
349 FftDirection::Inverse => &PRIME_CACHE_DB,
350 })
351 .get_or_init(|| RwLock::new(HashMap::new()));
352 _ = cache.write().ok().and_then(|mut x| x.insert(length, fft));
353 }
354}
355
356pub struct Zaft {}
357
358impl Zaft {
359 fn could_do_split_mixed_radix() -> bool {
360 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
361 if std::arch::is_x86_feature_detected!("avx2") && std::arch::is_x86_feature_detected!("fma")
362 {
363 return true;
364 }
365 #[cfg(all(target_arch = "aarch64", feature = "neon"))]
366 {
367 true
368 }
369 #[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
370 {
371 false
372 }
373 }
374
375 fn try_split_mixed_radix_butterflies<T: FftSample>(
376 _n_length: u64,
377 _q_length: u64,
378 _direction: FftDirection,
379 ) -> Result<Option<Arc<dyn FftExecutor<T> + Send + Sync>>, ZaftError>
380 where
381 f64: AsPrimitive<T>,
382 {
383 #[cfg(not(any(
384 all(target_arch = "aarch64", feature = "neon"),
385 all(target_arch = "x86_64", feature = "avx")
386 )))]
387 {
388 Ok(None)
389 }
390 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
391 if !std::arch::is_x86_feature_detected!("avx2")
392 || !std::arch::is_x86_feature_detected!("fma")
393 {
394 return Ok(None);
395 }
396 #[cfg(any(
397 all(target_arch = "aarch64", feature = "neon"),
398 all(target_arch = "x86_64", feature = "avx")
399 ))]
400 {
401 let min_length = _n_length.min(_q_length);
402 let max_length = _n_length.max(_q_length);
403
404 if !(2..=16).contains(&min_length) {
405 return Ok(None);
407 }
408
409 let q_fft = Zaft::strategy(max_length as usize, _direction)?;
411
412 let q_fft_opt = match min_length {
413 2 => T::mixed_radix_butterfly2(q_fft),
414 3 => T::mixed_radix_butterfly3(q_fft),
415 4 => T::mixed_radix_butterfly4(q_fft),
416 5 => T::mixed_radix_butterfly5(q_fft),
417 6 => T::mixed_radix_butterfly6(q_fft),
418 7 => T::mixed_radix_butterfly7(q_fft),
419 8 => T::mixed_radix_butterfly8(q_fft),
420 9 => T::mixed_radix_butterfly9(q_fft),
421 10 => T::mixed_radix_butterfly10(q_fft),
422 11 => T::mixed_radix_butterfly11(q_fft),
423 12 => T::mixed_radix_butterfly12(q_fft),
424 13 => T::mixed_radix_butterfly13(q_fft),
425 14 => T::mixed_radix_butterfly14(q_fft),
426 15 => T::mixed_radix_butterfly15(q_fft),
427 16 => T::mixed_radix_butterfly16(q_fft),
428 _ => unreachable!("min_length is outside the supported range [2, 16]."),
430 };
431
432 if let Some(q_fft_opt) = q_fft_opt? {
434 Ok(Some(q_fft_opt))
435 } else {
436 Ok(None)
437 }
438 }
439 }
440
441 fn make_mixed_radix<T: FftSample>(
442 direction: FftDirection,
443 prime_factors: PrimeFactors,
444 ) -> Result<Arc<dyn FftExecutor<T> + Send + Sync>, ZaftError>
445 where
446 f64: AsPrimitive<T>,
447 {
448 let factorization = &prime_factors.factorization;
449 let product = factorization.iter().map(|&x| x.0.pow(x.1)).product::<u64>();
450
451 let (n_length, q_length) = if product <= 529 {
452 match can_be_two_factors(factorization) {
453 None => match try_greedy_pure_power_split(factorization) {
454 None => split_factors_closest(factorization),
455 Some(values) => values,
456 },
457 Some(factors) => factors,
458 }
459 } else {
460 match try_greedy_pure_power_split(factorization) {
461 None => split_factors_closest(factorization),
462 Some(values) => values,
463 }
464 };
465
466 macro_rules! try_mixed_radix {
467 ($q: expr, $p: expr) => {{
468 return if let Some(executor) =
469 Zaft::try_split_mixed_radix_butterflies($q as u64, $p as u64, direction)?
470 {
471 Ok(executor)
472 } else {
473 let p_fft = Zaft::strategy($q as usize, direction)?;
474 let q_fft = Zaft::strategy($p as usize, direction)?;
475 if $q < $p {
476 T::mixed_radix(p_fft, q_fft)
477 } else {
478 T::mixed_radix(q_fft, p_fft)
479 }
480 };
481 }};
482 }
483
484 if prime_factors.is_power_of_two_and_three() {
485 let product2 = prime_factors
486 .factorization
487 .iter()
488 .find(|x| x.0 == 2)
489 .map(|x| x.0.pow(x.1))
490 .expect("Factor of 2 must present in 2^n*3^m branch");
491 let product3 = prime_factors
492 .factorization
493 .iter()
494 .find(|x| x.0 == 3)
495 .map(|x| x.0.pow(x.1))
496 .expect("Factor of 3 must present in 2^n*3^m branch");
497
498 let factor2 = prime_factors.factor_of_2();
499 let factor3 = prime_factors.factor_of_3();
500
501 if factor2 == 1 && factor3 > 3 && T::butterfly54(direction).is_some() {
502 try_mixed_radix!(54, product / 54)
503 }
504
505 if product == 1536 {
506 try_mixed_radix!(8, 192)
507 }
508
509 if factor3 >= 1 && factor2 >= 4 {
510 if product.is_multiple_of(36)
511 && product / 36 > 1
512 && product / 36 <= 16
513 && T::butterfly36(direction).is_some()
514 {
515 try_mixed_radix!(36, product / 36)
516 }
517 if factor2 > factor3 {
518 let mut factors_diff = 2u64.pow(factor2 - factor3);
519 let mut remainder_factor = product / factors_diff;
520 if remainder_factor <= 8 {
521 remainder_factor *= 2;
522 factors_diff /= 2;
523 }
524 try_mixed_radix!(factors_diff, remainder_factor)
525 }
526 if product.is_multiple_of(48)
527 && product / 48 > 1
528 && product / 48 <= 16
529 && T::butterfly48(direction).is_some()
530 {
531 try_mixed_radix!(48, product / 48)
532 }
533 }
534
535 return if let Some(executor) =
536 Zaft::try_split_mixed_radix_butterflies(product2, product3, direction)?
537 {
538 Ok(executor)
539 } else {
540 let p_fft = Zaft::strategy(product2 as usize, direction)?;
541 let q_fft = Zaft::strategy(product3 as usize, direction)?;
542 T::mixed_radix(p_fft, q_fft)
543 };
544 } else if prime_factors.is_power_of_two_and_five() {
545 let factor_of_5 = prime_factors.factor_of_5();
546 let factor_of_2 = prime_factors.factor_of_2();
547 if factor_of_5 == 1 {
548 if factor_of_2 >= 8 && factor_of_2 != 9 {
549 try_mixed_radix!(5, product / 5)
550 }
551 if (2..=6).contains(&factor_of_2) {
552 try_mixed_radix!(20, product / 20)
553 }
554 } else if factor_of_5 == 2 {
555 if factor_of_2 >= 3 && T::butterfly100(direction).is_some() {
556 #[cfg(all(target_arch = "aarch64", feature = "neon"))]
557 {
558 try_mixed_radix!(product / 100, 100)
559 }
560 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
561 {
562 use crate::util::has_valid_avx;
563 if has_valid_avx() {
564 try_mixed_radix!(product / 100, 100)
565 }
566 }
567 }
568 } else if factor_of_5 == 3 {
569 #[cfg(any(
570 all(target_arch = "aarch64", feature = "neon"),
571 all(target_arch = "x86_64", feature = "avx")
572 ))]
573 if product == 500 {
574 try_mixed_radix!(5, 100)
575 }
576 }
577 } else if prime_factors.is_power_of_three_and_five() {
578 let factor_of_5 = prime_factors.factor_of_5();
579 let factor_of_3 = prime_factors.factor_of_3();
580 if factor_of_5 == 1 && factor_of_3 > 1 {
581 try_mixed_radix!(5, product / 5)
582 } else if factor_of_5 == 2 && factor_of_5 > 1 && factor_of_3 > 2 && factor_of_3 < 10 {
583 try_mixed_radix!(25, product / 25)
585 }
586 } else if prime_factors.is_power_of_two_and_seven() {
587 let factor_of_7 = prime_factors.factor_of_7();
588 let factor_of_2 = prime_factors.factor_of_2();
589 if factor_of_2 > 1 && factor_of_7 == 1 {
590 try_mixed_radix!(14, product / 14)
591 }
592 } else if prime_factors.has_power_of_five_and_seven() {
593 let factor_of_7 = prime_factors.factor_of_7();
594 let factor_of_5 = prime_factors.factor_of_5();
595 #[allow(clippy::collapsible_if)]
596 if factor_of_7 == 1 || factor_of_5 == 1 {
597 if product == 560 || product == 2240 {
598 if T::butterfly35(direction).is_some() {
599 try_mixed_radix!(35, product / 35)
600 }
601 }
602 }
603 if (product == 210
604 || product == 280
605 || product == 315
606 || product == 350
607 || product == 420)
608 && T::butterfly35(direction).is_some()
609 {
610 try_mixed_radix!(35, product / 35)
611 }
612 if prime_factors.is_power_of_five_and_seven() && (factor_of_7 > 2 && factor_of_5 > 2) {
613 let product7 = prime_factors
614 .factorization
615 .iter()
616 .find(|x| x.0 == 7)
617 .map(|x| x.0.pow(x.1))
618 .expect("Power of 7 should exist if factor of 5^n*7^m branch");
619 let product5 = prime_factors
620 .factorization
621 .iter()
622 .find(|x| x.0 == 5)
623 .map(|x| x.0.pow(x.1))
624 .expect("Power of 5 should exist if factor of 5^n*7^m branch");
625 let p_fft = Zaft::strategy(product5 as usize, direction)?;
626 let q_fft = Zaft::strategy(product7 as usize, direction)?;
627 return if product5 < product7 {
628 T::mixed_radix(p_fft, q_fft)
629 } else {
630 T::mixed_radix(q_fft, p_fft)
631 };
632 }
633 } else if prime_factors.has_power_of_two_and_three() {
634 #[allow(clippy::collapsible_if)]
635 if (product == 84
636 || product == 294
637 || product == 252
638 || product == 378
639 || product == 504
640 || product == 672
641 || product == 756)
642 && T::butterfly42(direction).is_some()
643 {
644 try_mixed_radix!(42, product / 42)
645 }
646 let factor_of_2 = prime_factors.factor_of_2();
647 let factor_of_3 = prime_factors.factor_of_3();
648 let factor_of_5 = prime_factors.factor_of_5();
649
650 if (factor_of_2 == 1 && factor_of_3 == 1 && factor_of_5 > 1)
651 && T::butterfly30(direction).is_some()
652 {
653 try_mixed_radix!(30, product / 30)
655 }
656
657 if ((product.is_multiple_of(144) && (product / 144) <= 16)
658 || product == 5040
659 || product == 4896
660 || product == 8496
661 || product == 8352)
662 && T::butterfly144(direction).is_some()
663 {
664 try_mixed_radix!(product / 144, 144)
666 }
667
668 if ((product.is_multiple_of(72) && (product / 72) <= 16)
669 || product == 2088
670 || product == 3240
671 || product == 3816
672 || product == 4248)
673 && T::butterfly72(direction).is_some()
674 {
675 try_mixed_radix!(product / 72, 72)
677 }
678
679 if product == 858 && T::butterfly66(direction).is_some() {
680 try_mixed_radix!(66, product / 66)
682 }
683 }
684
685 if product.is_multiple_of(63)
686 && product / 63 <= 16
687 && product != 126
688 && T::butterfly64(direction).is_some()
689 {
690 try_mixed_radix!(63, product / 63)
692 }
693
694 if (product == 147 || product == 315 || product == 378 || product == 399)
695 && T::butterfly21(direction).is_some()
696 {
697 try_mixed_radix!(21, product / 21)
698 }
699
700 #[cfg(any(
701 all(target_arch = "aarch64", feature = "neon"),
702 all(target_arch = "x86_64", feature = "avx")
703 ))]
704 {
705 macro_rules! get_mixed_butterflies {
706 ($q: expr, $p: expr) => {{
707 if let Some(executor) =
708 Zaft::try_split_mixed_radix_butterflies($q as u64, $p as u64, direction)?
709 {
710 return Ok(executor);
711 }
712 }};
713 }
714 let factor_2 = prime_factors.factor_of_2();
715 let rem2_8 = factor_2 % 3;
716 if product.is_multiple_of(10) {
717 get_mixed_butterflies!(10, product / 10)
718 }
719 if factor_2 > 0 {
720 if rem2_8 == 1 {
721 get_mixed_butterflies!(2, product / 2)
722 }
723 if rem2_8 == 2 {
724 get_mixed_butterflies!(4, product / 4)
725 }
726 get_mixed_butterflies!(8, product / 8)
727 }
728 if product.is_multiple_of(14) {
729 get_mixed_butterflies!(14, product / 14)
730 }
731 if product.is_multiple_of(15) {
732 get_mixed_butterflies!(15, product / 15)
733 }
734 if product.is_multiple_of(12) {
735 get_mixed_butterflies!(12, product / 12)
736 }
737 if product.is_multiple_of(9) {
738 get_mixed_butterflies!(9, product / 9)
739 }
740 if product.is_multiple_of(7) {
741 get_mixed_butterflies!(7, product / 7)
742 }
743 if product.is_multiple_of(13) {
744 get_mixed_butterflies!(13, product / 13)
745 }
746 if product.is_multiple_of(11) {
747 get_mixed_butterflies!(11, product / 11)
748 }
749 if product.is_multiple_of(5) {
750 get_mixed_butterflies!(5, product / 5)
751 }
752 if product.is_multiple_of(3) {
753 get_mixed_butterflies!(3, product / 3)
754 }
755 match Zaft::try_split_mixed_radix_butterflies(n_length, q_length, direction) {
756 Ok(value) => match value {
757 None => {}
758 Some(executor) => return Ok(executor),
759 },
760 Err(err) => return Err(err),
761 }
762 }
763
764 let p_fft = Zaft::strategy(n_length as usize, direction)?;
765 let q_fft = Zaft::strategy(q_length as usize, direction)?;
766 if num_integer::gcd(q_length, n_length) == 1 && q_length < 33 && n_length <= 33 {
767 T::good_thomas(p_fft, q_fft)
768 } else {
769 T::mixed_radix(p_fft, q_fft)
770 }
771 }
772
773 fn make_prime<T: FftSample>(
774 n: usize,
775 direction: FftDirection,
776 ) -> Result<Arc<dyn FftExecutor<T> + Send + Sync>, ZaftError>
777 where
778 f64: AsPrimitive<T>,
779 {
780 if let Some(cached) = T::has_cached_prime(n, direction) {
781 return Ok(cached);
782 }
783 let convolve_prime = PrimeFactors::from_number(n as u64 - 1);
784 if n <= 6000 {
785 let bluesteins = [
786 ALWAYS_BLUESTEIN_1000.as_slice(),
787 ALWAYS_BLUESTEIN_2000.as_slice(),
788 ALWAYS_BLUESTEIN_3000.as_slice(),
789 ALWAYS_BLUESTEIN_4000.as_slice(),
790 ALWAYS_BLUESTEIN_5000.as_slice(),
791 ALWAYS_BLUESTEIN_6000.as_slice(),
792 ];
793 let subset = bluesteins[n / 1000];
794 if subset.contains(&n) {
795 return Zaft::make_bluestein(n, direction);
796 }
797 return Zaft::make_raders(n, direction);
798 }
799 let big_factor = convolve_prime.factorization.iter().any(|x| x.0 > 31);
801 let new_prime = if !big_factor {
802 Zaft::make_raders(n, direction)
803 } else {
804 Zaft::make_bluestein(n, direction)
805 };
806 let fft_executor = new_prime?;
807 T::put_prime_to_cache(direction, fft_executor.clone());
808 Ok(fft_executor)
809 }
810
811 fn make_raders<T: FftSample>(
812 n: usize,
813 direction: FftDirection,
814 ) -> Result<Arc<dyn FftExecutor<T> + Send + Sync>, ZaftError>
815 where
816 f64: AsPrimitive<T>,
817 {
818 let convolve_fft = Zaft::strategy(n - 1, direction);
819 T::raders(convolve_fft?, n, direction)
820 }
821
822 fn make_bluestein<T: FftSample>(
823 n: usize,
824 direction: FftDirection,
825 ) -> Result<Arc<dyn FftExecutor<T> + Send + Sync>, ZaftError>
826 where
827 f64: AsPrimitive<T>,
828 {
829 let min_inner_len = 2 * n - 1;
834 let inner_len_pow2 = min_inner_len.checked_next_power_of_two().unwrap();
835 let inner_len_factor3 = inner_len_pow2 / 4 * 3;
836
837 let inner_len = if inner_len_factor3 >= min_inner_len {
838 inner_len_factor3
839 } else {
840 inner_len_pow2
841 };
842 let convolve_fft = Zaft::strategy(inner_len, direction)?;
843 T::bluestein(convolve_fft, n, direction)
844 }
845
846 fn plan_butterfly<T: FftSample>(
847 n: usize,
848 fft_direction: FftDirection,
849 ) -> Option<Result<Arc<dyn FftExecutor<T> + Send + Sync>, ZaftError>> {
850 match n {
851 1 => return Some(T::butterfly1(fft_direction)),
852 2 => return Some(T::butterfly2(fft_direction)),
853 3 => return Some(T::butterfly3(fft_direction)),
854 4 => return Some(T::butterfly4(fft_direction)),
855 5 => return Some(T::butterfly5(fft_direction)),
856 6 => return Some(T::butterfly6(fft_direction)),
857 7 => return Some(T::butterfly7(fft_direction)),
858 8 => return Some(T::butterfly8(fft_direction)),
859 9 => return Some(T::butterfly9(fft_direction)),
860 10 => return Some(T::butterfly10(fft_direction)),
861 11 => return Some(T::butterfly11(fft_direction)),
862 12 => return Some(T::butterfly12(fft_direction)),
863 13 => return Some(T::butterfly13(fft_direction)),
864 14 => return Some(T::butterfly14(fft_direction)),
865 15 => return Some(T::butterfly15(fft_direction)),
866 16 => return Some(T::butterfly16(fft_direction)),
867 17 => return Some(T::butterfly17(fft_direction)),
868 18 => return Some(T::butterfly18(fft_direction)),
869 19 => return Some(T::butterfly19(fft_direction)),
870 20 => return Some(T::butterfly20(fft_direction)),
871 21 => {
872 return T::butterfly21(fft_direction).map(Ok);
873 }
874 23 => return Some(T::butterfly23(fft_direction)),
875 24 => {
876 return T::butterfly24(fft_direction).map(Ok);
877 }
878 25 => return Some(T::butterfly25(fft_direction)),
879 27 => return Some(T::butterfly27(fft_direction)),
880 28 => return T::butterfly28(fft_direction).map(Ok),
881 29 => return Some(T::butterfly29(fft_direction)),
882 30 => {
883 return T::butterfly30(fft_direction).map(Ok);
884 }
885 31 => return Some(T::butterfly31(fft_direction)),
886 32 => return Some(T::butterfly32(fft_direction)),
887 35 => {
888 return T::butterfly35(fft_direction).map(Ok);
889 }
890 36 => {
891 return T::butterfly36(fft_direction).map(Ok);
892 }
893 37 => return Some(T::butterfly37(fft_direction)),
894 40 => {
895 return T::butterfly40(fft_direction).map(Ok);
896 }
897 41 => return Some(T::butterfly41(fft_direction)),
898 42 => {
899 return T::butterfly42(fft_direction).map(Ok);
900 }
901 48 => {
902 return T::butterfly48(fft_direction).map(Ok);
903 }
904 49 => {
905 return T::butterfly49(fft_direction).map(Ok);
906 }
907 54 => {
908 return T::butterfly54(fft_direction).map(Ok);
909 }
910 63 => {
911 return T::butterfly63(fft_direction).map(Ok);
912 }
913 64 => {
914 return T::butterfly64(fft_direction).map(Ok);
915 }
916 66 => {
917 return T::butterfly66(fft_direction).map(Ok);
918 }
919 70 => {
920 return T::butterfly70(fft_direction).map(Ok);
921 }
922 72 => {
923 return T::butterfly72(fft_direction).map(Ok);
924 }
925 78 => {
926 return T::butterfly78(fft_direction).map(Ok);
927 }
928 81 => {
929 return T::butterfly81(fft_direction).map(Ok);
930 }
931 88 => {
932 return T::butterfly88(fft_direction).map(Ok);
933 }
934 96 => {
935 return T::butterfly96(fft_direction).map(Ok);
936 }
937 100 => {
938 return T::butterfly100(fft_direction).map(Ok);
939 }
940 108 => {
941 return T::butterfly108(fft_direction).map(Ok);
942 }
943 121 => {
944 return T::butterfly121(fft_direction).map(Ok);
945 }
946 125 => {
947 return T::butterfly125(fft_direction).map(Ok);
948 }
949 128 => {
950 return T::butterfly128(fft_direction).map(Ok);
951 }
952 144 => {
953 return T::butterfly144(fft_direction).map(Ok);
954 }
955 169 => {
956 return T::butterfly169(fft_direction).map(Ok);
957 }
958 192 => {
959 return T::butterfly192(fft_direction).map(Ok);
960 }
961 216 => {
962 return T::butterfly216(fft_direction).map(Ok);
963 }
964 243 => {
965 return T::butterfly243(fft_direction).map(Ok);
966 }
967 256 => {
968 return T::butterfly256(fft_direction).map(Ok);
969 }
970 512 => {
971 return T::butterfly512(fft_direction).map(Ok);
972 }
973 1024 => {
974 return T::butterfly1024(fft_direction).map(Ok);
975 }
976 _ => {}
977 }
978 None
979 }
980
981 pub(crate) fn strategy<T: FftSample>(
982 n: usize,
983 fft_direction: FftDirection,
984 ) -> Result<Arc<dyn FftExecutor<T> + Send + Sync>, ZaftError>
985 where
986 f64: AsPrimitive<T>,
987 {
988 if n == 0 {
989 return Err(ZaftError::ZeroSizedFft);
990 }
991 if n <= 512 || n == 1024 {
992 if let Some(bf) = Zaft::plan_butterfly(n, fft_direction) {
993 return bf;
994 }
995 }
996 let prime_factors = PrimeFactors::from_number(n as u64);
997 if prime_factors.is_power_of_three {
998 T::radix3(n, fft_direction)
1000 } else if prime_factors.is_power_of_five {
1001 T::radix5(n, fft_direction)
1003 } else if prime_factors.is_power_of_two {
1004 if Zaft::could_do_split_mixed_radix() {
1006 if n == 2048 {
1007 if let Some(bf) =
1008 T::mixed_radix_butterfly8(Zaft::strategy(n / 8, fft_direction)?)?
1009 {
1010 return Ok(bf);
1011 }
1012 }
1013 let rem3 = prime_factors.factor_of_2() % 3;
1014 if rem3 == 2 {
1015 if let Some(bf) =
1016 T::mixed_radix_butterfly4(Zaft::strategy(n / 4, fft_direction)?)?
1017 {
1018 return Ok(bf);
1019 }
1020 } else if rem3 == 1 {
1021 let has1024 = T::butterfly1024(fft_direction).is_some();
1022 if has1024 {
1023 if let Some(bf) =
1024 T::mixed_radix_butterfly8(Zaft::strategy(n / 8, fft_direction)?)?
1025 {
1026 return Ok(bf);
1027 }
1028 }
1029 if let Some(bf) =
1030 T::mixed_radix_butterfly2(Zaft::strategy(n / 2, fft_direction)?)?
1031 {
1032 return Ok(bf);
1033 }
1034 }
1035 if let Some(bf) = T::mixed_radix_butterfly8(Zaft::strategy(n / 8, fft_direction)?)?
1036 {
1037 return Ok(bf);
1038 }
1039 }
1040 T::radix4(n, fft_direction)
1041 } else if prime_factors.is_power_of_six {
1042 T::radix6(n, fft_direction)
1043 } else if prime_factors.is_power_of_seven {
1044 T::radix7(n, fft_direction)
1045 } else if prime_factors.is_power_of_ten {
1046 T::radix10(n, fft_direction)
1047 } else if prime_factors.is_power_of_eleven {
1048 T::radix11(n, fft_direction)
1049 } else if prime_factors.is_power_of_thirteen {
1050 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
1051 {
1052 if Zaft::could_do_split_mixed_radix() {
1053 let r = n / 13;
1054 if r == 13 {
1055 let right_fft = T::butterfly13(fft_direction)?;
1056 if let Ok(Some(v)) = T::mixed_radix_butterfly13(right_fft) {
1057 return Ok(v);
1058 }
1059 }
1060 let right_fft = T::radix13(r, fft_direction)?;
1061 if let Ok(Some(v)) = T::mixed_radix_butterfly13(right_fft) {
1062 return Ok(v);
1063 }
1064 }
1065 }
1066 T::radix13(n, fft_direction)
1067 } else if prime_factors.may_be_represented_in_mixed_radix() {
1068 Zaft::make_mixed_radix(fft_direction, prime_factors)
1069 } else if prime_factors.is_prime() {
1070 Zaft::make_prime(n, fft_direction)
1071 } else {
1072 T::dft(n, fft_direction)
1073 }
1074 }
1075
1076 pub fn make_r2c_fft_f32(
1088 n: usize,
1089 ) -> Result<Arc<dyn R2CFftExecutor<f32> + Send + Sync>, ZaftError> {
1090 strategy_r2c(n)
1091 }
1092
1093 pub fn make_c2r_fft_f32(
1105 n: usize,
1106 ) -> Result<Arc<dyn C2RFftExecutor<f32> + Send + Sync>, ZaftError> {
1107 if n == 1 {
1108 return Ok(Arc::new(OneSizedRealFft {
1109 phantom_data: Default::default(),
1110 }));
1111 }
1112 if n.is_multiple_of(2) {
1113 C2RFftEvenInterceptor::install(n, Zaft::strategy(n / 2, FftDirection::Inverse)?)
1114 .map(|x| Arc::new(x) as Arc<dyn C2RFftExecutor<f32> + Send + Sync>)
1115 } else {
1116 C2RFftOddInterceptor::install(n, Zaft::strategy(n, FftDirection::Inverse)?)
1117 .map(|x| Arc::new(x) as Arc<dyn C2RFftExecutor<f32> + Send + Sync>)
1118 }
1119 }
1120
1121 pub fn make_forward_fft_f32(
1132 n: usize,
1133 ) -> Result<Arc<dyn FftExecutor<f32> + Send + Sync>, ZaftError> {
1134 Zaft::strategy(n, FftDirection::Forward)
1135 }
1136
1137 pub fn make_forward_fft_f64(
1146 n: usize,
1147 ) -> Result<Arc<dyn FftExecutor<f64> + Send + Sync>, ZaftError> {
1148 Zaft::strategy(n, FftDirection::Forward)
1149 }
1150
1151 pub fn make_c2r_fft_f64(
1162 n: usize,
1163 ) -> Result<Arc<dyn C2RFftExecutor<f64> + Send + Sync>, ZaftError> {
1164 if n == 1 {
1165 return Ok(Arc::new(OneSizedRealFft {
1166 phantom_data: Default::default(),
1167 }));
1168 }
1169 if n.is_multiple_of(2) {
1170 C2RFftEvenInterceptor::install(n, Zaft::strategy(n / 2, FftDirection::Inverse)?)
1171 .map(|x| Arc::new(x) as Arc<dyn C2RFftExecutor<f64> + Send + Sync>)
1172 } else {
1173 C2RFftOddInterceptor::install(n, Zaft::strategy(n, FftDirection::Inverse)?)
1174 .map(|x| Arc::new(x) as Arc<dyn C2RFftExecutor<f64> + Send + Sync>)
1175 }
1176 }
1177
1178 pub fn make_r2c_fft_f64(
1189 n: usize,
1190 ) -> Result<Arc<dyn R2CFftExecutor<f64> + Send + Sync>, ZaftError> {
1191 strategy_r2c(n)
1192 }
1193
1194 pub fn make_inverse_fft_f32(
1206 n: usize,
1207 ) -> Result<Arc<dyn FftExecutor<f32> + Send + Sync>, ZaftError> {
1208 Zaft::strategy(n, FftDirection::Inverse)
1209 }
1210
1211 pub fn make_inverse_fft_f64(
1220 n: usize,
1221 ) -> Result<Arc<dyn FftExecutor<f64> + Send + Sync>, ZaftError> {
1222 Zaft::strategy(n, FftDirection::Inverse)
1223 }
1224
1225 pub fn make_2d_r2c_fft_f32(
1248 width: usize,
1249 height: usize,
1250 thread_count: usize,
1251 ) -> Result<Arc<dyn TwoDimensionalExecutorR2C<f32> + Send + Sync>, ZaftError> {
1252 if width * height == 0 {
1253 return Err(ZaftError::ZeroSizedFft);
1254 }
1255 let width_fft = Zaft::make_r2c_fft_f32(width)?;
1256 let height_fft = Zaft::make_forward_fft_f32(height)?;
1257 let width_scratch_length = width_fft.complex_scratch_length();
1258 let height_scratch_length = height_fft.scratch_length();
1259 Ok(Arc::new(TwoDimensionalR2C {
1260 width_r2c_executor: width_fft,
1261 height_c2c_executor: height_fft,
1262 thread_count: thread_count.max(1),
1263 width,
1264 height,
1265 transpose_width_to_height: f32::transpose_strategy((width / 2) + 1, height),
1266 width_scratch_length,
1267 height_scratch_length,
1268 }))
1269 }
1270
1271 pub fn make_2d_r2c_fft_f64(
1299 width: usize,
1300 height: usize,
1301 thread_count: usize,
1302 ) -> Result<Arc<dyn TwoDimensionalExecutorR2C<f64> + Send + Sync>, ZaftError> {
1303 if width * height == 0 {
1304 return Err(ZaftError::ZeroSizedFft);
1305 }
1306 let width_fft = Zaft::make_r2c_fft_f64(width)?;
1307 let height_fft = Zaft::make_forward_fft_f64(height)?;
1308 let width_scratch_length = width_fft.complex_scratch_length();
1309 let height_scratch_length = height_fft.scratch_length();
1310 Ok(Arc::new(TwoDimensionalR2C {
1311 width_r2c_executor: width_fft,
1312 height_c2c_executor: height_fft,
1313 thread_count: thread_count.max(1),
1314 width,
1315 height,
1316 transpose_width_to_height: f64::transpose_strategy((width / 2) + 1, height),
1317 width_scratch_length,
1318 height_scratch_length,
1319 }))
1320 }
1321
1322 pub fn make_2d_c2c_fft_f32(
1346 width: usize,
1347 height: usize,
1348 fft_direction: FftDirection,
1349 thread_count: usize,
1350 ) -> Result<Arc<dyn TwoDimensionalFftExecutor<f32> + Send + Sync>, ZaftError> {
1351 if width * height == 0 {
1352 return Err(ZaftError::ZeroSizedFft);
1353 }
1354 let fft_width = match fft_direction {
1355 FftDirection::Forward => width,
1356 FftDirection::Inverse => height,
1357 };
1358 let fft_height = match fft_direction {
1359 FftDirection::Forward => height,
1360 FftDirection::Inverse => width,
1361 };
1362 let width_fft = match fft_direction {
1363 FftDirection::Forward => Zaft::make_forward_fft_f32(fft_width)?,
1364 FftDirection::Inverse => Zaft::make_inverse_fft_f32(fft_width)?,
1365 };
1366 let height_fft = match fft_direction {
1367 FftDirection::Forward => Zaft::make_forward_fft_f32(fft_height)?,
1368 FftDirection::Inverse => Zaft::make_inverse_fft_f32(fft_height)?,
1369 };
1370 let oof_scratch_width = width_fft.out_of_place_scratch_length();
1371 let inplace_scratch_height = height_fft.scratch_length();
1372 Ok(Arc::new(TwoDimensionalC2C {
1373 width_c2c_executor: width_fft,
1374 height_c2c_executor: height_fft,
1375 thread_count: thread_count.max(1),
1376 width: fft_width,
1377 height: fft_height,
1378 transpose_width_to_height: f32::transpose_strategy(fft_width, fft_height),
1379 oof_width_scratch_size: oof_scratch_width,
1380 height_scratch_size: inplace_scratch_height,
1381 }))
1382 }
1383
1384 pub fn make_2d_c2c_fft_f64(
1408 width: usize,
1409 height: usize,
1410 fft_direction: FftDirection,
1411 thread_count: usize,
1412 ) -> Result<Arc<dyn TwoDimensionalFftExecutor<f64> + Send + Sync>, ZaftError> {
1413 if width * height == 0 {
1414 return Err(ZaftError::ZeroSizedFft);
1415 }
1416 let fft_width = match fft_direction {
1417 FftDirection::Forward => width,
1418 FftDirection::Inverse => height,
1419 };
1420 let fft_height = match fft_direction {
1421 FftDirection::Forward => height,
1422 FftDirection::Inverse => width,
1423 };
1424 let width_fft = match fft_direction {
1425 FftDirection::Forward => Zaft::make_forward_fft_f64(fft_width)?,
1426 FftDirection::Inverse => Zaft::make_inverse_fft_f64(fft_width)?,
1427 };
1428 let height_fft = match fft_direction {
1429 FftDirection::Forward => Zaft::make_forward_fft_f64(fft_height)?,
1430 FftDirection::Inverse => Zaft::make_inverse_fft_f64(fft_height)?,
1431 };
1432 let oof_scratch_width = width_fft.out_of_place_scratch_length();
1433 let inplace_scratch_height = height_fft.scratch_length();
1434 Ok(Arc::new(TwoDimensionalC2C {
1435 width_c2c_executor: width_fft,
1436 height_c2c_executor: height_fft,
1437 thread_count: thread_count.max(1),
1438 width: fft_width,
1439 height: fft_height,
1440 transpose_width_to_height: f64::transpose_strategy(fft_width, fft_height),
1441 oof_width_scratch_size: oof_scratch_width,
1442 height_scratch_size: inplace_scratch_height,
1443 }))
1444 }
1445
1446 pub fn make_2d_c2r_fft_f32(
1465 width: usize,
1466 height: usize,
1467 thread_count: usize,
1468 ) -> Result<Arc<dyn TwoDimensionalExecutorC2R<f32> + Send + Sync>, ZaftError> {
1469 if width * height == 0 {
1470 return Err(ZaftError::ZeroSizedFft);
1471 }
1472 let width_fft = Zaft::make_c2r_fft_f32(width)?;
1473 let height_fft = Zaft::make_inverse_fft_f32(height)?;
1474 let width_scratch_length = width_fft.complex_scratch_length();
1475 let height_scratch_length = height_fft.scratch_length();
1476 Ok(Arc::new(TwoDimensionalC2R {
1477 width_c2r_executor: width_fft,
1478 height_c2c_executor: height_fft,
1479 thread_count: thread_count.max(1),
1480 width,
1481 height,
1482 transpose_height_to_width: f32::transpose_strategy(height, (width / 2) + 1),
1483 width_scratch_length,
1484 height_scratch_length,
1485 }))
1486 }
1487
1488 pub fn make_2d_c2r_fft_f64(
1506 width: usize,
1507 height: usize,
1508 thread_count: usize,
1509 ) -> Result<Arc<dyn TwoDimensionalExecutorC2R<f64> + Send + Sync>, ZaftError> {
1510 if width * height == 0 {
1511 return Err(ZaftError::ZeroSizedFft);
1512 }
1513 let width_fft = Zaft::make_c2r_fft_f64(width)?;
1514 let height_fft = Zaft::make_inverse_fft_f64(height)?;
1515 let width_scratch_length = width_fft.complex_scratch_length();
1516 let height_scratch_length = height_fft.scratch_length();
1517 Ok(Arc::new(TwoDimensionalC2R {
1518 width_c2r_executor: width_fft,
1519 height_c2c_executor: height_fft,
1520 thread_count: thread_count.max(1),
1521 width,
1522 height,
1523 transpose_height_to_width: f64::transpose_strategy(height, (width / 2) + 1),
1524 width_scratch_length,
1525 height_scratch_length,
1526 }))
1527 }
1528}
1529
1530#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
1535pub enum FftDirection {
1536 Forward,
1539 Inverse,
1542}
1543
1544impl FftDirection {
1545 pub fn inverse(self) -> FftDirection {
1546 match self {
1547 FftDirection::Forward => FftDirection::Inverse,
1548 FftDirection::Inverse => FftDirection::Forward,
1549 }
1550 }
1551}
1552
1553impl Display for FftDirection {
1554 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1555 match self {
1556 FftDirection::Forward => f.write_str("FftDirection::Forward"),
1557 FftDirection::Inverse => f.write_str("FftDirection::Inverse"),
1558 }
1559 }
1560}
1561
1562#[cfg(test)]
1563mod tests {
1564 use crate::Zaft;
1565 use num_complex::Complex;
1566 use num_traits::Zero;
1567
1568 #[test]
1569 fn power_of_four() {
1570 fn is_power_of_four(n: u64) -> bool {
1571 n != 0 && (n & (n - 1)) == 0 && (n & 0x5555_5555_5555_5555) != 0
1572 }
1573 assert_eq!(is_power_of_four(4), true);
1574 assert_eq!(is_power_of_four(8), false);
1575 assert_eq!(is_power_of_four(16), true);
1576 assert_eq!(is_power_of_four(20), false);
1577 }
1578
1579 #[test]
1580 fn test_everything_f32() {
1581 for i in 1..2010 {
1582 let mut data = vec![Complex::new(0.0019528865, 0.); i];
1583 for (i, chunk) in data.iter_mut().enumerate() {
1584 *chunk = Complex::new(
1585 -0.19528865 + i as f32 * 0.001,
1586 0.0019528865 - i as f32 * 0.001,
1587 );
1588 }
1589 let zaft_exec = Zaft::make_forward_fft_f32(data.len()).expect("Failed to make FFT!");
1590 let zaft_inverse = Zaft::make_inverse_fft_f32(data.len()).expect("Failed to make FFT!");
1591 let reference_clone = data.clone();
1592 zaft_exec
1593 .execute(&mut data)
1594 .expect(&format!("Failed to execute forward FFT for size {i}!"));
1595 zaft_inverse
1596 .execute(&mut data)
1597 .expect(&format!("Failed to execute inverse FFT for size {i}!"));
1598 let data_len = 1. / data.len() as f32;
1599 for i in data.iter_mut() {
1600 *i *= data_len;
1601 }
1602 data.iter()
1603 .zip(reference_clone)
1604 .enumerate()
1605 .for_each(|(idx, (a, b))| {
1606 assert!(
1607 (a.re - b.re).abs() < 1e-2,
1608 "a_re {}, b_re {} at {idx}, for size {i}",
1609 a.re,
1610 b.re
1611 );
1612 assert!(
1613 (a.im - b.im).abs() < 1e-2,
1614 "a_re {}, b_re {} at {idx}, for size {i}",
1615 a.im,
1616 b.im
1617 );
1618 });
1619 }
1620 }
1621
1622 #[test]
1623 fn test_everything_oof_f32() {
1624 for i in 1..2010 {
1625 let mut data = vec![Complex::new(0.0019528865, 0.); i];
1626 let mut scratch = data.to_vec();
1627 for (i, chunk) in data.iter_mut().enumerate() {
1628 *chunk = Complex::new(
1629 -0.19528865 + i as f32 * 0.001,
1630 0.0019528865 - i as f32 * 0.001,
1631 );
1632 }
1633 let zaft_exec = Zaft::make_forward_fft_f32(data.len()).expect("Failed to make FFT!");
1634 let zaft_inverse = Zaft::make_inverse_fft_f32(data.len()).expect("Failed to make FFT!");
1635 let reference_clone = data.clone();
1636 zaft_exec
1637 .execute_out_of_place(&data, &mut scratch)
1638 .expect(&format!("Failed to execute forward FFT for size {i}!"));
1639 zaft_inverse
1640 .execute_out_of_place(&scratch, &mut data)
1641 .expect(&format!("Failed to execute inverse FFT for size {i}!"));
1642 let data_len = 1. / data.len() as f32;
1643 for i in data.iter_mut() {
1644 *i *= data_len;
1645 }
1646 data.iter()
1647 .zip(reference_clone)
1648 .enumerate()
1649 .for_each(|(idx, (a, b))| {
1650 assert!(
1651 (a.re - b.re).abs() < 1e-2,
1652 "a_re {}, b_re {} at {idx}, for size {i}",
1653 a.re,
1654 b.re
1655 );
1656 assert!(
1657 (a.im - b.im).abs() < 1e-2,
1658 "a_re {}, b_re {} at {idx}, for size {i}",
1659 a.im,
1660 b.im
1661 );
1662 });
1663 }
1664 }
1665
1666 #[test]
1667 fn test_everything_f64() {
1668 for i in 1..1900 {
1669 let mut data = vec![Complex::new(0.0019528865, 0.); i];
1670 for (i, chunk) in data.iter_mut().enumerate() {
1671 *chunk = Complex::new(
1672 -0.19528865 + i as f64 * 0.001,
1673 0.0019528865 - i as f64 * 0.001,
1674 );
1675 }
1676 let zaft_exec = Zaft::make_forward_fft_f64(data.len()).expect("Failed to make FFT!");
1677 let zaft_inverse = Zaft::make_inverse_fft_f64(data.len()).expect("Failed to make FFT!");
1678 let rust_fft_clone = data.clone();
1679 zaft_exec
1680 .execute(&mut data)
1681 .expect(&format!("Failed to execute forward FFT for size {i}!"));
1682 zaft_inverse
1683 .execute(&mut data)
1684 .expect(&format!("Failed to execute inverse FFT for size {i}!"));
1685 let data_len = 1. / data.len() as f64;
1686 for i in data.iter_mut() {
1687 *i *= data_len;
1688 }
1689 data.iter()
1690 .zip(rust_fft_clone)
1691 .enumerate()
1692 .for_each(|(idx, (a, b))| {
1693 assert!(
1694 (a.re - b.re).abs() < 1e-6,
1695 "a_re {}, b_re {} at {idx}, for size {i}",
1696 a.re,
1697 b.re
1698 );
1699 assert!(
1700 (a.im - b.im).abs() < 1e-6,
1701 "a_im {}, b_im {} at {idx}, for size {i}",
1702 a.im,
1703 b.im
1704 );
1705 });
1706 }
1707 }
1708
1709 #[test]
1710 fn test_everything_oof_f64() {
1711 for i in 1..1900 {
1712 let mut data = vec![Complex::new(0.0019528865, 0.); i];
1713 let mut scratch = data.clone();
1714 for (i, chunk) in data.iter_mut().enumerate() {
1715 *chunk = Complex::new(
1716 -0.19528865 + i as f64 * 0.001,
1717 0.0019528865 - i as f64 * 0.001,
1718 );
1719 }
1720 let zaft_exec = Zaft::make_forward_fft_f64(data.len()).expect("Failed to make FFT!");
1721 let zaft_inverse = Zaft::make_inverse_fft_f64(data.len()).expect("Failed to make FFT!");
1722 let rust_fft_clone = data.clone();
1723 zaft_exec
1724 .execute_out_of_place(&data, &mut scratch)
1725 .expect(&format!("Failed to execute forward FFT for size {i}!"));
1726 zaft_inverse
1727 .execute_out_of_place(&scratch, &mut data)
1728 .expect(&format!("Failed to execute inverse FFT for size {i}!"));
1729 let data_len = 1. / data.len() as f64;
1730 for i in data.iter_mut() {
1731 *i *= data_len;
1732 }
1733 data.iter()
1734 .zip(rust_fft_clone)
1735 .enumerate()
1736 .for_each(|(idx, (a, b))| {
1737 assert!(
1738 (a.re - b.re).abs() < 1e-6,
1739 "a_re {}, b_re {} at {idx}, for size {i}",
1740 a.re,
1741 b.re
1742 );
1743 assert!(
1744 (a.im - b.im).abs() < 1e-6,
1745 "a_im {}, b_im {} at {idx}, for size {i}",
1746 a.im,
1747 b.im
1748 );
1749 });
1750 }
1751 }
1752
1753 #[test]
1754 fn test_destructive_everything_f64() {
1755 for i in 1..1900 {
1756 let mut data = vec![Complex::new(0.0019528865, 0.); i];
1757 for (i, chunk) in data.iter_mut().enumerate() {
1758 *chunk = Complex::new(
1759 -0.19528865 + i as f64 * 0.001,
1760 0.0019528865 - i as f64 * 0.001,
1761 );
1762 }
1763 let zaft_exec = Zaft::make_forward_fft_f64(data.len()).expect("Failed to make FFT!");
1764 let zaft_inverse = Zaft::make_inverse_fft_f64(data.len()).expect("Failed to make FFT!");
1765 let rust_fft_clone = data.clone();
1766 let mut fwd = vec![Complex::zero(); data.len()];
1767 let mut scratch = vec![Complex::zero(); zaft_exec.destructive_scratch_length()];
1768 zaft_exec
1769 .execute_destructive_with_scratch(&mut data, &mut fwd, &mut scratch)
1770 .expect(&format!("Failed to execute forward FFT for size {i}!"));
1771 zaft_inverse
1772 .execute_destructive_with_scratch(&mut fwd, &mut data, &mut scratch)
1773 .expect(&format!("Failed to execute inverse FFT for size {i}!"));
1774 let data_len = 1. / data.len() as f64;
1775 for i in data.iter_mut() {
1776 *i *= data_len;
1777 }
1778 data.iter()
1779 .zip(rust_fft_clone)
1780 .enumerate()
1781 .for_each(|(idx, (a, b))| {
1782 assert!(
1783 (a.re - b.re).abs() < 1e-6,
1784 "a_re {}, b_re {} at {idx}, for size {i}",
1785 a.re,
1786 b.re
1787 );
1788 assert!(
1789 (a.im - b.im).abs() < 1e-6,
1790 "a_im {}, b_im {} at {idx}, for size {i}",
1791 a.im,
1792 b.im
1793 );
1794 });
1795 }
1796 }
1797
1798 #[test]
1799 fn test_destructive_everything_oof_f32() {
1800 for i in 1..2010 {
1801 let mut data = vec![Complex::new(0.0019528865, 0.); i];
1802 for (i, chunk) in data.iter_mut().enumerate() {
1803 *chunk = Complex::new(
1804 -0.19528865 + i as f32 * 0.001,
1805 0.0019528865 - i as f32 * 0.001,
1806 );
1807 }
1808 let zaft_exec = Zaft::make_forward_fft_f32(data.len()).expect("Failed to make FFT!");
1809 let zaft_inverse = Zaft::make_inverse_fft_f32(data.len()).expect("Failed to make FFT!");
1810 let reference_clone = data.clone();
1811 let mut scratch = vec![Complex::zero(); zaft_exec.destructive_scratch_length()];
1812 let mut target = vec![Complex::zero(); data.len()];
1813 zaft_exec
1814 .execute_destructive_with_scratch(&mut data, &mut target, &mut scratch)
1815 .expect(&format!("Failed to execute forward FFT for size {i}!"));
1816 zaft_inverse
1817 .execute_destructive_with_scratch(&mut target, &mut data, &mut scratch)
1818 .expect(&format!("Failed to execute inverse FFT for size {i}!"));
1819 let data_len = 1. / data.len() as f32;
1820 for i in data.iter_mut() {
1821 *i *= data_len;
1822 }
1823 data.iter()
1824 .zip(reference_clone)
1825 .enumerate()
1826 .for_each(|(idx, (a, b))| {
1827 assert!(
1828 (a.re - b.re).abs() < 1e-2,
1829 "a_re {}, b_re {} at {idx}, for size {i}",
1830 a.re,
1831 b.re
1832 );
1833 assert!(
1834 (a.im - b.im).abs() < 1e-2,
1835 "a_re {}, b_re {} at {idx}, for size {i}",
1836 a.im,
1837 b.im
1838 );
1839 });
1840 }
1841 }
1842}