1use crate::error::{StatsError, StatsResult};
7use std::collections::HashMap;
8
9pub struct ErrorMessages;
11
12impl ErrorMessages {
13 pub fn dimension_mismatch(expected: &str, actual: &str) -> StatsError {
15 StatsError::dimension_mismatch(format!(
16 "Array dimension mismatch: _expected {}, got {}. {}",
17 expected,
18 actual,
19 "Ensure all input arrays have compatible dimensions for the operation."
20 ))
21 }
22
23 pub fn length_mismatch(
25 array1_name: &str,
26 len1: usize,
27 array2_name: &str,
28 len2: usize,
29 ) -> StatsError {
30 StatsError::dimension_mismatch(format!(
31 "Array length mismatch: {} has {} elements, {} has {} elements. {}",
32 array1_name,
33 len1,
34 array2_name,
35 len2,
36 "Both arrays must have the same number of elements."
37 ))
38 }
39
40 pub fn empty_array(arrayname: &str) -> StatsError {
42 StatsError::invalid_argument(format!(
43 "Array '{}' cannot be empty. {}",
44 arrayname, "Provide an array with at least one element."
45 ))
46 }
47
48 pub fn insufficientdata(operation: &str, required: usize, actual: usize) -> StatsError {
50 StatsError::invalid_argument(format!(
51 "Insufficient data for {}: requires at least {} elements, got {}. {}",
52 operation,
53 required,
54 actual,
55 if required == 2 {
56 "Statistical calculations typically require at least 2 data points."
57 } else {
58 "Increase the sample size or use a different method."
59 }
60 ))
61 }
62
63 pub fn non_positive_value(parameter: &str, value: f64) -> StatsError {
65 StatsError::domain(format!(
66 "Parameter '{}' must be positive, got {}. {}",
67 parameter, value, "Ensure the value is greater than 0."
68 ))
69 }
70
71 pub fn invalid_probability(parameter: &str, value: f64) -> StatsError {
73 StatsError::domain(format!(
74 "Parameter '{}' must be a valid probability between 0 and 1, got {}. {}",
75 parameter, value, "Probability values must be in the range [0, 1]."
76 ))
77 }
78
79 pub fn nan_detected(context: &str) -> StatsError {
81 StatsError::invalid_argument(format!(
82 "NaN (Not a Number) values detected in {}. {}",
83 context, "Remove NaN values or use functions that handle missing data explicitly."
84 ))
85 }
86
87 pub fn infinite_value_detected(context: &str) -> StatsError {
89 StatsError::invalid_argument(format!(
90 "Infinite values detected in {}. {}",
91 context, "Check for overflow conditions or extreme values in your data."
92 ))
93 }
94
95 pub fn not_positive_definite(matrixname: &str) -> StatsError {
97 StatsError::computation(format!(
98 "Matrix '{}' is not positive definite. {}",
99 matrixname,
100 "Ensure the matrix is symmetric and all eigenvalues are positive, or use regularization."
101 ))
102 }
103
104 pub fn singular_matrix(matrixname: &str) -> StatsError {
106 StatsError::computation(format!(
107 "Matrix '{}' is singular (non-invertible). {}",
108 matrixname, "Check for linear dependencies in your data or add regularization."
109 ))
110 }
111
112 pub fn convergence_failure(algorithm: &str, iterations: usize) -> StatsError {
114 StatsError::ConvergenceError(format!(
115 "{} failed to converge after {} iterations. {}",
116 algorithm, iterations,
117 "Try increasing the maximum iterations, adjusting tolerance, or using different initial values."
118 ))
119 }
120
121 pub fn numerical_instability(operation: &str, suggestion: &str) -> StatsError {
123 StatsError::computation(format!(
124 "Numerical instability detected in {}. {}",
125 operation, suggestion
126 ))
127 }
128
129 pub fn unsupported_operation(operation: &str, context: &str) -> StatsError {
131 StatsError::not_implemented(format!(
132 "Operation '{}' is not supported for {}. {}",
133 operation,
134 context,
135 "Check the documentation for supported operations or consider alternative methods."
136 ))
137 }
138}
139
140pub struct ErrorValidator;
142
143impl ErrorValidator {
144 pub fn validate_array<T>(data: &[T], name: &str) -> StatsResult<()>
146 where
147 T: PartialOrd + Copy,
148 {
149 if data.is_empty() {
150 return Err(ErrorMessages::empty_array(name));
151 }
152 Ok(())
153 }
154
155 pub fn validate_finite_array(data: &[f64], name: &str) -> StatsResult<()> {
157 if data.is_empty() {
158 return Err(ErrorMessages::empty_array(name));
159 }
160
161 for (i, &value) in data.iter().enumerate() {
162 if value.is_nan() {
163 return Err(ErrorMessages::nan_detected(&format!("{}[{}]", name, i)));
164 }
165 if value.is_infinite() {
166 return Err(ErrorMessages::infinite_value_detected(&format!(
167 "{}[{}]",
168 name, i
169 )));
170 }
171 }
172 Ok(())
173 }
174
175 pub fn validate_probability(value: f64, name: &str) -> StatsResult<()> {
177 if !(0.0..=1.0).contains(&value) {
178 return Err(ErrorMessages::invalid_probability(name, value));
179 }
180 if value.is_nan() {
181 return Err(ErrorMessages::nan_detected(name));
182 }
183 Ok(())
184 }
185
186 pub fn validate_positive(value: f64, name: &str) -> StatsResult<()> {
188 if value <= 0.0 {
189 return Err(ErrorMessages::non_positive_value(name, value));
190 }
191 if value.is_nan() {
192 return Err(ErrorMessages::nan_detected(name));
193 }
194 if value.is_infinite() {
195 return Err(ErrorMessages::infinite_value_detected(name));
196 }
197 Ok(())
198 }
199
200 pub fn validate_same_length<T, U>(
202 arr1: &[T],
203 arr1_name: &str,
204 arr2: &[U],
205 arr2_name: &str,
206 ) -> StatsResult<()> {
207 if arr1.len() != arr2.len() {
208 return Err(ErrorMessages::length_mismatch(
209 arr1_name,
210 arr1.len(),
211 arr2_name,
212 arr2.len(),
213 ));
214 }
215 Ok(())
216 }
217
218 pub fn validate_samplesize(size: usize, minimum: usize, operation: &str) -> StatsResult<()> {
220 if size < minimum {
221 return Err(ErrorMessages::insufficientdata(operation, minimum, size));
222 }
223 Ok(())
224 }
225}
226
227#[derive(Debug, Clone, Copy)]
229pub enum PerformanceImpact {
230 None,
232 Minimal,
234 Moderate,
236 Significant,
238}
239
240pub struct RecoverySuggestions;
242
243impl RecoverySuggestions {
244 pub fn get_suggestions(error: &StatsError) -> Vec<(String, PerformanceImpact)> {
246 match error {
247 StatsError::DimensionMismatch(_) => vec![
248 (
249 "Reshape arrays to have compatible dimensions".to_string(),
250 PerformanceImpact::None,
251 ),
252 (
253 "Use broadcasting-compatible operations".to_string(),
254 PerformanceImpact::Minimal,
255 ),
256 (
257 "Transpose matrices if needed".to_string(),
258 PerformanceImpact::Minimal,
259 ),
260 ],
261 StatsError::InvalidArgument(msg) if msg.contains("empty") => vec![
262 (
263 "Provide non-empty input arrays".to_string(),
264 PerformanceImpact::None,
265 ),
266 (
267 "Use default values for empty inputs".to_string(),
268 PerformanceImpact::Minimal,
269 ),
270 (
271 "Filter out empty arrays before processing".to_string(),
272 PerformanceImpact::Minimal,
273 ),
274 ],
275 StatsError::InvalidArgument(msg) if msg.contains("NaN") => vec![
276 (
277 "Remove NaN values using data.dropna()".to_string(),
278 PerformanceImpact::Minimal,
279 ),
280 (
281 "Use interpolation to fill NaN values".to_string(),
282 PerformanceImpact::Moderate,
283 ),
284 (
285 "Use statistical methods that handle NaN explicitly".to_string(),
286 PerformanceImpact::Minimal,
287 ),
288 ],
289 StatsError::ComputationError(msg) if msg.contains("singular") => vec![
290 (
291 "Add regularization (e.g., ridge regression)".to_string(),
292 PerformanceImpact::Minimal,
293 ),
294 (
295 "Use pseudo-inverse instead of inverse".to_string(),
296 PerformanceImpact::Moderate,
297 ),
298 (
299 "Check for multicollinearity in data".to_string(),
300 PerformanceImpact::None,
301 ),
302 ],
303 StatsError::ConvergenceError(_) => vec![
304 (
305 "Increase maximum iterations".to_string(),
306 PerformanceImpact::Moderate,
307 ),
308 (
309 "Adjust convergence tolerance".to_string(),
310 PerformanceImpact::None,
311 ),
312 (
313 "Use different initial values".to_string(),
314 PerformanceImpact::None,
315 ),
316 (
317 "Try a different optimization algorithm".to_string(),
318 PerformanceImpact::Significant,
319 ),
320 ],
321 StatsError::DomainError(_) => vec![
322 (
323 "Check parameter bounds and constraints".to_string(),
324 PerformanceImpact::None,
325 ),
326 (
327 "Scale or normalize input data".to_string(),
328 PerformanceImpact::Minimal,
329 ),
330 (
331 "Use robust statistical methods".to_string(),
332 PerformanceImpact::Moderate,
333 ),
334 ],
335 _ => vec![
336 (
337 "Check input data for validity".to_string(),
338 PerformanceImpact::None,
339 ),
340 (
341 "Refer to function documentation".to_string(),
342 PerformanceImpact::None,
343 ),
344 ],
345 }
346 }
347
348 pub fn get_context_suggestions(operation: &str) -> HashMap<String, Vec<String>> {
350 let mut suggestions = HashMap::new();
351
352 match operation {
353 "correlation" => {
354 suggestions.insert(
355 "data_preparation".to_string(),
356 vec![
357 "Ensure data is numeric and finite".to_string(),
358 "Consider outlier detection and removal".to_string(),
359 "Check for missing values".to_string(),
360 ],
361 );
362 suggestions.insert(
363 "performance".to_string(),
364 vec![
365 "Use SIMD-optimized functions for large datasets".to_string(),
366 "Consider parallel computation for correlation matrices".to_string(),
367 ],
368 );
369 }
370 "regression" => {
371 suggestions.insert(
372 "data_preparation".to_string(),
373 vec![
374 "Check for multicollinearity".to_string(),
375 "Normalize features if needed".to_string(),
376 "Consider feature selection".to_string(),
377 ],
378 );
379 suggestions.insert(
380 "model_selection".to_string(),
381 vec![
382 "Use regularization for high-dimensional data".to_string(),
383 "Consider robust regression for outliers".to_string(),
384 ],
385 );
386 }
387 "hypothesis_testing" => {
388 suggestions.insert(
389 "assumptions".to_string(),
390 vec![
391 "Check normality assumptions".to_string(),
392 "Verify independence of observations".to_string(),
393 "Consider non-parametric alternatives".to_string(),
394 ],
395 );
396 suggestions.insert(
397 "interpretation".to_string(),
398 vec![
399 "Adjust for multiple comparisons if needed".to_string(),
400 "Consider effect size in addition to p-values".to_string(),
401 ],
402 );
403 }
404 _ => {
405 suggestions.insert(
406 "general".to_string(),
407 vec![
408 "Validate input data quality".to_string(),
409 "Check function prerequisites".to_string(),
410 ],
411 );
412 }
413 }
414
415 suggestions
416 }
417}
418
419pub struct StandardizedErrorReporter;
421
422impl StandardizedErrorReporter {
423 pub fn generate_report(error: &StatsError, context: Option<&str>) -> String {
425 let mut report = String::new();
426
427 report.push_str(&format!("❌ Error: {}\n\n", error));
429
430 if let Some(ctx) = context {
432 report.push_str(&format!("📍 Context: {}\n\n", ctx));
433 }
434
435 let suggestions = RecoverySuggestions::get_suggestions(error);
437 if !suggestions.is_empty() {
438 report.push_str("💡 Suggested Solutions:\n");
439 for (i, (suggestion, impact)) in suggestions.iter().enumerate() {
440 let impact_icon = match impact {
441 PerformanceImpact::None => "⚡",
442 PerformanceImpact::Minimal => "🔋",
443 PerformanceImpact::Moderate => "⏱️",
444 PerformanceImpact::Significant => "⚠️",
445 };
446 report.push_str(&format!(" {}. {} {}\n", i + 1, impact_icon, suggestion));
447 }
448 report.push('\n');
449 }
450
451 report.push_str("Legend: ⚡ No impact, 🔋 Minimal, ⏱️ Moderate, ⚠️ Significant\n");
453
454 report
455 }
456}
457
458#[derive(Debug, Clone)]
460pub struct EnhancedErrorContext {
461 pub function_name: String,
463 pub module_name: String,
465 pub data_info: DataDiagnostics,
467 pub system_info: SystemDiagnostics,
469 pub recovery_actions: Vec<RecoveryAction>,
471}
472
473#[derive(Debug, Clone)]
475pub struct DataDiagnostics {
476 pub shape: Vec<usize>,
478 pub data_type: String,
480 pub summary: StatsSummary,
482 pub quality_issues: Vec<DataQualityIssue>,
484}
485
486#[derive(Debug, Clone)]
488pub struct StatsSummary {
489 pub min: Option<f64>,
490 pub max: Option<f64>,
491 pub mean: Option<f64>,
492 pub std: Option<f64>,
493 pub nan_count: usize,
494 pub inf_count: usize,
495 pub finite_count: usize,
496}
497
498#[derive(Debug, Clone, PartialEq)]
500pub enum DataQualityIssue {
501 HasNaN,
502 HasInfinite,
503 HasNegative,
504 HasZeros,
505 Constant,
506 HighSkewness,
507 Outliers(usize),
508 SmallSample(usize),
509}
510
511#[derive(Debug, Clone)]
513pub struct SystemDiagnostics {
514 pub available_memory_mb: Option<usize>,
516 pub cpu_info: String,
518 pub simd_available: bool,
520 pub thread_count: usize,
522}
523
524#[derive(Debug, Clone)]
526pub struct RecoveryAction {
527 pub description: String,
529 pub priority: u8,
531 pub performance_impact: PerformanceImpact,
533 pub code_example: Option<String>,
535 pub automatic: bool,
537}
538
539pub struct BatchErrorHandler {
541 errors: Vec<(usize, StatsError, EnhancedErrorContext)>,
542 warnings: Vec<(usize, String)>,
543}
544
545impl BatchErrorHandler {
546 pub fn new() -> Self {
547 Self {
548 errors: Vec::new(),
549 warnings: Vec::new(),
550 }
551 }
552
553 pub fn add_error(&mut self, index: usize, error: StatsError, context: EnhancedErrorContext) {
555 self.errors.push((index, error, context));
556 }
557
558 pub fn add_warning(&mut self, index: usize, warning: String) {
560 self.warnings.push((index, warning));
561 }
562
563 pub fn generate_batch_report(&self) -> String {
565 let mut report = String::new();
566
567 if !self.errors.is_empty() {
568 report.push_str(&format!(
569 "🚨 Batch Processing Errors ({} errors):\n\n",
570 self.errors.len()
571 ));
572
573 for (i, (index, error, context)) in self.errors.iter().enumerate() {
574 report.push_str(&format!("Error {} (Item {}):\n", i + 1, index));
575 report.push_str(&format!(" ❌ {}\n", error));
576 report.push_str(&format!(
577 " 📍 Function: {}::{}\n",
578 context.module_name, context.function_name
579 ));
580 report.push_str(&format!(" 📊 Data: {:?}\n", context.data_info.shape));
581
582 if !context.recovery_actions.is_empty() {
583 report.push_str(" 💡 Suggested Actions:\n");
584 for action in &context.recovery_actions {
585 let priority_icon = match action.priority {
586 1 => "🔴",
587 2 => "🟡",
588 3 => "🟢",
589 _ => "⚪",
590 };
591 report.push_str(&format!(" {} {}\n", priority_icon, action.description));
592 }
593 }
594 report.push('\n');
595 }
596 }
597
598 if !self.warnings.is_empty() {
599 report.push_str(&format!(
600 "⚠️ Batch Processing Warnings ({} warnings):\n\n",
601 self.warnings.len()
602 ));
603
604 for (index, warning) in &self.warnings {
605 report.push_str(&format!(" Item {}: {}\n", index, warning));
606 }
607 }
608
609 report
610 }
611
612 pub fn get_error_summary(&self) -> HashMap<String, usize> {
614 let mut summary = HashMap::new();
615
616 for (_, error_, _) in &self.errors {
617 let error_type = match error_ {
618 StatsError::ComputationError(_) => "Computation",
619 StatsError::DomainError(_) => "Domain",
620 StatsError::DimensionMismatch(_) => "Dimension",
621 StatsError::InvalidArgument(_) => "Invalid Argument",
622 StatsError::NotImplementedError(_) => "Not Implemented",
623 StatsError::ConvergenceError(_) => "Convergence",
624 StatsError::CoreError(_) => "Core",
625 StatsError::InsufficientData(_) => "Insufficient Data",
626 StatsError::InvalidInput(_) => "Invalid Input",
627 StatsError::NotImplemented(_) => "Not Implemented",
628 StatsError::DistributionError(_) => "Distribution",
629 };
630
631 *summary.entry(error_type.to_string()).or_insert(0) += 1;
632 }
633
634 summary
635 }
636}
637
638impl Default for BatchErrorHandler {
639 fn default() -> Self {
640 Self::new()
641 }
642}
643
644pub struct ErrorDiagnostics;
646
647impl ErrorDiagnostics {
648 pub fn diagnose_array_f64(data: &[f64], name: &str) -> DataDiagnostics {
650 let mut quality_issues = Vec::new();
651 let mut nan_count = 0;
652 let mut inf_count = 0;
653 let mut finite_values = Vec::new();
654 let mut has_negative = false;
655 let mut has_zeros = false;
656
657 for &value in data {
658 if value.is_nan() {
659 nan_count += 1;
660 } else if value.is_infinite() {
661 inf_count += 1;
662 } else {
663 finite_values.push(value);
664 if value < 0.0 {
665 has_negative = true;
666 }
667 if value == 0.0 {
668 has_zeros = true;
669 }
670 }
671 }
672
673 let (min, max, mean, std) = if !finite_values.is_empty() {
675 let min = finite_values.iter().fold(f64::INFINITY, |a, &b| a.min(b));
676 let max = finite_values
677 .iter()
678 .fold(f64::NEG_INFINITY, |a, &b| a.max(b));
679 let mean = finite_values.iter().sum::<f64>() / finite_values.len() as f64;
680 let variance = finite_values
681 .iter()
682 .map(|&x| (x - mean).powi(2))
683 .sum::<f64>()
684 / finite_values.len() as f64;
685 let std = variance.sqrt();
686 (Some(min), Some(max), Some(mean), Some(std))
687 } else {
688 (None, None, None, None)
689 };
690
691 if nan_count > 0 {
693 quality_issues.push(DataQualityIssue::HasNaN);
694 }
695 if inf_count > 0 {
696 quality_issues.push(DataQualityIssue::HasInfinite);
697 }
698 if has_negative {
699 quality_issues.push(DataQualityIssue::HasNegative);
700 }
701 if has_zeros {
702 quality_issues.push(DataQualityIssue::HasZeros);
703 }
704 if finite_values.len() < 2 {
705 quality_issues.push(DataQualityIssue::SmallSample(finite_values.len()));
706 }
707
708 if let (Some(min_val), Some(max_val)) = (min, max) {
710 if (max_val - min_val).abs() < 1e-15 {
711 quality_issues.push(DataQualityIssue::Constant);
712 }
713 }
714
715 if let (Some(mean_val), Some(std_val)) = (mean, std) {
717 if std_val > 0.0 {
718 let outlier_count = finite_values
719 .iter()
720 .filter(|&&x| (x - mean_val).abs() > 3.0 * std_val)
721 .count();
722 if outlier_count > 0 {
723 quality_issues.push(DataQualityIssue::Outliers(outlier_count));
724 }
725 }
726 }
727
728 DataDiagnostics {
729 shape: vec![data.len()],
730 data_type: "f64".to_string(),
731 summary: StatsSummary {
732 min,
733 max,
734 mean,
735 std,
736 nan_count,
737 inf_count,
738 finite_count: finite_values.len(),
739 },
740 quality_issues,
741 }
742 }
743
744 pub fn get_system_diagnostics() -> SystemDiagnostics {
746 use scirs2_core::simd_ops::PlatformCapabilities;
747
748 let capabilities = PlatformCapabilities::detect();
749 let thread_count = num_cpus::get();
750
751 SystemDiagnostics {
752 available_memory_mb: Self::get_available_memory_mb(),
753 cpu_info: format!("Threads: {}", thread_count),
754 simd_available: capabilities.simd_available,
755 thread_count,
756 }
757 }
758
759 fn get_available_memory_mb() -> Option<usize> {
760 Some(8192) }
763}
764
765pub struct InterModuleErrorChecker;
767
768impl InterModuleErrorChecker {
769 pub fn validate_error_consistency(
771 module_errors: &HashMap<String, Vec<StatsError>>,
772 ) -> Vec<String> {
773 let mut inconsistencies = Vec::new();
774
775 let mut error_patterns: HashMap<String, Vec<String>> = HashMap::new();
777
778 for (module, errors) in module_errors {
779 for error in errors {
780 let pattern = Self::extract_error_pattern(error);
781 error_patterns
782 .entry(pattern)
783 .or_default()
784 .push(module.clone());
785 }
786 }
787
788 for (pattern, modules) in error_patterns {
790 if modules.len() > 1 {
791 let unique_modules: std::collections::HashSet<_> = modules.into_iter().collect();
792 if unique_modules.len() > 1 {
793 inconsistencies.push(format!(
794 "Error pattern '{}' appears inconsistently across modules: {:?}",
795 pattern, unique_modules
796 ));
797 }
798 }
799 }
800
801 inconsistencies
802 }
803
804 fn extract_error_pattern(error: &StatsError) -> String {
805 match error {
806 StatsError::DimensionMismatch(_) => "dimension_mismatch".to_string(),
807 StatsError::InvalidArgument(msg) if msg.contains("empty") => "empty_array".to_string(),
808 StatsError::InvalidArgument(msg) if msg.contains("NaN") => "nan_values".to_string(),
809 StatsError::DomainError(msg) if msg.contains("positive") => "non_positive".to_string(),
810 StatsError::DomainError(msg) if msg.contains("probability") => {
811 "invalid_probability".to_string()
812 }
813 StatsError::ConvergenceError(_) => "convergence_failure".to_string(),
814 StatsError::ComputationError(msg) if msg.contains("singular") => {
815 "singular_matrix".to_string()
816 }
817 _ => "other".to_string(),
818 }
819 }
820}
821
822pub struct AutoRecoverySystem;
824
825impl AutoRecoverySystem {
826 pub fn attempt_auto_recovery(
828 error: &StatsError,
829 context: &EnhancedErrorContext,
830 ) -> Option<RecoveryAction> {
831 match error {
832 StatsError::InvalidArgument(msg) if msg.contains("NaN") => Some(RecoveryAction {
833 description: "Automatically remove NaN values".to_string(),
834 priority: 1,
835 performance_impact: PerformanceImpact::Minimal,
836 code_example: Some(
837 "let cleandata = data.iter().filter(|x| x.is_finite()).collect();".to_string(),
838 ),
839 automatic: true,
840 }),
841 StatsError::DimensionMismatch(_) => {
842 Some(RecoveryAction {
843 description: "Attempt automatic dimension alignment".to_string(),
844 priority: 2,
845 performance_impact: PerformanceImpact::Minimal,
846 code_example: Some(
847 "let aligneddata = data.broadcast_to(targetshape);".to_string(),
848 ),
849 automatic: false, })
851 }
852 StatsError::ComputationError(msg) if msg.contains("singular") => Some(RecoveryAction {
853 description: "Add regularization to handle singularity".to_string(),
854 priority: 1,
855 performance_impact: PerformanceImpact::Minimal,
856 code_example: Some("let regularized = matrix + Array2::eye(n) * 1e-6;".to_string()),
857 automatic: true,
858 }),
859 _ => None,
860 }
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867
868 #[test]
869 fn test_error_messages() {
870 let err = ErrorMessages::length_mismatch("x", 5, "y", 3);
871 assert!(err.to_string().contains("Array length mismatch"));
872 assert!(err.to_string().contains("same number of elements"));
873 }
874
875 #[test]
876 fn test_error_validator() {
877 let empty_data: &[f64] = &[];
878 assert!(ErrorValidator::validate_array(empty_data, "test").is_err());
879
880 let finitedata = [1.0, 2.0, 3.0];
881 assert!(ErrorValidator::validate_finite_array(&finitedata, "test").is_ok());
882
883 let nandata = [1.0, f64::NAN, 3.0];
884 assert!(ErrorValidator::validate_finite_array(&nandata, "test").is_err());
885 }
886
887 #[test]
888 fn test_recovery_suggestions() {
889 let err = ErrorMessages::empty_array("data");
890 let suggestions = RecoverySuggestions::get_suggestions(&err);
891 assert!(!suggestions.is_empty());
892 }
893
894 #[test]
895 fn test_enhanced_error_context() {
896 let data = [1.0, 2.0, f64::NAN, 4.0];
897 let diagnostics = ErrorDiagnostics::diagnose_array_f64(&data, "testdata");
898
899 assert_eq!(diagnostics.shape, vec![4]);
900 assert_eq!(diagnostics.summary.nan_count, 1);
901 assert_eq!(diagnostics.summary.finite_count, 3);
902 assert!(diagnostics
903 .quality_issues
904 .contains(&DataQualityIssue::HasNaN));
905 }
906
907 #[test]
908 fn test_batch_error_handler() {
909 let mut handler = BatchErrorHandler::new();
910
911 let error = ErrorMessages::empty_array("test");
912 let context = EnhancedErrorContext {
913 function_name: "test_function".to_string(),
914 module_name: "test_module".to_string(),
915 data_info: ErrorDiagnostics::diagnose_array_f64(&[], "empty"),
916 system_info: ErrorDiagnostics::get_system_diagnostics(),
917 recovery_actions: vec![],
918 };
919
920 handler.add_error(0, error, context);
921 handler.add_warning(1, "This is a test warning".to_string());
922
923 let report = handler.generate_batch_report();
924 assert!(report.contains("Batch Processing Errors"));
925 assert!(report.contains("Batch Processing Warnings"));
926
927 let summary = handler.get_error_summary();
928 assert_eq!(summary.get("Invalid Argument"), Some(&1));
929 }
930
931 #[test]
932 fn test_auto_recovery_system() {
933 let error = ErrorMessages::nan_detected("test context");
934 let context = EnhancedErrorContext {
935 function_name: "test".to_string(),
936 module_name: "test".to_string(),
937 data_info: ErrorDiagnostics::diagnose_array_f64(&[f64::NAN], "test"),
938 system_info: ErrorDiagnostics::get_system_diagnostics(),
939 recovery_actions: vec![],
940 };
941
942 let recovery = AutoRecoverySystem::attempt_auto_recovery(&error, &context);
943 assert!(recovery.is_some());
944 assert!(recovery.unwrap().automatic);
945 }
946}