ftl_numkernel/errors.rs
1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! This module defines error types and traits for validating real and complex values.
4//!
5//! The module provides:
6//! - [`RealValueErrors`]: An enum representing errors that can occur during the validation of real (floating point) numbers.
7//! - [`ComplexValueErrors`]: An enum representing errors that can occur during the validation of complex numbers.
8//! - [`RealValueValidator`]: A trait for validating real values.
9//! - [`ComplexValueValidator`]: A trait for validating complex values.
10
11use num::Complex;
12use std::{backtrace::Backtrace, num::FpCategory};
13use thiserror::Error;
14
15//-------------------------------------------------------------
16/// Errors that can be generated from the validation of a real (floating point) number.
17#[derive(Debug, Error)]
18pub enum RealValueErrors<RealType> {
19 /// The value is infinite.
20 ///
21 /// This error occurs when the value being validated is infinite.
22 #[error("the value is infinite!")]
23 Infinite {
24 /// The backtrace of the error.
25 backtrace: Backtrace,
26 },
27
28 /// The value is NaN (Not a Number).
29 ///
30 /// This error occurs when the value being validated is NaN (Not a Number).
31 #[error("the value is NaN!")]
32 NaN {
33 /// The backtrace of the error.
34 backtrace: Backtrace,
35 },
36
37 /// The value is [subnormal](https://en.wikipedia.org/wiki/Subnormal_number).
38 ///
39 /// This error occurs when the value being validated is subnormal.
40 #[error("the value ({value:?}) is subnormal!")]
41 Subnormal {
42 /// The subnormal value.
43 value: RealType,
44
45 /// The backtrace of the error.
46 backtrace: Backtrace,
47 },
48}
49
50/// Errors that can be generated from the validation of a complex number.
51#[derive(Debug, Error)]
52pub enum ComplexValueErrors<RealType, ComplexType> {
53 /// The real part is invalid.
54 ///
55 /// This error occurs when the real part of the complex number is invalid.
56 #[error("the value ({value:?}) has an invalid real part!")]
57 InvalidRealPart {
58 /// The complex value that caused the error.
59 value: ComplexType,
60
61 /// The error that occurred while validating the real part.
62 #[source]
63 #[backtrace]
64 real_part_error: RealValueErrors<RealType>,
65 },
66
67 /// The imaginary part is invalid.
68 ///
69 /// This error occurs when the imaginary part of the complex number is invalid.
70 #[error("the value ({value:?}) has an invalid imaginary part!")]
71 InvalidImaginaryPart {
72 /// The complex value that caused the error.
73 value: ComplexType,
74
75 /// The error that occurred while validating the imaginary part.
76 #[source]
77 #[backtrace]
78 imaginary_part_error: RealValueErrors<RealType>,
79 },
80}
81//-------------------------------------------------------------
82
83//-------------------------------------------------------------
84/// A trait for validating real values.
85///
86/// This trait provides a method for validating real values, ensuring that they
87/// are not infinite, NaN, or subnormal. If the value is valid, it returns `Ok(self)`.
88/// Otherwise, it returns an appropriate error variant of `RealValueErrors`.
89pub trait RealValueValidator: Sized {
90 /// Validates the real value.
91 ///
92 /// # Returns
93 ///
94 /// * `Ok(self)` if the value is valid (i.e., it is not infinite, NaN, or subnormal).
95 /// * `Err(RealValueErrors::Infinite)` if the value is infinite.
96 /// * `Err(RealValueErrors::NaN)` if the value is NaN.
97 /// * `Err(RealValueErrors::Subnormal)` if the value is subnormal.
98 fn validate(self) -> Result<Self, RealValueErrors<Self>>;
99}
100
101impl RealValueValidator for f64 {
102 /// Validates the `f64` value.
103 ///
104 /// This implementation checks the floating-point category of the value and
105 /// returns an appropriate error if the value is infinite, NaN, or subnormal.
106 ///
107 /// # Returns
108 ///
109 /// * `Ok(self)` if the value is valid (i.e., it is not infinite, NaN, or subnormal).
110 /// * `Err(RealValueErrors::Infinite)` if the value is infinite.
111 /// * `Err(RealValueErrors::NaN)` if the value is NaN.
112 /// * `Err(RealValueErrors::Subnormal)` if the value is subnormal.
113 fn validate(self) -> Result<Self, RealValueErrors<Self>> {
114 let fp_type = self.classify();
115 match fp_type {
116 FpCategory::Infinite => Err(RealValueErrors::Infinite {
117 backtrace: Backtrace::force_capture(),
118 }),
119 FpCategory::Nan => Err(RealValueErrors::NaN {
120 backtrace: Backtrace::force_capture(),
121 }),
122 FpCategory::Subnormal => Err(RealValueErrors::Subnormal {
123 value: self,
124 backtrace: Backtrace::force_capture(),
125 }),
126 FpCategory::Zero | FpCategory::Normal => Ok(self),
127 }
128 }
129}
130
131#[cfg(feature = "rug")]
132impl RealValueValidator for rug::Float {
133 /// Validates the `rug::Float` value.
134 ///
135 /// This implementation checks the floating-point category of the value and
136 /// returns an appropriate error if the value is infinite, NaN, or subnormal.
137 ///
138 /// # Returns
139 ///
140 /// * `Ok(self)` if the value is valid (i.e., it is not infinite, NaN, or subnormal).
141 /// * `Err(RealValueErrors::Infinite)` if the value is infinite.
142 /// * `Err(RealValueErrors::NaN)` if the value is NaN.
143 /// * `Err(RealValueErrors::Subnormal)` if the value is subnormal.
144 fn validate(self) -> Result<Self, RealValueErrors<Self>> {
145 let fp_type = self.classify();
146 match fp_type {
147 FpCategory::Infinite => Err(RealValueErrors::Infinite {
148 backtrace: Backtrace::force_capture(),
149 }),
150 FpCategory::Nan => Err(RealValueErrors::NaN {
151 backtrace: Backtrace::force_capture(),
152 }),
153 FpCategory::Subnormal => Err(RealValueErrors::Subnormal {
154 value: self,
155 backtrace: Backtrace::force_capture(),
156 }),
157 FpCategory::Zero | FpCategory::Normal => Ok(self),
158 }
159 }
160}
161//-------------------------------------------------------------
162
163//-------------------------------------------------------------
164/// A trait for validating complex values.
165///
166/// This trait provides a method for validating complex values, ensuring that their
167/// real and imaginary parts are not infinite, NaN, or subnormal. If the value is valid,
168/// it returns `Ok(self)`. Otherwise, it returns an appropriate error variant of `ComplexValueErrors`.
169pub trait ComplexValueValidator: Sized {
170 type RealType: Sized;
171
172 /// Validates the complex value.
173 ///
174 /// # Returns
175 ///
176 /// * `Ok(self)` if the value is valid (i.e., both the real and imaginary parts are not infinite, NaN, or subnormal).
177 /// * `Err(ComplexValueErrors::InvalidRealPart)` if the real part is invalid.
178 /// * `Err(ComplexValueErrors::InvalidImaginaryPart)` if the imaginary part is invalid.
179 fn validate(self) -> Result<Self, ComplexValueErrors<Self::RealType, Self>>;
180}
181
182impl ComplexValueValidator for Complex<f64> {
183 type RealType = f64;
184
185 /// Validates the `Complex<f64>` value.
186 ///
187 /// This implementation checks the floating-point category of the real and imaginary parts
188 /// of the complex value and returns an appropriate error if either part is infinite, NaN, or subnormal.
189 ///
190 /// # Returns
191 ///
192 /// * `Ok(self)` if the value is valid (i.e., both the real and imaginary parts are not infinite, NaN, or subnormal).
193 /// * `Err(ComplexValueErrors::InvalidRealPart)` if the real part is invalid.
194 /// * `Err(ComplexValueErrors::InvalidImaginaryPart)` if the imaginary part is invalid.
195 fn validate(self) -> Result<Self, ComplexValueErrors<Self::RealType, Self>> {
196 let real_part = match self.re.validate() {
197 Ok(real_part) => real_part,
198 Err(real_part_error) => {
199 return Err(ComplexValueErrors::InvalidRealPart {
200 value: self,
201 real_part_error,
202 })
203 }
204 };
205 let imaginary_part = match self.im.validate() {
206 Ok(imaginary_part) => imaginary_part,
207 Err(imaginary_part_error) => {
208 return Err(ComplexValueErrors::InvalidImaginaryPart {
209 value: self,
210 imaginary_part_error,
211 })
212 }
213 };
214 Ok(Self {
215 re: real_part,
216 im: imaginary_part,
217 })
218 }
219}
220
221#[cfg(feature = "rug")]
222impl ComplexValueValidator for rug::Complex {
223 type RealType = rug::Float;
224
225 /// Validates the `rug::Complex` value.
226 ///
227 /// This implementation checks the floating-point category of the real and imaginary parts
228 /// of the complex value and returns an appropriate error if either part is infinite, NaN, or subnormal.
229 ///
230 /// # Returns
231 ///
232 /// * `Ok(self)` if the value is valid (i.e., both the real and imaginary parts are not infinite, NaN, or subnormal).
233 /// * `Err(ComplexValueErrors::InvalidRealPart)` if the real part is invalid.
234 /// * `Err(ComplexValueErrors::InvalidImaginaryPart)` if the imaginary part is invalid.
235 fn validate(self) -> Result<Self, ComplexValueErrors<Self::RealType, Self>> {
236 let (re, im) = self.clone().into_real_imag();
237
238 let _real_part = match re.validate() {
239 Ok(real_part) => real_part,
240 Err(real_part_error) => {
241 return Err(ComplexValueErrors::InvalidRealPart {
242 value: self,
243 real_part_error,
244 })
245 }
246 };
247 let _imaginary_part = match im.validate() {
248 Ok(imaginary_part) => imaginary_part,
249 Err(imaginary_part_error) => {
250 return Err(ComplexValueErrors::InvalidImaginaryPart {
251 value: self,
252 imaginary_part_error,
253 })
254 }
255 };
256 Ok(self)
257 }
258}
259//-------------------------------------------------------------
260
261//-------------------------------------------------------------
262/// Errors that can be generated by the conversion from a `f64` value.
263#[derive(Debug, Error)]
264pub enum TryFromf64Errors {
265 /// The input value is invalid.
266 ///
267 /// This error occurs when the input value being converted is invalid.
268 #[error("the input value is invalid!")]
269 ValidationError {
270 /// The source error that occurred during validation.
271 #[from]
272 source: RealValueErrors<f64>,
273 },
274
275 /// The input `value` is not representable exactly with the asked `precision`.
276 ///
277 /// This error occurs when the input `f64` value cannot be exactly represented with the specified precision.
278 #[error("the input f64 value ({value:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision.")]
279 NonRepresentableExactly {
280 /// The input `f64` value.
281 value: f64,
282
283 /// The precision that was requested.
284 precision: u32,
285
286 /// The backtrace of the error.
287 backtrace: Backtrace,
288 },
289}
290
291// Conditional compilation for the `rug` feature
292#[cfg(feature = "rug")]
293/// Errors that can be generated by the conversion from a `rug::Float` value.
294#[derive(Debug, Error)]
295pub enum TryFromRugFloatErrors {
296 /// The input value is invalid.
297 ///
298 /// This error occurs when the input value being converted is invalid.
299 #[error("the input value is invalid!")]
300 ValidationError {
301 /// The source error that occurred during validation.
302 #[from]
303 source: RealValueErrors<f64>,
304 },
305
306 /// The input `value` is not representable exactly with the asked precision.
307 ///
308 /// This error occurs when the input `rug::Float` value cannot be exactly represented with the specified precision.
309 #[error("the input rug::Float value ({value:?}) has a precision of {precision_in:?} and cannot be exactly represented with the specific asked precision ({precision_asked:?}). The input value is approximated to ({value_approx:?}). Try to increase the precision.")]
310 NonRepresentableExactly {
311 /// The input `rug::Float` value.
312 value: rug::Float,
313
314 /// The precision of the input value.
315 precision_in: u32,
316
317 /// The precision that was requested.
318 precision_asked: u32,
319
320 /// The approximated value.
321 value_approx: rug::Float,
322
323 /// The backtrace of the error.
324 backtrace: Backtrace,
325 },
326}
327//-------------------------------------------------------------
328
329//-------------------------------------------------------------
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 mod native64 {
336 use super::*;
337
338 mod real {
339 use super::*;
340
341 #[test]
342 fn test_f64_validate_infinite() {
343 let value = f64::INFINITY;
344 let result = value.validate();
345 assert!(matches!(result, Err(RealValueErrors::Infinite { .. })));
346 }
347
348 #[test]
349 fn test_f64_validate_nan() {
350 let value = f64::NAN;
351 let result = value.validate();
352 assert!(matches!(result, Err(RealValueErrors::NaN { .. })));
353 }
354
355 #[test]
356 fn test_f64_validate_subnormal() {
357 let value = f64::MIN_POSITIVE / 2.0;
358 let result = value.validate();
359 assert!(matches!(result, Err(RealValueErrors::Subnormal { .. })));
360 }
361
362 #[test]
363 fn test_f64_validate_zero() {
364 let value = 0.0;
365 let result = value.validate();
366 assert!(matches!(result, Ok(0.0)));
367 }
368
369 #[test]
370 fn test_f64_validate_normal() {
371 let value = 1.0;
372 let result = value.validate();
373 assert!(matches!(result, Ok(1.0)));
374 }
375 }
376
377 mod complex {
378 use super::*;
379
380 #[test]
381 fn test_complex_f64_validate_invalid_real_part() {
382 let value = Complex::new(f64::INFINITY, 1.0);
383 let result = value.validate();
384 assert!(matches!(
385 result,
386 Err(ComplexValueErrors::InvalidRealPart { .. })
387 ));
388
389 let value = Complex::new(f64::NAN, 1.0);
390 let result = value.validate();
391 assert!(matches!(
392 result,
393 Err(ComplexValueErrors::InvalidRealPart { .. })
394 ));
395
396 let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
397 let result = value.validate();
398 assert!(matches!(
399 result,
400 Err(ComplexValueErrors::InvalidRealPart { .. })
401 ));
402 }
403
404 #[test]
405 fn test_complex_f64_validate_invalid_imaginary_part() {
406 let value = Complex::new(1.0, f64::INFINITY);
407 let result = value.validate();
408 assert!(matches!(
409 result,
410 Err(ComplexValueErrors::InvalidImaginaryPart { .. })
411 ));
412
413 let value = Complex::new(1.0, f64::NAN);
414 let result = value.validate();
415 assert!(matches!(
416 result,
417 Err(ComplexValueErrors::InvalidImaginaryPart { .. })
418 ));
419
420 let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
421 let result = value.validate();
422 assert!(matches!(
423 result,
424 Err(ComplexValueErrors::InvalidImaginaryPart { .. })
425 ));
426 }
427
428 #[test]
429 fn test_complex_f64_validate_zero() {
430 let value = Complex::new(0.0, 0.0);
431 let result = value.validate();
432 assert!(matches!(result, Ok(_)));
433 }
434
435 #[test]
436 fn test_complex_f64_validate_normal() {
437 let value = Complex::new(1.0, 1.0);
438 let result = value.validate();
439 assert!(matches!(result, Ok(_)));
440 }
441 }
442 }
443
444 #[cfg(feature = "rug")]
445 mod rug53 {
446 use super::*;
447 use rug::Float;
448
449 mod real {
450 use super::*;
451
452 #[test]
453 fn test_rug_float_validate_infinite() {
454 let value = Float::with_val(53, f64::INFINITY);
455 let result = value.validate();
456 assert!(matches!(result, Err(RealValueErrors::Infinite { .. })));
457 }
458
459 #[test]
460 fn test_rug_float_validate_nan() {
461 let value = Float::with_val(53, f64::NAN);
462 let result = value.validate();
463 assert!(matches!(result, Err(RealValueErrors::NaN { .. })));
464 }
465
466 #[test]
467 fn test_rug_float_validate_subnormal() {
468 let value = Float::with_val(53, f64::MIN_POSITIVE);
469 println!("value: {:?}", value);
470 let value = Float::with_val(53, f64::MIN_POSITIVE / 2.0);
471 println!("value: {:?}", value);
472 let result = value.validate();
473 println!("result: {:?}", result);
474 // assert!(matches!(result, Err(RealValueErrors::Subnormal { .. })));
475
476 // rug::Float is never subnormal
477 assert!(matches!(result, Ok(..)));
478 }
479
480 #[test]
481 fn test_rug_float_validate_zero() {
482 let value = Float::with_val(53, 0.0);
483 let result = value.validate();
484 assert!(matches!(result, Ok(_)));
485 }
486
487 #[test]
488 fn test_rug_float_validate_normal() {
489 let value = Float::with_val(53, 1.0);
490 let result = value.validate();
491 assert!(matches!(result, Ok(_)));
492 }
493 }
494
495 mod complex {
496 use super::*;
497 use rug::Complex;
498
499 #[test]
500 fn test_complex_rug_float_validate_invalid_real_part() {
501 let value = Complex::with_val(
502 53,
503 (Float::with_val(53, f64::INFINITY), Float::with_val(53, 1.0)),
504 );
505 let result = value.validate();
506 assert!(matches!(
507 result,
508 Err(ComplexValueErrors::InvalidRealPart { .. })
509 ));
510
511 let value = Complex::with_val(
512 53,
513 (Float::with_val(53, f64::NAN), Float::with_val(53, 1.0)),
514 );
515 let result = value.validate();
516 assert!(matches!(
517 result,
518 Err(ComplexValueErrors::InvalidRealPart { .. })
519 ));
520
521 let value = Complex::with_val(
522 53,
523 (
524 Float::with_val(53, f64::MIN_POSITIVE / 2.0),
525 Float::with_val(53, 1.0),
526 ),
527 );
528 let result = value.validate();
529 // rug::Float is never subnormal
530 assert!(matches!(result, Ok(_)));
531 }
532
533 #[test]
534 fn test_complex_rug_float_validate_invalid_imaginary_part() {
535 let value = Complex::with_val(
536 53,
537 (Float::with_val(53, 1.0), Float::with_val(53, f64::INFINITY)),
538 );
539 let result = value.validate();
540 assert!(matches!(
541 result,
542 Err(ComplexValueErrors::InvalidImaginaryPart { .. })
543 ));
544
545 let value = Complex::with_val(
546 53,
547 (Float::with_val(53, 1.0), Float::with_val(53, f64::NAN)),
548 );
549 let result = value.validate();
550 assert!(matches!(
551 result,
552 Err(ComplexValueErrors::InvalidImaginaryPart { .. })
553 ));
554
555 let value = Complex::with_val(
556 53,
557 (
558 Float::with_val(53, 1.0),
559 Float::with_val(53, f64::MIN_POSITIVE / 2.0),
560 ),
561 );
562 let result = value.validate();
563 // rug::Float is never subnormal
564 assert!(matches!(result, Ok(_)));
565 }
566
567 #[test]
568 fn test_complex_rug_float_validate_zero() {
569 let zero =
570 Complex::with_val(53, (Float::with_val(53, 0.0), Float::with_val(53, 0.0)));
571 let result = zero.validate();
572 assert!(matches!(result, Ok(_)));
573 }
574
575 #[test]
576 fn test_complex_rug_float_validate_normal() {
577 let value =
578 Complex::with_val(53, (Float::with_val(53, 1.0), Float::with_val(53, 1.0)));
579 let result = value.validate();
580 assert!(matches!(result, Ok(_)));
581 }
582 }
583 }
584}