1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum InterpolateError {
8 #[error("Invalid input data: {message}")]
10 InvalidInput { message: String },
11
12 #[error("Point {point:?} is outside domain [{min}, {max}] in {context}")]
14 OutOfDomain {
15 point: String,
16 min: String,
17 max: String,
18 context: String,
19 },
20
21 #[error("Invalid {parameter}: expected {expected}, got {actual} in {context}")]
23 InvalidParameter {
24 parameter: String,
25 expected: String,
26 actual: String,
27 context: String,
28 },
29
30 #[error("Shape mismatch: expected {expected}, got {actual} for {object}")]
32 ShapeMismatch {
33 expected: String,
34 actual: String,
35 object: String,
36 },
37
38 #[error("Computation error: {0}")]
40 ComputationError(String),
41
42 #[error("Shape error: {0}")]
44 ShapeError(String),
45
46 #[error("Not implemented: {0}")]
48 NotImplemented(String),
49
50 #[error("Invalid value: {0}")]
52 InvalidValue(String),
53
54 #[error("Dimension mismatch: {0}")]
56 DimensionMismatch(String),
57
58 #[error("Out of bounds: {0}")]
60 OutOfBounds(String),
61
62 #[error("Invalid state: {0}")]
64 InvalidState(String),
65
66 #[error("Invalid operation: {0}")]
68 InvalidOperation(String),
69
70 #[error("Point was mapped to {0}")]
73 MappedPoint(f64),
74
75 #[error("Point was mapped to equivalent")]
78 MappedPointGeneric(Box<dyn std::any::Any + Send + Sync>),
79
80 #[error("Index error: {0}")]
82 IndexError(String),
83
84 #[error("IO error: {0}")]
86 IoError(String),
87
88 #[error("Linear algebra error: {0}")]
90 LinalgError(String),
91
92 #[error("Numerical error: {0}")]
94 NumericalError(String),
95
96 #[error("Unsupported operation: {0}")]
98 UnsupportedOperation(String),
99
100 #[error("Insufficient data: {0}")]
102 InsufficientData(String),
103
104 #[error("Interpolation failed: {0}")]
106 InterpolationFailed(String),
107
108 #[error("Missing points data: interpolator requires training points")]
110 MissingPoints,
111
112 #[error("Missing values data: interpolator requires training values")]
114 MissingValues,
115
116 #[error("Numerical instability: {message}")]
118 NumericalInstability { message: String },
119}
120
121impl From<scirs2_core::ndarray::ShapeError> for InterpolateError {
122 fn from(err: scirs2_core::ndarray::ShapeError) -> Self {
123 InterpolateError::ShapeError(err.to_string())
124 }
125}
126
127impl From<scirs2_core::CoreError> for InterpolateError {
128 fn from(err: scirs2_core::CoreError) -> Self {
129 InterpolateError::ComputationError(err.to_string())
130 }
131}
132
133pub type InterpolateResult<T> = Result<T, InterpolateError>;
135
136impl InterpolateError {
137 pub fn invalid_input(message: impl Into<String>) -> Self {
139 Self::InvalidInput {
140 message: message.into(),
141 }
142 }
143
144 pub fn out_of_domain<T: std::fmt::Display>(
146 point: T,
147 min: T,
148 max: T,
149 context: impl Into<String>,
150 ) -> Self {
151 Self::OutOfDomain {
152 point: point.to_string(),
153 min: min.to_string(),
154 max: max.to_string(),
155 context: context.into(),
156 }
157 }
158
159 pub fn invalid_parameter<T: std::fmt::Display>(
161 parameter: impl Into<String>,
162 expected: impl Into<String>,
163 actual: T,
164 context: impl Into<String>,
165 ) -> Self {
166 Self::InvalidParameter {
167 parameter: parameter.into(),
168 expected: expected.into(),
169 actual: actual.to_string(),
170 context: context.into(),
171 }
172 }
173
174 pub fn shape_mismatch(
176 expected: impl Into<String>,
177 actual: impl Into<String>,
178 object: impl Into<String>,
179 ) -> Self {
180 Self::ShapeMismatch {
181 expected: expected.into(),
182 actual: actual.into(),
183 object: object.into(),
184 }
185 }
186
187 pub fn dimension_mismatch(expected: usize, actual: usize, context: &str) -> Self {
189 Self::DimensionMismatch(format!(
190 "Dimension mismatch in {context}: expected {expected}, got {actual}"
191 ))
192 }
193
194 pub fn empty_data(context: &str) -> Self {
196 Self::InsufficientData(format!("Empty input data provided to {context}"))
197 }
198
199 pub fn convergence_failure(method: &str, iterations: usize) -> Self {
201 Self::ComputationError(format!(
202 "{method} failed to converge after {iterations} iterations"
203 ))
204 }
205
206 pub fn numerical_instability(context: &str, details: &str) -> Self {
208 Self::NumericalError(format!("Numerical instability in {context}: {details}"))
209 }
210
211 pub fn numerical_error(message: impl Into<String>) -> Self {
213 Self::NumericalError(message.into())
214 }
215
216 pub fn insufficient_points(required: usize, provided: usize, method: &str) -> Self {
218 Self::InsufficientData(format!(
219 "{method} requires at least {required} points, but only {provided} provided"
220 ))
221 }
222
223 pub fn invalid_parameter_with_suggestion<T: std::fmt::Display>(
225 parameter: impl Into<String>,
226 value: T,
227 context: impl Into<String>,
228 suggestion: impl Into<String>,
229 ) -> Self {
230 Self::InvalidParameter {
231 parameter: parameter.into(),
232 expected: suggestion.into(),
233 actual: value.to_string(),
234 context: context.into(),
235 }
236 }
237
238 pub fn out_of_domain_with_suggestion<T: std::fmt::Display>(
240 point: T,
241 min: T,
242 max: T,
243 context: impl Into<String>,
244 suggestion: impl Into<String>,
245 ) -> Self {
246 let context_str = context.into();
247 let suggestion_str = suggestion.into();
248 Self::OutOfDomain {
249 point: point.to_string(),
250 min: min.to_string(),
251 max: max.to_string(),
252 context: format!("{context_str} - Suggestion: {suggestion_str}"),
253 }
254 }
255
256 pub fn numerical_instability_with_advice(context: &str, details: &str, advice: &str) -> Self {
258 Self::NumericalError(format!(
259 "Numerical instability in {context}: {details} - ADVICE: {advice}"
260 ))
261 }
262
263 pub fn convergence_failure_with_advice(method: &str, iterations: usize, advice: &str) -> Self {
265 Self::ComputationError(format!(
266 "{method} failed to converge after {iterations} iterations - RECOMMENDATION: {advice}"
267 ))
268 }
269
270 pub fn matrix_conditioning_error(
272 condition_number: f64,
273 context: &str,
274 recommended_regularization: Option<f64>,
275 ) -> Self {
276 let advice = if let Some(reg) = recommended_regularization {
277 format!(
278 "Matrix is ill-conditioned (condition number: {condition_number:.2e}). Try regularization parameter ≥ {reg:.2e}"
279 )
280 } else {
281 format!(
282 "Matrix is ill-conditioned (condition number: {condition_number:.2e}). Consider data preprocessing or regularization"
283 )
284 };
285
286 Self::LinalgError(format!(
287 "{context}: {} - SOLUTION: {advice}",
288 if condition_number > 1e16 {
289 "Severe numerical instability"
290 } else {
291 "Poor numerical conditioning"
292 }
293 ))
294 }
295
296 pub fn data_quality_error(issue: &str, context: &str, preprocessingadvice: &str) -> Self {
298 Self::InvalidInput {
299 message: format!(
300 "{issue} detected in {context}: Data may be unsuitable for interpolation - DATA PREPROCESSING: {preprocessingadvice}"
301 ),
302 }
303 }
304
305 pub fn method_selection_error(
307 attempted_method: &str,
308 data_characteristics: &str,
309 recommended_alternatives: &[&str],
310 ) -> Self {
311 let alternatives = recommended_alternatives.join(", ");
312 Self::UnsupportedOperation(format!(
313 "{attempted_method} is not suitable for data with {data_characteristics}: Consider using a different interpolation method - ALTERNATIVES: Try {alternatives}"
314 ))
315 }
316
317 pub fn performance_warning(
319 operation: &str,
320 data_size: usize,
321 optimization_advice: &str,
322 ) -> Self {
323 Self::ComputationError(format!(
324 "{operation} may be slow for {data_size} data points - OPTIMIZATION: {optimization_advice}"
325 ))
326 }
327}