// Copyright (c) 2024 RustyTorch++ Team // Licensed under the Apache License, Version 2.0 //! Utility functions and helpers for finite element analysis. pub mod io; pub mod math; pub mod profiling; pub mod visualization; #[cfg(test)] mod math_tests; use nalgebra::Vector3; use std::time::{Duration, Instant}; pub use io::*; pub use math::*; pub use profiling::*; pub use visualization::*; /// Coordinate system transformations. pub struct CoordinateTransforms; impl CoordinateTransforms { /// Convert Cartesian to cylindrical coordinates. pub fn cartesian_to_cylindrical(point: &Vector3) -> Vector3 { let r = (point.x * point.x + point.y * point.y).sqrt(); let theta = point.y.atan2(point.x); let z = point.z; Vector3::new(r, theta, z) } /// Convert cylindrical to Cartesian coordinates. pub fn cylindrical_to_cartesian(point: &Vector3) -> Vector3 { let x = point.x * point.y.cos(); let y = point.x * point.y.sin(); let z = point.z; Vector3::new(x, y, z) } /// Convert Cartesian to spherical coordinates. pub fn cartesian_to_spherical(point: &Vector3) -> Vector3 { let r = point.norm(); let theta = (point.z / r).acos(); let phi = point.y.atan2(point.x); Vector3::new(r, theta, phi) } /// Convert spherical to Cartesian coordinates. pub fn spherical_to_cartesian(point: &Vector3) -> Vector3 { let x = point.x * point.y.sin() * point.z.cos(); let y = point.x * point.y.sin() * point.z.sin(); let z = point.x * point.y.cos(); Vector3::new(x, y, z) } } /// Memory usage utilities. pub struct MemoryUtils; impl MemoryUtils { /// Get current memory usage in bytes. pub fn current_usage() -> usize { // Simplified implementation - would use proper memory tracking 0 } /// Get peak memory usage in bytes. pub fn peak_usage() -> usize { // Simplified implementation 0 } /// Format memory size in human-readable format. pub fn format_bytes(bytes: usize) -> String { const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"]; let mut size = bytes as f64; let mut unit_index = 0; while size >= 1024.0 && unit_index < UNITS.len() - 1 { size /= 1024.0; unit_index += 1; } format!("{:.2} {}", size, UNITS[unit_index]) } /// Estimate memory requirement for sparse matrix. pub fn estimate_sparse_matrix_memory( nnz: usize, index_size: usize, value_size: usize, ) -> usize { nnz * (index_size + value_size) + index_size * 2 // Row pointers } /// Estimate memory requirement for dense matrix. pub fn estimate_dense_matrix_memory(rows: usize, cols: usize, element_size: usize) -> usize { rows * cols * element_size } } /// Performance benchmarking utilities. pub struct BenchmarkUtils; impl BenchmarkUtils { /// Measure execution time of a function. pub fn time_function(f: F) -> (R, Duration) where F: FnOnce() -> R, { let start = Instant::now(); let result = f(); let duration = start.elapsed(); (result, duration) } /// Run benchmark multiple times and get statistics. pub fn benchmark_function(f: F, iterations: usize) -> BenchmarkStats where F: Fn(), { let mut times = Vec::with_capacity(iterations); for _ in 0..iterations { let ((), duration) = Self::time_function(&f); times.push(duration); } BenchmarkStats::from_times(times) } /// Create a simple progress bar. pub fn create_progress_bar(total: usize) -> ProgressBar { ProgressBar::new(total) } } /// Benchmark statistics. #[derive(Debug, Clone)] pub struct BenchmarkStats { pub mean: Duration, pub std_dev: Duration, pub min: Duration, pub max: Duration, pub iterations: usize, } impl BenchmarkStats { /// Create statistics from timing measurements. pub fn from_times(times: Vec) -> Self { if times.is_empty() { return Self { mean: Duration::new(0, 0), std_dev: Duration::new(0, 0), min: Duration::new(0, 0), max: Duration::new(0, 0), iterations: 0, }; } let iterations = times.len(); let total_nanos: u64 = times.iter().map(|d| d.as_nanos() as u64).sum(); let mean_nanos = total_nanos / iterations as u64; let mean = Duration::from_nanos(mean_nanos); let min = times.iter().min().copied().unwrap_or_default(); let max = times.iter().max().copied().unwrap_or_default(); // Calculate standard deviation let variance: f64 = times .iter() .map(|d| { let diff = d.as_nanos() as f64 - mean_nanos as f64; diff * diff }) .sum::() / iterations as f64; let std_dev = Duration::from_nanos(variance.sqrt() as u64); Self { mean, std_dev, min, max, iterations, } } } impl std::fmt::Display for BenchmarkStats { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "Benchmark Statistics ({} iterations):", self.iterations)?; writeln!(f, " Mean: {:.3} ms", self.mean.as_secs_f64() * 1000.0)?; writeln!( f, " Std Dev: {:.3} ms", self.std_dev.as_secs_f64() * 1000.0 )?; writeln!(f, " Min: {:.3} ms", self.min.as_secs_f64() * 1000.0)?; writeln!(f, " Max: {:.3} ms", self.max.as_secs_f64() * 1000.0)?; Ok(()) } } /// Simple progress bar for console output. #[derive(Debug)] pub struct ProgressBar { total: usize, current: usize, width: usize, start_time: Instant, } impl ProgressBar { /// Create a new progress bar. pub fn new(total: usize) -> Self { Self { total, current: 0, width: 50, start_time: Instant::now(), } } /// Set the width of the progress bar. pub fn set_width(&mut self, width: usize) { self.width = width; } /// Update progress and display. pub fn update(&mut self, current: usize) { self.current = current; self.display(); } /// Increment progress by one. pub fn increment(&mut self) { self.current += 1; self.display(); } /// Display the progress bar. fn display(&self) { let percentage = if self.total > 0 { (self.current as f64 / self.total as f64 * 100.0).min(100.0) } else { 0.0 }; let filled = (percentage / 100.0 * self.width as f64) as usize; let empty = self.width - filled; let elapsed = self.start_time.elapsed(); let rate = if elapsed.as_secs() > 0 { self.current as f64 / elapsed.as_secs_f64() } else { 0.0 }; let eta = if rate > 0.0 && self.current < self.total { Duration::from_secs_f64((self.total - self.current) as f64 / rate) } else { Duration::new(0, 0) }; print!( "\r[{}{}] {:.1}% ({}/{}) Rate: {:.1}/s ETA: {:.0}s", "█".repeat(filled), "░".repeat(empty), percentage, self.current, self.total, rate, eta.as_secs_f64() ); use std::io::{self, Write}; io::stdout().flush().unwrap(); if self.current >= self.total { println!(); // New line when complete } } /// Mark as complete. pub fn finish(&mut self) { self.current = self.total; self.display(); } } /// String utilities for FEA. pub struct StringUtils; impl StringUtils { /// Convert scientific notation to readable format. pub fn format_scientific(value: f64, precision: usize) -> String { if value.abs() < 1e-15 { "0".to_string() } else if value.abs() >= 1e6 || value.abs() < 1e-3 { format!("{value:.precision$e}") } else { format!("{value:.precision$}") } } /// Format duration in human-readable format. pub fn format_duration(duration: Duration) -> String { let total_secs = duration.as_secs_f64(); if total_secs < 1.0 { format!("{:.1} ms", total_secs * 1000.0) } else if total_secs < 60.0 { format!("{total_secs:.2} s") } else if total_secs < 3600.0 { let minutes = (total_secs / 60.0) as u32; let seconds = total_secs % 60.0; format!("{minutes}m {seconds:.1}s") } else { let hours = (total_secs / 3600.0) as u32; let minutes = ((total_secs % 3600.0) / 60.0) as u32; format!("{hours}h {minutes}m") } } /// Generate unique identifier. pub fn generate_id() -> String { use std::time::{SystemTime, UNIX_EPOCH}; let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_nanos(); format!("fea_{timestamp}") } } #[cfg(test)] mod tests { use super::*; #[test] fn test_coordinate_transforms() { let cartesian = Vector3::new(1.0, 1.0, 1.0); let cylindrical = CoordinateTransforms::cartesian_to_cylindrical(&cartesian); let back_to_cartesian = CoordinateTransforms::cylindrical_to_cartesian(&cylindrical); assert!((cartesian - back_to_cartesian).norm() < 1e-10); } #[test] fn test_memory_utils() { let formatted = MemoryUtils::format_bytes(1024 * 1024); assert_eq!(formatted, "1.00 MB"); let formatted = MemoryUtils::format_bytes(1536); assert_eq!(formatted, "1.50 KB"); } #[test] fn test_benchmark_stats() { let times = vec![ Duration::from_millis(10), Duration::from_millis(12), Duration::from_millis(11), Duration::from_millis(13), Duration::from_millis(9), ]; let stats = BenchmarkStats::from_times(times); assert_eq!(stats.iterations, 5); assert!(stats.mean.as_millis() > 0); assert!(stats.min <= stats.mean); assert!(stats.max >= stats.mean); } #[test] fn test_progress_bar() { let mut progress = ProgressBar::new(100); progress.set_width(10); progress.update(50); assert_eq!(progress.current, 50); assert_eq!(progress.total, 100); } #[test] fn test_string_utils() { let scientific = StringUtils::format_scientific(1.23e-6, 2); assert!(scientific.contains("e")); let duration = StringUtils::format_duration(Duration::from_secs(65)); assert!(duration.contains("m")); let id = StringUtils::generate_id(); assert!(id.starts_with("fea_")); } #[test] fn test_benchmark_function() { let stats = BenchmarkUtils::benchmark_function( || { std::thread::sleep(Duration::from_millis(1)); }, 3, ); assert_eq!(stats.iterations, 3); assert!(stats.mean.as_millis() >= 1); } }