1use scirs2_core::error::{CoreError, ErrorContext, ErrorLocation};
4use thiserror::Error;
5
6#[derive(Error, Debug, Clone)]
8pub enum StatsError {
9 #[error("Computation error: {0}")]
11 ComputationError(String),
12
13 #[error("Domain error: {0}")]
15 DomainError(String),
16
17 #[error("Dimension mismatch error: {0}")]
19 DimensionMismatch(String),
20
21 #[error("Invalid argument: {0}")]
23 InvalidArgument(String),
24
25 #[error("Not implemented: {0}")]
27 NotImplementedError(String),
28
29 #[error("Convergence error: {0}")]
31 ConvergenceError(String),
32
33 #[error("Insufficient data: {0}")]
35 InsufficientData(String),
36
37 #[error("Invalid input: {0}")]
39 InvalidInput(String),
40
41 #[error("Not implemented: {0}")]
43 NotImplemented(String),
44
45 #[error("{0}")]
47 CoreError(#[from] CoreError),
48
49 #[error("Random distribution error: {0}")]
51 DistributionError(String),
52}
53
54pub trait StatsErrorExt {
65 fn context<S: Into<String>>(self, context: S) -> Self;
67
68 fn suggestion<S: Into<String>>(self, suggestion: S) -> Self;
70}
71
72impl StatsError {
73 pub fn computation<S: Into<String>>(message: S) -> Self {
75 StatsError::ComputationError(message.into())
76 }
77
78 pub fn domain<S: Into<String>>(message: S) -> Self {
80 StatsError::DomainError(message.into())
81 }
82
83 pub fn dimension_mismatch<S: Into<String>>(message: S) -> Self {
85 StatsError::DimensionMismatch(message.into())
86 }
87
88 pub fn invalid_argument<S: Into<String>>(message: S) -> Self {
90 StatsError::InvalidArgument(message.into())
91 }
92
93 pub fn not_implemented<S: Into<String>>(message: S) -> Self {
95 StatsError::NotImplementedError(message.into())
96 }
97
98 pub fn insufficientdata<S: Into<String>>(message: S) -> Self {
100 StatsError::InsufficientData(message.into())
101 }
102
103 pub fn invalid_input<S: Into<String>>(message: S) -> Self {
105 StatsError::InvalidInput(message.into())
106 }
107
108 pub fn with_suggestion(&self) -> String {
110 match self {
111 StatsError::DomainError(msg) => {
112 if msg.contains("must be positive") {
113 format!(
114 "{msg}
115Suggestion: Ensure the value is greater than 0"
116 )
117 } else if msg.contains("probability") {
118 format!(
119 "{msg}
120Suggestion: Probability values must be between 0 and 1 (inclusive)"
121 )
122 } else if msg.contains("degrees of freedom") {
123 format!(
124 "{msg}
125Suggestion: Degrees of freedom must be a positive value"
126 )
127 } else {
128 msg.clone()
129 }
130 }
131 StatsError::DimensionMismatch(msg) => {
132 if msg.contains("same length") {
133 format!(
134 "{msg}
135Suggestion: Ensure both arrays have the same number of elements"
136 )
137 } else if msg.contains("square matrix") {
138 format!(
139 "{msg}
140Suggestion: The input matrix must have equal number of rows and columns"
141 )
142 } else {
143 format!(
144 "{msg}
145Suggestion: Check that input dimensions match the function requirements"
146 )
147 }
148 }
149 StatsError::InvalidArgument(msg) => {
150 if msg.contains("empty") {
151 format!(
152 "{msg}
153Suggestion: Provide a non-empty array or collection"
154 )
155 } else if msg.contains("NaN") || msg.contains("nan") {
156 format!(
157 "{msg}
158Suggestion: Remove or handle NaN values before computation"
159 )
160 } else if msg.contains("infinite") || msg.contains("inf") {
161 format!(
162 "{msg}
163Suggestion: Check for and handle infinite values in your data"
164 )
165 } else {
166 format!(
167 "{msg}
168Suggestion: Verify that all input arguments meet the function requirements"
169 )
170 }
171 }
172 StatsError::NotImplementedError(msg) => {
173 format!("{msg}
174Suggestion: This feature is not yet available. Consider using an alternative method or check for updates")
175 }
176 StatsError::ComputationError(msg) => {
177 if msg.contains("overflow") {
178 format!(
179 "{msg}
180Suggestion: Try scaling your input data or using a more numerically stable algorithm"
181 )
182 } else if msg.contains("convergence") {
183 format!(
184 "{msg}
185Suggestion: Try adjusting convergence parameters or using different initial values"
186 )
187 } else {
188 format!(
189 "{msg}
190Suggestion: Check input data for numerical issues or extreme values"
191 )
192 }
193 }
194 StatsError::ConvergenceError(msg) => {
195 format!("{msg}
196Suggestion: Try adjusting convergence parameters, using different initial values, or increasing the maximum number of iterations")
197 }
198 StatsError::InsufficientData(msg) => {
199 format!(
200 "{msg}
201Suggestion: Increase sample size or use methods designed for small datasets"
202 )
203 }
204 StatsError::InvalidInput(msg) => {
205 format!(
206 "{msg}
207Suggestion: Check input format and ensure data meets function requirements"
208 )
209 }
210 StatsError::NotImplemented(msg) => {
211 format!("{msg}
212Suggestion: This feature is not yet available. Consider using an alternative method or check for updates")
213 }
214 StatsError::CoreError(err) => {
215 format!(
216 "{err}
217Suggestion: {}",
218 "Refer to the core error for more details"
219 )
220 }
221 StatsError::DistributionError(msg) => {
222 format!(
223 "{msg}
224Suggestion: Check distribution parameters and ensure they are within valid ranges"
225 )
226 }
227 }
228 }
229}
230
231pub type StatsResult<T> = Result<T, StatsError>;
233
234#[allow(dead_code)]
236pub fn convert_to_validation_error<T, S: Into<String>>(
237 result: StatsResult<T>,
238 message: S,
239) -> Result<T, CoreError> {
240 match result {
241 Ok(val) => Ok(val),
242 Err(err) => Err(CoreError::ValidationError(
243 ErrorContext::new(format!("{}: {err}", message.into()))
244 .with_location(ErrorLocation::new(file!(), line!())),
245 )),
246 }
247}