1pub use rustfft::num_complex;
162pub use rustfft::num_traits;
163pub use rustfft::FftNum;
164
165use rustfft::num_complex::Complex;
166use rustfft::num_traits::Zero;
167use rustfft::FftPlanner;
168use std::collections::HashMap;
169use std::error;
170use std::fmt;
171use std::sync::Arc;
172
173type Res<T> = Result<T, FftError>;
174
175pub enum FftError {
177 InputBuffer(usize, usize),
182 OutputBuffer(usize, usize),
187 ScratchBuffer(usize, usize),
192 InputValues(bool, bool),
199}
200
201impl FftError {
202 fn fmt_internal(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 let desc = match self {
204 Self::InputBuffer(expected, got) => {
205 format!("Wrong length of input, expected {}, got {}", expected, got)
206 }
207 Self::OutputBuffer(expected, got) => {
208 format!("Wrong length of output, expected {}, got {}", expected, got)
209 }
210 Self::ScratchBuffer(expected, got) => {
211 format!(
212 "Scratch buffer of size {} is too small, must be at least {} long",
213 got, expected
214 )
215 }
216 Self::InputValues(first, last) => match (first, last) {
217 (true, false) => "Imaginary part of first value was non-zero.".to_string(),
218 (false, true) => "Imaginary part of last value was non-zero.".to_string(),
219 (true, true) => {
220 "Imaginary parts of both first and last values were non-zero.".to_string()
221 }
222 (false, false) => unreachable!(),
223 },
224 };
225 write!(f, "{}", desc)
226 }
227}
228
229impl fmt::Debug for FftError {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 self.fmt_internal(f)
232 }
233}
234
235impl fmt::Display for FftError {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 self.fmt_internal(f)
238 }
239}
240
241impl error::Error for FftError {}
242
243fn compute_twiddle<T: FftNum>(index: usize, fft_len: usize) -> Complex<T> {
244 let constant = -2f64 * std::f64::consts::PI / fft_len as f64;
245 let angle = constant * index as f64;
246 Complex {
247 re: T::from_f64(angle.cos()).unwrap(),
248 im: T::from_f64(angle.sin()).unwrap(),
249 }
250}
251
252pub struct RealToComplexOdd<T> {
253 length: usize,
254 fft: std::sync::Arc<dyn rustfft::Fft<T>>,
255 scratch_len: usize,
256}
257
258pub struct RealToComplexEven<T> {
259 twiddles: Vec<Complex<T>>,
260 length: usize,
261 fft: std::sync::Arc<dyn rustfft::Fft<T>>,
262 scratch_len: usize,
263}
264
265pub struct ComplexToRealOdd<T> {
266 length: usize,
267 fft: std::sync::Arc<dyn rustfft::Fft<T>>,
268 scratch_len: usize,
269}
270
271pub struct ComplexToRealEven<T> {
272 twiddles: Vec<Complex<T>>,
273 length: usize,
274 fft: std::sync::Arc<dyn rustfft::Fft<T>>,
275 scratch_len: usize,
276}
277
278#[allow(clippy::len_without_is_empty)]
281pub trait RealToComplex<T>: Sync + Send {
282 fn process(&self, input: &mut [T], output: &mut [Complex<T>]) -> Res<()>;
290
291 fn process_with_scratch(
297 &self,
298 input: &mut [T],
299 output: &mut [Complex<T>],
300 scratch: &mut [Complex<T>],
301 ) -> Res<()>;
302
303 fn get_scratch_len(&self) -> usize;
305
306 fn len(&self) -> usize;
309
310 fn complex_len(&self) -> usize {
312 self.len() / 2 + 1
313 }
314
315 fn make_input_vec(&self) -> Vec<T>;
317
318 fn make_output_vec(&self) -> Vec<Complex<T>>;
320
321 fn make_scratch_vec(&self) -> Vec<Complex<T>>;
323}
324
325#[allow(clippy::len_without_is_empty)]
328pub trait ComplexToReal<T>: Sync + Send {
329 fn process(&self, input: &mut [Complex<T>], output: &mut [T]) -> Res<()>;
341
342 fn process_with_scratch(
348 &self,
349 input: &mut [Complex<T>],
350 output: &mut [T],
351 scratch: &mut [Complex<T>],
352 ) -> Res<()>;
353
354 fn get_scratch_len(&self) -> usize;
356
357 fn len(&self) -> usize;
360
361 fn complex_len(&self) -> usize {
363 self.len() / 2 + 1
364 }
365
366 fn make_input_vec(&self) -> Vec<Complex<T>>;
368
369 fn make_output_vec(&self) -> Vec<T>;
371
372 fn make_scratch_vec(&self) -> Vec<Complex<T>>;
374}
375
376fn zip3<A, B, C>(a: A, b: B, c: C) -> impl Iterator<Item = (A::Item, B::Item, C::Item)>
377where
378 A: IntoIterator,
379 B: IntoIterator,
380 C: IntoIterator,
381{
382 a.into_iter()
383 .zip(b.into_iter().zip(c))
384 .map(|(x, (y, z))| (x, y, z))
385}
386
387pub struct RealFftPlanner<T: FftNum> {
391 planner: FftPlanner<T>,
392 r2c_cache: HashMap<usize, Arc<dyn RealToComplex<T>>>,
393 c2r_cache: HashMap<usize, Arc<dyn ComplexToReal<T>>>,
394}
395
396impl<T: FftNum> RealFftPlanner<T> {
397 pub fn new() -> Self {
399 let planner = FftPlanner::<T>::new();
400 Self {
401 r2c_cache: HashMap::new(),
402 c2r_cache: HashMap::new(),
403 planner,
404 }
405 }
406
407 pub fn plan_fft_forward(&mut self, len: usize) -> Arc<dyn RealToComplex<T>> {
411 if let Some(fft) = self.r2c_cache.get(&len) {
412 Arc::clone(fft)
413 } else {
414 let fft = if len % 2 > 0 {
415 Arc::new(RealToComplexOdd::new(len, &mut self.planner)) as Arc<dyn RealToComplex<T>>
416 } else {
417 Arc::new(RealToComplexEven::new(len, &mut self.planner))
418 as Arc<dyn RealToComplex<T>>
419 };
420 self.r2c_cache.insert(len, Arc::clone(&fft));
421 fft
422 }
423 }
424
425 pub fn plan_fft_inverse(&mut self, len: usize) -> Arc<dyn ComplexToReal<T>> {
429 if let Some(fft) = self.c2r_cache.get(&len) {
430 Arc::clone(fft)
431 } else {
432 let fft = if len % 2 > 0 {
433 Arc::new(ComplexToRealOdd::new(len, &mut self.planner)) as Arc<dyn ComplexToReal<T>>
434 } else {
435 Arc::new(ComplexToRealEven::new(len, &mut self.planner))
436 as Arc<dyn ComplexToReal<T>>
437 };
438 self.c2r_cache.insert(len, Arc::clone(&fft));
439 fft
440 }
441 }
442}
443
444impl<T: FftNum> Default for RealFftPlanner<T> {
445 fn default() -> Self {
446 Self::new()
447 }
448}
449
450impl<T: FftNum> RealToComplexOdd<T> {
451 pub fn new(length: usize, fft_planner: &mut FftPlanner<T>) -> Self {
455 if length % 2 == 0 {
456 panic!("Length must be odd, got {}", length,);
457 }
458 let fft = fft_planner.plan_fft_forward(length);
459 let scratch_len = fft.get_inplace_scratch_len() + length;
460 RealToComplexOdd {
461 length,
462 fft,
463 scratch_len,
464 }
465 }
466}
467
468impl<T: FftNum> RealToComplex<T> for RealToComplexOdd<T> {
469 fn process(&self, input: &mut [T], output: &mut [Complex<T>]) -> Res<()> {
470 let mut scratch = self.make_scratch_vec();
471 self.process_with_scratch(input, output, &mut scratch)
472 }
473
474 fn process_with_scratch(
475 &self,
476 input: &mut [T],
477 output: &mut [Complex<T>],
478 scratch: &mut [Complex<T>],
479 ) -> Res<()> {
480 if input.len() != self.length {
481 return Err(FftError::InputBuffer(self.length, input.len()));
482 }
483 let expected_output_buffer_size = self.complex_len();
484 if output.len() != expected_output_buffer_size {
485 return Err(FftError::OutputBuffer(
486 expected_output_buffer_size,
487 output.len(),
488 ));
489 }
490 if scratch.len() < (self.scratch_len) {
491 return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len()));
492 }
493 let (buffer, fft_scratch) = scratch.split_at_mut(self.length);
494
495 for (val, buf) in input.iter().zip(buffer.iter_mut()) {
496 *buf = Complex::new(*val, T::zero());
497 }
498 self.fft.process_with_scratch(buffer, fft_scratch);
500 output.copy_from_slice(&buffer[0..self.complex_len()]);
501 if let Some(elem) = output.first_mut() {
502 elem.im = T::zero();
503 }
504 Ok(())
505 }
506
507 fn get_scratch_len(&self) -> usize {
508 self.scratch_len
509 }
510
511 fn len(&self) -> usize {
512 self.length
513 }
514
515 fn make_input_vec(&self) -> Vec<T> {
516 vec![T::zero(); self.len()]
517 }
518
519 fn make_output_vec(&self) -> Vec<Complex<T>> {
520 vec![Complex::zero(); self.complex_len()]
521 }
522
523 fn make_scratch_vec(&self) -> Vec<Complex<T>> {
524 vec![Complex::zero(); self.get_scratch_len()]
525 }
526}
527
528impl<T: FftNum> RealToComplexEven<T> {
529 pub fn new(length: usize, fft_planner: &mut FftPlanner<T>) -> Self {
533 if length % 2 > 0 {
534 panic!("Length must be even, got {}", length,);
535 }
536 let twiddle_count = if length % 4 == 0 {
537 length / 4
538 } else {
539 length / 4 + 1
540 };
541 let twiddles: Vec<Complex<T>> = (1..twiddle_count)
542 .map(|i| compute_twiddle(i, length) * T::from_f64(0.5).unwrap())
543 .collect();
544 let fft = fft_planner.plan_fft_forward(length / 2);
545 let scratch_len = fft.get_outofplace_scratch_len();
546 RealToComplexEven {
547 twiddles,
548 length,
549 fft,
550 scratch_len,
551 }
552 }
553}
554
555impl<T: FftNum> RealToComplex<T> for RealToComplexEven<T> {
556 fn process(&self, input: &mut [T], output: &mut [Complex<T>]) -> Res<()> {
557 let mut scratch = self.make_scratch_vec();
558 self.process_with_scratch(input, output, &mut scratch)
559 }
560
561 fn process_with_scratch(
562 &self,
563 input: &mut [T],
564 output: &mut [Complex<T>],
565 scratch: &mut [Complex<T>],
566 ) -> Res<()> {
567 if input.len() != self.length {
568 return Err(FftError::InputBuffer(self.length, input.len()));
569 }
570 let expected_output_buffer_size = self.complex_len();
571 if output.len() != expected_output_buffer_size {
572 return Err(FftError::OutputBuffer(
573 expected_output_buffer_size,
574 output.len(),
575 ));
576 }
577 if scratch.len() < (self.scratch_len) {
578 return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len()));
579 }
580
581 let fftlen = self.length / 2;
582 let buf_in = unsafe {
583 let ptr = input.as_mut_ptr() as *mut Complex<T>;
584 let len = input.len();
585 std::slice::from_raw_parts_mut(ptr, len / 2)
586 };
587
588 self.fft
590 .process_outofplace_with_scratch(buf_in, &mut output[0..fftlen], scratch);
591 let (mut output_left, mut output_right) = output.split_at_mut(output.len() / 2);
592
593 match (output_left.first_mut(), output_right.last_mut()) {
595 (Some(first_element), Some(last_element)) => {
596 let first_value = *first_element;
598 *first_element = Complex {
599 re: first_value.re + first_value.im,
600 im: T::zero(),
601 };
602 *last_element = Complex {
603 re: first_value.re - first_value.im,
604 im: T::zero(),
605 };
606
607 output_left = &mut output_left[1..];
609 let right_len = output_right.len();
610 output_right = &mut output_right[..right_len - 1];
611 }
612 _ => {
613 return Ok(());
614 }
615 }
616 for (twiddle, out, out_rev) in zip3(
618 self.twiddles.iter(),
619 output_left.iter_mut(),
620 output_right.iter_mut().rev(),
621 ) {
622 let sum = *out + *out_rev;
623 let diff = *out - *out_rev;
624 let half = T::from_f64(0.5).unwrap();
625 let twiddled_re_sum = sum * twiddle.re;
630 let twiddled_im_sum = sum * twiddle.im;
631 let twiddled_re_diff = diff * twiddle.re;
632 let twiddled_im_diff = diff * twiddle.im;
633 let half_sum_re = half * sum.re;
634 let half_diff_im = half * diff.im;
635
636 let output_twiddled_real = twiddled_re_sum.im + twiddled_im_diff.re;
637 let output_twiddled_im = twiddled_im_sum.im - twiddled_re_diff.re;
638
639 *out = Complex {
641 re: half_sum_re + output_twiddled_real,
642 im: half_diff_im + output_twiddled_im,
643 };
644
645 *out_rev = Complex {
646 re: half_sum_re - output_twiddled_real,
647 im: output_twiddled_im - half_diff_im,
648 };
649 }
650
651 if output.len() % 2 == 1 {
653 if let Some(center_element) = output.get_mut(output.len() / 2) {
654 center_element.im = -center_element.im;
655 }
656 }
657 Ok(())
658 }
659 fn get_scratch_len(&self) -> usize {
660 self.scratch_len
661 }
662
663 fn len(&self) -> usize {
664 self.length
665 }
666
667 fn make_input_vec(&self) -> Vec<T> {
668 vec![T::zero(); self.len()]
669 }
670
671 fn make_output_vec(&self) -> Vec<Complex<T>> {
672 vec![Complex::zero(); self.complex_len()]
673 }
674
675 fn make_scratch_vec(&self) -> Vec<Complex<T>> {
676 vec![Complex::zero(); self.get_scratch_len()]
677 }
678}
679
680impl<T: FftNum> ComplexToRealOdd<T> {
681 pub fn new(length: usize, fft_planner: &mut FftPlanner<T>) -> Self {
686 if length % 2 == 0 {
687 panic!("Length must be odd, got {}", length,);
688 }
689 let fft = fft_planner.plan_fft_inverse(length);
690 let scratch_len = length + fft.get_inplace_scratch_len();
691 ComplexToRealOdd {
692 length,
693 fft,
694 scratch_len,
695 }
696 }
697}
698
699impl<T: FftNum> ComplexToReal<T> for ComplexToRealOdd<T> {
700 fn process(&self, input: &mut [Complex<T>], output: &mut [T]) -> Res<()> {
701 let mut scratch = self.make_scratch_vec();
702 self.process_with_scratch(input, output, &mut scratch)
703 }
704
705 fn process_with_scratch(
706 &self,
707 input: &mut [Complex<T>],
708 output: &mut [T],
709 scratch: &mut [Complex<T>],
710 ) -> Res<()> {
711 let expected_input_buffer_size = self.complex_len();
712 if input.len() != expected_input_buffer_size {
713 return Err(FftError::InputBuffer(
714 expected_input_buffer_size,
715 input.len(),
716 ));
717 }
718 if output.len() != self.length {
719 return Err(FftError::OutputBuffer(self.length, output.len()));
720 }
721 if scratch.len() < (self.scratch_len) {
722 return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len()));
723 }
724
725 let first_invalid = if input[0].im != T::from_f64(0.0).unwrap() {
726 input[0].im = T::from_f64(0.0).unwrap();
727 true
728 } else {
729 false
730 };
731
732 let (buffer, fft_scratch) = scratch.split_at_mut(self.length);
733
734 buffer[0..input.len()].copy_from_slice(input);
735 for (buf, val) in buffer
736 .iter_mut()
737 .rev()
738 .take(self.length / 2)
739 .zip(input.iter().skip(1))
740 {
741 *buf = val.conj();
742 }
743 self.fft.process_with_scratch(buffer, fft_scratch);
744 for (val, out) in buffer.iter().zip(output.iter_mut()) {
745 *out = val.re;
746 }
747 if first_invalid {
748 return Err(FftError::InputValues(true, false));
749 }
750 Ok(())
751 }
752
753 fn get_scratch_len(&self) -> usize {
754 self.scratch_len
755 }
756
757 fn len(&self) -> usize {
758 self.length
759 }
760
761 fn make_input_vec(&self) -> Vec<Complex<T>> {
762 vec![Complex::zero(); self.complex_len()]
763 }
764
765 fn make_output_vec(&self) -> Vec<T> {
766 vec![T::zero(); self.len()]
767 }
768
769 fn make_scratch_vec(&self) -> Vec<Complex<T>> {
770 vec![Complex::zero(); self.get_scratch_len()]
771 }
772}
773
774impl<T: FftNum> ComplexToRealEven<T> {
775 pub fn new(length: usize, fft_planner: &mut FftPlanner<T>) -> Self {
780 if length % 2 > 0 {
781 panic!("Length must be even, got {}", length,);
782 }
783 let twiddle_count = if length % 4 == 0 {
784 length / 4
785 } else {
786 length / 4 + 1
787 };
788 let twiddles: Vec<Complex<T>> = (1..twiddle_count)
789 .map(|i| compute_twiddle(i, length).conj())
790 .collect();
791 let fft = fft_planner.plan_fft_inverse(length / 2);
792 let scratch_len = fft.get_outofplace_scratch_len();
793 ComplexToRealEven {
794 twiddles,
795 length,
796 fft,
797 scratch_len,
798 }
799 }
800}
801impl<T: FftNum> ComplexToReal<T> for ComplexToRealEven<T> {
802 fn process(&self, input: &mut [Complex<T>], output: &mut [T]) -> Res<()> {
803 let mut scratch = self.make_scratch_vec();
804 self.process_with_scratch(input, output, &mut scratch)
805 }
806
807 fn process_with_scratch(
808 &self,
809 input: &mut [Complex<T>],
810 output: &mut [T],
811 scratch: &mut [Complex<T>],
812 ) -> Res<()> {
813 let expected_input_buffer_size = self.complex_len();
814 if input.len() != expected_input_buffer_size {
815 return Err(FftError::InputBuffer(
816 expected_input_buffer_size,
817 input.len(),
818 ));
819 }
820 if output.len() != self.length {
821 return Err(FftError::OutputBuffer(self.length, output.len()));
822 }
823 if scratch.len() < (self.scratch_len) {
824 return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len()));
825 }
826 if input.is_empty() {
827 return Ok(());
828 }
829 let first_invalid = if input[0].im != T::from_f64(0.0).unwrap() {
830 input[0].im = T::from_f64(0.0).unwrap();
831 true
832 } else {
833 false
834 };
835 let last_invalid = if input[input.len() - 1].im != T::from_f64(0.0).unwrap() {
836 input[input.len() - 1].im = T::from_f64(0.0).unwrap();
837 true
838 } else {
839 false
840 };
841
842 let (mut input_left, mut input_right) = input.split_at_mut(input.len() / 2);
843
844 match (input_left.first_mut(), input_right.last_mut()) {
847 (Some(first_input), Some(last_input)) => {
848 let first_sum = *first_input + *last_input;
849 let first_diff = *first_input - *last_input;
850
851 *first_input = Complex {
852 re: first_sum.re - first_sum.im,
853 im: first_diff.re - first_diff.im,
854 };
855
856 input_left = &mut input_left[1..];
857 let right_len = input_right.len();
858 input_right = &mut input_right[..right_len - 1];
859 }
860 _ => return Ok(()),
861 };
862
863 for (twiddle, fft_input, fft_input_rev) in zip3(
865 self.twiddles.iter(),
866 input_left.iter_mut(),
867 input_right.iter_mut().rev(),
868 ) {
869 let sum = *fft_input + *fft_input_rev;
870 let diff = *fft_input - *fft_input_rev;
871
872 let twiddled_re_sum = sum * twiddle.re;
877 let twiddled_im_sum = sum * twiddle.im;
878 let twiddled_re_diff = diff * twiddle.re;
879 let twiddled_im_diff = diff * twiddle.im;
880
881 let output_twiddled_real = twiddled_re_sum.im + twiddled_im_diff.re;
882 let output_twiddled_im = twiddled_im_sum.im - twiddled_re_diff.re;
883
884 *fft_input = Complex {
886 re: sum.re - output_twiddled_real,
887 im: diff.im - output_twiddled_im,
888 };
889 *fft_input_rev = Complex {
890 re: sum.re + output_twiddled_real,
891 im: -output_twiddled_im - diff.im,
892 }
893 }
894
895 if input.len() % 2 == 1 {
897 let center_element = input[input.len() / 2];
898 let doubled = center_element + center_element;
899 input[input.len() / 2] = doubled.conj();
900 }
901
902 let buf_out = unsafe {
904 let ptr = output.as_mut_ptr() as *mut Complex<T>;
905 let len = output.len();
906 std::slice::from_raw_parts_mut(ptr, len / 2)
907 };
908 self.fft
909 .process_outofplace_with_scratch(&mut input[..buf_out.len()], buf_out, scratch);
910 if first_invalid || last_invalid {
911 return Err(FftError::InputValues(first_invalid, last_invalid));
912 }
913 Ok(())
914 }
915
916 fn get_scratch_len(&self) -> usize {
917 self.scratch_len
918 }
919
920 fn len(&self) -> usize {
921 self.length
922 }
923
924 fn make_input_vec(&self) -> Vec<Complex<T>> {
925 vec![Complex::zero(); self.complex_len()]
926 }
927
928 fn make_output_vec(&self) -> Vec<T> {
929 vec![T::zero(); self.len()]
930 }
931
932 fn make_scratch_vec(&self) -> Vec<Complex<T>> {
933 vec![Complex::zero(); self.get_scratch_len()]
934 }
935}
936
937#[cfg(test)]
938mod tests {
939 use crate::FftError;
940 use crate::RealFftPlanner;
941 use rand::Rng;
942 use rustfft::num_complex::Complex;
943 use rustfft::num_traits::{Float, Zero};
944 use rustfft::FftPlanner;
945 use std::error::Error;
946 use std::ops::Sub;
947
948 fn compare_complex<T: Float + Sub>(a: &[Complex<T>], b: &[Complex<T>]) -> T {
950 a.iter()
951 .zip(b.iter())
952 .fold(T::zero(), |maxdiff, (val_a, val_b)| {
953 let diff = (val_a - val_b).norm();
954 if maxdiff > diff {
955 maxdiff
956 } else {
957 diff
958 }
959 })
960 }
961
962 fn compare_scalars<T: Float + Sub>(a: &[T], b: &[T]) -> T {
964 a.iter()
965 .zip(b.iter())
966 .fold(T::zero(), |maxdiff, (val_a, val_b)| {
967 let diff = (*val_a - *val_b).abs();
968 if maxdiff > diff {
969 maxdiff
970 } else {
971 diff
972 }
973 })
974 }
975
976 #[test]
978 fn complex_to_real_64() {
979 for length in 1..1000 {
980 let mut real_planner = RealFftPlanner::<f64>::new();
981 let c2r = real_planner.plan_fft_inverse(length);
982 let mut out_a = c2r.make_output_vec();
983 let mut indata = c2r.make_input_vec();
984 let mut rustfft_check: Vec<Complex<f64>> = vec![Complex::zero(); length];
985 let mut rng = rand::thread_rng();
986 for val in indata.iter_mut() {
987 *val = Complex::new(rng.gen::<f64>(), rng.gen::<f64>());
988 }
989 indata[0].im = 0.0;
990 if length % 2 == 0 {
991 indata[length / 2].im = 0.0;
992 }
993 for (val_long, val) in rustfft_check
994 .iter_mut()
995 .take(c2r.complex_len())
996 .zip(indata.iter())
997 {
998 *val_long = *val;
999 }
1000 for (val_long, val) in rustfft_check
1001 .iter_mut()
1002 .rev()
1003 .take(length / 2)
1004 .zip(indata.iter().skip(1))
1005 {
1006 *val_long = val.conj();
1007 }
1008 let mut fft_planner = FftPlanner::<f64>::new();
1009 let fft = fft_planner.plan_fft_inverse(length);
1010
1011 c2r.process(&mut indata, &mut out_a).unwrap();
1012 fft.process(&mut rustfft_check);
1013
1014 let check_real = rustfft_check.iter().map(|val| val.re).collect::<Vec<f64>>();
1015 let maxdiff = compare_scalars(&out_a, &check_real);
1016 assert!(
1017 maxdiff < 1.0e-9,
1018 "Length: {}, too large error: {}",
1019 length,
1020 maxdiff
1021 );
1022 }
1023 }
1024
1025 #[test]
1027 fn complex_to_real_32() {
1028 for length in 1..1000 {
1029 let mut real_planner = RealFftPlanner::<f32>::new();
1030 let c2r = real_planner.plan_fft_inverse(length);
1031 let mut out_a = c2r.make_output_vec();
1032 let mut indata = c2r.make_input_vec();
1033 let mut rustfft_check: Vec<Complex<f32>> = vec![Complex::zero(); length];
1034 let mut rng = rand::thread_rng();
1035 for val in indata.iter_mut() {
1036 *val = Complex::new(rng.gen::<f32>(), rng.gen::<f32>());
1037 }
1038 indata[0].im = 0.0;
1039 if length % 2 == 0 {
1040 indata[length / 2].im = 0.0;
1041 }
1042 for (val_long, val) in rustfft_check
1043 .iter_mut()
1044 .take(c2r.complex_len())
1045 .zip(indata.iter())
1046 {
1047 *val_long = *val;
1048 }
1049 for (val_long, val) in rustfft_check
1050 .iter_mut()
1051 .rev()
1052 .take(length / 2)
1053 .zip(indata.iter().skip(1))
1054 {
1055 *val_long = val.conj();
1056 }
1057 let mut fft_planner = FftPlanner::<f32>::new();
1058 let fft = fft_planner.plan_fft_inverse(length);
1059
1060 c2r.process(&mut indata, &mut out_a).unwrap();
1061 fft.process(&mut rustfft_check);
1062
1063 let check_real = rustfft_check.iter().map(|val| val.re).collect::<Vec<f32>>();
1064 let maxdiff = compare_scalars(&out_a, &check_real);
1065 assert!(
1066 maxdiff < 5.0e-4,
1067 "Length: {}, too large error: {}",
1068 length,
1069 maxdiff
1070 );
1071 }
1072 }
1073
1074 #[test]
1076 fn complex_to_real_errors_even() {
1077 let length = 100;
1078 let mut real_planner = RealFftPlanner::<f64>::new();
1079 let c2r = real_planner.plan_fft_inverse(length);
1080 let mut out_a = c2r.make_output_vec();
1081 let mut indata = c2r.make_input_vec();
1082 let mut rng = rand::thread_rng();
1083
1084 for val in indata.iter_mut() {
1086 *val = Complex::new(rng.gen::<f64>(), rng.gen::<f64>());
1087 }
1088 indata[0].im = 0.0;
1089 indata[50].im = 0.0;
1090 assert!(c2r.process(&mut indata, &mut out_a).is_ok());
1092
1093 for val in indata.iter_mut() {
1095 *val = Complex::new(rng.gen::<f64>(), rng.gen::<f64>());
1096 }
1097 indata[50].im = 0.0;
1098 let res = c2r.process(&mut indata, &mut out_a);
1099 assert!(res.is_err());
1100 assert!(matches!(res, Err(FftError::InputValues(true, false))));
1101
1102 for val in indata.iter_mut() {
1104 *val = Complex::new(rng.gen::<f64>(), rng.gen::<f64>());
1105 }
1106 indata[0].im = 0.0;
1107 let res = c2r.process(&mut indata, &mut out_a);
1108 assert!(res.is_err());
1109 assert!(matches!(res, Err(FftError::InputValues(false, true))));
1110 }
1111
1112 #[test]
1114 fn complex_to_real_errors_odd() {
1115 let length = 101;
1116 let mut real_planner = RealFftPlanner::<f64>::new();
1117 let c2r = real_planner.plan_fft_inverse(length);
1118 let mut out_a = c2r.make_output_vec();
1119 let mut indata = c2r.make_input_vec();
1120 let mut rng = rand::thread_rng();
1121
1122 for val in indata.iter_mut() {
1124 *val = Complex::new(rng.gen::<f64>(), rng.gen::<f64>());
1125 }
1126 indata[0].im = 0.0;
1127 assert!(c2r.process(&mut indata, &mut out_a).is_ok());
1129
1130 for val in indata.iter_mut() {
1132 *val = Complex::new(rng.gen::<f64>(), rng.gen::<f64>());
1133 }
1134 let res = c2r.process(&mut indata, &mut out_a);
1135 assert!(res.is_err());
1136 assert!(matches!(res, Err(FftError::InputValues(true, false))));
1137 }
1138
1139 #[test]
1141 fn real_to_complex_64() {
1142 for length in 1..1000 {
1143 let mut real_planner = RealFftPlanner::<f64>::new();
1144 let r2c = real_planner.plan_fft_forward(length);
1145 let mut out_a = r2c.make_output_vec();
1146 let mut indata = r2c.make_input_vec();
1147 let mut rng = rand::thread_rng();
1148 for val in indata.iter_mut() {
1149 *val = rng.gen::<f64>();
1150 }
1151 let mut rustfft_check = indata
1152 .iter()
1153 .map(Complex::from)
1154 .collect::<Vec<Complex<f64>>>();
1155 let mut fft_planner = FftPlanner::<f64>::new();
1156 let fft = fft_planner.plan_fft_forward(length);
1157 fft.process(&mut rustfft_check);
1158 r2c.process(&mut indata, &mut out_a).unwrap();
1159 assert_eq!(out_a[0].im, 0.0, "First imaginary component must be zero");
1160 if length % 2 == 0 {
1161 assert_eq!(
1162 out_a.last().unwrap().im,
1163 0.0,
1164 "Last imaginary component for even lengths must be zero"
1165 );
1166 }
1167 let maxdiff = compare_complex(&out_a, &rustfft_check[0..r2c.complex_len()]);
1168 assert!(
1169 maxdiff < 1.0e-9,
1170 "Length: {}, too large error: {}",
1171 length,
1172 maxdiff
1173 );
1174 }
1175 }
1176
1177 #[test]
1179 fn real_to_complex_32() {
1180 for length in 1..1000 {
1181 let mut real_planner = RealFftPlanner::<f32>::new();
1182 let r2c = real_planner.plan_fft_forward(length);
1183 let mut out_a = r2c.make_output_vec();
1184 let mut indata = r2c.make_input_vec();
1185 let mut rng = rand::thread_rng();
1186 for val in indata.iter_mut() {
1187 *val = rng.gen::<f32>();
1188 }
1189 let mut rustfft_check = indata
1190 .iter()
1191 .map(Complex::from)
1192 .collect::<Vec<Complex<f32>>>();
1193 let mut fft_planner = FftPlanner::<f32>::new();
1194 let fft = fft_planner.plan_fft_forward(length);
1195 fft.process(&mut rustfft_check);
1196 r2c.process(&mut indata, &mut out_a).unwrap();
1197 assert_eq!(out_a[0].im, 0.0, "First imaginary component must be zero");
1198 if length % 2 == 0 {
1199 assert_eq!(
1200 out_a.last().unwrap().im,
1201 0.0,
1202 "Last imaginary component for even lengths must be zero"
1203 );
1204 }
1205 let maxdiff = compare_complex(&out_a, &rustfft_check[0..r2c.complex_len()]);
1206 assert!(
1207 maxdiff < 5.0e-4,
1208 "Length: {}, too large error: {}",
1209 length,
1210 maxdiff
1211 );
1212 }
1213 }
1214
1215 #[allow(dead_code)]
1217 fn test_error() -> Result<(), Box<dyn Error>> {
1218 let mut real_planner = RealFftPlanner::<f64>::new();
1219 let r2c = real_planner.plan_fft_forward(100);
1220 let mut out_a = r2c.make_output_vec();
1221 let mut indata = r2c.make_input_vec();
1222 r2c.process(&mut indata, &mut out_a)?;
1223 Ok(())
1224 }
1225}