├── .gitignore ├── examples ├── fft1.rs ├── fft_norm.rs ├── rfft2.rs └── fft2.rs ├── Cargo.toml ├── LICENSE ├── CHANGELOG.md ├── benches ├── ndrustfft.rs └── ndrustfft_par.rs ├── README.md ├── Cargo.lock └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /src/main.rs 3 | -------------------------------------------------------------------------------- /examples/fft1.rs: -------------------------------------------------------------------------------- 1 | //! Perform 1-dimensional Fourier Transform. 2 | //! 3 | //! cargo run --example fft1 4 | use ndarray::{array, Array1}; 5 | use ndrustfft::{ndfft, ndifft}; 6 | use ndrustfft::{Complex, FftHandler}; 7 | 8 | fn main() { 9 | let mut x = array![1., 2., 3.].mapv(|x| Complex::new(x, x)); 10 | let mut xhat = Array1::zeros(3); 11 | let mut handler = FftHandler::::new(3); 12 | ndfft(&x, &mut xhat, &mut handler, 0); 13 | ndifft(&xhat, &mut x, &mut handler, 0); 14 | println!("xhat: {xhat}"); 15 | println!("x: {x}"); // 1+1.j, 2+2.j, 3+3.j 16 | } 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ndrustfft" 3 | version = "0.6.0" 4 | authors = ["preiter "] 5 | edition = "2018" 6 | description = "N-dimensional FFT, real-to-complex FFT and real-to-real DCT" 7 | repository = "https://github.com/preiter93/ndrustfft" 8 | keywords = ["fft", "rustfft", "realfft", "rustdct", "ndarray"] 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | [lib] 13 | name = "ndrustfft" 14 | path = "src/lib.rs" 15 | 16 | [dependencies] 17 | ndarray = { version = "0.17" } 18 | rustfft = { version = "6", default-features = false } 19 | num-traits = "0.2" 20 | rustdct = "0.7" 21 | realfft = "3" 22 | 23 | [dev-dependencies] 24 | criterion = { version = "0.3.4", features = ["html_reports"] } 25 | 26 | [[bench]] 27 | name = "ndrustfft" 28 | harness = false 29 | 30 | [[bench]] 31 | name = "ndrustfft_par" 32 | harness = false 33 | 34 | [features] 35 | default = ["avx", "sse", "neon", "parallel"] 36 | avx = ["rustfft/avx"] 37 | sse = ["rustfft/sse"] 38 | neon = ["rustfft/neon"] 39 | parallel = ["ndarray/rayon"] 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Philipp Reiter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Released 2 | -------- 3 | 4 | 0.7.0 - 08 Nov 2025 5 | =================== 6 | 7 | - Bump ndarray to 0.17 8 | 9 | 0.5.0 - 18 Aug 2024 10 | =================== 11 | 12 | - Bump ndarray to 0.16 13 | 14 | 0.4.5 - 27 June 2024 15 | =================== 16 | 17 | - Use non-mutable references to handlers 18 | 19 | 0.4.4 - 17 April 2024 20 | =================== 21 | 22 | - Improved documentation 23 | 24 | 0.4.3 - 17 April 2024 25 | =================== 26 | 27 | - Fixed mistake in documentation 28 | 29 | 0.4.2 - 28 October 2023 30 | =================== 31 | 32 | Added 33 | ------- 34 | Feature flags (all enabled by default) 35 | - parallel: Enables parallel iterators 36 | - avx/sse/neon: Enables rustfft's feature flags 37 | 38 | 0.4.1 - 23 June 2023 39 | =================== 40 | 41 | Bugfix 42 | ------- 43 | - Fixed normalization of rfft for inputs with odd number of elements 44 | 45 | 0.4 - 15 March 2023 46 | =================== 47 | 48 | Changed 49 | ------- 50 | - Add support for non-standard layout input arrays. 51 | - Add support for custom fft normalization. See `examples/fft_norm`. 52 | - Remove specification of the minor version, use `rustfft` v6 53 | - Remove specification of the minor version, use `realfft` v3 54 | - Better performance 55 | 56 | 0.3.0 57 | ===== 58 | 59 | Changed 60 | ------- 61 | - Update of `realfft` and `realdct` 62 | 63 | 0.2.2 64 | ===== 65 | 66 | Changed 67 | ------- 68 | - The undefined first and last elements of real-to-complex transforms are now actively set to zero 69 | 70 | -------------------------------------------------------------------------------- /examples/fft_norm.rs: -------------------------------------------------------------------------------- 1 | //! Perform 1-dimensional Fourier Transform with 2 | //! different normalizations 3 | //! 4 | //! cargo run --example fft_norm 5 | use ndarray::{array, Array1}; 6 | use ndrustfft::{ndfft, ndifft, Complex, FftHandler, Normalization}; 7 | 8 | fn main() { 9 | let n = 3; 10 | let v: Array1> = array![1., 2., 3.].mapv(|x| Complex::new(x, x)); 11 | let mut vhat: Array1> = Array1::zeros(n); 12 | let mut v2: Array1> = Array1::zeros(n); 13 | 14 | println!("{:?}", v); // 1+1.j, 2+2.j, 3+3.j 15 | 16 | // Default normalization 17 | let mut handler = FftHandler::::new(n).normalization(Normalization::Default); 18 | ndfft(&v.clone(), &mut vhat, &mut handler, 0); 19 | ndifft(&vhat, &mut v2, &mut handler, 0); 20 | println!("{:?}", v2); // 1+1.j, 2+2.j, 3+3.j 21 | 22 | // No normalization 23 | let mut handler = FftHandler::::new(n).normalization(Normalization::None); 24 | ndfft(&v.clone(), &mut vhat, &mut handler, 0); 25 | ndifft(&vhat, &mut v2, &mut handler, 0); 26 | println!("{:?}", v2); // 3+3.j, 6+6.j, 9+9.j 27 | 28 | // Custom normalization 29 | let mut handler = FftHandler::::new(n).normalization(Normalization::Custom(my_norm)); 30 | ndfft(&v.clone(), &mut vhat, &mut handler, 0); 31 | ndifft(&vhat, &mut v2, &mut handler, 0); 32 | println!("{:?}", v2); // 2+2.j, 4+4.j, 6+6.j 33 | } 34 | 35 | fn my_norm(data: &mut [Complex]) { 36 | let n = 2. / data.len() as f64; 37 | for d in data.iter_mut() { 38 | *d = *d * n; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/rfft2.rs: -------------------------------------------------------------------------------- 1 | //! Perform 2-dimensional Fourier Transform. 2 | //! 3 | //! For real-to-complex transforms, the real transform 4 | //! is performed over the last axis, whie the remaining 5 | //! transforms are complex. 6 | //! 7 | //! cargo run --example rfft2 8 | use ndarray::{array, Array2, Zip}; 9 | use ndrustfft::{ndfft, ndfft_r2c, ndifft, ndifft_r2c}; 10 | use ndrustfft::{Complex, FftHandler, R2cFftHandler}; 11 | // // Use parallel transforms: 12 | // use ndrustfft::{ 13 | // ndfft_par as ndfft, ndfft_r2c_par as ndfft_r2c, ndifft_par as ndifft, 14 | // ndifft_r2c_par as ndifft_r2c, 15 | // }; 16 | 17 | fn main() { 18 | let (nx, ny) = (3, 3); 19 | 20 | // Init arrays 21 | let v = array![[1., 2., 3.], [4., 5., 6.], [7., 8., 9.],]; 22 | let mut vhat: Array2> = Array2::zeros((nx, ny / 2 + 1)); 23 | 24 | // Init handlers 25 | let mut handler_ax0 = FftHandler::::new(nx); 26 | let mut handler_ax1 = R2cFftHandler::::new(ny); 27 | 28 | // Transform 29 | { 30 | let mut work: Array2> = Array2::zeros((nx, ny / 2 + 1)); 31 | ndfft_r2c(&v, &mut work, &mut handler_ax1, 1); 32 | ndfft(&work, &mut vhat, &mut handler_ax0, 0); 33 | } 34 | 35 | // Assert with numpys solution (rounded to five digits) 36 | let numpy_vhat = array![ 37 | [Complex::new(45., 0.), Complex::new(-4.5, 2.59808)], 38 | [Complex::new(-13.5, 7.79423), Complex::new(0., 0.)], 39 | [Complex::new(-13.5, -7.79423), Complex::new(0., 0.)], 40 | ]; 41 | Zip::from(&vhat).and(&numpy_vhat).for_each(|&x, &y| { 42 | if (x.re - y.re).abs() > 1e-4 || (x.im - y.im).abs() > 1e-4 { 43 | panic!("Large difference of values, got {} expected {}.", x, y) 44 | }; 45 | }); 46 | 47 | // Inverse Transform 48 | let mut v_new: Array2 = Array2::zeros((nx, ny)); 49 | { 50 | let mut work: Array2> = Array2::zeros((nx, ny / 2 + 1)); 51 | ndifft(&vhat, &mut work, &mut handler_ax0, 0); 52 | ndifft_r2c(&work, &mut v_new, &mut handler_ax1, 1); 53 | } 54 | 55 | // Assert with original array 56 | Zip::from(&v_new).and(&v).for_each(|&x, &y| { 57 | if (x - y).abs() > 1e-4 { 58 | panic!("Large difference of values, got {} expected {}.", x, y) 59 | }; 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /examples/fft2.rs: -------------------------------------------------------------------------------- 1 | //! Perform 2-dimensional Fourier Transform. 2 | //! 3 | //! cargo run --example fft2 4 | use ndarray::{array, Array2, Zip}; 5 | use ndrustfft::{ndfft, ndifft}; 6 | use ndrustfft::{Complex, FftHandler}; 7 | // // Use parallel transforms: 8 | // use ndrustfft::{ndfft_par as ndfft, ndifft_par as ndifft}; 9 | 10 | fn main() { 11 | let (nx, ny) = (3, 3); 12 | 13 | // Init arrays 14 | let v: Array2> = 15 | array![[1., 2., 3.], [4., 5., 6.], [7., 8., 9.],].mapv(|x| Complex::new(x, x)); 16 | let mut vhat: Array2> = Array2::zeros((nx, ny)); 17 | 18 | // Init handlers 19 | let mut handler_ax0 = FftHandler::::new(nx); 20 | let mut handler_ax1 = FftHandler::::new(ny); 21 | 22 | // Transform 23 | { 24 | let mut work: Array2> = Array2::zeros((nx, ny)); 25 | ndfft(&v, &mut work, &mut handler_ax1, 1); 26 | ndfft(&work, &mut vhat, &mut handler_ax0, 0); 27 | } 28 | 29 | // Assert with numpys solution (rounded to five digits) 30 | let numpy_vhat = array![ 31 | [ 32 | Complex::new(45., 45.), 33 | Complex::new(-7.09808, -1.90192), 34 | Complex::new(-1.90192, -7.09808) 35 | ], 36 | [ 37 | Complex::new(-21.29423, -5.70577), 38 | Complex::new(0., 0.), 39 | Complex::new(0., 0.) 40 | ], 41 | [ 42 | Complex::new(-5.70577, -21.29423), 43 | Complex::new(0., 0.), 44 | Complex::new(0., 0.) 45 | ], 46 | ]; 47 | Zip::from(&vhat).and(&numpy_vhat).for_each(|&x, &y| { 48 | if (x.re - y.re).abs() > 1e-4 || (x.im - y.im).abs() > 1e-4 { 49 | panic!("Large difference of values, got {} expected {}.", x, y) 50 | }; 51 | }); 52 | 53 | // Inverse Transform 54 | let mut v_new: Array2> = Array2::zeros((nx, ny)); 55 | { 56 | let mut work: Array2> = Array2::zeros((nx, ny)); 57 | ndifft(&vhat, &mut work, &mut handler_ax0, 0); 58 | ndifft(&work, &mut v_new, &mut handler_ax1, 1); 59 | } 60 | 61 | // Assert with original array 62 | Zip::from(&v_new).and(&v).for_each(|&x, &y| { 63 | if (x.re - y.re).abs() > 1e-4 || (x.im - y.im).abs() > 1e-4 { 64 | panic!("Large difference of values, got {} expected {}.", x, y) 65 | }; 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /benches/ndrustfft.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use ndarray::{Array, Dim, Ix}; 3 | use ndrustfft::{nddct1, DctHandler}; 4 | use ndrustfft::{ndfft, Complex, FftHandler}; 5 | use ndrustfft::{ndfft_r2c, R2cFftHandler}; 6 | const FFT_SIZES: [usize; 4] = [128, 264, 512, 1024]; 7 | const DCT_SIZES: [usize; 4] = [129, 265, 513, 1025]; 8 | 9 | pub fn bench_fft2d(c: &mut Criterion) { 10 | let mut group = c.benchmark_group("fft2d"); 11 | for n in FFT_SIZES.iter() { 12 | let name = format!("Size: {}", *n); 13 | let mut data = Array::, Dim<[Ix; 2]>>::zeros((*n, *n)); 14 | let mut vhat = Array::, Dim<[Ix; 2]>>::zeros((*n, *n)); 15 | for (i, v) in data.iter_mut().enumerate() { 16 | v.re = i as f64; 17 | v.im = i as f64; 18 | } 19 | let mut handler: FftHandler = FftHandler::new(*n); 20 | group.bench_function(&name, |b| { 21 | b.iter(|| ndfft(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 0)) 22 | }); 23 | } 24 | group.finish(); 25 | } 26 | 27 | pub fn bench_rfft2d(c: &mut Criterion) { 28 | let mut group = c.benchmark_group("rfft2d"); 29 | for n in FFT_SIZES.iter() { 30 | let name = format!("Size: {}", *n); 31 | let m = *n / 2 + 1; 32 | let mut data = Array::>::zeros((*n, *n)); 33 | let mut vhat = Array::, Dim<[Ix; 2]>>::zeros((m, *n)); 34 | for (i, v) in data.iter_mut().enumerate() { 35 | *v = i as f64; 36 | } 37 | let mut handler = R2cFftHandler::::new(*n); 38 | group.bench_function(&name, |b| { 39 | b.iter(|| ndfft_r2c(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 0)) 40 | }); 41 | } 42 | group.finish(); 43 | } 44 | 45 | pub fn bench_dct2d(c: &mut Criterion) { 46 | let mut group = c.benchmark_group("dct2d"); 47 | for n in DCT_SIZES.iter() { 48 | let name = format!("Size: {}", *n); 49 | let mut data = Array::>::zeros((*n, *n)); 50 | let mut vhat = Array::>::zeros((*n, *n)); 51 | for (i, v) in data.iter_mut().enumerate() { 52 | *v = i as f64; 53 | } 54 | let mut handler: DctHandler = DctHandler::new(*n); 55 | group.bench_function(&name, |b| { 56 | b.iter(|| nddct1(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 0)) 57 | }); 58 | } 59 | group.finish(); 60 | } 61 | 62 | criterion_group!(benches, bench_fft2d, bench_rfft2d, bench_dct2d); 63 | criterion_main!(benches); 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ndrustfft 2 | 3 | ## ndrustfft: *n*-dimensional complex-to-complex FFT, real-to-complex FFT and real-to-real DCT 4 | 5 | This library is a wrapper for `RustFFT`, `RustDCT` and `RealFft` 6 | that enables performing FFTs and DCTs of complex- and real-valued 7 | data on *n*-dimensional arrays (ndarray). 8 | 9 | ndrustfft provides Handler structs for FFT's and DCTs, which must be provided alongside 10 | with the arrays to the respective functions (see below) . 11 | The Handlers implement a process function, which is a wrapper around Rustfft's 12 | process. 13 | Transforms along the outermost axis are in general the fastest, while transforms along 14 | other axis' will temporarily create copies of the input array. 15 | 16 | ### Parallel 17 | The library ships all functions with a parallel version 18 | which leverages the parallel iterators of the ndarray crate. 19 | 20 | ### Available transforms 21 | #### Complex-to-complex 22 | - `fft` : [`ndfft`], [`ndfft_par`] 23 | - `ifft`: [`ndifft`],[`ndifft_par`] 24 | #### Real-to-complex 25 | - `fft_r2c` : [`ndfft_r2c`], [`ndfft_r2c_par`], 26 | #### Complex-to-real 27 | - `ifft_r2c`: [`ndifft_r2c`],[`ndifft_r2c_par`] 28 | #### Real-to-real 29 | - `dct1`: [`nddct1`],[`nddct1_par`] 30 | - `dct2`: [`nddct2`],[`nddct2_par`] 31 | - `dct3`: [`nddct3`],[`nddct3_par`] 32 | - `dct4`: [`nddct4`],[`nddct4_par`] 33 | 34 | ### Example 35 | 2-Dimensional real-to-complex fft along first axis 36 | ```rust 37 | use ndarray::{Array2, Dim, Ix}; 38 | use ndrustfft::{ndfft_r2c, Complex, R2cFftHandler}; 39 | 40 | let (nx, ny) = (6, 4); 41 | let mut data = Array2::::zeros((nx, ny)); 42 | let mut vhat = Array2::>::zeros((nx / 2 + 1, ny)); 43 | for (i, v) in data.iter_mut().enumerate() { 44 | *v = i as f64; 45 | } 46 | let mut fft_handler = R2cFftHandler::::new(nx); 47 | ndfft_r2c( 48 | &data.view(), 49 | &mut vhat.view_mut(), 50 | &mut fft_handler, 51 | 0, 52 | ); 53 | ``` 54 | 55 | ## Normalization 56 | `RustFFT`, `RustDCT` and `RealFft` do not normalise, 57 | while this library applies normalization as scipy by default. 58 | This means, inverse ffts are divided by a factor of `data.len()`, 59 | and dcts are multiplied by two. It is possible to switch from the 60 | default normalization to no normalization, or to apply a custom 61 | normalization by using the normalization builder. 62 | 63 | See: `examples/fft_norm` 64 | 65 | ## Features 66 | 67 | - parallel: Enables parallel transform using `ndarrays` + `rayon` (enabled by default) 68 | - avx: Enables `rustfft`'s avx feature (enabled by default) 69 | - sse: Enables `rustfft`'s sse feature (enabled by default) 70 | - neon: Enables `rustfft`'s neon feature (enabled by default) 71 | 72 | ## Documentation 73 | [docs.rs](https://docs.rs/ndrustfft/) 74 | 75 | ## Versions 76 | [Changelog](CHANGELOG.md) 77 | 78 | License: MIT 79 | -------------------------------------------------------------------------------- /benches/ndrustfft_par.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | #[cfg(feature = "parallel")] 3 | use ndarray::{Array, Dim, Ix}; 4 | #[cfg(feature = "parallel")] 5 | use ndrustfft::{ 6 | nddct1_par, ndfft_par, ndfft_r2c_par, Complex, DctHandler, FftHandler, R2cFftHandler, 7 | }; 8 | #[cfg(feature = "parallel")] 9 | const FFT_SIZES: [usize; 4] = [128, 264, 512, 1024]; 10 | #[cfg(feature = "parallel")] 11 | const DCT_SIZES: [usize; 4] = [129, 265, 513, 1025]; 12 | 13 | #[cfg(feature = "parallel")] 14 | pub fn bench_fft2d(c: &mut Criterion) { 15 | let mut group = c.benchmark_group("fft2d_par"); 16 | for n in FFT_SIZES.iter() { 17 | let name = format!("Size: {}", *n); 18 | let mut data = Array::, Dim<[Ix; 2]>>::zeros((*n, *n)); 19 | let mut vhat = Array::, Dim<[Ix; 2]>>::zeros((*n, *n)); 20 | for (i, v) in data.iter_mut().enumerate() { 21 | v.re = i as f64; 22 | v.im = i as f64; 23 | } 24 | let mut handler: FftHandler = FftHandler::new(*n); 25 | group.bench_function(&name, |b| { 26 | b.iter(|| ndfft_par(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 0)) 27 | }); 28 | } 29 | group.finish(); 30 | } 31 | 32 | #[cfg(feature = "parallel")] 33 | pub fn bench_rfft2d(c: &mut Criterion) { 34 | let mut group = c.benchmark_group("rfft2d_par"); 35 | for n in FFT_SIZES.iter() { 36 | let name = format!("Size: {}", *n); 37 | let m = *n / 2 + 1; 38 | let mut data = Array::>::zeros((*n, *n)); 39 | let mut vhat = Array::, Dim<[Ix; 2]>>::zeros((m, *n)); 40 | for (i, v) in data.iter_mut().enumerate() { 41 | *v = i as f64; 42 | } 43 | let mut handler = R2cFftHandler::::new(*n); 44 | group.bench_function(&name, |b| { 45 | b.iter(|| ndfft_r2c_par(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 0)) 46 | }); 47 | } 48 | group.finish(); 49 | } 50 | 51 | #[cfg(feature = "parallel")] 52 | pub fn bench_dct2d(c: &mut Criterion) { 53 | let mut group = c.benchmark_group("dct2d_par"); 54 | for n in DCT_SIZES.iter() { 55 | let name = format!("Size: {}", *n); 56 | let mut data = Array::>::zeros((*n, *n)); 57 | let mut vhat = Array::>::zeros((*n, *n)); 58 | for (i, v) in data.iter_mut().enumerate() { 59 | *v = i as f64; 60 | } 61 | let mut handler: DctHandler = DctHandler::new(*n); 62 | group.bench_function(&name, |b| { 63 | b.iter(|| nddct1_par(&mut data.view_mut(), &mut vhat.view_mut(), &mut handler, 0)) 64 | }); 65 | } 66 | group.finish(); 67 | } 68 | 69 | #[cfg(feature = "parallel")] 70 | criterion_group!(benches, bench_fft2d, bench_rfft2d, bench_dct2d); 71 | #[cfg(feature = "parallel")] 72 | criterion_main!(benches); 73 | 74 | #[cfg(not(feature = "parallel"))] 75 | pub fn bench_stub(_c: &mut Criterion) {} 76 | #[cfg(not(feature = "parallel"))] 77 | criterion_main!(benches); 78 | #[cfg(not(feature = "parallel"))] 79 | criterion_group!(benches, bench_stub); 80 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "atty" 7 | version = "0.2.14" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 10 | dependencies = [ 11 | "hermit-abi", 12 | "libc", 13 | "winapi", 14 | ] 15 | 16 | [[package]] 17 | name = "autocfg" 18 | version = "1.0.1" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 21 | 22 | [[package]] 23 | name = "bitflags" 24 | version = "1.2.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 27 | 28 | [[package]] 29 | name = "bstr" 30 | version = "0.2.16" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" 33 | dependencies = [ 34 | "lazy_static", 35 | "memchr", 36 | "regex-automata", 37 | "serde", 38 | ] 39 | 40 | [[package]] 41 | name = "bumpalo" 42 | version = "3.7.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 45 | 46 | [[package]] 47 | name = "cast" 48 | version = "0.2.6" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374" 51 | dependencies = [ 52 | "rustc_version", 53 | ] 54 | 55 | [[package]] 56 | name = "cfg-if" 57 | version = "1.0.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 60 | 61 | [[package]] 62 | name = "clap" 63 | version = "2.33.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 66 | dependencies = [ 67 | "bitflags", 68 | "textwrap", 69 | "unicode-width", 70 | ] 71 | 72 | [[package]] 73 | name = "criterion" 74 | version = "0.3.4" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" 77 | dependencies = [ 78 | "atty", 79 | "cast", 80 | "clap", 81 | "criterion-plot", 82 | "csv", 83 | "itertools 0.10.1", 84 | "lazy_static", 85 | "num-traits", 86 | "oorandom", 87 | "plotters", 88 | "rayon", 89 | "regex", 90 | "serde", 91 | "serde_cbor", 92 | "serde_derive", 93 | "serde_json", 94 | "tinytemplate", 95 | "walkdir", 96 | ] 97 | 98 | [[package]] 99 | name = "criterion-plot" 100 | version = "0.4.3" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" 103 | dependencies = [ 104 | "cast", 105 | "itertools 0.9.0", 106 | ] 107 | 108 | [[package]] 109 | name = "crossbeam-deque" 110 | version = "0.8.5" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 113 | dependencies = [ 114 | "crossbeam-epoch", 115 | "crossbeam-utils", 116 | ] 117 | 118 | [[package]] 119 | name = "crossbeam-epoch" 120 | version = "0.9.18" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 123 | dependencies = [ 124 | "crossbeam-utils", 125 | ] 126 | 127 | [[package]] 128 | name = "crossbeam-utils" 129 | version = "0.8.20" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 132 | 133 | [[package]] 134 | name = "csv" 135 | version = "1.1.6" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" 138 | dependencies = [ 139 | "bstr", 140 | "csv-core", 141 | "itoa", 142 | "ryu", 143 | "serde", 144 | ] 145 | 146 | [[package]] 147 | name = "csv-core" 148 | version = "0.1.10" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 151 | dependencies = [ 152 | "memchr", 153 | ] 154 | 155 | [[package]] 156 | name = "either" 157 | version = "1.6.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 160 | 161 | [[package]] 162 | name = "half" 163 | version = "1.7.1" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" 166 | 167 | [[package]] 168 | name = "hermit-abi" 169 | version = "0.1.19" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 172 | dependencies = [ 173 | "libc", 174 | ] 175 | 176 | [[package]] 177 | name = "itertools" 178 | version = "0.9.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 181 | dependencies = [ 182 | "either", 183 | ] 184 | 185 | [[package]] 186 | name = "itertools" 187 | version = "0.10.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" 190 | dependencies = [ 191 | "either", 192 | ] 193 | 194 | [[package]] 195 | name = "itoa" 196 | version = "0.4.7" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 199 | 200 | [[package]] 201 | name = "js-sys" 202 | version = "0.3.51" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 205 | dependencies = [ 206 | "wasm-bindgen", 207 | ] 208 | 209 | [[package]] 210 | name = "lazy_static" 211 | version = "1.4.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 214 | 215 | [[package]] 216 | name = "libc" 217 | version = "0.2.97" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" 220 | 221 | [[package]] 222 | name = "log" 223 | version = "0.4.14" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 226 | dependencies = [ 227 | "cfg-if", 228 | ] 229 | 230 | [[package]] 231 | name = "matrixmultiply" 232 | version = "0.3.9" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" 235 | dependencies = [ 236 | "autocfg", 237 | "rawpointer", 238 | ] 239 | 240 | [[package]] 241 | name = "memchr" 242 | version = "2.4.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 245 | 246 | [[package]] 247 | name = "ndarray" 248 | version = "0.17.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "0c7c9125e8f6f10c9da3aad044cc918cf8784fa34de857b1aa68038eb05a50a9" 251 | dependencies = [ 252 | "matrixmultiply", 253 | "num-complex", 254 | "num-integer", 255 | "num-traits", 256 | "portable-atomic", 257 | "portable-atomic-util", 258 | "rawpointer", 259 | "rayon", 260 | ] 261 | 262 | [[package]] 263 | name = "ndrustfft" 264 | version = "0.6.0" 265 | dependencies = [ 266 | "criterion", 267 | "ndarray", 268 | "num-traits", 269 | "realfft", 270 | "rustdct", 271 | "rustfft", 272 | ] 273 | 274 | [[package]] 275 | name = "num-complex" 276 | version = "0.4.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" 279 | dependencies = [ 280 | "num-traits", 281 | ] 282 | 283 | [[package]] 284 | name = "num-integer" 285 | version = "0.1.44" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 288 | dependencies = [ 289 | "autocfg", 290 | "num-traits", 291 | ] 292 | 293 | [[package]] 294 | name = "num-traits" 295 | version = "0.2.14" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 298 | dependencies = [ 299 | "autocfg", 300 | ] 301 | 302 | [[package]] 303 | name = "oorandom" 304 | version = "11.1.3" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 307 | 308 | [[package]] 309 | name = "pest" 310 | version = "2.1.3" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 313 | dependencies = [ 314 | "ucd-trie", 315 | ] 316 | 317 | [[package]] 318 | name = "plotters" 319 | version = "0.3.1" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" 322 | dependencies = [ 323 | "num-traits", 324 | "plotters-backend", 325 | "plotters-svg", 326 | "wasm-bindgen", 327 | "web-sys", 328 | ] 329 | 330 | [[package]] 331 | name = "plotters-backend" 332 | version = "0.3.1" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "fd8be10f7485c8a323ea100b20d6052c27cf5968f08f8e3a56ee9f0cf38ebd3d" 335 | 336 | [[package]] 337 | name = "plotters-svg" 338 | version = "0.3.1" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" 341 | dependencies = [ 342 | "plotters-backend", 343 | ] 344 | 345 | [[package]] 346 | name = "portable-atomic" 347 | version = "1.7.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 350 | 351 | [[package]] 352 | name = "portable-atomic-util" 353 | version = "0.2.2" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "fcdd8420072e66d54a407b3316991fe946ce3ab1083a7f575b2463866624704d" 356 | dependencies = [ 357 | "portable-atomic", 358 | ] 359 | 360 | [[package]] 361 | name = "primal-check" 362 | version = "0.3.3" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0" 365 | dependencies = [ 366 | "num-integer", 367 | ] 368 | 369 | [[package]] 370 | name = "proc-macro2" 371 | version = "1.0.27" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 374 | dependencies = [ 375 | "unicode-xid", 376 | ] 377 | 378 | [[package]] 379 | name = "quote" 380 | version = "1.0.9" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 383 | dependencies = [ 384 | "proc-macro2", 385 | ] 386 | 387 | [[package]] 388 | name = "rawpointer" 389 | version = "0.2.1" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 392 | 393 | [[package]] 394 | name = "rayon" 395 | version = "1.10.0" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 398 | dependencies = [ 399 | "either", 400 | "rayon-core", 401 | ] 402 | 403 | [[package]] 404 | name = "rayon-core" 405 | version = "1.12.1" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 408 | dependencies = [ 409 | "crossbeam-deque", 410 | "crossbeam-utils", 411 | ] 412 | 413 | [[package]] 414 | name = "realfft" 415 | version = "3.2.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "93d6b8e8f0c6d2234aa58048d7290c60bf92cd36fd2888cd8331c66ad4f2e1d2" 418 | dependencies = [ 419 | "rustfft", 420 | ] 421 | 422 | [[package]] 423 | name = "regex" 424 | version = "1.5.4" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 427 | dependencies = [ 428 | "regex-syntax", 429 | ] 430 | 431 | [[package]] 432 | name = "regex-automata" 433 | version = "0.1.10" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 436 | 437 | [[package]] 438 | name = "regex-syntax" 439 | version = "0.6.25" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 442 | 443 | [[package]] 444 | name = "rustc_version" 445 | version = "0.3.3" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" 448 | dependencies = [ 449 | "semver", 450 | ] 451 | 452 | [[package]] 453 | name = "rustdct" 454 | version = "0.7.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "620247053ace0eddd3e607c66423537e45c3ae36008e6d6fac1552f51ea1a63a" 457 | dependencies = [ 458 | "rustfft", 459 | ] 460 | 461 | [[package]] 462 | name = "rustfft" 463 | version = "6.1.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434" 466 | dependencies = [ 467 | "num-complex", 468 | "num-integer", 469 | "num-traits", 470 | "primal-check", 471 | "strength_reduce", 472 | "transpose", 473 | "version_check", 474 | ] 475 | 476 | [[package]] 477 | name = "ryu" 478 | version = "1.0.5" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 481 | 482 | [[package]] 483 | name = "same-file" 484 | version = "1.0.6" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 487 | dependencies = [ 488 | "winapi-util", 489 | ] 490 | 491 | [[package]] 492 | name = "semver" 493 | version = "0.11.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 496 | dependencies = [ 497 | "semver-parser", 498 | ] 499 | 500 | [[package]] 501 | name = "semver-parser" 502 | version = "0.10.2" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 505 | dependencies = [ 506 | "pest", 507 | ] 508 | 509 | [[package]] 510 | name = "serde" 511 | version = "1.0.126" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" 514 | 515 | [[package]] 516 | name = "serde_cbor" 517 | version = "0.11.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" 520 | dependencies = [ 521 | "half", 522 | "serde", 523 | ] 524 | 525 | [[package]] 526 | name = "serde_derive" 527 | version = "1.0.126" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" 530 | dependencies = [ 531 | "proc-macro2", 532 | "quote", 533 | "syn", 534 | ] 535 | 536 | [[package]] 537 | name = "serde_json" 538 | version = "1.0.64" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 541 | dependencies = [ 542 | "itoa", 543 | "ryu", 544 | "serde", 545 | ] 546 | 547 | [[package]] 548 | name = "strength_reduce" 549 | version = "0.2.4" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" 552 | 553 | [[package]] 554 | name = "syn" 555 | version = "1.0.73" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" 558 | dependencies = [ 559 | "proc-macro2", 560 | "quote", 561 | "unicode-xid", 562 | ] 563 | 564 | [[package]] 565 | name = "textwrap" 566 | version = "0.11.0" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 569 | dependencies = [ 570 | "unicode-width", 571 | ] 572 | 573 | [[package]] 574 | name = "tinytemplate" 575 | version = "1.2.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 578 | dependencies = [ 579 | "serde", 580 | "serde_json", 581 | ] 582 | 583 | [[package]] 584 | name = "transpose" 585 | version = "0.2.1" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "95f9c900aa98b6ea43aee227fd680550cdec726526aab8ac801549eadb25e39f" 588 | dependencies = [ 589 | "num-integer", 590 | "strength_reduce", 591 | ] 592 | 593 | [[package]] 594 | name = "ucd-trie" 595 | version = "0.1.3" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 598 | 599 | [[package]] 600 | name = "unicode-width" 601 | version = "0.1.8" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 604 | 605 | [[package]] 606 | name = "unicode-xid" 607 | version = "0.2.2" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 610 | 611 | [[package]] 612 | name = "version_check" 613 | version = "0.9.4" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 616 | 617 | [[package]] 618 | name = "walkdir" 619 | version = "2.3.2" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" 622 | dependencies = [ 623 | "same-file", 624 | "winapi", 625 | "winapi-util", 626 | ] 627 | 628 | [[package]] 629 | name = "wasm-bindgen" 630 | version = "0.2.74" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 633 | dependencies = [ 634 | "cfg-if", 635 | "wasm-bindgen-macro", 636 | ] 637 | 638 | [[package]] 639 | name = "wasm-bindgen-backend" 640 | version = "0.2.74" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 643 | dependencies = [ 644 | "bumpalo", 645 | "lazy_static", 646 | "log", 647 | "proc-macro2", 648 | "quote", 649 | "syn", 650 | "wasm-bindgen-shared", 651 | ] 652 | 653 | [[package]] 654 | name = "wasm-bindgen-macro" 655 | version = "0.2.74" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 658 | dependencies = [ 659 | "quote", 660 | "wasm-bindgen-macro-support", 661 | ] 662 | 663 | [[package]] 664 | name = "wasm-bindgen-macro-support" 665 | version = "0.2.74" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 668 | dependencies = [ 669 | "proc-macro2", 670 | "quote", 671 | "syn", 672 | "wasm-bindgen-backend", 673 | "wasm-bindgen-shared", 674 | ] 675 | 676 | [[package]] 677 | name = "wasm-bindgen-shared" 678 | version = "0.2.74" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 681 | 682 | [[package]] 683 | name = "web-sys" 684 | version = "0.3.51" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 687 | dependencies = [ 688 | "js-sys", 689 | "wasm-bindgen", 690 | ] 691 | 692 | [[package]] 693 | name = "winapi" 694 | version = "0.3.9" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 697 | dependencies = [ 698 | "winapi-i686-pc-windows-gnu", 699 | "winapi-x86_64-pc-windows-gnu", 700 | ] 701 | 702 | [[package]] 703 | name = "winapi-i686-pc-windows-gnu" 704 | version = "0.4.0" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 707 | 708 | [[package]] 709 | name = "winapi-util" 710 | version = "0.1.5" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 713 | dependencies = [ 714 | "winapi", 715 | ] 716 | 717 | [[package]] 718 | name = "winapi-x86_64-pc-windows-gnu" 719 | version = "0.4.0" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 722 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # ndrustfft: *n*-dimensional complex-to-complex FFT, real-to-complex FFT and real-to-real DCT 2 | //! 3 | //! This library is a wrapper for `RustFFT`, `RustDCT` and `RealFft` 4 | //! that enables performing FFTs and DCTs of complex- and real-valued 5 | //! data on *n*-dimensional arrays (ndarray). 6 | //! 7 | //! ndrustfft provides Handler structs for FFT's and DCTs, which must be provided alongside 8 | //! with the arrays to the respective functions (see below) . 9 | //! The Handlers implement a process function, which is a wrapper around Rustfft's 10 | //! process. 11 | //! Transforms along the outermost axis are in general the fastest, while transforms along 12 | //! other axis' will temporarily create copies of the input array. 13 | //! 14 | //! ## Parallel 15 | //! The library ships all functions with a parallel version 16 | //! which leverages the parallel iterators of the ndarray crate. 17 | //! 18 | //! ## Available transforms 19 | //! ### Complex-to-complex 20 | //! - `fft` : [`ndfft`], [`ndfft_par`] 21 | //! - `ifft`: [`ndifft`],[`ndifft_par`] 22 | //! ### Real-to-complex 23 | //! - `fft_r2c` : [`ndfft_r2c`], [`ndfft_r2c_par`], 24 | //! ### Complex-to-real 25 | //! - `ifft_r2c`: [`ndifft_r2c`],[`ndifft_r2c_par`] 26 | //! ### Real-to-real 27 | //! - `dct1`: [`nddct1`],[`nddct1_par`] 28 | //! - `dct2`: [`nddct2`],[`nddct2_par`] 29 | //! - `dct3`: [`nddct3`],[`nddct3_par`] 30 | //! - `dct4`: [`nddct4`],[`nddct4_par`] 31 | //! 32 | //! ## Example 33 | //! 2-Dimensional real-to-complex fft along first axis 34 | //! ``` 35 | //! use ndarray::{Array2, Dim, Ix}; 36 | //! use ndrustfft::{ndfft_r2c, Complex, R2cFftHandler}; 37 | //! 38 | //! let (nx, ny) = (6, 4); 39 | //! let mut data = Array2::::zeros((nx, ny)); 40 | //! let mut vhat = Array2::>::zeros((nx / 2 + 1, ny)); 41 | //! for (i, v) in data.iter_mut().enumerate() { 42 | //! *v = i as f64; 43 | //! } 44 | //! let mut fft_handler = R2cFftHandler::::new(nx); 45 | //! ndfft_r2c( 46 | //! &data.view(), 47 | //! &mut vhat.view_mut(), 48 | //! &mut fft_handler, 49 | //! 0, 50 | //! ); 51 | //! ``` 52 | //! 53 | //! # Normalization 54 | //! `RustFFT`, `RustDCT` and `RealFft` do not normalise, 55 | //! while this library applies normalization as scipy by default. 56 | //! This means, inverse ffts are divided by a factor of `data.len()`, 57 | //! and dcts are multiplied by two. It is possible to switch from the 58 | //! default normalization to no normalization, or to apply a custom 59 | //! normalization by using the normalization builder. 60 | //! 61 | //! See: `examples/fft_norm` 62 | //! 63 | //! # Features 64 | //! 65 | //! - parallel: Enables parallel transform using `ndarrays` + `rayon` (enabled by default) 66 | //! - avx: Enables `rustfft`'s avx feature (enabled by default) 67 | //! - sse: Enables `rustfft`'s sse feature (enabled by default) 68 | //! - neon: Enables `rustfft`'s neon feature (enabled by default) 69 | //! 70 | //! # Documentation 71 | //! [docs.rs](https://docs.rs/ndrustfft/) 72 | //! 73 | //! # Versions 74 | //! [Changelog](CHANGELOG.md) 75 | #![warn(missing_docs)] 76 | extern crate ndarray; 77 | extern crate rustfft; 78 | use ndarray::{Array1, ArrayBase, Axis, Dimension, Zip}; 79 | use ndarray::{Data, DataMut}; 80 | use num_traits::FloatConst; 81 | use realfft::{ComplexToReal, RealFftPlanner, RealToComplex}; 82 | use rustdct::{Dct1, DctPlanner, TransformType2And3, TransformType4}; 83 | pub use rustfft::num_complex::Complex; 84 | pub use rustfft::num_traits::Zero; 85 | pub use rustfft::FftNum; 86 | use rustfft::{Fft, FftPlanner}; 87 | use std::sync::Arc; 88 | 89 | /// Represents different types of normalization methods. 90 | #[derive(Clone)] 91 | pub enum Normalization { 92 | /// No normalization applied, output equals `rustfft`, `realfft` or `rustdct`. 93 | None, 94 | /// Applies normalization similar to scipy's default behavior. 95 | Default, 96 | /// Applies a custom normalization function provided as a closure. 97 | Custom(fn(&mut [T])), 98 | } 99 | 100 | macro_rules! create_transform { 101 | ( 102 | $(#[$meta:meta])* $i: ident, $a: ty, $b: ty, $h: ty, $p: ident 103 | ) => { 104 | $(#[$meta])* 105 | pub fn $i( 106 | input: &ArrayBase, 107 | output: &mut ArrayBase, 108 | handler: &$h, 109 | axis: usize, 110 | ) where 111 | T: FftNum + FloatConst, 112 | R: Data, 113 | S: Data + DataMut, 114 | D: Dimension, 115 | { 116 | let n = output.shape()[axis]; 117 | if input.is_standard_layout() && output.is_standard_layout() { 118 | let outer_axis = input.ndim() - 1; 119 | if axis == outer_axis { 120 | Zip::from(input.rows()) 121 | .and(output.rows_mut()) 122 | .for_each(|x, mut y| { 123 | handler.$p(x.as_slice().unwrap(), y.as_slice_mut().unwrap()); 124 | }); 125 | } else { 126 | let mut outvec = Array1::zeros(output.shape()[axis]); 127 | let mut input = input.view(); 128 | input.swap_axes(outer_axis, axis); 129 | output.swap_axes(outer_axis, axis); 130 | Zip::from(input.rows()) 131 | .and(output.rows_mut()) 132 | .for_each(|x, mut y| { 133 | handler.$p(&x.to_vec(), outvec.as_slice_mut().unwrap()); 134 | y.assign(&outvec); 135 | }); 136 | output.swap_axes(outer_axis, axis); 137 | } 138 | } else { 139 | Zip::from(input.lanes(Axis(axis))) 140 | .and(output.lanes_mut(Axis(axis))) 141 | .for_each(|x, mut y| { 142 | if let Some(x_s) = x.as_slice() { 143 | if let Some(y_s) = y.as_slice_mut() { 144 | // x and y are contiguous 145 | handler.$p(x_s, y_s); 146 | } else { 147 | let mut outvec = Array1::zeros(n); 148 | // x is contiguous, y is not contiguous 149 | handler.$p(x_s, outvec.as_slice_mut().unwrap()); 150 | y.assign(&outvec); 151 | } 152 | } else { 153 | if let Some(y_s) = y.as_slice_mut() { 154 | // x is not contiguous, y is contiguous 155 | handler.$p(&x.to_vec(), y_s); 156 | } else { 157 | let mut outvec = Array1::zeros(n); 158 | // x and y are not contiguous 159 | handler.$p(&x.to_vec(), outvec.as_slice_mut().unwrap()); 160 | y.assign(&outvec); 161 | } 162 | } 163 | }); 164 | } 165 | } 166 | }; 167 | } 168 | 169 | #[cfg(feature = "parallel")] 170 | macro_rules! create_transform_par { 171 | ( 172 | $(#[$meta:meta])* $i: ident, $a: ty, $b: ty, $h: ty, $p: ident 173 | ) => { 174 | $(#[$meta])* 175 | pub fn $i( 176 | input: &ArrayBase, 177 | output: &mut ArrayBase, 178 | handler: &$h, 179 | axis: usize, 180 | ) where 181 | T: FftNum + FloatConst, 182 | R: Data, 183 | S: Data + DataMut, 184 | D: Dimension, 185 | { 186 | let n = output.shape()[axis]; 187 | if input.is_standard_layout() && output.is_standard_layout() { 188 | let outer_axis = input.ndim() - 1; 189 | if axis == outer_axis { 190 | Zip::from(input.rows()) 191 | .and(output.rows_mut()) 192 | .par_for_each(|x, mut y| { 193 | handler.$p(x.as_slice().unwrap(), y.as_slice_mut().unwrap()); 194 | }); 195 | } else { 196 | let n = output.shape()[axis]; 197 | let mut input = input.view(); 198 | input.swap_axes(outer_axis, axis); 199 | output.swap_axes(outer_axis, axis); 200 | Zip::from(input.rows()) 201 | .and(output.rows_mut()) 202 | .par_for_each(|x, mut y| { 203 | let mut outvec = Array1::zeros(n); 204 | handler.$p(&x.to_vec(), outvec.as_slice_mut().unwrap()); 205 | y.assign(&outvec); 206 | }); 207 | output.swap_axes(outer_axis, axis); 208 | } 209 | } else { 210 | Zip::from(input.lanes(Axis(axis))) 211 | .and(output.lanes_mut(Axis(axis))) 212 | .par_for_each(|x, mut y| { 213 | if let Some(x_s) = x.as_slice() { 214 | if let Some(y_s) = y.as_slice_mut() { 215 | // x and y are contiguous 216 | handler.$p(x_s, y_s); 217 | } else { 218 | let mut outvec = Array1::zeros(n); 219 | // x is contiguous, y is not contiguous 220 | handler.$p(x_s, outvec.as_slice_mut().unwrap()); 221 | y.assign(&outvec); 222 | } 223 | } else { 224 | if let Some(y_s) = y.as_slice_mut() { 225 | // x is not contiguous, y is contiguous 226 | handler.$p(&x.to_vec(), y_s); 227 | } else { 228 | let mut outvec = Array1::zeros(n); 229 | // x and y are not contiguous 230 | handler.$p(&x.to_vec(), outvec.as_slice_mut().unwrap()); 231 | y.assign(&outvec); 232 | } 233 | } 234 | }); 235 | } 236 | } 237 | }; 238 | } 239 | 240 | /// # *n*-dimensional complex-to-complex Fourier Transform. 241 | /// 242 | /// Transforms a complex ndarray of size *n* to a complex array of size 243 | /// *n* and vice versa. The transformation is performed along a single 244 | /// axis, all other array dimensions are unaffected. 245 | /// Performs best on sizes which are mutiple of 2 or 3. 246 | /// 247 | /// The accompanying functions for the forward transform are [`ndfft`] (serial) and 248 | /// [`ndfft_par`] (parallel). 249 | /// 250 | /// The accompanying functions for the inverse transform are [`ndifft`] (serial) and 251 | /// [`ndifft_par`] (parallel). 252 | /// 253 | /// # Example 254 | /// 2-Dimensional complex-to-complex fft along first axis 255 | /// ``` 256 | /// use ndarray::{Array2, Dim, Ix}; 257 | /// use ndrustfft::{ndfft, Complex, FftHandler}; 258 | /// 259 | /// let (nx, ny) = (6, 4); 260 | /// let mut data = Array2::>::zeros((nx, ny)); 261 | /// let mut vhat = Array2::>::zeros((nx, ny)); 262 | /// for (i, v) in data.iter_mut().enumerate() { 263 | /// v.re = i as f64; 264 | /// v.im = i as f64; 265 | /// } 266 | /// let mut fft_handler: FftHandler = FftHandler::new(nx); 267 | /// ndfft(&data, &mut vhat, &mut fft_handler, 0); 268 | /// ``` 269 | #[derive(Clone)] 270 | pub struct FftHandler { 271 | n: usize, 272 | plan_fwd: Arc>, 273 | plan_bwd: Arc>, 274 | norm: Normalization>, 275 | } 276 | 277 | impl FftHandler { 278 | /// Creates a new `FftHandler`. 279 | /// 280 | /// # Arguments 281 | /// 282 | /// * `n` - Length of array along axis of which fft will be performed. 283 | /// The size of the complex array after the fft is performed will be of 284 | /// size *n*. 285 | /// 286 | /// # Examples 287 | /// 288 | /// ``` 289 | /// use ndrustfft::FftHandler; 290 | /// let handler: FftHandler = FftHandler::new(10); 291 | /// ``` 292 | #[allow(clippy::similar_names)] 293 | #[must_use] 294 | pub fn new(n: usize) -> Self { 295 | let mut planner = FftPlanner::::new(); 296 | let fwd = planner.plan_fft_forward(n); 297 | let bwd = planner.plan_fft_inverse(n); 298 | FftHandler:: { 299 | n, 300 | plan_fwd: Arc::clone(&fwd), 301 | plan_bwd: Arc::clone(&bwd), 302 | norm: Normalization::Default, 303 | } 304 | } 305 | 306 | /// This method allows modifying the normalization applied to the backward transform. See [`Normalization`] for more details. 307 | #[must_use] 308 | pub fn normalization(mut self, norm: Normalization>) -> Self { 309 | self.norm = norm; 310 | self 311 | } 312 | 313 | fn fft_lane(&self, data: &[Complex], out: &mut [Complex]) { 314 | Self::assert_size(self.n, data.len()); 315 | Self::assert_size(self.n, out.len()); 316 | out.clone_from_slice(data); 317 | self.plan_fwd.process(out); 318 | } 319 | 320 | #[allow(clippy::cast_precision_loss)] 321 | fn ifft_lane(&self, data: &[Complex], out: &mut [Complex]) { 322 | Self::assert_size(self.n, data.len()); 323 | Self::assert_size(self.n, out.len()); 324 | out.clone_from_slice(data); 325 | self.plan_bwd.process(out); 326 | match self.norm { 327 | Normalization::None => (), 328 | Normalization::Default => Self::norm_default(out), 329 | Normalization::Custom(f) => f(out), 330 | } 331 | } 332 | 333 | fn norm_default(data: &mut [Complex]) { 334 | let n = T::one() / T::from_usize(data.len()).unwrap(); 335 | for d in &mut *data { 336 | *d = *d * n; 337 | } 338 | } 339 | 340 | fn assert_size(n: usize, size: usize) { 341 | assert!( 342 | n == size, 343 | "Size mismatch in fft, got {} expected {}", 344 | size, 345 | n 346 | ); 347 | } 348 | } 349 | 350 | create_transform!( 351 | /// Complex-to-complex Fourier Transform (serial). 352 | /// # Example 353 | /// ``` 354 | /// use ndarray::{Array2, Dim, Ix}; 355 | /// use ndrustfft::{ndfft, Complex, FftHandler}; 356 | /// 357 | /// let (nx, ny) = (6, 4); 358 | /// let mut data = Array2::>::zeros((nx, ny)); 359 | /// let mut vhat = Array2::>::zeros((nx, ny)); 360 | /// for (i, v) in data.iter_mut().enumerate() { 361 | /// v.re = i as f64; 362 | /// v.im = -1.0*i as f64; 363 | /// } 364 | /// let mut handler: FftHandler = FftHandler::new(ny); 365 | /// ndfft(&data, &mut vhat, &mut handler, 1); 366 | /// ``` 367 | ndfft, 368 | Complex, 369 | Complex, 370 | FftHandler, 371 | fft_lane 372 | ); 373 | 374 | create_transform!( 375 | /// Complex-to-complex Inverse Fourier Transform (serial). 376 | /// # Example 377 | /// ``` 378 | /// use ndarray::Array2; 379 | /// use ndrustfft::{ndfft, ndifft, Complex, FftHandler}; 380 | /// 381 | /// let (nx, ny) = (6, 4); 382 | /// let mut data = Array2::>::zeros((nx, ny)); 383 | /// let mut vhat = Array2::>::zeros((nx, ny)); 384 | /// for (i, v) in data.iter_mut().enumerate() { 385 | /// v.re = i as f64; 386 | /// v.im = -1.0*i as f64; 387 | /// } 388 | /// let mut handler: FftHandler = FftHandler::new(ny); 389 | /// ndfft(&data, &mut vhat, &mut handler, 1); 390 | /// ndifft(&vhat, &mut data, &mut handler, 1); 391 | /// ``` 392 | ndifft, 393 | Complex, 394 | Complex, 395 | FftHandler, 396 | ifft_lane 397 | ); 398 | 399 | #[cfg(feature = "parallel")] 400 | create_transform_par!( 401 | /// Complex-to-complex Fourier Transform (parallel). 402 | /// 403 | /// Further infos: see [`ndfft`] 404 | ndfft_par, 405 | Complex, 406 | Complex, 407 | FftHandler, 408 | fft_lane 409 | ); 410 | 411 | #[cfg(feature = "parallel")] 412 | create_transform_par!( 413 | /// Complex-to-complex inverse Fourier Transform (parallel). 414 | /// 415 | /// Further infos: see [`ndifft`] 416 | ndifft_par, 417 | Complex, 418 | Complex, 419 | FftHandler, 420 | ifft_lane 421 | ); 422 | 423 | /// # *n*-dimensional real-to-complex Fourier Transform. 424 | /// 425 | /// Transforms a real ndarray of size *n* to a complex array of size 426 | /// *n/2+1* and vice versa. The transformation is performed along a single 427 | /// axis, all other array dimensions are unaffected. 428 | /// Performs best on sizes which are mutiple of 2 or 3. 429 | /// 430 | /// The accompanying functions for the forward transform are [`ndfft_r2c`] (serial) and 431 | /// [`ndfft_r2c_par`] (parallel). 432 | /// 433 | /// The accompanying functions for the inverse transform are [`ndifft_r2c`] (serial) and 434 | /// [`ndifft_r2c_par`] (parallel). 435 | /// 436 | /// # Example 437 | /// 2-Dimensional real-to-complex fft along first axis 438 | /// ``` 439 | /// use ndarray::{Array2, Dim, Ix}; 440 | /// use ndrustfft::{ndfft_r2c, Complex, R2cFftHandler}; 441 | /// 442 | /// let (nx, ny) = (6, 4); 443 | /// let mut data = Array2::::zeros((nx, ny)); 444 | /// let mut vhat = Array2::>::zeros((nx / 2 + 1, ny)); 445 | /// for (i, v) in data.iter_mut().enumerate() { 446 | /// *v = i as f64; 447 | /// } 448 | /// let mut fft_handler = R2cFftHandler::::new(nx); 449 | /// ndfft_r2c(&data, &mut vhat, &mut fft_handler, 0); 450 | /// ``` 451 | #[derive(Clone)] 452 | pub struct R2cFftHandler { 453 | n: usize, 454 | m: usize, 455 | plan_fwd: Arc>, 456 | plan_bwd: Arc>, 457 | norm: Normalization>, 458 | } 459 | 460 | impl R2cFftHandler { 461 | /// Creates a new `RealFftPlanner`. 462 | /// 463 | /// # Arguments 464 | /// 465 | /// * `n` - Length of array along axis of which fft will be performed. 466 | /// The size of the complex array after the fft is performed will be of 467 | /// size *n / 2 + 1*. 468 | /// 469 | /// # Examples 470 | /// 471 | /// ``` 472 | /// use ndrustfft::R2cFftHandler; 473 | /// let handler = R2cFftHandler::::new(10); 474 | /// ``` 475 | #[allow(clippy::similar_names)] 476 | #[must_use] 477 | pub fn new(n: usize) -> Self { 478 | let mut planner = RealFftPlanner::::new(); 479 | let fwd = planner.plan_fft_forward(n); 480 | let bwd = planner.plan_fft_inverse(n); 481 | Self { 482 | n, 483 | m: n / 2 + 1, 484 | plan_fwd: Arc::clone(&fwd), 485 | plan_bwd: Arc::clone(&bwd), 486 | norm: Normalization::Default, 487 | } 488 | } 489 | 490 | /// This method allows modifying the normalization applied to the backward transform. See [`Normalization`] for more details. 491 | #[must_use] 492 | pub fn normalization(mut self, norm: Normalization>) -> Self { 493 | self.norm = norm; 494 | self 495 | } 496 | 497 | fn fft_r2c_lane(&self, data: &[T], out: &mut [Complex]) { 498 | Self::assert_size(self.n, data.len()); 499 | Self::assert_size(self.m, out.len()); 500 | let mut buffer = vec![T::zero(); self.n]; 501 | buffer.clone_from_slice(data); 502 | self.plan_fwd.process(&mut buffer, out).unwrap(); 503 | } 504 | 505 | #[allow(clippy::cast_precision_loss)] 506 | fn ifft_r2c_lane(&self, data: &[Complex], out: &mut [T]) { 507 | Self::assert_size(self.m, data.len()); 508 | Self::assert_size(self.n, out.len()); 509 | let mut buffer = vec![Complex::zero(); self.m]; 510 | buffer.clone_from_slice(data); 511 | match self.norm { 512 | Normalization::None => (), 513 | Normalization::Default => Self::norm_default(&mut buffer, self.n), 514 | Normalization::Custom(f) => f(&mut buffer), 515 | } 516 | // First element must be real 517 | buffer[0].im = T::zero(); 518 | // If original vector is even, last element must be real 519 | if self.n % 2 == 0 { 520 | buffer[self.m - 1].im = T::zero(); 521 | } 522 | self.plan_bwd.process(&mut buffer, out).unwrap(); 523 | } 524 | 525 | fn norm_default(data: &mut [Complex], size: usize) { 526 | let n = T::one() / T::from_usize(size).unwrap(); 527 | for d in &mut *data { 528 | d.re = d.re * n; 529 | d.im = d.im * n; 530 | } 531 | } 532 | 533 | fn assert_size(n: usize, size: usize) { 534 | assert!( 535 | n == size, 536 | "Size mismatch in fft, got {} expected {}", 537 | size, 538 | n 539 | ); 540 | } 541 | } 542 | 543 | create_transform!( 544 | /// Real-to-complex Fourier Transform (serial). 545 | /// # Example 546 | /// ``` 547 | /// use ndarray::Array2; 548 | /// use ndrustfft::{ndfft_r2c, Complex, R2cFftHandler}; 549 | /// 550 | /// let (nx, ny) = (6, 4); 551 | /// let mut data = Array2::::zeros((nx, ny)); 552 | /// let mut vhat = Array2::>::zeros((nx / 2 + 1, ny)); 553 | /// for (i, v) in data.iter_mut().enumerate() { 554 | /// *v = i as f64; 555 | /// } 556 | /// let mut handler = R2cFftHandler::::new(nx); 557 | /// ndfft_r2c(&data, &mut vhat, &mut handler, 0); 558 | /// ``` 559 | ndfft_r2c, 560 | T, 561 | Complex, 562 | R2cFftHandler, 563 | fft_r2c_lane 564 | ); 565 | 566 | create_transform!( 567 | /// Complex-to-real inverse Fourier Transform (serial). 568 | /// # Example 569 | /// ``` 570 | /// use ndarray::Array2; 571 | /// use ndrustfft::{ndifft_r2c, Complex, R2cFftHandler}; 572 | /// 573 | /// let (nx, ny) = (6, 4); 574 | /// let mut data = Array2::::zeros((nx, ny)); 575 | /// let mut vhat = Array2::>::zeros((nx / 2 + 1, ny)); 576 | /// for (i, v) in vhat.iter_mut().enumerate() { 577 | /// v.re = i as f64; 578 | /// } 579 | /// let mut handler = R2cFftHandler::::new(nx); 580 | /// ndifft_r2c(&vhat, &mut data, &mut handler, 0); 581 | /// ``` 582 | ndifft_r2c, 583 | Complex, 584 | T, 585 | R2cFftHandler, 586 | ifft_r2c_lane 587 | ); 588 | 589 | #[cfg(feature = "parallel")] 590 | create_transform_par!( 591 | /// Real-to-complex Fourier Transform (parallel). 592 | /// 593 | /// Further infos: see [`ndfft_r2c`] 594 | ndfft_r2c_par, 595 | T, 596 | Complex, 597 | R2cFftHandler, 598 | fft_r2c_lane 599 | ); 600 | 601 | #[cfg(feature = "parallel")] 602 | create_transform_par!( 603 | /// Complex-to-real inverse Fourier Transform (parallel). 604 | /// 605 | /// Further infos: see [`ndifft_r2c`] 606 | ndifft_r2c_par, 607 | Complex, 608 | T, 609 | R2cFftHandler, 610 | ifft_r2c_lane 611 | ); 612 | 613 | /// # *n*-dimensional real-to-real Cosine Transform. 614 | /// 615 | /// The dct transforms a real ndarray of size *n* to a real array of size *n*. 616 | /// The transformation is performed along a single axis, all other array 617 | /// dimensions are unaffected. 618 | /// Performs best on sizes where *2(n-1)* is a mutiple of 2 or 3. The crate 619 | /// contains benchmarks, see benches folder, where different sizes can be 620 | /// tested to optmize performance. 621 | /// 622 | /// The accompanying functions are [`nddct1`] (serial) and 623 | /// [`nddct1_par`] (parallel). 624 | /// 625 | /// # Example 626 | /// 2-Dimensional real-to-real dft along second axis 627 | /// ``` 628 | /// use ndarray::Array2; 629 | /// use ndrustfft::{DctHandler, nddct1}; 630 | /// 631 | /// let (nx, ny) = (6, 4); 632 | /// let mut data = Array2::::zeros((nx, ny)); 633 | /// let mut vhat = Array2::::zeros((nx, ny)); 634 | /// for (i, v) in data.iter_mut().enumerate() { 635 | /// *v = i as f64; 636 | /// } 637 | /// let mut handler: DctHandler = DctHandler::new(ny); 638 | /// nddct1(&data, &mut vhat, &mut handler, 1); 639 | /// ``` 640 | #[derive(Clone)] 641 | pub struct DctHandler { 642 | n: usize, 643 | plan_dct1: Arc>, 644 | plan_dct2: Arc>, 645 | plan_dct3: Arc>, 646 | plan_dct4: Arc>, 647 | norm: Normalization, 648 | } 649 | 650 | impl DctHandler { 651 | /// Creates a new `DctHandler`. 652 | /// 653 | /// # Arguments 654 | /// 655 | /// * `n` - Length of array along axis of which dct will be performed. 656 | /// The size and type of the array will be the same after the transform. 657 | /// 658 | /// # Examples 659 | /// 660 | /// ``` 661 | /// use ndrustfft::DctHandler; 662 | /// let handler: DctHandler = DctHandler::new(10); 663 | /// ``` 664 | #[must_use] 665 | pub fn new(n: usize) -> Self { 666 | let mut planner = DctPlanner::::new(); 667 | let dct1 = planner.plan_dct1(n); 668 | let dct2 = planner.plan_dct2(n); 669 | let dct3 = planner.plan_dct3(n); 670 | let dct4 = planner.plan_dct4(n); 671 | Self { 672 | n, 673 | plan_dct1: Arc::clone(&dct1), 674 | plan_dct2: Arc::clone(&dct2), 675 | plan_dct3: Arc::clone(&dct3), 676 | plan_dct4: Arc::clone(&dct4), 677 | norm: Normalization::Default, 678 | } 679 | } 680 | 681 | /// This method allows modifying the normalization applied to the backward transform. See [`Normalization`] for more details. 682 | #[must_use] 683 | pub fn normalization(mut self, norm: Normalization) -> Self { 684 | self.norm = norm; 685 | self 686 | } 687 | 688 | fn dct1_lane(&self, data: &[T], out: &mut [T]) { 689 | Self::assert_size(self, data.len()); 690 | Self::assert_size(self, out.len()); 691 | out.clone_from_slice(data); 692 | match self.norm { 693 | Normalization::None => (), 694 | Normalization::Default => Self::norm_default(out), 695 | Normalization::Custom(f) => f(out), 696 | } 697 | self.plan_dct1.process_dct1(out); 698 | } 699 | 700 | fn dct2_lane(&self, data: &[T], out: &mut [T]) { 701 | Self::assert_size(self, data.len()); 702 | Self::assert_size(self, out.len()); 703 | out.clone_from_slice(data); 704 | match self.norm { 705 | Normalization::None => (), 706 | Normalization::Default => Self::norm_default(out), 707 | Normalization::Custom(f) => f(out), 708 | } 709 | self.plan_dct2.process_dct2(out); 710 | } 711 | 712 | fn dct3_lane(&self, data: &[T], out: &mut [T]) { 713 | Self::assert_size(self, data.len()); 714 | Self::assert_size(self, out.len()); 715 | out.clone_from_slice(data); 716 | match self.norm { 717 | Normalization::None => (), 718 | Normalization::Default => Self::norm_default(out), 719 | Normalization::Custom(f) => f(out), 720 | } 721 | self.plan_dct3.process_dct3(out); 722 | } 723 | 724 | fn dct4_lane(&self, data: &[T], out: &mut [T]) { 725 | Self::assert_size(self, data.len()); 726 | Self::assert_size(self, out.len()); 727 | out.clone_from_slice(data); 728 | match self.norm { 729 | Normalization::None => (), 730 | Normalization::Default => Self::norm_default(out), 731 | Normalization::Custom(f) => f(out), 732 | } 733 | self.plan_dct4.process_dct4(out); 734 | } 735 | 736 | fn norm_default(data: &mut [T]) { 737 | let two = T::one() + T::one(); 738 | for d in &mut *data { 739 | *d = *d * two; 740 | } 741 | } 742 | 743 | fn assert_size(&self, size: usize) { 744 | assert!( 745 | self.n == size, 746 | "Size mismatch in dct, got {} expected {}", 747 | size, 748 | self.n 749 | ); 750 | } 751 | } 752 | 753 | create_transform!( 754 | /// Real-to-real Discrete Cosine Transform of type 1 DCT-I (serial). 755 | /// 756 | /// # Example 757 | /// ``` 758 | /// use ndarray::Array2; 759 | /// use ndrustfft::{DctHandler, nddct1}; 760 | /// 761 | /// let (nx, ny) = (6, 4); 762 | /// let mut data = Array2::::zeros((nx, ny)); 763 | /// let mut vhat = Array2::::zeros((nx, ny)); 764 | /// for (i, v) in data.iter_mut().enumerate() { 765 | /// *v = i as f64; 766 | /// } 767 | /// let mut handler: DctHandler = DctHandler::new(ny); 768 | /// nddct1(&data, &mut vhat, &mut handler, 1); 769 | /// ``` 770 | nddct1, 771 | T, 772 | T, 773 | DctHandler, 774 | dct1_lane 775 | ); 776 | 777 | #[cfg(feature = "parallel")] 778 | create_transform_par!( 779 | /// Real-to-real Discrete Cosine Transform of type 1 DCT-I (parallel). 780 | /// 781 | /// Further infos: see [`nddct1`] 782 | nddct1_par, 783 | T, 784 | T, 785 | DctHandler, 786 | dct1_lane 787 | ); 788 | 789 | create_transform!( 790 | /// Real-to-real Discrete Cosine Transform of type 2 DCT-2 (serial). 791 | nddct2, 792 | T, 793 | T, 794 | DctHandler, 795 | dct2_lane 796 | ); 797 | 798 | #[cfg(feature = "parallel")] 799 | create_transform_par!( 800 | /// Real-to-real Discrete Cosine Transform of type 2 DCT-2 (parallel). 801 | nddct2_par, 802 | T, 803 | T, 804 | DctHandler, 805 | dct2_lane 806 | ); 807 | 808 | create_transform!( 809 | /// Real-to-real Discrete Cosine Transform of type 3 DCT-3 (serial). 810 | nddct3, 811 | T, 812 | T, 813 | DctHandler, 814 | dct3_lane 815 | ); 816 | 817 | #[cfg(feature = "parallel")] 818 | create_transform_par!( 819 | /// Real-to-real Discrete Cosine Transform of type 3 DCT-3 (parallel). 820 | nddct3_par, 821 | T, 822 | T, 823 | DctHandler, 824 | dct3_lane 825 | ); 826 | 827 | create_transform!( 828 | /// Real-to-real Discrete Cosine Transform of type 4 DCT-4 (serial). 829 | nddct4, 830 | T, 831 | T, 832 | DctHandler, 833 | dct4_lane 834 | ); 835 | 836 | #[cfg(feature = "parallel")] 837 | create_transform_par!( 838 | /// Real-to-real Discrete Cosine Transform of type 4 DCT-4 (parallel). 839 | nddct4_par, 840 | T, 841 | T, 842 | DctHandler, 843 | dct4_lane 844 | ); 845 | 846 | /// Tests 847 | #[cfg(test)] 848 | mod test { 849 | use super::*; 850 | use ndarray::{array, Array2, ShapeBuilder}; 851 | 852 | fn approx_eq(result: &ArrayBase, expected: &ArrayBase) 853 | where 854 | A: FftNum + std::fmt::Display + std::cmp::PartialOrd, 855 | S: ndarray::Data, 856 | D: Dimension, 857 | { 858 | let dif = A::from_f64(1e-3).unwrap(); 859 | for (a, b) in expected.iter().zip(result.iter()) { 860 | if (*a - *b).abs() > dif { 861 | panic!("Large difference of values, got {} expected {}.", b, a) 862 | } 863 | } 864 | } 865 | 866 | fn approx_eq_complex(result: &ArrayBase, expected: &ArrayBase) 867 | where 868 | A: FftNum + std::fmt::Display + std::cmp::PartialOrd, 869 | S: ndarray::Data>, 870 | D: Dimension, 871 | { 872 | let dif = A::from_f64(1e-3).unwrap(); 873 | for (a, b) in expected.iter().zip(result.iter()) { 874 | if (a.re - b.re).abs() > dif || (a.im - b.im).abs() > dif { 875 | panic!("Large difference of values, got {} expected {}.", b, a) 876 | } 877 | } 878 | } 879 | 880 | fn test_matrix() -> Array2 { 881 | array![ 882 | [0.1, 1.908, -0.035, -0.278, 0.264, -1.349], 883 | [0.88, 0.86, -0.267, -0.809, 1.374, 0.757], 884 | [1.418, -0.68, 0.814, 0.852, -0.613, 0.468], 885 | [0.817, -0.697, -2.157, 0.447, -0.949, 2.243], 886 | [-0.474, -0.09, -0.567, -0.772, 0.021, 2.455], 887 | [-0.745, 1.52, 0.509, -0.066, 2.802, -0.042], 888 | ] 889 | } 890 | 891 | fn test_matrix_complex() -> Array2> { 892 | test_matrix().mapv(|x| Complex::new(x, x)) 893 | } 894 | 895 | fn test_matrix_complex_f() -> Array2> { 896 | let mut arr = Array2::zeros((6, 6).f()); 897 | for (a, b) in arr.iter_mut().zip(test_matrix_complex().iter()) { 898 | *a = *b 899 | } 900 | arr 901 | } 902 | 903 | #[test] 904 | fn test_fft() { 905 | // Solution from np.fft.fft 906 | let solution_re = array![ 907 | [0.61, 3.105, 2.508, 0.048, -3.652, -2.019], 908 | [2.795, 0.612, 0.219, 1.179, -2.801, 3.276], 909 | [2.259, 0.601, 0.045, 0.979, 4.506, 0.118], 910 | [-0.296, -0.896, 0.544, -4.282, 3.544, 6.288], 911 | [0.573, -0.96, -3.85, -2.613, -0.461, 4.467], 912 | [3.978, -2.229, 0.133, 1.154, -6.544, -0.962], 913 | ]; 914 | 915 | let solution_im = array![ 916 | [0.61, -2.019, -3.652, 0.048, 2.508, 3.105], 917 | [2.795, 3.276, -2.801, 1.179, 0.219, 0.612], 918 | [2.259, 0.118, 4.506, 0.979, 0.045, 0.601], 919 | [-0.296, 6.288, 3.544, -4.282, 0.544, -0.896], 920 | [0.573, 4.467, -0.461, -2.613, -3.85, -0.96], 921 | [3.978, -0.962, -6.544, 1.154, 0.133, -2.229], 922 | ]; 923 | 924 | let mut solution: Array2> = Array2::zeros(solution_re.raw_dim()); 925 | for (s, (s_re, s_im)) in solution 926 | .iter_mut() 927 | .zip(solution_re.iter().zip(solution_im.iter())) 928 | { 929 | s.re = *s_re; 930 | s.im = *s_im; 931 | } 932 | 933 | // Setup 934 | let mut v = test_matrix_complex(); 935 | let v_copy = v.clone(); 936 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 937 | let mut vhat = Array2::>::zeros((nx, ny)); 938 | let mut handler: FftHandler = FftHandler::new(ny); 939 | 940 | // Transform 941 | ndfft(&v, &mut vhat, &mut handler, 1); 942 | ndifft(&vhat, &mut v, &mut handler, 1); 943 | 944 | // Assert 945 | approx_eq_complex(&vhat, &solution); 946 | approx_eq_complex(&v, &v_copy); 947 | } 948 | 949 | #[cfg(feature = "parallel")] 950 | #[test] 951 | fn test_fft_par() { 952 | // Solution from np.fft.fft 953 | let solution_re = array![ 954 | [0.61, 3.105, 2.508, 0.048, -3.652, -2.019], 955 | [2.795, 0.612, 0.219, 1.179, -2.801, 3.276], 956 | [2.259, 0.601, 0.045, 0.979, 4.506, 0.118], 957 | [-0.296, -0.896, 0.544, -4.282, 3.544, 6.288], 958 | [0.573, -0.96, -3.85, -2.613, -0.461, 4.467], 959 | [3.978, -2.229, 0.133, 1.154, -6.544, -0.962], 960 | ]; 961 | 962 | let solution_im = array![ 963 | [0.61, -2.019, -3.652, 0.048, 2.508, 3.105], 964 | [2.795, 3.276, -2.801, 1.179, 0.219, 0.612], 965 | [2.259, 0.118, 4.506, 0.979, 0.045, 0.601], 966 | [-0.296, 6.288, 3.544, -4.282, 0.544, -0.896], 967 | [0.573, 4.467, -0.461, -2.613, -3.85, -0.96], 968 | [3.978, -0.962, -6.544, 1.154, 0.133, -2.229], 969 | ]; 970 | 971 | let mut solution: Array2> = Array2::zeros(solution_re.raw_dim()); 972 | for (s, (s_re, s_im)) in solution 973 | .iter_mut() 974 | .zip(solution_re.iter().zip(solution_im.iter())) 975 | { 976 | s.re = *s_re; 977 | s.im = *s_im; 978 | } 979 | 980 | // Setup 981 | let mut v = test_matrix_complex(); 982 | let v_copy = v.clone(); 983 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 984 | let mut vhat = Array2::>::zeros((nx, ny)); 985 | let mut handler: FftHandler = FftHandler::new(ny); 986 | 987 | // Transform 988 | ndfft_par(&v, &mut vhat, &mut handler, 1); 989 | ndifft_par(&vhat, &mut v, &mut handler, 1); 990 | 991 | // Assert 992 | approx_eq_complex(&vhat, &solution); 993 | approx_eq_complex(&v, &v_copy); 994 | } 995 | 996 | #[test] 997 | fn test_fft_f_layout() { 998 | // Solution from np.fft.fft 999 | let solution_re = array![ 1000 | [0.61, 3.105, 2.508, 0.048, -3.652, -2.019], 1001 | [2.795, 0.612, 0.219, 1.179, -2.801, 3.276], 1002 | [2.259, 0.601, 0.045, 0.979, 4.506, 0.118], 1003 | [-0.296, -0.896, 0.544, -4.282, 3.544, 6.288], 1004 | [0.573, -0.96, -3.85, -2.613, -0.461, 4.467], 1005 | [3.978, -2.229, 0.133, 1.154, -6.544, -0.962], 1006 | ]; 1007 | 1008 | let solution_im = array![ 1009 | [0.61, -2.019, -3.652, 0.048, 2.508, 3.105], 1010 | [2.795, 3.276, -2.801, 1.179, 0.219, 0.612], 1011 | [2.259, 0.118, 4.506, 0.979, 0.045, 0.601], 1012 | [-0.296, 6.288, 3.544, -4.282, 0.544, -0.896], 1013 | [0.573, 4.467, -0.461, -2.613, -3.85, -0.96], 1014 | [3.978, -0.962, -6.544, 1.154, 0.133, -2.229], 1015 | ]; 1016 | 1017 | let mut solution: Array2> = Array2::zeros(solution_re.raw_dim()); 1018 | for (s, (s_re, s_im)) in solution 1019 | .iter_mut() 1020 | .zip(solution_re.iter().zip(solution_im.iter())) 1021 | { 1022 | s.re = *s_re; 1023 | s.im = *s_im; 1024 | } 1025 | 1026 | // Setup 1027 | let mut v = test_matrix_complex_f(); 1028 | let v_copy = v.clone(); 1029 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1030 | let mut vhat = Array2::>::zeros((nx, ny)); 1031 | let mut handler: FftHandler = FftHandler::new(ny); 1032 | 1033 | // Transform 1034 | ndfft(&v, &mut vhat, &mut handler, 1); 1035 | ndifft(&vhat, &mut v, &mut handler, 1); 1036 | 1037 | // Assert 1038 | approx_eq_complex(&vhat, &solution); 1039 | approx_eq_complex(&v, &v_copy); 1040 | } 1041 | 1042 | #[test] 1043 | fn test_fft_r2c() { 1044 | // Solution from np.fft.rfft 1045 | let solution_re = array![ 1046 | [0.61, 0.543, -0.572, 0.048], 1047 | [2.795, 1.944, -1.291, 1.179], 1048 | [2.259, 0.36, 2.275, 0.979], 1049 | [-0.296, 2.696, 2.044, -4.282], 1050 | [0.573, 1.753, -2.155, -2.613], 1051 | [3.978, -1.596, -3.205, 1.154], 1052 | ]; 1053 | 1054 | let solution_im = array![ 1055 | [0., -2.562, -3.08, 0.], 1056 | [0., 1.332, -1.51, 0.], 1057 | [0., -0.242, 2.23, 0.], 1058 | [0., 3.592, 1.5, 0.], 1059 | [0., 2.713, 1.695, 0.], 1060 | [0., 0.633, -3.339, 0.], 1061 | ]; 1062 | 1063 | let mut solution: Array2> = Array2::zeros(solution_re.raw_dim()); 1064 | for (s, (s_re, s_im)) in solution 1065 | .iter_mut() 1066 | .zip(solution_re.iter().zip(solution_im.iter())) 1067 | { 1068 | s.re = *s_re; 1069 | s.im = *s_im; 1070 | } 1071 | 1072 | // Setup 1073 | let mut v = test_matrix(); 1074 | let v_copy = v.clone(); 1075 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1076 | let mut vhat = Array2::>::zeros((nx, ny / 2 + 1)); 1077 | let mut handler = R2cFftHandler::::new(ny); 1078 | 1079 | // Transform 1080 | ndfft_r2c(&v, &mut vhat, &mut handler, 1); 1081 | ndifft_r2c(&vhat, &mut v, &mut handler, 1); 1082 | 1083 | // Assert 1084 | approx_eq_complex(&vhat, &solution); 1085 | approx_eq(&v, &v_copy); 1086 | } 1087 | 1088 | #[cfg(feature = "parallel")] 1089 | #[test] 1090 | fn test_fft_r2c_par() { 1091 | // Solution from np.fft.rfft 1092 | let solution_re = array![ 1093 | [0.61, 0.543, -0.572, 0.048], 1094 | [2.795, 1.944, -1.291, 1.179], 1095 | [2.259, 0.36, 2.275, 0.979], 1096 | [-0.296, 2.696, 2.044, -4.282], 1097 | [0.573, 1.753, -2.155, -2.613], 1098 | [3.978, -1.596, -3.205, 1.154], 1099 | ]; 1100 | 1101 | let solution_im = array![ 1102 | [0., -2.562, -3.08, 0.], 1103 | [0., 1.332, -1.51, 0.], 1104 | [0., -0.242, 2.23, 0.], 1105 | [0., 3.592, 1.5, 0.], 1106 | [0., 2.713, 1.695, 0.], 1107 | [0., 0.633, -3.339, 0.], 1108 | ]; 1109 | 1110 | let mut solution: Array2> = Array2::zeros(solution_re.raw_dim()); 1111 | for (s, (s_re, s_im)) in solution 1112 | .iter_mut() 1113 | .zip(solution_re.iter().zip(solution_im.iter())) 1114 | { 1115 | s.re = *s_re; 1116 | s.im = *s_im; 1117 | } 1118 | 1119 | // Setup 1120 | let mut v = test_matrix(); 1121 | let v_copy = v.clone(); 1122 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1123 | let mut vhat = Array2::>::zeros((nx, ny / 2 + 1)); 1124 | let mut handler = R2cFftHandler::::new(ny); 1125 | 1126 | // Transform 1127 | ndfft_r2c_par(&v, &mut vhat, &mut handler, 1); 1128 | ndifft_r2c_par(&vhat, &mut v, &mut handler, 1); 1129 | 1130 | // Assert 1131 | approx_eq_complex(&vhat, &solution); 1132 | approx_eq(&v, &v_copy); 1133 | } 1134 | 1135 | #[test] 1136 | fn test_ifft_c2r_first_last_element() { 1137 | let n = 6; 1138 | let mut v = Array1::::zeros(n); 1139 | let mut vhat = Array1::>::zeros(n / 2 + 1); 1140 | let solution_numpy_first_elem: Array1 = 1141 | array![0.16667, 0.16667, 0.16667, 0.16667, 0.16667, 0.16667]; 1142 | let solution_numpy_last_elem: Array1 = 1143 | array![0.16667, -0.16667, 0.16667, -0.16667, 0.16667, -0.16667]; 1144 | let mut rfft_handler = R2cFftHandler::::new(n); 1145 | 1146 | // First element should be purely real, thus the imaginary 1147 | // part should not matter. However, original realfft gives 1148 | // different results for different imaginary parts 1149 | vhat[0].re = 1.; 1150 | vhat[0].im = 100.; 1151 | // backward 1152 | ndifft_r2c(&vhat, &mut v, &mut rfft_handler, 0); 1153 | // assert 1154 | approx_eq(&v, &solution_numpy_first_elem); 1155 | 1156 | // Same for last element, if input is even 1157 | for v in vhat.iter_mut() { 1158 | v.re = 0.; 1159 | v.im = 0.; 1160 | } 1161 | vhat[3].re = 1.; 1162 | vhat[3].im = 100.; 1163 | // backward 1164 | ndifft_r2c(&vhat, &mut v, &mut rfft_handler, 0); 1165 | // assert 1166 | approx_eq(&v, &solution_numpy_last_elem); 1167 | } 1168 | 1169 | #[test] 1170 | fn test_fft_r2c_odd() { 1171 | // Setup 1172 | let mut v = array![[1., 2., 3.], [4., 5., 6.], [7., 8., 9.],]; 1173 | let v_copy = v.clone(); 1174 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1175 | let mut vhat = Array2::>::zeros((nx, ny / 2 + 1)); 1176 | let mut handler = R2cFftHandler::::new(ny); 1177 | 1178 | // Transform 1179 | ndfft_r2c(&v, &mut vhat, &mut handler, 1); 1180 | ndifft_r2c(&vhat, &mut v, &mut handler, 1); 1181 | 1182 | // Assert 1183 | approx_eq(&v, &v_copy); 1184 | } 1185 | 1186 | #[cfg(feature = "parallel")] 1187 | #[test] 1188 | fn test_fft_r2c_odd_par() { 1189 | // Setup 1190 | let mut v = array![[1., 2., 3.], [4., 5., 6.], [7., 8., 9.],]; 1191 | let v_copy = v.clone(); 1192 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1193 | let mut vhat = Array2::>::zeros((nx, ny / 2 + 1)); 1194 | let mut handler = R2cFftHandler::::new(ny); 1195 | 1196 | // Transform 1197 | ndfft_r2c_par(&v, &mut vhat, &mut handler, 1); 1198 | ndifft_r2c_par(&vhat, &mut v, &mut handler, 1); 1199 | 1200 | // Assert 1201 | approx_eq(&v, &v_copy); 1202 | } 1203 | 1204 | #[test] 1205 | fn test_dct1() { 1206 | // Solution from scipy.fft.dct(x, type=1) 1207 | let solution = array![ 1208 | [2.469, 4.259, 0.6, 0.04, -4.957, -1.353], 1209 | [3.953, -0.374, 4.759, -0.436, -2.643, 2.235], 1210 | [2.632, 0.818, -1.609, 1.053, 5.008, 1.008], 1211 | [-3.652, -2.628, 4.81, 2.632, 4.666, -7.138], 1212 | [-0.835, -2.982, 4.105, -3.192, 1.265, -2.297], 1213 | [8.743, -2.422, 1.167, -0.841, -7.506, 3.011], 1214 | ]; 1215 | 1216 | // Setup 1217 | let v = test_matrix(); 1218 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1219 | let mut vhat = Array2::::zeros((nx, ny)); 1220 | let mut handler: DctHandler = DctHandler::new(ny); 1221 | 1222 | // Transform 1223 | nddct1(&v, &mut vhat, &mut handler, 1); 1224 | 1225 | // Assert 1226 | approx_eq(&vhat, &solution); 1227 | } 1228 | 1229 | #[cfg(feature = "parallel")] 1230 | #[test] 1231 | fn test_dct1_par() { 1232 | // Solution from scipy.fft.dct(x, type=1) 1233 | let solution = array![ 1234 | [2.469, 4.259, 0.6, 0.04, -4.957, -1.353], 1235 | [3.953, -0.374, 4.759, -0.436, -2.643, 2.235], 1236 | [2.632, 0.818, -1.609, 1.053, 5.008, 1.008], 1237 | [-3.652, -2.628, 4.81, 2.632, 4.666, -7.138], 1238 | [-0.835, -2.982, 4.105, -3.192, 1.265, -2.297], 1239 | [8.743, -2.422, 1.167, -0.841, -7.506, 3.011], 1240 | ]; 1241 | 1242 | // Setup 1243 | let v = test_matrix(); 1244 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1245 | let mut vhat = Array2::::zeros((nx, ny)); 1246 | let mut handler: DctHandler = DctHandler::new(ny); 1247 | 1248 | // Transform 1249 | nddct1_par(&v, &mut vhat, &mut handler, 1); 1250 | 1251 | // Assert 1252 | approx_eq(&vhat, &solution); 1253 | } 1254 | 1255 | #[test] 1256 | fn test_dct2() { 1257 | // Solution from scipy.fft.dct(x, type=2) 1258 | let solution = array![ 1259 | [1.22, 5.25, -1.621, -0.619, -5.906, -1.105], 1260 | [5.59, -0.209, 4.699, 0.134, -3.907, 1.838], 1261 | [4.518, 1.721, 0.381, 1.492, 6.138, 0.513], 1262 | [-0.592, -3.746, 8.262, 1.31, 4.642, -6.125], 1263 | [1.146, -5.709, 5.75, -4.275, 0.78, -0.963], 1264 | [7.956, -2.873, -2.13, 0.006, -8.988, 2.56], 1265 | ]; 1266 | 1267 | // Setup 1268 | let v = test_matrix(); 1269 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1270 | let mut vhat = Array2::::zeros((nx, ny)); 1271 | let mut handler: DctHandler = DctHandler::new(ny); 1272 | 1273 | // Transform 1274 | nddct2(&v, &mut vhat, &mut handler, 1); 1275 | 1276 | // Assert 1277 | approx_eq(&vhat, &solution); 1278 | } 1279 | 1280 | #[cfg(feature = "parallel")] 1281 | #[test] 1282 | fn test_dct2_par() { 1283 | // Solution from scipy.fft.dct(x, type=2) 1284 | let solution = array![ 1285 | [1.22, 5.25, -1.621, -0.619, -5.906, -1.105], 1286 | [5.59, -0.209, 4.699, 0.134, -3.907, 1.838], 1287 | [4.518, 1.721, 0.381, 1.492, 6.138, 0.513], 1288 | [-0.592, -3.746, 8.262, 1.31, 4.642, -6.125], 1289 | [1.146, -5.709, 5.75, -4.275, 0.78, -0.963], 1290 | [7.956, -2.873, -2.13, 0.006, -8.988, 2.56], 1291 | ]; 1292 | 1293 | // Setup 1294 | let v = test_matrix(); 1295 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1296 | let mut vhat = Array2::::zeros((nx, ny)); 1297 | let mut handler: DctHandler = DctHandler::new(ny); 1298 | 1299 | // Transform 1300 | nddct2_par(&v, &mut vhat, &mut handler, 1); 1301 | 1302 | // Assert 1303 | approx_eq(&vhat, &solution); 1304 | } 1305 | 1306 | #[test] 1307 | fn test_dct3() { 1308 | // Solution from scipy.fft.dct(x, type=3) 1309 | let solution = array![ 1310 | [2.898, 4.571, -0.801, 1.65, -5.427, -2.291], 1311 | [2.701, -0.578, 5.768, -0.335, -3.158, 0.882], 1312 | [2.348, -0.184, -1.258, 0.048, 5.472, 2.081], 1313 | [-3.421, -2.075, 6.944, 0.264, 7.505, -4.315], 1314 | [-1.43, -3.023, 6.317, -5.259, 1.991, -1.44], 1315 | [5.76, -4.047, 1.974, 0.376, -8.651, 0.117], 1316 | ]; 1317 | 1318 | // Setup 1319 | let v = test_matrix(); 1320 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1321 | let mut vhat = Array2::::zeros((nx, ny)); 1322 | let mut handler: DctHandler = DctHandler::new(ny); 1323 | 1324 | // Transform 1325 | nddct3(&v, &mut vhat, &mut handler, 1); 1326 | 1327 | // Assert 1328 | approx_eq(&vhat, &solution); 1329 | } 1330 | 1331 | #[cfg(feature = "parallel")] 1332 | #[test] 1333 | fn test_dct3_par() { 1334 | // Solution from scipy.fft.dct(x, type=3) 1335 | let solution = array![ 1336 | [2.898, 4.571, -0.801, 1.65, -5.427, -2.291], 1337 | [2.701, -0.578, 5.768, -0.335, -3.158, 0.882], 1338 | [2.348, -0.184, -1.258, 0.048, 5.472, 2.081], 1339 | [-3.421, -2.075, 6.944, 0.264, 7.505, -4.315], 1340 | [-1.43, -3.023, 6.317, -5.259, 1.991, -1.44], 1341 | [5.76, -4.047, 1.974, 0.376, -8.651, 0.117], 1342 | ]; 1343 | 1344 | // Setup 1345 | let v = test_matrix(); 1346 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1347 | let mut vhat = Array2::::zeros((nx, ny)); 1348 | let mut handler: DctHandler = DctHandler::new(ny); 1349 | 1350 | // Transform 1351 | nddct3_par(&v, &mut vhat, &mut handler, 1); 1352 | 1353 | // Assert 1354 | approx_eq(&vhat, &solution); 1355 | } 1356 | 1357 | #[test] 1358 | fn test_dct4() { 1359 | // Solution from scipy.fft.dct(x, type=4) 1360 | let solution = array![ 1361 | [3.18, 2.73, -2.314, -2.007, -5.996, 2.127], 1362 | [3.175, 0.865, 4.939, -4.305, -0.443, 1.568], 1363 | [3.537, 0.677, 0.371, 4.186, 4.528, -1.531], 1364 | [-2.687, 1.838, 6.968, 0.899, 2.456, -8.79], 1365 | [-2.289, -1.002, 3.67, -5.705, 3.867, -4.349], 1366 | [4.192, -5.626, 1.789, -6.057, -4.61, 4.627], 1367 | ]; 1368 | 1369 | // Setup 1370 | let v = test_matrix(); 1371 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1372 | let mut vhat = Array2::::zeros((nx, ny)); 1373 | let mut handler: DctHandler = DctHandler::new(ny); 1374 | 1375 | // Transform 1376 | nddct4(&v, &mut vhat, &mut handler, 1); 1377 | 1378 | // Assert 1379 | approx_eq(&vhat, &solution); 1380 | } 1381 | 1382 | #[cfg(feature = "parallel")] 1383 | #[test] 1384 | fn test_dct4_par() { 1385 | // Solution from scipy.fft.dct(x, type=4) 1386 | let solution = array![ 1387 | [3.18, 2.73, -2.314, -2.007, -5.996, 2.127], 1388 | [3.175, 0.865, 4.939, -4.305, -0.443, 1.568], 1389 | [3.537, 0.677, 0.371, 4.186, 4.528, -1.531], 1390 | [-2.687, 1.838, 6.968, 0.899, 2.456, -8.79], 1391 | [-2.289, -1.002, 3.67, -5.705, 3.867, -4.349], 1392 | [4.192, -5.626, 1.789, -6.057, -4.61, 4.627], 1393 | ]; 1394 | 1395 | // Setup 1396 | let v = test_matrix(); 1397 | let (nx, ny) = (v.shape()[0], v.shape()[1]); 1398 | let mut vhat = Array2::::zeros((nx, ny)); 1399 | let mut handler: DctHandler = DctHandler::new(ny); 1400 | 1401 | // Transform 1402 | nddct4_par(&v, &mut vhat, &mut handler, 1); 1403 | 1404 | // Assert 1405 | approx_eq(&vhat, &solution); 1406 | } 1407 | } 1408 | --------------------------------------------------------------------------------