showcase_calculator/core/
mod.rs1pub mod evaluator;
8pub mod history;
9mod operations;
10pub mod parser;
11
12pub use operations::{Calculator, Operation};
13
14use std::collections::VecDeque;
15
16pub type CalcResult<T> = Result<T, CalcError>;
18
19#[derive(Debug, Clone, PartialEq)]
21pub enum CalcError {
22 DivisionByZero,
24 Overflow,
26 ParseError(String),
28 EmptyExpression,
30 InvalidResult(String),
32 AnomalyViolation(AnomalyViolation),
34}
35
36impl std::fmt::Display for CalcError {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 Self::DivisionByZero => write!(f, "Division by zero"),
40 Self::Overflow => write!(f, "Overflow: result exceeds maximum value"),
41 Self::ParseError(msg) => write!(f, "Invalid expression: {msg}"),
42 Self::EmptyExpression => write!(f, "Empty expression"),
43 Self::InvalidResult(msg) => write!(f, "Invalid result: {msg}"),
44 Self::AnomalyViolation(v) => write!(f, "Anomaly violation: {v}"),
45 }
46 }
47}
48
49impl std::error::Error for CalcError {}
50
51#[derive(Debug, Clone, PartialEq)]
53pub enum AnomalyViolation {
54 NaN,
56 Infinite,
58 Overflow(f64),
60}
61
62impl std::fmt::Display for AnomalyViolation {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 Self::NaN => write!(f, "NaN detected"),
66 Self::Infinite => write!(f, "Infinite value detected"),
67 Self::Overflow(v) => write!(f, "Overflow: {v} exceeds maximum magnitude"),
68 }
69 }
70}
71
72#[derive(Debug, Clone)]
77pub struct AnomalyValidator {
78 pub max_magnitude: f64,
80 pub check_special_values: bool,
82 history: VecDeque<f64>,
84 max_history: usize,
86}
87
88impl Default for AnomalyValidator {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl AnomalyValidator {
95 pub const DEFAULT_MAX_MAGNITUDE: f64 = 1e100;
97
98 #[must_use]
100 pub fn new() -> Self {
101 Self {
102 max_magnitude: Self::DEFAULT_MAX_MAGNITUDE,
103 check_special_values: true,
104 history: VecDeque::new(),
105 max_history: 100,
106 }
107 }
108
109 #[must_use]
111 pub fn with_max_magnitude(max_magnitude: f64) -> Self {
112 Self {
113 max_magnitude,
114 check_special_values: true,
115 history: VecDeque::new(),
116 max_history: 100,
117 }
118 }
119
120 pub fn validate(&mut self, result: f64) -> Result<f64, AnomalyViolation> {
124 if self.check_special_values && result.is_nan() {
126 return Err(AnomalyViolation::NaN);
127 }
128
129 if self.check_special_values && result.is_infinite() {
131 return Err(AnomalyViolation::Infinite);
132 }
133
134 if result.abs() > self.max_magnitude {
136 return Err(AnomalyViolation::Overflow(result));
137 }
138
139 self.record_result(result);
141
142 Ok(result)
143 }
144
145 fn record_result(&mut self, result: f64) {
147 if self.history.len() >= self.max_history {
148 self.history.pop_front();
149 }
150 self.history.push_back(result);
151 }
152
153 #[must_use]
155 pub fn history(&self) -> &VecDeque<f64> {
156 &self.history
157 }
158
159 pub fn clear_history(&mut self) {
161 self.history.clear();
162 }
163
164 #[must_use]
166 pub fn history_len(&self) -> usize {
167 self.history.len()
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
178 fn test_calc_error_display_division_by_zero() {
179 let err = CalcError::DivisionByZero;
180 assert_eq!(format!("{err}"), "Division by zero");
181 }
182
183 #[test]
184 fn test_calc_error_display_overflow() {
185 let err = CalcError::Overflow;
186 assert_eq!(format!("{err}"), "Overflow: result exceeds maximum value");
187 }
188
189 #[test]
190 fn test_calc_error_display_parse_error() {
191 let err = CalcError::ParseError("unexpected token".into());
192 assert_eq!(format!("{err}"), "Invalid expression: unexpected token");
193 }
194
195 #[test]
196 fn test_calc_error_display_empty_expression() {
197 let err = CalcError::EmptyExpression;
198 assert_eq!(format!("{err}"), "Empty expression");
199 }
200
201 #[test]
202 fn test_calc_error_display_invalid_result() {
203 let err = CalcError::InvalidResult("NaN".into());
204 assert_eq!(format!("{err}"), "Invalid result: NaN");
205 }
206
207 #[test]
208 fn test_calc_error_display_jidoka_violation() {
209 let err = CalcError::AnomalyViolation(AnomalyViolation::NaN);
210 assert_eq!(format!("{err}"), "Anomaly violation: NaN detected");
211 }
212
213 #[test]
214 fn test_calc_error_is_error_trait() {
215 let err: Box<dyn std::error::Error> = Box::new(CalcError::DivisionByZero);
216 assert!(err.to_string().contains("Division"));
217 }
218
219 #[test]
222 fn test_jidoka_violation_display_nan() {
223 let v = AnomalyViolation::NaN;
224 assert_eq!(format!("{v}"), "NaN detected");
225 }
226
227 #[test]
228 fn test_jidoka_violation_display_infinite() {
229 let v = AnomalyViolation::Infinite;
230 assert_eq!(format!("{v}"), "Infinite value detected");
231 }
232
233 #[test]
234 fn test_jidoka_violation_display_overflow() {
235 let v = AnomalyViolation::Overflow(1e200);
236 assert!(format!("{v}").contains("exceeds maximum magnitude"));
237 }
238
239 #[test]
242 fn test_jidoka_validator_new() {
243 let v = AnomalyValidator::new();
244 assert_eq!(v.max_magnitude, AnomalyValidator::DEFAULT_MAX_MAGNITUDE);
245 assert!(v.check_special_values);
246 assert!(v.history.is_empty());
247 }
248
249 #[test]
250 fn test_jidoka_validator_default() {
251 let v = AnomalyValidator::default();
252 assert_eq!(v.max_magnitude, AnomalyValidator::DEFAULT_MAX_MAGNITUDE);
253 }
254
255 #[test]
256 fn test_jidoka_validator_with_max_magnitude() {
257 let v = AnomalyValidator::with_max_magnitude(100.0);
258 assert_eq!(v.max_magnitude, 100.0);
259 }
260
261 #[test]
262 fn test_jidoka_validate_valid_result() {
263 let mut v = AnomalyValidator::new();
264 assert_eq!(v.validate(42.0), Ok(42.0));
265 }
266
267 #[test]
268 fn test_jidoka_validate_nan() {
269 let mut v = AnomalyValidator::new();
270 assert_eq!(v.validate(f64::NAN), Err(AnomalyViolation::NaN));
271 }
272
273 #[test]
274 fn test_jidoka_validate_positive_infinity() {
275 let mut v = AnomalyValidator::new();
276 assert_eq!(v.validate(f64::INFINITY), Err(AnomalyViolation::Infinite));
277 }
278
279 #[test]
280 fn test_jidoka_validate_negative_infinity() {
281 let mut v = AnomalyValidator::new();
282 assert_eq!(
283 v.validate(f64::NEG_INFINITY),
284 Err(AnomalyViolation::Infinite)
285 );
286 }
287
288 #[test]
289 fn test_jidoka_validate_overflow_positive() {
290 let mut v = AnomalyValidator::with_max_magnitude(100.0);
291 let result = v.validate(150.0);
292 assert!(matches!(result, Err(AnomalyViolation::Overflow(_))));
293 }
294
295 #[test]
296 fn test_jidoka_validate_overflow_negative() {
297 let mut v = AnomalyValidator::with_max_magnitude(100.0);
298 let result = v.validate(-150.0);
299 assert!(matches!(result, Err(AnomalyViolation::Overflow(_))));
300 }
301
302 #[test]
303 fn test_jidoka_validate_at_boundary() {
304 let mut v = AnomalyValidator::with_max_magnitude(100.0);
305 assert_eq!(v.validate(100.0), Ok(100.0));
306 assert_eq!(v.validate(-100.0), Ok(-100.0));
307 }
308
309 #[test]
310 fn test_jidoka_history_recording() {
311 let mut v = AnomalyValidator::new();
312 v.validate(1.0).unwrap();
313 v.validate(2.0).unwrap();
314 v.validate(3.0).unwrap();
315 assert_eq!(v.history_len(), 3);
316 assert_eq!(
317 v.history().iter().copied().collect::<Vec<_>>(),
318 vec![1.0, 2.0, 3.0]
319 );
320 }
321
322 #[test]
323 fn test_jidoka_history_clear() {
324 let mut v = AnomalyValidator::new();
325 v.validate(1.0).unwrap();
326 v.validate(2.0).unwrap();
327 v.clear_history();
328 assert_eq!(v.history_len(), 0);
329 }
330
331 #[test]
332 fn test_jidoka_history_max_size() {
333 let mut v = AnomalyValidator::new();
334 v.max_history = 3;
335 for i in 0..5 {
336 v.validate(i as f64).unwrap();
337 }
338 assert_eq!(v.history_len(), 3);
339 assert_eq!(
340 v.history().iter().copied().collect::<Vec<_>>(),
341 vec![2.0, 3.0, 4.0]
342 );
343 }
344
345 #[test]
346 fn test_jidoka_special_values_disabled() {
347 let mut v = AnomalyValidator::new();
348 v.check_special_values = false;
349 assert!(v.validate(f64::NAN).is_ok());
351 }
352}