use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::numeric::{Float, FromPrimitive, Zero};
use std::cell::RefCell;
use std::fmt::{Debug, Display};
use std::ops::{Add, Div, Mul, Sub};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ExtrapolateMode {
#[default]
Extrapolate,
Periodic,
Nan,
Error,
}
#[derive(Debug)]
pub struct BSplineWorkspace<T> {
pub(crate) coeffs: RefCell<Array1<T>>,
pub(crate) poly_buf: RefCell<Array1<T>>,
pub(crate) basis_buf: RefCell<Array1<T>>,
pub(crate) matrix_buf: RefCell<Array2<T>>,
pub(crate) memory_stats: RefCell<WorkspaceMemoryStats>,
}
#[derive(Debug, Clone, Default)]
pub struct WorkspaceMemoryStats {
pub peak_memory_bytes: usize,
pub current_memory_bytes: usize,
pub allocations_avoided: usize,
pub resize_count: usize,
pub evaluation_count: usize,
}
impl WorkspaceMemoryStats {
pub fn efficiency_ratio(&self) -> f64 {
if self.evaluation_count == 0 {
0.0
} else {
self.allocations_avoided as f64 / self.evaluation_count as f64
}
}
pub fn peak_memory_mb(&self) -> f64 {
self.peak_memory_bytes as f64 / (1024.0 * 1024.0)
}
pub fn update_memory_usage(&mut self, current_bytes: usize) {
self.current_memory_bytes = current_bytes;
if current_bytes > self.peak_memory_bytes {
self.peak_memory_bytes = current_bytes;
}
}
pub fn record_allocation_avoided(&mut self) {
self.allocations_avoided += 1;
}
pub fn record_resize(&mut self) {
self.resize_count += 1;
}
pub fn record_evaluation(&mut self) {
self.evaluation_count += 1;
}
pub fn reset(&mut self) {
self.peak_memory_bytes = 0;
self.current_memory_bytes = 0;
self.allocations_avoided = 0;
self.resize_count = 0;
self.evaluation_count = 0;
}
pub fn estimated_memory_saved_mb(&self, avg_allocation_size_bytes: usize) -> f64 {
(self.allocations_avoided * avg_allocation_size_bytes) as f64 / (1024.0 * 1024.0)
}
}
impl<T> BSplineWorkspace<T>
where
T: Float + FromPrimitive + Zero + Clone,
{
pub fn new() -> Self {
Self::with_capacity(16, 16)
}
pub fn with_capacity(array_capacity: usize, matrix_capacity: usize) -> Self {
Self {
coeffs: RefCell::new(Array1::zeros(array_capacity)),
poly_buf: RefCell::new(Array1::zeros(array_capacity)),
basis_buf: RefCell::new(Array1::zeros(array_capacity)),
matrix_buf: RefCell::new(Array2::zeros((matrix_capacity, matrix_capacity))),
memory_stats: RefCell::new(WorkspaceMemoryStats::default()),
}
}
pub fn current_memory_bytes(&self) -> usize {
self.memory_stats.borrow().current_memory_bytes
}
pub fn peak_memory_bytes(&self) -> usize {
self.memory_stats.borrow().peak_memory_bytes
}
pub fn efficiency_ratio(&self) -> f64 {
self.memory_stats.borrow().efficiency_ratio()
}
pub fn evaluation_count(&self) -> usize {
self.memory_stats.borrow().evaluation_count
}
pub fn allocations_avoided(&self) -> usize {
self.memory_stats.borrow().allocations_avoided
}
pub fn get_statistics(&self) -> WorkspaceMemoryStats {
self.memory_stats.borrow().clone()
}
pub fn reset_statistics(&self) {
self.memory_stats.borrow_mut().reset();
}
pub fn ensure_coeffs_capacity(&self, capacity: usize) {
let mut coeffs = self.coeffs.borrow_mut();
if coeffs.len() < capacity {
*coeffs = Array1::zeros(capacity);
self.memory_stats.borrow_mut().record_resize();
} else {
self.memory_stats.borrow_mut().record_allocation_avoided();
}
}
pub fn ensure_poly_capacity(&self, capacity: usize) {
let mut poly_buf = self.poly_buf.borrow_mut();
if poly_buf.len() < capacity {
*poly_buf = Array1::zeros(capacity);
self.memory_stats.borrow_mut().record_resize();
} else {
self.memory_stats.borrow_mut().record_allocation_avoided();
}
}
pub fn ensure_basis_capacity(&self, capacity: usize) {
let mut basis_buf = self.basis_buf.borrow_mut();
if basis_buf.len() < capacity {
*basis_buf = Array1::zeros(capacity);
self.memory_stats.borrow_mut().record_resize();
} else {
self.memory_stats.borrow_mut().record_allocation_avoided();
}
}
pub fn ensure_matrix_capacity(&self, rows: usize, cols: usize) {
let mut matrix_buf = self.matrix_buf.borrow_mut();
if matrix_buf.nrows() < rows || matrix_buf.ncols() < cols {
*matrix_buf = Array2::zeros((rows, cols));
self.memory_stats.borrow_mut().record_resize();
} else {
self.memory_stats.borrow_mut().record_allocation_avoided();
}
}
pub fn record_evaluation(&self) {
self.memory_stats.borrow_mut().record_evaluation();
let coeffs_size = self.coeffs.borrow().len() * std::mem::size_of::<T>();
let poly_size = self.poly_buf.borrow().len() * std::mem::size_of::<T>();
let basis_size = self.basis_buf.borrow().len() * std::mem::size_of::<T>();
let matrix_size = {
let matrix = self.matrix_buf.borrow();
matrix.nrows() * matrix.ncols() * std::mem::size_of::<T>()
};
let total_size = coeffs_size + poly_size + basis_size + matrix_size;
self.memory_stats
.borrow_mut()
.update_memory_usage(total_size);
}
pub fn clear(&self) {
*self.coeffs.borrow_mut() = Array1::zeros(1);
*self.poly_buf.borrow_mut() = Array1::zeros(1);
*self.basis_buf.borrow_mut() = Array1::zeros(1);
*self.matrix_buf.borrow_mut() = Array2::zeros((1, 1));
}
pub fn memory_report(&self) -> String {
let stats = self.get_statistics();
format!(
"BSpline Workspace Memory Report:\n\
Peak Memory: {:.2} MB\n\
Current Memory: {:.2} MB\n\
Evaluations: {}\n\
Allocations Avoided: {}\n\
Efficiency Ratio: {:.2}%\n\
Resizes: {}",
stats.peak_memory_mb(),
stats.current_memory_bytes as f64 / (1024.0 * 1024.0),
stats.evaluation_count,
stats.allocations_avoided,
stats.efficiency_ratio() * 100.0,
stats.resize_count
)
}
}
impl<T> Default for BSplineWorkspace<T>
where
T: Float + FromPrimitive + Zero + Clone,
{
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for BSplineWorkspace<T>
where
T: Float + FromPrimitive + Zero + Clone,
{
fn clone(&self) -> Self {
Self {
coeffs: RefCell::new(self.coeffs.borrow().clone()),
poly_buf: RefCell::new(self.poly_buf.borrow().clone()),
basis_buf: RefCell::new(self.basis_buf.borrow().clone()),
matrix_buf: RefCell::new(self.matrix_buf.borrow().clone()),
memory_stats: RefCell::new(self.memory_stats.borrow().clone()),
}
}
}
pub struct BSplineWorkspaceBuilder<T> {
array_capacity: usize,
matrix_capacity: usize,
_phantom: std::marker::PhantomData<T>,
}
impl<T> Default for BSplineWorkspaceBuilder<T> {
fn default() -> Self {
Self {
array_capacity: 16,
matrix_capacity: 16,
_phantom: std::marker::PhantomData,
}
}
}
impl<T> BSplineWorkspaceBuilder<T>
where
T: Float + FromPrimitive + Zero + Clone,
{
pub fn new() -> Self {
Self::default()
}
pub fn with_array_capacity(mut self, capacity: usize) -> Self {
self.array_capacity = capacity;
self
}
pub fn with_matrix_capacity(mut self, capacity: usize) -> Self {
self.matrix_capacity = capacity;
self
}
pub fn build(self) -> BSplineWorkspace<T> {
BSplineWorkspace::with_capacity(self.array_capacity, self.matrix_capacity)
}
}
pub trait WorkspaceProvider<T> {
fn get_workspace(&self) -> &BSplineWorkspace<T>;
fn is_workspace_enabled(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct WorkspaceConfig {
pub initial_array_capacity: usize,
pub initial_matrix_capacity: usize,
pub max_memory_mb: f64,
pub auto_memory_management: bool,
}
impl Default for WorkspaceConfig {
fn default() -> Self {
Self {
initial_array_capacity: 16,
initial_matrix_capacity: 16,
max_memory_mb: 100.0, auto_memory_management: true,
}
}
}