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}