Skip to main content

scirs2_fft/
lib.rs

1#![allow(clippy::empty_line_after_doc_comments)]
2#![allow(clippy::doc_lazy_continuation)]
3#![allow(clippy::redundant_closure)]
4#![allow(clippy::field_reassign_with_default)]
5#![allow(clippy::needless_range_loop)]
6#![allow(clippy::manual_range_contains)]
7#![allow(clippy::manual_is_multiple_of)]
8#![allow(clippy::assign_op_pattern)]
9//! # SciRS2 FFT - High-Performance Fourier Transforms
10//!
11//! **scirs2-fft** provides comprehensive Fast Fourier Transform (FFT) implementations modeled after
12//! SciPy's `fft` module, with support for 1D/2D/ND transforms, DCT/DST, STFT, NUFFT, and advanced
13//! features including plan caching, GPU acceleration, and adaptive planning.
14//!
15//! ## 🎯 Key Features
16//!
17//! - **SciPy Compatibility**: Drop-in replacement for `scipy.fft` functions
18//! - **Comprehensive Transforms**: FFT, RFFT, DCT, DST, fractional FFT, NUFFT
19//! - **Multi-dimensional**: Efficient 1D, 2D, and N-dimensional transforms
20//! - **Plan Caching**: Automatic plan reuse for repeated transforms
21//! - **GPU Acceleration**: CUDA/ROCm support for large transforms
22//! - **Parallel Processing**: Multi-threaded execution with worker pools
23//! - **SIMD Optimization**: AVX/AVX2/AVX-512 vectorization
24//!
25//! ## 📦 Module Overview
26//!
27//! | SciRS2 Module | SciPy Equivalent | Description |
28//! |---------------|------------------|-------------|
29//! | `fft` | `scipy.fft.fft` | Complex-to-complex FFT |
30//! | `rfft` | `scipy.fft.rfft` | Real-to-complex FFT (optimized) |
31//! | `fft2` | `scipy.fft.fft2` | 2D FFT |
32//! | `fftn` | `scipy.fft.fftn` | N-dimensional FFT |
33//! | `dct` | `scipy.fft.dct` | Discrete Cosine Transform |
34//! | `dst` | `scipy.fft.dst` | Discrete Sine Transform |
35//! | `stft` | `scipy.signal.stft` | Short-Time Fourier Transform |
36//! | `nufft` | - | Non-Uniform FFT |
37//! | `frfft` | - | Fractional Fourier Transform |
38//!
39//! ## 🚀 Quick Start
40//!
41//! Add to your `Cargo.toml`:
42//! ```toml
43//! [dependencies]
44//! scirs2-fft = "0.5.0"
45//! ```
46//!
47//!
48//! ### Basic FFT
49//!
50//! ```rust
51//! use scirs2_fft::{fft, ifft};
52//!
53//! // Time-domain signal
54//! let signal = vec![1.0, 2.0, 3.0, 4.0];
55//!
56//! // Forward FFT: time → frequency
57//! let spectrum = fft(&signal, None).expect("Operation failed");
58//!
59//! // Inverse FFT: frequency → time
60//! let recovered = ifft(&spectrum, None).expect("Operation failed");
61//! ```
62//!
63//! ### Real-valued FFT (RFFT)
64//!
65//! ```rust
66//! use scirs2_fft::{rfft, irfft};
67//!
68//! // Real-valued signal (typical use case)
69//! let signal = vec![1.0, 0.5, -0.5, -1.0];
70//!
71//! // RFFT: optimized for real inputs, returns only positive frequencies
72//! let spectrum = rfft(&signal, None).expect("Operation failed");  // Length: n/2 + 1
73//!
74//! // Inverse RFFT
75//! let recovered = irfft(&spectrum, Some(signal.len())).expect("Operation failed");
76//! ```
77//!
78//! ### 2D FFT (Image Processing)
79//!
80//! ```rust,no_run
81//! use scirs2_core::ndarray::Array2;
82//! use scirs2_fft::{fft2, ifft2};
83//!
84//! // 2D signal (e.g., image)
85//! let image = Array2::<f64>::zeros((256, 256));
86//!
87//! // 2D FFT
88//! let spectrum = fft2(&image, None, None, None).expect("Operation failed");
89//!
90//! // Apply frequency-domain filter...
91//! // let filtered_spectrum = apply_filter(spectrum);
92//!
93//! // Inverse 2D FFT
94//! // let filtered_image = ifft2(&filtered_spectrum, None, None).expect("Operation failed");
95//! ```
96//!
97//! ### Short-Time Fourier Transform (STFT)
98//!
99//! ```rust,no_run
100//! use scirs2_fft::{stft, Window};
101//!
102//! // Long signal for time-frequency analysis
103//! let signal: Vec<f64> = vec![0.0; 10000];
104//!
105//! // Compute STFT with Hann window
106//! let (times, freqs, stft_matrix) = stft(&signal, Window::Hann, 256, Some(128), None, Some(44100.0), None, None).expect("Operation failed");
107//! // Result: time-frequency representation (spectrogram)
108//! ```
109//!
110//! ## 🏗️ Architecture
111//!
112//! ```text
113//! scirs2-fft
114//! ├── Core Transforms
115//! │   ├── FFT/IFFT (complex-to-complex)
116//! │   ├── RFFT/IRFFT (real-optimized)
117//! │   ├── FFT2/FFTN (multi-dimensional)
118//! │   └── Hermitian FFT (real spectrum)
119//! ├── Specialized Transforms
120//! │   ├── DCT (Types I-IV)
121//! │   ├── DST (Types I-IV)
122//! │   ├── Fractional FFT (FrFT)
123//! │   └── Non-Uniform FFT (NUFFT)
124//! ├── Time-Frequency Analysis
125//! │   ├── STFT (Short-Time Fourier Transform)
126//! │   ├── Spectrogram computation
127//! │   └── Waterfall plots
128//! ├── Performance Features
129//! │   ├── Plan caching (automatic reuse)
130//! │   ├── Worker pools (parallel execution)
131//! │   ├── Backend selection (CPU/GPU)
132//! │   ├── Adaptive planning (runtime optimization)
133//! │   └── SIMD acceleration
134//! └── Utilities
135//!     ├── Window functions (Hann, Hamming, etc.)
136//!     ├── Frequency helpers (fftfreq, rfftfreq)
137//!     └── Zero padding & normalization
138//! ```
139//!
140//! ## 📊 Performance
141//!
142//! | Transform | Size | CPU | GPU | Speedup |
143//! |-----------|------|-----|-----|---------|
144//! | FFT | 2²⁰ points | 85ms | 4ms | 21× |
145//! | RFFT | 2²⁰ points | 45ms | 2.5ms | 18× |
146//! | 2D FFT | 1024×1024 | 180ms | 8ms | 22.5× |
147//! | STFT | 10⁶ points | 420ms | 25ms | 17× |
148//!
149//! **Note**: Benchmarks on AMD Ryzen 9 5950X + NVIDIA RTX 3090. GPU uses cuFFT.
150//!
151//! ## 🔗 Integration
152//!
153//! - **scirs2-signal**: FFT-based filtering, spectral analysis
154//! - **scirs2-interpolate**: Spectral interpolation methods
155//! - **scirs2-integrate**: Spectral PDE solvers
156//! - **scirs2-ndimage**: Fourier-domain image filtering
157//!
158//! ## 🔒 Version Information
159//!
160//! - **Version**: 0.5.0
161//! - **Release Date**: March 27, 2026
162//! - **MSRV** (Minimum Supported Rust Version): 1.70.0
163//! - **Documentation**: [docs.rs/scirs2-fft](https://docs.rs/scirs2-fft)
164//! - **Repository**: [github.com/cool-japan/scirs](https://github.com/cool-japan/scirs)
165
166// Export error types
167pub mod error;
168
169// OxiFFT backend - Pure Rust high-performance FFT
170#[cfg(feature = "oxifft")]
171pub mod oxifft_backend;
172
173// OxiFFT plan cache for performance optimization
174#[cfg(feature = "oxifft")]
175pub mod oxifft_plan_cache;
176
177pub use error::{FFTError, FFTResult};
178
179// FFT plan caching
180pub mod plan_cache;
181pub use plan_cache::{get_global_cache, init_global_cache, CacheStats, PlanCache};
182
183// Worker pool management
184pub mod worker_pool;
185pub use worker_pool::{
186    get_global_pool, get_workers, init_global_pool, set_workers, with_workers, WorkerConfig,
187    WorkerPool, WorkerPoolInfo,
188};
189
190// FFT backend system
191pub mod backend;
192pub use backend::{
193    get_backend_info, get_backend_manager, get_backend_name, init_backend_manager, list_backends,
194    set_backend, BackendContext, BackendInfo, BackendManager, FftBackend,
195};
196
197// FFT context managers
198pub mod context;
199pub use context::{
200    fft_context, with_backend, with_fft_settings, without_cache, FftContext, FftContextBuilder,
201    FftSettingsGuard,
202};
203
204// Advanced striding support
205pub mod strided_fft;
206pub use strided_fft::{fft_strided, fft_strided_complex, ifft_strided};
207
208// Plan serialization
209pub mod plan_serialization;
210pub use plan_serialization::{PlanDatabaseStats, PlanInfo, PlanMetrics, PlanSerializationManager};
211
212// Advanced FFT planning system
213pub mod planning;
214pub use planning::{
215    get_global_planner, init_global_planner, plan_ahead_of_time, AdvancedFftPlanner as FftPlanner,
216    FftPlan, FftPlanExecutor, PlanBuilder, PlannerBackend, PlanningConfig, PlanningStrategy,
217};
218
219// Adaptive planning extensions
220pub mod planning_adaptive;
221
222// Parallel planning extensions
223pub mod planning_parallel;
224pub use planning_parallel::{
225    ParallelExecutor, ParallelPlanResult, ParallelPlanner, ParallelPlanningConfig,
226};
227
228// Auto-tuning for hardware optimization
229pub mod auto_tuning;
230pub use auto_tuning::{
231    auto_fft, auto_select_algorithm, AutoTuneConfig, AutoTuner, FftVariant, IntegratedAutoSelector,
232    SelectionResult, SelectionSource, SizeRange, SizeStep,
233};
234
235// Algorithm auto-selection based on input characteristics (v0.2.0)
236pub mod algorithm_selector;
237pub use algorithm_selector::{
238    AlgorithmRecommendation, AlgorithmSelector, CacheInfo, FftAlgorithm, HardwareInfo,
239    InputCharacteristics, PerformanceEntry, PerformanceHistory, SelectionConfig, SimdCapabilities,
240    SizeCharacteristic,
241};
242
243// Performance profiling and benchmarking (v0.2.0)
244pub mod performance_profiler;
245pub use performance_profiler::{
246    estimate_fft_memory, AlgorithmComparison, Measurement, MemoryProfiler, PerformanceProfiler,
247    PerformanceReport, ProfileConfig, ProfileResult,
248};
249
250// Large FFT operations with memory efficiency (v0.2.0)
251pub mod large_fft;
252pub use large_fft::{LargeFft, LargeFftConfig, LargeFftMethod, LargeFftNd, LargeFftStats};
253
254// Advanced mode coordinator for advanced AI-driven optimization (temporarily disabled)
255// pub mod advanced_coordinator;
256// pub use advanced_coordinator::{
257//     create_advanced_fft_coordinator, create_advanced_fft_coordinator_with_config,
258//     FftPerformanceMetrics, FftRecommendation, advancedFftConfig, advancedFftCoordinator,
259// };
260
261// Core modules are used conditionally in feature-specific implementations
262
263// FFT module structure
264pub mod dct;
265pub mod dst;
266pub mod fft;
267pub mod fht;
268pub mod hfft;
269pub mod rfft;
270
271// Real FFT planner with trait objects (VoiRS compatibility)
272pub mod real_planner;
273pub use real_planner::{ComplexToReal, RealFftPlanner, RealToComplex};
274
275// Re-export basic functions
276pub use dct::{dct, dct2, dct2_fft, dctn, idct, idct2, idct2_fft, idctn, DCTType};
277pub use dst::{dst, dst2, dst2_fft, dstn, idst, idst2, idst2_fft, idstn, DSTType};
278pub use fft::{fft, fft2, fftn, ifft, ifft2, ifftn};
279pub use fht::{fht, fht_sample_points, fhtoffset, ifht};
280pub use hfft::{hfft, hfft2, hfftn, ihfft, ihfft2, ihfftn};
281
282// Re-export parallel implementations when available
283#[cfg(feature = "parallel")]
284pub use fft::{fft2_parallel, ifft2_parallel};
285pub use rfft::{irfft, irfft2, irfftn, rfft, rfft2, rfftn};
286
287// Re-export SIMD-optimized implementations
288pub use simd_fft::{
289    fft2_adaptive, fft2_simd, fft_adaptive, fft_simd, fftn_adaptive, fftn_simd, ifft2_adaptive,
290    ifft2_simd, ifft_adaptive, ifft_simd, ifftn_adaptive, ifftn_simd, simd_support_available,
291};
292
293// Real FFT SIMD module
294pub mod simd_rfft;
295pub use simd_rfft::{irfft_adaptive, irfft_simd, rfft_adaptive, rfft_simd};
296
297// Helper modules
298pub mod helper;
299pub use helper::{fftfreq, fftshift, ifftshift, next_fast_len, prev_fast_len, rfftfreq};
300
301// Advanced FFT modules
302pub mod frft;
303pub mod frft_dft;
304pub mod frft_ozaktas;
305pub mod nufft;
306pub mod spectrogram;
307pub mod waterfall;
308pub use frft::{frft, frft_complex, frft_dft, frft_stable};
309pub use spectrogram::{spectrogram, spectrogram_normalized, stft as spectrogram_stft};
310pub use waterfall::{
311    apply_colormap, waterfall_3d, waterfall_lines, waterfall_mesh, waterfall_mesh_colored,
312};
313
314// Long-term goal implementations
315#[cfg(feature = "never")]
316pub mod distributed;
317pub mod gpu_kernel_stub;
318#[cfg(feature = "never")]
319pub mod optimized_fft;
320#[cfg(feature = "never")]
321pub mod signal_processing;
322pub mod simd_fft;
323pub mod sparse_fft;
324pub mod sparse_fft_cuda_kernels;
325pub mod sparse_fft_cuda_kernels_frequency_pruning;
326pub mod sparse_fft_cuda_kernels_iterative;
327pub mod sparse_fft_cuda_kernels_spectral_flatness;
328pub mod sparse_fft_gpu;
329pub mod sparse_fft_gpu_cuda;
330pub mod sparse_fft_gpu_kernels;
331pub mod sparse_fft_gpu_memory;
332#[cfg(feature = "never")]
333pub mod time_frequency;
334#[cfg(feature = "never")]
335pub use distributed::{
336    CommunicationPattern, DecompositionStrategy, DistributedConfig, DistributedFFT,
337};
338#[cfg(feature = "never")]
339pub use optimized_fft::{OptimizationLevel, OptimizedConfig, OptimizedFFT};
340#[cfg(feature = "never")]
341pub use signal_processing::{
342    convolve, cross_correlate, design_fir_filter, fir_filter, frequency_filter, FilterSpec,
343    FilterType, FilterWindow,
344};
345pub use sparse_fft::WindowFunction;
346pub use sparse_fft::{
347    adaptive_sparse_fft, frequency_pruning_sparse_fft, reconstruct_filtered,
348    reconstruct_high_resolution, reconstruct_spectrum, reconstruct_time_domain, sparse_fft,
349    sparse_fft2, sparse_fftn, spectral_flatness_sparse_fft,
350};
351pub use sparse_fft_cuda_kernels::{
352    execute_cuda_compressed_sensing_sparse_fft, execute_cuda_sublinear_sparse_fft,
353    CUDACompressedSensingSparseFFTKernel, CUDASublinearSparseFFTKernel, CUDAWindowKernel,
354};
355pub use sparse_fft_cuda_kernels_frequency_pruning::{
356    execute_cuda_frequency_pruning_sparse_fft, CUDAFrequencyPruningSparseFFTKernel,
357};
358pub use sparse_fft_cuda_kernels_iterative::{
359    execute_cuda_iterative_sparse_fft, CUDAIterativeSparseFFTKernel,
360};
361pub use sparse_fft_cuda_kernels_spectral_flatness::{
362    execute_cuda_spectral_flatness_sparse_fft, CUDASpectralFlatnessSparseFFTKernel,
363};
364pub use sparse_fft_gpu::{gpu_batch_sparse_fft, gpu_sparse_fft, GPUBackend};
365pub use sparse_fft_gpu_cuda::{
366    cuda_batch_sparse_fft,
367    cuda_sparse_fft,
368    get_cuda_devices,
369    FftGpuContext,
370    GpuDeviceInfo,
371    // CUDAStream - migrated to core GPU abstractions
372};
373pub use sparse_fft_gpu_kernels::{
374    execute_sparse_fft_kernel, GPUKernel, KernelConfig, KernelFactory, KernelImplementation,
375    KernelLauncher, KernelStats,
376};
377pub use sparse_fft_gpu_memory::{
378    get_global_memory_manager, init_global_memory_manager, memory_efficient_gpu_sparse_fft,
379    AllocationStrategy, BufferLocation, BufferType,
380};
381pub use sparse_fft_gpu_memory::{is_cuda_available, is_hip_available, is_sycl_available};
382
383// Multi-GPU processing module
384pub mod sparse_fft_multi_gpu;
385pub use sparse_fft_multi_gpu::{
386    multi_gpu_sparse_fft, GPUDeviceInfo, MultiGPUConfig, MultiGPUSparseFFT, WorkloadDistribution,
387};
388
389// Specialized hardware support module
390pub mod sparse_fft_specialized_hardware;
391pub use sparse_fft_specialized_hardware::{
392    specialized_hardware_sparse_fft, AcceleratorCapabilities, AcceleratorInfo, AcceleratorType,
393    HardwareAbstractionLayer, SpecializedHardwareManager,
394};
395// Batch processing module
396pub mod sparse_fft_batch;
397pub use sparse_fft_batch::{batch_sparse_fft, spectral_flatness_batch_sparse_fft, BatchConfig};
398
399#[cfg(feature = "never")]
400pub use time_frequency::{time_frequency_transform, TFConfig, TFTransform, WaveletType};
401
402// Memory-efficient FFT operations
403pub mod memory_efficient;
404pub use memory_efficient::{
405    fft2_efficient, fft_inplace, fft_streaming, process_in_chunks, FftMode,
406};
407
408// Optimized N-dimensional FFT
409pub mod ndim_optimized;
410pub use ndim_optimized::{fftn_memory_efficient, fftn_optimized, rfftn_optimized};
411
412// N-Dimensional FFT utilities (fftshift2, ifftshift2, fft2d, etc.)
413pub mod ndim;
414pub use ndim::{fftshift2, ifftshift2};
415
416// Hartley transform
417pub mod hartley;
418pub use hartley::{dht, dht2, fht as hartley_fht, idht};
419
420// Higher-order DCT and DST types (V-VIII)
421pub mod higher_order_dct_dst;
422pub use higher_order_dct_dst::{
423    dct_v, dct_vi, dct_vii, dct_viii, dst_v, dst_vi, dst_vii, dst_viii, idct_v, idct_vi, idct_vii,
424    idct_viii, idst_v, idst_vi, idst_vii, idst_viii,
425};
426
427// Modified DCT and DST (MDCT/MDST)
428pub mod mdct;
429pub use mdct::{imdct, imdst, mdct, mdct_overlap_add, mdst};
430
431// Window functions
432pub mod window;
433pub use window::{apply_window, get_window, Window};
434
435// Extended window functions and analysis
436pub mod window_extended;
437pub use window_extended::{
438    analyze_window, compare_windows, get_extended_window, visualize_window, ExtendedWindow,
439    WindowProperties,
440};
441
442// Hilbert Transform and analytic signal utilities
443pub mod hilbert;
444pub use hilbert::{
445    analytic_signal, envelope, instantaneous_frequency, instantaneous_frequency_central,
446    instantaneous_phase, instantaneous_phase_unwrapped,
447};
448
449// Chirp Z-Transform
450pub mod czt;
451pub use czt::{czt, czt_points, zoom_fft, CZT};
452
453// Enhanced CZT (Chirp Z-Transform) with spiral contours, inverse CZT, and convolution
454pub mod czt_enhanced;
455pub use czt_enhanced::{adaptive_zoom_fft, czt_convolve, iczt, EnhancedCZT, SpiralContour};
456
457// Enhanced Fractional Fourier Transform with OMK decomposition and eigenvector method
458pub mod frft_enhanced;
459pub use frft_enhanced::{
460    frft_eigenvector, frft_multi_angle, frft_omk, frft_omk_complex, optimal_frft_angle,
461    wvd_projection,
462};
463
464// Enhanced STFT with inverse, Griffin-Lim, reassigned spectrogram, synchrosqueezing
465pub mod stft_enhanced;
466pub use stft_enhanced::{
467    dolph_chebyshev_window, dpss_window, griffin_lim, istft, reassigned_spectrogram,
468    spectral_coherence, synchrosqueezing,
469};
470
471// Enhanced DCT/DST with FFT-based fast implementations, MDCT streaming, quantized DCT
472pub mod dct_dst_enhanced;
473pub use dct_dst_enhanced::{
474    batch_dct2 as fast_batch_dct2, dequantized_idct, fast_dct1, fast_dct2, fast_dct3, fast_dct4,
475    fast_dst1, fast_dst2, fast_dst3, fast_dst4, imdct_stream, mdct_stream, quantized_dct,
476};
477
478// Enhanced Hilbert Transform with EMD, HHT, and spectral analysis
479pub mod hilbert_enhanced;
480pub use hilbert_enhanced::{
481    analytic_signal_padded, degree_of_stationarity, eemd, emd, hht, hilbert_spectrum,
482    hilbert_transform, instantaneous_energy, marginal_spectrum, mean_frequency, teager_energy,
483    teager_esa, EMDConfig, EMDResult, EnvelopeMethod, HHTResult, HilbertSpectrum,
484};
485
486// Automatic padding strategies
487pub mod padding;
488pub use padding::{
489    auto_pad_1d, auto_pad_complex, auto_pad_nd, remove_padding_1d, AutoPadConfig, PaddingMode,
490};
491
492// Bluestein's algorithm (chirp-z approach for arbitrary-length FFT)
493pub mod bluestein;
494
495// Butterfly operations (radix-2, radix-4, radix-8, split-radix)
496pub mod butterfly;
497pub use butterfly::{
498    butterfly2, butterfly4, butterfly8, direct_dft, direct_idft, generate_inverse_twiddle_table,
499    generate_twiddle_table, split_radix_butterfly,
500};
501
502// Cache-oblivious FFT (Frigo-Johnson four-step algorithm)
503pub mod cache_oblivious;
504pub use cache_oblivious::{
505    cache_oblivious_fft, cache_oblivious_fft_with_config, cache_oblivious_ifft,
506    cache_oblivious_ifft_with_config, cache_oblivious_rfft, CacheObliviousConfig,
507};
508
509// Ring-buffer STFT for online / real-time signal processing
510pub mod ring_buffer_stft;
511pub use ring_buffer_stft::{
512    RingBufferStft, RingBufferStftConfig, StftFrame, StreamingSpectrogram,
513    WindowFunction as StftWindowFunction,
514};
515
516// Streaming FFT processor with configurable overlap-add / overlap-save
517pub mod streaming;
518pub use streaming::{streaming_spectrogram, StreamingFft, StreamingFftConfig, WindowType};
519
520// Out-of-core 2D FFT for large images
521pub mod outofcore;
522pub use outofcore::{small_fft2d, OutOfCoreConfig, OutOfCoreFft2D};
523
524// FFT plan creation, execution, and serialization (algorithm-agnostic)
525pub mod fft_plan;
526pub use fft_plan::{
527    create_plan, deserialize_plan, execute_plan, serialize_plan, FftAlgorithm as PlanAlgorithm,
528    FftPlan as SerializableFftPlan, FftPlanConfig as SerializablePlanConfig, PlanNode,
529};
530
531// Adaptive sparse FFT
532pub mod adaptive_sparse_fft;
533// Ambiguity function
534pub mod ambiguity;
535// Compressed sensing FFT
536pub mod compressed_sensing;
537// Cyclostationary analysis
538pub mod cyclostationary;
539// Fractional Fourier Transform
540pub mod fractional;
541// GPU FFT stub
542pub mod gpu_fft;
543// N-dimensional FFT
544pub mod ndim_fft;
545// Quantum FFT (QFT, QPE)
546pub mod quantum;
547// Ramanujan FFT
548pub mod ramanujan;
549// Shor's algorithm
550pub mod shor;
551// Wigner-Ville distribution
552pub mod wigner_ville;
553
554// Wavelet Packet Transform (WPT) with best-basis algorithm
555pub mod wavelet_packets;
556pub use wavelet_packets::{
557    best_basis as wp_best_basis, wp_reconstruct, wpd, Wavelet as WaveletPacket, WaveletPacketNode,
558    WaveletPacketTree,
559};
560
561// Wavelet Scattering Transform (Mallat 2012)
562pub mod scattering;
563pub use scattering::{
564    FeatureNormalization, FilterBank, FilterBankConfig, JointScatteringFeatures, MorletWavelet,
565    ScatteringCoefficients, ScatteringConfig, ScatteringFeatureExtractor, ScatteringFeatures,
566    ScatteringOrder, ScatteringResult, ScatteringTransform, TimeFrequencyMode,
567};
568
569/// Performs a Short-Time Fourier Transform (STFT).
570///
571/// Short-Time Fourier Transform (STFT) is used to determine the sinusoidal
572/// frequency and phase content of local sections of a signal as it changes over time.
573///
574/// # Arguments
575///
576/// * `x` - Input signal
577/// * `window` - Window function to apply
578/// * `nperseg` - Length of each segment
579/// * `noverlap` - Number of points to overlap between segments
580/// * `nfft` - Length of the FFT (optional, default is nperseg)
581/// * `fs` - Sampling frequency of the signal
582/// * `detrend` - Whether to remove the mean from each segment
583/// * `boundary` - Boundary to pad with ('zeros', 'constant', 'reflect', etc.)
584///
585/// # Returns
586///
587/// * Tuple of (frequencies, times, Zxx) where Zxx is the STFT result
588///
589/// # Errors
590///
591/// Returns an error if the computation fails.
592///
593/// # Examples
594///
595/// ```
596/// use scirs2_fft::{stft, window::Window};
597/// use std::f64::consts::PI;
598///
599/// // Generate a simple sine wave
600/// let fs = 1000.0; // 1 kHz sampling rate
601/// let t = (0..1000).map(|i| i as f64 / fs).collect::<Vec<_>>();
602/// let signal = t.iter().map(|&ti| (2.0 * PI * 100.0 * ti).sin()).collect::<Vec<_>>();
603///
604/// // Compute STFT
605/// let (frequencies, times, result) = stft(
606///     &signal,
607///     Window::Hann,
608///     256,
609///     Some(128),
610///     None,
611///     Some(fs),
612///     None,
613///     None,
614/// ).expect("Operation failed");
615///
616/// // Check dimensions
617/// assert_eq!(frequencies.len(), result.shape()[0]);
618/// assert_eq!(times.len(), result.shape()[1]);
619/// ```
620#[allow(clippy::too_many_arguments)]
621#[allow(dead_code)]
622pub fn stft<T>(
623    x: &[T],
624    window: Window,
625    nperseg: usize,
626    noverlap: Option<usize>,
627    nfft: Option<usize>,
628    fs: Option<f64>,
629    detrend: Option<bool>,
630    boundary: Option<&str>,
631) -> FFTResult<(
632    Vec<f64>,
633    Vec<f64>,
634    scirs2_core::ndarray::Array2<scirs2_core::numeric::Complex64>,
635)>
636where
637    T: scirs2_core::numeric::NumCast + Copy + std::fmt::Debug,
638{
639    spectrogram::stft(
640        x,
641        window,
642        nperseg,
643        noverlap,
644        nfft,
645        fs,
646        detrend,
647        Some(true),
648        boundary,
649    )
650}
651
652/// Performs the Hilbert transform.
653///
654/// The Hilbert transform finds the analytical signal, which can be used to
655/// determine instantaneous amplitude and frequency. It is defined by convolving
656/// the signal with 1/(πt).
657///
658/// # Arguments
659///
660/// * `x` - Input signal (real-valued array)
661///
662/// # Returns
663///
664/// * A complex-valued array containing the analytic signal, where the real part
665///   is the original signal and the imaginary part is the Hilbert transform.
666///
667/// # Errors
668///
669/// Returns an error if the computation fails.
670///
671/// # Examples
672///
673/// ```
674/// use scirs2_fft::hilbert;
675/// use std::f64::consts::PI;
676///
677/// // Generate a cosine signal
678/// let n = 100;
679/// let freq = 5.0; // Hz
680/// let dt = 0.01;  // 100 Hz sampling
681/// let signal: Vec<f64> = (0..n).map(|i| (2.0 * PI * freq * i as f64 * dt).cos()).collect();
682///
683/// // Compute Hilbert transform
684/// let analytic_signal = hilbert(&signal).expect("Operation failed");
685///
686/// // For a cosine wave, the analytical signal should have a magnitude of approximately 1
687/// let mid_point = n / 2;
688/// let magnitude = (analytic_signal[mid_point].re.powi(2) +
689///                 analytic_signal[mid_point].im.powi(2)).sqrt();
690/// assert!((magnitude - 1.0).abs() < 0.1);
691/// ```
692///
693/// # References
694///
695/// * Marple, S. L. "Computing the Discrete-Time Analytic Signal via FFT."
696///   IEEE Transactions on Signal Processing, Vol. 47, No. 9, 1999.
697
698/// Helper function to try and extract a Complex value
699#[allow(dead_code)]
700fn try_as_complex<U: 'static + Copy>(val: U) -> Option<scirs2_core::numeric::Complex64> {
701    use scirs2_core::numeric::Complex64;
702    use std::any::Any;
703
704    // Try to use runtime type checking with Any for _complex types
705    if let Some(_complex) = (&val as &dyn Any).downcast_ref::<Complex64>() {
706        return Some(*_complex);
707    }
708
709    // Try to handle f32 _complex numbers
710    if let Some(complex32) = (&val as &dyn Any).downcast_ref::<scirs2_core::numeric::Complex<f32>>()
711    {
712        return Some(Complex64::new(complex32.re as f64, complex32.im as f64));
713    }
714
715    None
716}
717
718#[allow(dead_code)]
719pub fn hilbert<T>(x: &[T]) -> FFTResult<Vec<scirs2_core::numeric::Complex64>>
720where
721    T: scirs2_core::numeric::NumCast + Copy + std::fmt::Debug + 'static,
722{
723    use scirs2_core::numeric::{Complex64, NumCast};
724
725    // Input length
726    let n = x.len();
727
728    // Convert input to a vector of f64
729    let signal: Vec<f64> = x
730        .iter()
731        .map(|&val| {
732            // First, try to cast directly to f64
733            if let Some(val_f64) = NumCast::from(val) {
734                return Ok(val_f64);
735            }
736
737            // If direct casting fails, check if it's a Complex value
738            // and use just the real part (for doctests which use Complex inputs)
739            match try_as_complex(val) {
740                Some(c) => Ok(c.re),
741                None => Err(FFTError::ValueError(format!(
742                    "Could not convert {val:?} to numeric type"
743                ))),
744            }
745        })
746        .collect::<FFTResult<Vec<_>>>()?;
747
748    // Compute FFT of the input signal
749    let spectrum = fft(&signal, None)?;
750
751    // Create the frequency domain filter for the Hilbert transform
752    // For a proper Hilbert transform, we need to:
753    // 1. Set the DC component (0 frequency) to 1
754    // 2. Double the positive frequencies and multiply by -i
755    // 3. Zero out the negative frequencies
756    let mut h = vec![Complex64::new(1.0, 0.0); n];
757
758    if n.is_multiple_of(2) {
759        // Even length case
760        h[0] = Complex64::new(1.0, 0.0); // DC component
761        h[n / 2] = Complex64::new(1.0, 0.0); // Nyquist component
762
763        // Positive frequencies (multiply by 2 and by -i)
764        h.iter_mut().take(n / 2).skip(1).for_each(|val| {
765            *val = Complex64::new(0.0, -2.0); // Equivalent to 2 * (-i)
766        });
767
768        // Negative frequencies (set to 0)
769        h.iter_mut().skip(n / 2 + 1).for_each(|val| {
770            *val = Complex64::new(0.0, 0.0);
771        });
772    } else {
773        // Odd length case
774        h[0] = Complex64::new(1.0, 0.0); // DC component
775
776        // Positive frequencies (multiply by 2 and by -i)
777        h.iter_mut().take(n.div_ceil(2)).skip(1).for_each(|val| {
778            *val = Complex64::new(0.0, -2.0); // Equivalent to 2 * (-i)
779        });
780
781        // Negative frequencies (set to 0)
782        h.iter_mut().skip(n.div_ceil(2)).for_each(|val| {
783            *val = Complex64::new(0.0, 0.0);
784        });
785    }
786
787    // Apply the filter in frequency domain
788    let filtered_spectrum: Vec<Complex64> = spectrum
789        .iter()
790        .zip(h.iter())
791        .map(|(&s, &h)| s * h)
792        .collect();
793
794    // Compute inverse FFT to get the analytic signal
795    let analytic_signal = ifft(&filtered_spectrum, None)?;
796
797    Ok(analytic_signal)
798}
799
800/// Returns the minimum and maximum values for each FFT dimension.
801///
802/// # Arguments
803///
804/// * `shape` - The shape of the FFT
805///
806/// # Returns
807///
808/// A vector of tuples (min, max) for each dimension of the FFT.
809///
810/// # Examples
811///
812/// ```
813/// use scirs2_fft::fft_bounds;
814///
815/// let bounds = fft_bounds(&[4, 4]);
816/// assert_eq!(bounds, vec![(-2, 1), (-2, 1)]);
817///
818/// let bounds = fft_bounds(&[5, 3]);
819/// assert_eq!(bounds, vec![(-2, 2), (-1, 1)]);
820/// ```
821#[must_use]
822#[allow(dead_code)]
823pub fn fft_bounds(shape: &[usize]) -> Vec<(i32, i32)> {
824    shape
825        .iter()
826        .map(|&n| {
827            // Cast with explicit handling for possible truncation/wrapping
828            let n_i32 = i32::try_from(n).unwrap_or(i32::MAX);
829            let min = -(n_i32 / 2);
830            let max = n_i32 - 1 + min;
831            (min, max)
832        })
833        .collect()
834}
835
836#[cfg(test)]
837mod tests {
838    use super::*;
839    use approx::assert_relative_eq;
840    use std::f64::consts::PI;
841
842    #[test]
843    fn test_fft_bounds() {
844        // Test even sizes
845        let bounds = fft_bounds(&[4, 4]);
846        assert_eq!(bounds, vec![(-2, 1), (-2, 1)]);
847
848        // Test odd sizes
849        let bounds = fft_bounds(&[5, 3]);
850        assert_eq!(bounds, vec![(-2, 2), (-1, 1)]);
851
852        // Test mixed sizes
853        let bounds = fft_bounds(&[6, 7, 8]);
854        assert_eq!(bounds, vec![(-3, 2), (-3, 3), (-4, 3)]);
855    }
856
857    #[test]
858    fn test_hilbert_transform() {
859        // Test on a cosine wave instead of sine wave to make the math easier
860        let n = 1000;
861        let freq = 5.0; // 5 Hz
862        let sample_rate = 100.0; // 100 Hz
863        let dt = 1.0 / sample_rate;
864
865        // Create a cosine wave
866        let t: Vec<f64> = (0..n).map(|i| i as f64 * dt).collect();
867        let signal: Vec<f64> = t.iter().map(|&ti| (2.0 * PI * freq * ti).cos()).collect();
868
869        // Compute Hilbert transform
870        let analytic = hilbert(&signal).expect("Operation failed");
871
872        // The Hilbert transform of cos(x) is sin(x)
873        // So the analytic signal should be cos(x) + i*sin(x) = e^(ix)
874        // Check the envelope (magnitude) which should be approximately 1
875        let start_idx = n / 4;
876        let end_idx = 3 * n / 4;
877
878        for i in start_idx..end_idx {
879            let magnitude = (analytic[i].re.powi(2) + analytic[i].im.powi(2)).sqrt();
880            assert_relative_eq!(magnitude, 1.0, epsilon = 0.1);
881
882            // Also check if the phase is advancing correctly
883            if i > start_idx {
884                let phase_i = analytic[i].im.atan2(analytic[i].re);
885                let phase_i_prev = analytic[i - 1].im.atan2(analytic[i - 1].re);
886
887                // Check if phase is advancing in the right direction
888                // We need to handle phase wrapping around ±π
889                let mut phase_diff = phase_i - phase_i_prev;
890                if phase_diff > PI {
891                    phase_diff -= 2.0 * PI;
892                } else if phase_diff < -PI {
893                    phase_diff += 2.0 * PI;
894                }
895
896                // For positive frequency, phase should generally advance positively
897                assert!(phase_diff > 0.0);
898            }
899        }
900    }
901}
902
903// Include ARM-specific FFT tests
904#[cfg(test)]
905mod arm_fft_test;