#![allow(clippy::type_complexity)]
use oxifft::{Complex, Direction, Flags, Plan, Plan2D, R2rPlan, RealPlan, RealPlan2D};
use oxifft::rdft::solvers::R2rKind;
use std::collections::HashMap;
use std::sync::Mutex;
use crate::error::{FFTError, FFTResult};
static R2C_CACHE: Mutex<Option<HashMap<usize, RealPlan<f64>>>> = Mutex::new(None);
static C2C_FWD_CACHE: Mutex<Option<HashMap<usize, Plan<f64>>>> = Mutex::new(None);
static C2C_BWD_CACHE: Mutex<Option<HashMap<usize, Plan<f64>>>> = Mutex::new(None);
static C2R_CACHE: Mutex<Option<HashMap<usize, RealPlan<f64>>>> = Mutex::new(None);
static DCT2_CACHE: Mutex<Option<HashMap<usize, R2rPlan<f64>>>> = Mutex::new(None);
static IDCT2_CACHE: Mutex<Option<HashMap<usize, R2rPlan<f64>>>> = Mutex::new(None);
static DST2_CACHE: Mutex<Option<HashMap<usize, R2rPlan<f64>>>> = Mutex::new(None);
static IDST2_CACHE: Mutex<Option<HashMap<usize, R2rPlan<f64>>>> = Mutex::new(None);
static R2C_2D_CACHE: Mutex<Option<HashMap<(usize, usize), RealPlan2D<f64>>>> = Mutex::new(None);
static C2C_2D_FWD_CACHE: Mutex<Option<HashMap<(usize, usize), Plan2D<f64>>>> = Mutex::new(None);
static C2C_2D_BWD_CACHE: Mutex<Option<HashMap<(usize, usize), Plan2D<f64>>>> = Mutex::new(None);
static C2R_2D_CACHE: Mutex<Option<HashMap<(usize, usize), RealPlan2D<f64>>>> = Mutex::new(None);
pub fn execute_r2c(input: &[f64], output: &mut [Complex<f64>]) -> FFTResult<()> {
let n = input.len();
let mut cache = R2C_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock R2C cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = RealPlan::r2c_1d(n, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!("Failed to create R2C plan for size {}", n))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute_r2c(input, output);
Ok(())
}
pub fn execute_c2c(
input: &[Complex<f64>],
output: &mut [Complex<f64>],
direction: Direction,
) -> FFTResult<()> {
let n = input.len();
let cache = match direction {
Direction::Forward => &C2C_FWD_CACHE,
Direction::Backward => &C2C_BWD_CACHE,
};
let mut cache_guard = cache.lock().map_err(|e| {
FFTError::ComputationError(format!("Failed to lock C2C {:?} cache: {}", direction, e))
})?;
if cache_guard.is_none() {
*cache_guard = Some(HashMap::new());
}
let cache_map = cache_guard
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = Plan::dft_1d(n, direction, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!(
"Failed to create C2C {:?} plan for size {}",
direction, n
))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute(input, output);
Ok(())
}
pub fn execute_c2r(input: &[Complex<f64>], output: &mut [f64], n: usize) -> FFTResult<()> {
let mut cache = C2R_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock C2R cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = RealPlan::c2r_1d(n, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!("Failed to create C2R plan for size {}", n))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute_c2r_unnormalized(input, output);
Ok(())
}
pub fn execute_dct2(input: &[f64], output: &mut [f64]) -> FFTResult<()> {
let n = input.len();
let mut cache = DCT2_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock DCT2 cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = R2rPlan::r2r_1d(n, R2rKind::Redft10, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!("Failed to create DCT2 plan for size {}", n))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute(input, output);
Ok(())
}
pub fn execute_idct2(input: &[f64], output: &mut [f64]) -> FFTResult<()> {
let n = input.len();
let mut cache = IDCT2_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock IDCT2 cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = R2rPlan::r2r_1d(n, R2rKind::Redft01, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!("Failed to create IDCT2 plan for size {}", n))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute(input, output);
Ok(())
}
pub fn execute_dst2(input: &[f64], output: &mut [f64]) -> FFTResult<()> {
let n = input.len();
let mut cache = DST2_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock DST2 cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = R2rPlan::r2r_1d(n, R2rKind::Rodft10, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!("Failed to create DST2 plan for size {}", n))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute(input, output);
Ok(())
}
pub fn execute_idst2(input: &[f64], output: &mut [f64]) -> FFTResult<()> {
let n = input.len();
let mut cache = IDST2_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock IDST2 cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(n) {
let plan = R2rPlan::r2r_1d(n, R2rKind::Rodft01, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!("Failed to create IDST2 plan for size {}", n))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&n)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute(input, output);
Ok(())
}
pub fn execute_r2c_2d(
input: &[f64],
output: &mut [Complex<f64>],
rows: usize,
cols: usize,
) -> FFTResult<()> {
let key = (rows, cols);
let mut cache = R2C_2D_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock 2D R2C cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(key) {
let plan = RealPlan2D::r2c(rows, cols, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!(
"Failed to create 2D R2C plan for size {}x{}",
rows, cols
))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&key)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute_r2c(input, output);
Ok(())
}
pub fn execute_c2c_2d(
input: &[Complex<f64>],
output: &mut [Complex<f64>],
rows: usize,
cols: usize,
direction: Direction,
) -> FFTResult<()> {
let key = (rows, cols);
let cache = match direction {
Direction::Forward => &C2C_2D_FWD_CACHE,
Direction::Backward => &C2C_2D_BWD_CACHE,
};
let mut cache_guard = cache.lock().map_err(|e| {
FFTError::ComputationError(format!(
"Failed to lock 2D C2C {:?} cache: {}",
direction, e
))
})?;
if cache_guard.is_none() {
*cache_guard = Some(HashMap::new());
}
let cache_map = cache_guard
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(key) {
let plan = Plan2D::new(rows, cols, direction, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!(
"Failed to create 2D C2C {:?} plan for size {}x{}",
direction, rows, cols
))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&key)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute(input, output);
Ok(())
}
pub fn execute_c2r_2d(
input: &[Complex<f64>],
output: &mut [f64],
rows: usize,
cols: usize,
) -> FFTResult<()> {
let key = (rows, cols);
let mut cache = C2R_2D_CACHE
.lock()
.map_err(|e| FFTError::ComputationError(format!("Failed to lock 2D C2R cache: {}", e)))?;
if cache.is_none() {
*cache = Some(HashMap::new());
}
let cache_map = cache
.as_mut()
.ok_or_else(|| FFTError::ComputationError("Cache initialization failed".to_string()))?;
if let std::collections::hash_map::Entry::Vacant(e) = cache_map.entry(key) {
let plan = RealPlan2D::c2r(rows, cols, Flags::ESTIMATE).ok_or_else(|| {
FFTError::ComputationError(format!(
"Failed to create 2D C2R plan for size {}x{}",
rows, cols
))
})?;
e.insert(plan);
}
let plan = cache_map
.get(&key)
.ok_or_else(|| FFTError::ComputationError("Failed to get cached plan".to_string()))?;
plan.execute_c2r(input, output);
Ok(())
}
pub fn clear_all_caches() {
if let Ok(mut cache) = R2C_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = C2C_FWD_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = C2C_BWD_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = C2R_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = DCT2_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = IDCT2_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = DST2_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = IDST2_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = R2C_2D_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = C2C_2D_FWD_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = C2C_2D_BWD_CACHE.lock() {
*cache = None;
}
if let Ok(mut cache) = C2R_2D_CACHE.lock() {
*cache = None;
}
}
#[derive(Debug, Clone, Default)]
pub struct CacheStats {
pub r2c_count: usize,
pub c2c_fwd_count: usize,
pub c2c_bwd_count: usize,
pub c2r_count: usize,
pub dct2_count: usize,
pub idct2_count: usize,
pub dst2_count: usize,
pub idst2_count: usize,
pub r2c_2d_count: usize,
pub c2c_2d_fwd_count: usize,
pub c2c_2d_bwd_count: usize,
pub c2r_2d_count: usize,
}
pub fn get_cache_stats() -> CacheStats {
let mut stats = CacheStats::default();
if let Ok(cache) = R2C_CACHE.lock() {
if let Some(ref map) = *cache {
stats.r2c_count = map.len();
}
}
if let Ok(cache) = C2C_FWD_CACHE.lock() {
if let Some(ref map) = *cache {
stats.c2c_fwd_count = map.len();
}
}
if let Ok(cache) = C2C_BWD_CACHE.lock() {
if let Some(ref map) = *cache {
stats.c2c_bwd_count = map.len();
}
}
if let Ok(cache) = C2R_CACHE.lock() {
if let Some(ref map) = *cache {
stats.c2r_count = map.len();
}
}
if let Ok(cache) = DCT2_CACHE.lock() {
if let Some(ref map) = *cache {
stats.dct2_count = map.len();
}
}
if let Ok(cache) = IDCT2_CACHE.lock() {
if let Some(ref map) = *cache {
stats.idct2_count = map.len();
}
}
if let Ok(cache) = DST2_CACHE.lock() {
if let Some(ref map) = *cache {
stats.dst2_count = map.len();
}
}
if let Ok(cache) = IDST2_CACHE.lock() {
if let Some(ref map) = *cache {
stats.idst2_count = map.len();
}
}
if let Ok(cache) = R2C_2D_CACHE.lock() {
if let Some(ref map) = *cache {
stats.r2c_2d_count = map.len();
}
}
if let Ok(cache) = C2C_2D_FWD_CACHE.lock() {
if let Some(ref map) = *cache {
stats.c2c_2d_fwd_count = map.len();
}
}
if let Ok(cache) = C2C_2D_BWD_CACHE.lock() {
if let Some(ref map) = *cache {
stats.c2c_2d_bwd_count = map.len();
}
}
if let Ok(cache) = C2R_2D_CACHE.lock() {
if let Some(ref map) = *cache {
stats.c2r_2d_count = map.len();
}
}
stats
}