### ComplexToRealEven Initialization Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Constructor and setup logic for even-length complex-to-real inverse FFTs. ```rust impl ComplexToRealEven { /// Create a new ComplexToRealEven inverse FFT for complex input spectra. /// The `length` parameter refers to the length of the resulting real-valued signal. /// Uses the given FftPlanner to build the inner FFT. /// Panics if the length is not even. pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self { if length % 2 > 0 { panic!("Length must be even, got {}", length,); } let twiddle_count = if length % 4 == 0 { length / 4 } else { length / 4 + 1 }; let twiddles: Vec> = (1..twiddle_count) .map(|i| compute_twiddle(i, length).conj()) .collect(); let fft = fft_planner.plan_fft_inverse(length / 2); ``` -------------------------------- ### Representing Real-to-Complex FFT Data Source: https://docs.rs/realfft/3.5.0/index.html Examples of data layouts for real-valued vectors and their corresponding FFT outputs. ```text x = [x0r, x1r, x2r, x3r, x4r, x5r] ``` ```text x_c = [(x0r, 0), (x1r, 0), (x2r, 0), (x3r, 0), (x4r, 0), (x5r, 0)] ``` ```text FFT(x_c) = [(X0r, X0i), (X1r, X1i), (X2r, X2i), (X3r, X3i), (X4r, X4i), (X5r, X5i)] ``` ```text FFT(x_c) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, 0), (X2r, -X2i), (X1r, -X1i)] ``` ```text FFT(x_c) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, X3i), (X3r, -X3i), (X2r, -X2i), (X1r, -X1i)] ``` ```text RealFFT(x) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, 0)] ``` ```text RealFFT(x) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, X3i)] ``` -------------------------------- ### Representing real-valued signal as complex Source: https://docs.rs/realfft/3.5.0/realfft Example of a 6-element real vector converted to complex by adding zero-valued imaginary parts. ```text x = [x0r, x1r, x2r, x3r, x4r, x5r] ``` ```text x_c = [(x0r, 0), (x1r, 0), (x2r, 0), (x3r, 0), (x4r, 0), (x5r, 0)] ``` -------------------------------- ### Generate and run benchmarks Source: https://docs.rs/realfft/3.5.0/realfft Commands to generate documentation and run performance benchmarks for the crate. ```bash cargo doc --open ``` ```bash cargo bench ``` -------------------------------- ### RealFftPlanner::new Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealFftPlanner.html Creates a new instance of the RealFftPlanner. ```APIDOC ### pub fn new() -> Self Creates a new planner instance. ``` -------------------------------- ### Generate documentation Source: https://docs.rs/realfft/3.5.0/realfft/index.html Command to generate and open the crate documentation. ```bash cargo doc --open ``` -------------------------------- ### Run benchmarks Source: https://docs.rs/realfft/3.5.0/realfft/index.html Command to execute performance benchmarks. ```bash cargo bench ``` -------------------------------- ### RealFftPlanner Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html A planner used to create and cache FFT instances. ```APIDOC ## RealFftPlanner ### Description Used to create FFT instances. It caches results internally, so it is recommended to reuse the same planner instance. ### Methods - **new() -> Self**: Creates a new RealFftPlanner instance. ``` -------------------------------- ### Utility Methods Source: https://docs.rs/realfft/3.5.0/realfft/trait.RealToComplex.html Methods for retrieving FFT configuration details and creating appropriately sized vectors. ```APIDOC ## Utility Methods ### fn get_scratch_len(&self) -> usize Returns the minimum length required for the scratch buffer. ### fn len(&self) -> usize Returns the length of the real signal input. ### fn complex_len(&self) -> usize Returns the number of complex data points in the output. ### fn make_input_vec(&self) -> Vec Creates an input vector of the correct type and length. ### fn make_output_vec(&self) -> Vec> Creates an output vector of the correct type and length. ### fn make_scratch_vec(&self) -> Vec> Creates a scratch vector of the correct type and length. ``` -------------------------------- ### RealFftPlanner Source: https://docs.rs/realfft/3.5.0/realfft/all.html The primary entry point for creating FFT plans for real-to-complex and complex-to-real transformations. ```APIDOC ## RealFftPlanner ### Description Provides methods to plan FFT operations for real-to-complex and complex-to-real transformations. ### Usage Use `RealFftPlanner` to instantiate the appropriate FFT processors based on the desired transform type and length. ``` -------------------------------- ### RealToComplexEven Initialization Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Initialization logic for even-length real-to-complex FFTs, which panics if the length is not even. ```rust impl RealToComplexEven { /// Create a new RealToComplex forward FFT for real-valued input data of a given length, /// and uses the given FftPlanner to build the inner FFT. /// Panics if the length is not even. pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self { if length % 2 > 0 { panic!("Length must be even, got {}", length,); } ``` -------------------------------- ### Plan Real-to-Complex and Complex-to-Real FFTs Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Methods for planning forward and inverse FFTs with caching to reuse existing references for the same length. ```rust pub fn plan_fft_forward(&mut self, len: usize) -> Arc> { if let Some(fft) = self.r2c_cache.get(&len) { Arc::clone(fft) } else { let fft = if len % 2 > 0 { Arc::new(RealToComplexOdd::new(len, &mut self.planner)) as Arc> } else { Arc::new(RealToComplexEven::new(len, &mut self.planner)) as Arc> }; self.r2c_cache.insert(len, Arc::clone(&fft)); fft } } /// Plan a complex-to-real inverse FFT. Returns the FFT in a shared reference. /// If requesting a second inverse FFT of the same length, /// the planner will return a new reference to the already existing one. pub fn plan_fft_inverse(&mut self, len: usize) -> Arc> { if let Some(fft) = self.c2r_cache.get(&len) { Arc::clone(fft) } else { let fft = if len % 2 > 0 { Arc::new(ComplexToRealOdd::new(len, &mut self.planner)) as Arc> } else { Arc::new(ComplexToRealEven::new(len, &mut self.planner)) as Arc> }; self.c2r_cache.insert(len, Arc::clone(&fft)); fft } } ``` -------------------------------- ### Perform Forward and Inverse FFT with RealFftPlanner Source: https://docs.rs/realfft/3.5.0/realfft Use RealFftPlanner to plan and execute forward and inverse FFTs on real-valued data. ```rust use realfft::RealFftPlanner; use rustfft::num_complex::Complex; use rustfft::num_traits::Zero; let length = 256; // make a planner let mut real_planner = RealFftPlanner::::new(); // create a FFT let r2c = real_planner.plan_fft_forward(length); // make a dummy real-valued signal (filled with zeros) let mut indata = r2c.make_input_vec(); // make a vector for storing the spectrum let mut spectrum = r2c.make_output_vec(); // Are they the length we expect? assert_eq!(indata.len(), length); assert_eq!(spectrum.len(), length/2+1); // forward transform the signal r2c.process(&mut indata, &mut spectrum).unwrap(); // create an inverse FFT let c2r = real_planner.plan_fft_inverse(length); // create a vector for storing the output let mut outdata = c2r.make_output_vec(); assert_eq!(outdata.len(), length); // inverse transform the spectrum back to a real-valued signal c2r.process(&mut spectrum, &mut outdata).unwrap(); ``` -------------------------------- ### RealToComplexOdd Initialization and Processing Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Handles creation and processing logic for odd-length real-to-complex FFTs. ```rust impl RealToComplexOdd { /// Create a new RealToComplex forward FFT for real-valued input data of a given length, /// and uses the given FftPlanner to build the inner FFT. /// Panics if the length is not odd. pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self { if length % 2 == 0 { panic!("Length must be odd, got {}", length,); } let fft = fft_planner.plan_fft_forward(length); let scratch_len = fft.get_inplace_scratch_len() + length; RealToComplexOdd { length, fft, scratch_len, } } } impl RealToComplex for RealToComplexOdd { fn process(&self, input: &mut [T], output: &mut [Complex]) -> Res<()> { let mut scratch = self.make_scratch_vec(); self.process_with_scratch(input, output, &mut scratch) } fn process_with_scratch( &self, input: &mut [T], output: &mut [Complex], scratch: &mut [Complex], ) -> Res<()> { if input.len() != self.length { return Err(FftError::InputBuffer(self.length, input.len())); } let expected_output_buffer_size = self.complex_len(); if output.len() != expected_output_buffer_size { return Err(FftError::OutputBuffer( expected_output_buffer_size, output.len(), )); } if scratch.len() < (self.scratch_len) { return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len())); } let (buffer, fft_scratch) = scratch.split_at_mut(self.length); for (val, buf) in input.iter().zip(buffer.iter_mut()) { *buf = Complex::new(*val, T::zero()); } // FFT and store result in buffer_out self.fft.process_with_scratch(buffer, fft_scratch); output.copy_from_slice(&buffer[0..self.complex_len()]); if let Some(elem) = output.first_mut() { elem.im = T::zero(); } Ok(()) } fn get_scratch_len(&self) -> usize { self.scratch_len } fn len(&self) -> usize { self.length } fn make_input_vec(&self) -> Vec { vec![T::zero(); self.len()] } fn make_output_vec(&self) -> Vec> { vec![Complex::zero(); self.complex_len()] } fn make_scratch_vec(&self) -> Vec> { vec![Complex::zero(); self.get_scratch_len()] } } ``` -------------------------------- ### Default Implementation for RealFftPlanner Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Provides a default implementation for the RealFftPlanner using its new method. ```rust impl Default for RealFftPlanner { fn default() -> Self { Self::new() } } ``` -------------------------------- ### ComplexToRealEven::process_with_scratch Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealEven.html Performs an inverse transform using a provided scratch buffer. ```APIDOC ## fn process_with_scratch(&self, input: &mut [Complex], output: &mut [T], scratch: &mut [Complex]) -> Result<(), FftError> ### Description Inverse transform a complex spectrum, similar to `process()`. This method uses the provided scratch buffer instead of allocating new scratch space, which is more efficient for multiple calls. ``` -------------------------------- ### Initialize RealToComplexEven Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Calculates twiddle factors and plans the forward FFT for the RealToComplexEven struct. ```rust let twiddle_count = if length % 4 == 0 { length / 4 } else { length / 4 + 1 }; let twiddles: Vec> = (1..twiddle_count) .map(|i| compute_twiddle(i, length) * T::from_f64(0.5).unwrap()) .collect(); let fft = fft_planner.plan_fft_forward(length / 2); let scratch_len = fft.get_outofplace_scratch_len(); RealToComplexEven { twiddles, length, fft, scratch_len, } } } ``` -------------------------------- ### ComplexToRealOdd Implementation Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Methods for handling odd-length inverse FFTs, including buffer management and scratch space allocation. ```rust fn get_scratch_len(&self) -> usize { self.scratch_len } fn len(&self) -> usize { self.length } fn make_input_vec(&self) -> Vec { vec![T::zero(); self.len()] } fn make_output_vec(&self) -> Vec> { vec![Complex::zero(); self.complex_len()] } fn make_scratch_vec(&self) -> Vec> { vec![Complex::zero(); self.get_scratch_len()] } } impl ComplexToRealOdd { /// Create a new ComplexToRealOdd inverse FFT for complex input spectra. /// The `length` parameter refers to the length of the resulting real-valued signal. /// Uses the given FftPlanner to build the inner FFT. /// Panics if the length is not odd. pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self { if length % 2 == 0 { panic!("Length must be odd, got {}", length,); } let fft = fft_planner.plan_fft_inverse(length); let scratch_len = length + fft.get_inplace_scratch_len(); ComplexToRealOdd { length, fft, scratch_len, } } } ``` -------------------------------- ### ComplexToRealOdd::process_with_scratch Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealOdd.html Performs the inverse transform using a provided scratch buffer for improved performance. ```APIDOC ## fn process_with_scratch(&self, input: &mut [Complex], output: &mut [T], scratch: &mut [Complex]) -> Result<(), FftError> ### Description Inverse transform a complex spectrum, similar to `process()`. The difference is that this method uses the provided scratch buffer instead of allocating new scratch space. This is faster if the same scratch buffer is used for multiple calls. ``` -------------------------------- ### fn process_with_scratch Source: https://docs.rs/realfft/3.5.0/realfft/trait.RealToComplex.html Transforms a signal of N real-valued samples using a provided scratch buffer to avoid internal allocations. ```APIDOC ## fn process_with_scratch(&self, input: &mut [T], output: &mut [Complex], scratch: &mut [Complex]) -> Result<(), FftError> ### Description Similar to process(), but uses the provided scratch buffer instead of allocating new space, which is more efficient for repeated calls. ### Parameters - **input** (&mut [T]) - Required - The real-valued input signal buffer. - **output** (&mut [Complex]) - Required - The buffer to store the resulting complex spectrum. - **scratch** (&mut [Complex]) - Required - A pre-allocated scratch buffer. ### Returns - **Result<(), FftError>** - Returns Ok(()) on success, or an FftError if slice lengths are incorrect. ``` -------------------------------- ### RealFFT Buffer and Vector Management Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Methods for managing scratch space and allocating input/output vectors for FFT operations. ```rust fn get_scratch_len(&self) -> usize { self.scratch_len } fn len(&self) -> usize { self.length } fn make_input_vec(&self) -> Vec> { vec![Complex::zero(); self.complex_len()] } fn make_output_vec(&self) -> Vec { vec![T::zero(); self.len()] } fn make_scratch_vec(&self) -> Vec> { vec![Complex::zero(); self.get_scratch_len()] } ``` -------------------------------- ### ComplexToRealEven::new Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealEven.html Creates a new ComplexToRealEven inverse FFT instance for a specified signal length. ```APIDOC ## pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self ### Description Create a new ComplexToRealEven inverse FFT for complex input spectra. The `length` parameter refers to the length of the resulting real-valued signal. Uses the given FftPlanner to build the inner FFT. Panics if the length is not even. ``` -------------------------------- ### Process FFT with Scratch Space Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Validates input/output buffers and executes the FFT processing logic with scratch space. ```rust impl RealToComplex for RealToComplexEven { fn process(&self, input: &mut [T], output: &mut [Complex]) -> Res<()> { let mut scratch = self.make_scratch_vec(); self.process_with_scratch(input, output, &mut scratch) } fn process_with_scratch( &self, input: &mut [T], output: &mut [Complex], scratch: &mut [Complex], ) -> Res<()> { if input.len() != self.length { return Err(FftError::InputBuffer(self.length, input.len())); } let expected_output_buffer_size = self.complex_len(); if output.len() != expected_output_buffer_size { return Err(FftError::OutputBuffer( expected_output_buffer_size, output.len(), )); } if scratch.len() < (self.scratch_len) { return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len())); } let fftlen = self.length / 2; let buf_in = unsafe { let ptr = input.as_mut_ptr() as *mut Complex; let len = input.len(); std::slice::from_raw_parts_mut(ptr, len / 2) }; // FFT and store result in buffer_out self.fft .process_outofplace_with_scratch(buf_in, &mut output[0..fftlen], scratch); let (mut output_left, mut output_right) = output.split_at_mut(output.len() / 2); // The first and last element don't require any twiddle factors, so skip that work match (output_left.first_mut(), output_right.last_mut()) { (Some(first_element), Some(last_element)) => { // The first and last elements are just a sum and difference of the first value's real and imaginary values let first_value = *first_element; *first_element = Complex { re: first_value.re + first_value.im, im: T::zero(), }; *last_element = Complex { re: first_value.re - first_value.im, im: T::zero(), }; // Chop the first and last element off of our slices so that the loop below doesn't have to deal with them output_left = &mut output_left[1..]; let right_len = output_right.len(); output_right = &mut output_right[..right_len - 1]; } _ => { return Ok(()); } } // Loop over the remaining elements and apply twiddle factors on them for (twiddle, out, out_rev) in zip3( self.twiddles.iter(), output_left.iter_mut(), output_right.iter_mut().rev(), ) { let sum = *out + *out_rev; let diff = *out - *out_rev; let half = T::from_f64(0.5).unwrap(); // Apply twiddle factors. Theoretically we'd have to load 2 separate twiddle factors here, one for the beginning // and one for the end. But the twiddle factor for the end is just the twiddle for the beginning, with the // real part negated. Since it's the same twiddle, we can factor out a ton of math ops and cut the number of // multiplications in half. let twiddled_re_sum = sum * twiddle.re; let twiddled_im_sum = sum * twiddle.im; let twiddled_re_diff = diff * twiddle.re; let twiddled_im_diff = diff * twiddle.im; let half_sum_re = half * sum.re; let half_diff_im = half * diff.im; let output_twiddled_real = twiddled_re_sum.im + twiddled_im_diff.re; let output_twiddled_im = twiddled_im_sum.im - twiddled_re_diff.re; // We finally have all the data we need to write the transformed data back out where we found it. *out = Complex { re: half_sum_re + output_twiddled_real, im: half_diff_im + output_twiddled_im, }; *out_rev = Complex { re: half_sum_re - output_twiddled_real, im: half_diff_im - output_twiddled_im, }; } Ok(()) } } ``` -------------------------------- ### Handle errors with the ? operator Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Demonstrates that the RealFFT process methods are compatible with the ? operator for error propagation. ```rust #[allow(dead_code)] fn test_error() -> Result<(), Box> { let mut real_planner = RealFftPlanner::::new(); let r2c = real_planner.plan_fft_forward(100); let mut out_a = r2c.make_output_vec(); let mut indata = r2c.make_input_vec(); r2c.process(&mut indata, &mut out_a)?; Ok(()) } ``` -------------------------------- ### fn process Source: https://docs.rs/realfft/3.5.0/realfft/trait.RealToComplex.html Transforms a signal of N real-valued samples into a complex spectrum. The input buffer is used as scratch space and its contents should be considered garbage after the call. ```APIDOC ## fn process(&self, input: &mut [T], output: &mut [Complex]) -> Result<(), FftError> ### Description Transforms a signal of N real-valued samples, storing the resulting complex spectrum in the N/2+1 element long output slice. The input buffer is used as scratch space. ### Parameters - **input** (&mut [T]) - Required - The real-valued input signal buffer. - **output** (&mut [Complex]) - Required - The buffer to store the resulting complex spectrum. ### Returns - **Result<(), FftError>** - Returns Ok(()) on success, or an FftError if slice lengths are incorrect. ``` -------------------------------- ### FFT Comparison Utilities Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Helper functions to calculate the maximum difference between complex or scalar arrays. ```rust fn compare_complex(a: &[Complex], b: &[Complex]) -> T { a.iter() .zip(b.iter()) .fold(T::zero(), |maxdiff, (val_a, val_b)| { let diff = (val_a - val_b).norm(); if maxdiff > diff { maxdiff } else { diff } }) } fn compare_scalars(a: &[T], b: &[T]) -> T { a.iter() .zip(b.iter()) .fold(T::zero(), |maxdiff, (val_a, val_b)| { let diff = (*val_a - *val_b).abs(); if maxdiff > diff { maxdiff } else { diff } }) } ``` -------------------------------- ### ComplexToRealEven::process Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealEven.html Performs an inverse transform on a complex spectrum to produce a real-valued signal. ```APIDOC ## fn process(&self, input: &mut [Complex], output: &mut [T]) -> Result<(), FftError> ### Description Inverse transform a complex spectrum corresponding to a real-valued signal of length N. The input is a slice of complex values with length N/2+1. The resulting real-valued signal is stored in the output slice of length N. The input buffer is used as scratch space. ``` -------------------------------- ### RealFftPlanner::plan_fft_forward Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealFftPlanner.html Plans a real-to-complex forward FFT of a specified length. ```APIDOC ### pub fn plan_fft_forward(&mut self, len: usize) -> Arc> Plans a real-to-complex forward FFT. Returns the FFT in a shared reference. If requesting a second forward FFT of the same length, the planner will return a new reference to the already existing one. ``` -------------------------------- ### RealToComplexEven::process_with_scratch Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealToComplexEven.html Transforms a signal of N real-valued samples using a provided scratch buffer. ```APIDOC ## fn process_with_scratch(&self, input: &mut [T], output: &mut [Complex], scratch: &mut [Complex]) -> Result<(), FftError> ### Description Transform a signal of N real-valued samples, similar to process(). This method uses the provided scratch buffer instead of allocating new scratch space, which is faster for multiple calls. ``` -------------------------------- ### Compare RealToComplex with Standard FFT Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Validates the forward RealToComplex transformation by comparing results against a standard FFT implementation for various lengths. ```rust #[test] fn real_to_complex_64() { for length in 1..1000 { let mut real_planner = RealFftPlanner::::new(); let r2c = real_planner.plan_fft_forward(length); let mut out_a = r2c.make_output_vec(); let mut indata = r2c.make_input_vec(); let mut rng = rand::rng(); for val in indata.iter_mut() { *val = rng.random::(); } let mut rustfft_check = indata .iter() .map(Complex::from) .collect::>>(); let mut fft_planner = FftPlanner::::new(); let fft = fft_planner.plan_fft_forward(length); fft.process(&mut rustfft_check); r2c.process(&mut indata, &mut out_a).unwrap(); assert_eq!(out_a[0].im, 0.0, "First imaginary component must be zero"); if length % 2 == 0 { assert_eq!( out_a.last().unwrap().im, 0.0, "Last imaginary component for even lengths must be zero" ); } let maxdiff = compare_complex(&out_a, &rustfft_check[0..r2c.complex_len()]); assert!( maxdiff < 1.0e-9, ); } } ``` -------------------------------- ### ComplexToRealOdd Processing Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Core processing logic for odd-length complex-to-real inverse FFTs. ```rust impl ComplexToReal for ComplexToRealOdd { fn process(&self, input: &mut [Complex], output: &mut [T]) -> Res<()> { let mut scratch = self.make_scratch_vec(); self.process_with_scratch(input, output, &mut scratch) } fn process_with_scratch( &self, input: &mut [Complex], output: &mut [T], scratch: &mut [Complex], ) -> Res<()> { let expected_input_buffer_size = self.complex_len(); if input.len() != expected_input_buffer_size { return Err(FftError::InputBuffer( expected_input_buffer_size, input.len(), )); } if output.len() != self.length { return Err(FftError::OutputBuffer(self.length, output.len())); } if scratch.len() < (self.scratch_len) { return Err(FftError::ScratchBuffer(self.scratch_len, scratch.len())); } let first_invalid = if input[0].im != T::from_f64(0.0).unwrap() { input[0].im = T::from_f64(0.0).unwrap(); true } else { false }; let (buffer, fft_scratch) = scratch.split_at_mut(self.length); buffer[0..input.len()].copy_from_slice(input); for (buf, val) in buffer .iter_mut() .rev() .take(self.length / 2) .zip(input.iter().skip(1)) { *buf = val.conj(); } self.fft.process_with_scratch(buffer, fft_scratch); for (val, out) in buffer.iter().zip(output.iter_mut()) { *out = val.re; } if first_invalid { return Err(FftError::InputValues(true, false)); } Ok(()) } fn get_scratch_len(&self) -> usize { self.scratch_len } fn len(&self) -> usize { self.length } fn make_input_vec(&self) -> Vec> { vec![Complex::zero(); self.complex_len()] } fn make_output_vec(&self) -> Vec { vec![T::zero(); self.len()] } fn make_scratch_vec(&self) -> Vec> { vec![Complex::zero(); self.get_scratch_len()] } } ``` -------------------------------- ### RealToComplexEven::new Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealToComplexEven.html Creates a new RealToComplex forward FFT instance for real-valued input data of a specified length. ```APIDOC ## pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self ### Description Creates a new RealToComplex forward FFT for real-valued input data of a given length, and uses the given FftPlanner to build the inner FFT. Panics if the length is not even. ``` -------------------------------- ### RealToComplexOdd::process_with_scratch Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealToComplexOdd.html Transforms a signal of N real-valued samples using a provided scratch buffer. ```APIDOC ## fn process_with_scratch(&self, input: &mut [T], output: &mut [Complex], scratch: &mut [Complex]) -> Result<(), FftError> ### Description Transform a signal of N real-valued samples, similar to process(). This method uses the provided scratch buffer instead of allocating new scratch space, which is more efficient for multiple calls. ``` -------------------------------- ### RealFftPlanner::plan_fft_inverse Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealFftPlanner.html Plans a complex-to-real inverse FFT of a specified length. ```APIDOC ### pub fn plan_fft_inverse(&mut self, len: usize) -> Arc> Plans a complex-to-real inverse FFT. Returns the FFT in a shared reference. If requesting a second inverse FFT of the same length, the planner will return a new reference to the already existing one. ``` -------------------------------- ### ComplexToRealOdd::new Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealOdd.html Creates a new ComplexToRealOdd inverse FFT instance. The length parameter specifies the length of the resulting real-valued signal and must be odd. ```APIDOC ## pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self ### Description Create a new ComplexToRealOdd inverse FFT for complex input spectra. The `length` parameter refers to the length of the resulting real-valued signal. Uses the given FftPlanner to build the inner FFT. Panics if the length is not odd. ``` -------------------------------- ### ComplexToRealEven::get_scratch_len Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealEven.html Returns the required length for the scratch buffer. ```APIDOC ## fn get_scratch_len(&self) -> usize ### Description Get the minimum length of the scratch space needed for `process_with_scratch`. ``` -------------------------------- ### Compare RealToComplex with standard FFT Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Validates the RealToComplex transformation by comparing output against a standard complex FFT implementation across various lengths. ```rust #[test] fn real_to_complex_32() { for length in 1..1000 { let mut real_planner = RealFftPlanner::::new(); let r2c = real_planner.plan_fft_forward(length); let mut out_a = r2c.make_output_vec(); let mut indata = r2c.make_input_vec(); let mut rng = rand::rng(); for val in indata.iter_mut() { *val = rng.random::(); } let mut rustfft_check = indata .iter() .map(Complex::from) .collect::>>(); let mut fft_planner = FftPlanner::::new(); let fft = fft_planner.plan_fft_forward(length); fft.process(&mut rustfft_check); r2c.process(&mut indata, &mut out_a).unwrap(); assert_eq!(out_a[0].im, 0.0, "First imaginary component must be zero"); if length % 2 == 0 { assert_eq!( out_a.last().unwrap().im, 0.0, "Last imaginary component for even lengths must be zero" ); } let maxdiff = compare_complex(&out_a, &rustfft_check[0..r2c.complex_len()]); assert!( maxdiff < 5.0e-4, "Length: {}, too large error: {}", length, maxdiff ); } } ``` -------------------------------- ### RealFftPlanner Structure Definition Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealFftPlanner.html The internal structure definition for RealFftPlanner, which requires the FftNum trait. ```rust pub struct RealFftPlanner { /* private fields */ } ``` -------------------------------- ### Re-exporting dependencies Source: https://docs.rs/realfft/3.5.0/realfft Exposes num_complex and num_traits from the rustfft crate. ```rust pub use rustfft::num_complex; ``` ```rust pub use rustfft::num_traits; ``` -------------------------------- ### ComplexToRealOdd::process Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealOdd.html Performs the inverse transform on a complex spectrum to produce a real-valued signal. ```APIDOC ## fn process(&self, input: &mut [Complex], output: &mut [T]) -> Result<(), FftError> ### Description Inverse transform a complex spectrum corresponding to a real-valued signal of length N. The input is a slice of complex values with length N/2+1 (with N/2 rounded down). The resulting real-valued signal is stored in the output slice of length N. The input buffer is used as scratch space, so the contents of input should be considered garbage after calling. ``` -------------------------------- ### Traits Source: https://docs.rs/realfft/3.5.0/realfft/all.html Core traits defining the interface for FFT operations. ```APIDOC ## Traits ### ComplexToReal Trait for complex-to-real FFT operations. ### RealToComplex Trait for real-to-complex FFT operations. ### FftNum Trait defining the numeric requirements for FFT operations. ``` -------------------------------- ### RealToComplexOdd::new Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealToComplexOdd.html Creates a new RealToComplex forward FFT instance for real-valued input data of a specified odd length. ```APIDOC ## pub fn new(length: usize, fft_planner: &mut FftPlanner) -> Self ### Description Creates a new RealToComplex forward FFT for real-valued input data of a given length, and uses the given FftPlanner to build the inner FFT. Panics if the length is not odd. ``` -------------------------------- ### ComplexToReal Inverse FFT Validation Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Unit tests comparing RealFFT inverse operations against standard rustfft inverse FFT for f64 and f32 precision. ```rust #[test] fn complex_to_real_64() { for length in 1..1000 { let mut real_planner = RealFftPlanner::::new(); let c2r = real_planner.plan_fft_inverse(length); let mut out_a = c2r.make_output_vec(); let mut indata = c2r.make_input_vec(); let mut rustfft_check: Vec> = vec![Complex::zero(); length]; let mut rng = rand::rng(); for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } indata[0].im = 0.0; if length % 2 == 0 { indata[length / 2].im = 0.0; } for (val_long, val) in rustfft_check .iter_mut() .take(c2r.complex_len()) .zip(indata.iter()) { *val_long = *val; } for (val_long, val) in rustfft_check .iter_mut() .rev() .take(length / 2) .zip(indata.iter().skip(1)) { *val_long = val.conj(); } let mut fft_planner = FftPlanner::::new(); let fft = fft_planner.plan_fft_inverse(length); c2r.process(&mut indata, &mut out_a).unwrap(); fft.process(&mut rustfft_check); let check_real = rustfft_check.iter().map(|val| val.re).collect::>(); let maxdiff = compare_scalars(&out_a, &check_real); assert!( maxdiff < 1.0e-9, "Length: {}, too large error: {}", length, maxdiff ); } } ``` ```rust #[test] fn complex_to_real_32() { for length in 1..1000 { let mut real_planner = RealFftPlanner::::new(); let c2r = real_planner.plan_fft_inverse(length); let mut out_a = c2r.make_output_vec(); let mut indata = c2r.make_input_vec(); let mut rustfft_check: Vec> = vec![Complex::zero(); length]; let mut rng = rand::rng(); for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } indata[0].im = 0.0; if length % 2 == 0 { indata[length / 2].im = 0.0; } for (val_long, val) in rustfft_check .iter_mut() .take(c2r.complex_len()) .zip(indata.iter()) { *val_long = *val; } ``` -------------------------------- ### RealToComplexEven::process Source: https://docs.rs/realfft/3.5.0/realfft/struct.RealToComplexEven.html Transforms a signal of N real-valued samples into a complex spectrum. ```APIDOC ## fn process(&self, input: &mut [T], output: &mut [Complex]) -> Result<(), FftError> ### Description Transform a signal of N real-valued samples, storing the resulting complex spectrum in the N/2+1 element long output slice. The input buffer is used as scratch space, so the contents of input should be considered garbage after calling. ``` -------------------------------- ### Complex FFT output of real-valued input Source: https://docs.rs/realfft/3.5.0/realfft The resulting complex spectrum from a standard complex FFT on a real-valued input. ```text FFT(x_c) = [(X0r, X0i), (X1r, X1i), (X2r, X2i), (X3r, X3i), (X4r, X4i), (X5r, X5i)] ``` ```text FFT(x_c) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, 0), (X2r, -X2i), (X1r, -X1i)] ``` ```text FFT(x_c) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, X3i), (X3r, -X3i), (X2r, -X2i), (X1r, -X1i)] ``` -------------------------------- ### Real-to-complex FFT output Source: https://docs.rs/realfft/3.5.0/realfft The compact output format produced by the RealFFT library for real-valued inputs. ```text RealFFT(x) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, 0)] ``` ```text RealFFT(x) = [(X0r, 0), (X1r, X1i), (X2r, X2i), (X3r, X3i)] ``` -------------------------------- ### ComplexToRealEven Struct Definition Source: https://docs.rs/realfft/3.5.0/realfft/struct.ComplexToRealEven.html The definition of the ComplexToRealEven struct. ```rust pub struct ComplexToRealEven { /* private fields */ } ``` -------------------------------- ### Compute twiddle factors Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Internal helper function to calculate complex twiddle factors for FFT algorithms. ```rust fn compute_twiddle(index: usize, fft_len: usize) -> Complex { let constant = -2f64 * std::f64::consts::PI / fft_len as f64; let angle = constant * index as f64; Complex { re: T::from_f64(angle.cos()).unwrap(), im: T::from_f64(angle.sin()).unwrap(), } } ``` -------------------------------- ### FftNum Trait Implementation Source: https://docs.rs/realfft/3.5.0/realfft/trait.FftNum.html The blanket implementation of FftNum for types satisfying the required trait bounds. ```rust impl FftNum for T where T: Copy + FromPrimitive + Signed + Sync + Send + Debug + 'static, ``` -------------------------------- ### Validate ComplexToReal Error Handling for Odd Lengths Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Tests that the inverse FFT correctly identifies invalid input data at the first point for odd-length sequences. ```rust #[test] fn complex_to_real_errors_odd() { let length = 101; let mut real_planner = RealFftPlanner::::new(); let c2r = real_planner.plan_fft_inverse(length); let mut out_a = c2r.make_output_vec(); let mut indata = c2r.make_input_vec(); let mut rng = rand::rng(); // Make some valid data for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } indata[0].im = 0.0; // this should be ok assert!(c2r.process(&mut indata, &mut out_a).is_ok()); // Make some invalid data, first point invalid for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } let res = c2r.process(&mut indata, &mut out_a); assert!(res.is_err()); assert!(matches!(res, Err(FftError::InputValues(true, false)))); } ``` -------------------------------- ### Validate ComplexToReal Error Handling for Even Lengths Source: https://docs.rs/realfft/3.5.0/src/realfft/lib.rs.html Tests that the inverse FFT correctly identifies invalid input data at the first and last points for even-length sequences. ```rust #[test] fn complex_to_real_errors_even() { let length = 100; let mut real_planner = RealFftPlanner::::new(); let c2r = real_planner.plan_fft_inverse(length); let mut out_a = c2r.make_output_vec(); let mut indata = c2r.make_input_vec(); let mut rng = rand::rng(); // Make some valid data for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } indata[0].im = 0.0; indata[50].im = 0.0; // this should be ok assert!(c2r.process(&mut indata, &mut out_a).is_ok()); // Make some invalid data, first point invalid for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } indata[50].im = 0.0; let res = c2r.process(&mut indata, &mut out_a); assert!(res.is_err()); assert!(matches!(res, Err(FftError::InputValues(true, false)))); // Make some invalid data, last point invalid for val in indata.iter_mut() { *val = Complex::new(rng.random::(), rng.random::()); } indata[0].im = 0.0; let res = c2r.process(&mut indata, &mut out_a); assert!(res.is_err()); assert!(matches!(res, Err(FftError::InputValues(false, true)))); } ``` -------------------------------- ### Define FftError Enum Source: https://docs.rs/realfft/3.5.0/realfft/enum.FftError.html The structure of the FftError enum, detailing variants for input, output, and scratch buffer size errors, as well as input value validation errors. ```rust pub enum FftError { InputBuffer(usize, usize), OutputBuffer(usize, usize), ScratchBuffer(usize, usize), InputValues(bool, bool), } ```