#![deny(rustdoc::broken_intra_doc_links)]
use num::Complex;
use std::{backtrace::Backtrace, num::FpCategory};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RealValueErrors<RealType> {
#[error("the value is infinite!")]
Infinite {
backtrace: Backtrace,
},
#[error("the value is NaN!")]
NaN {
backtrace: Backtrace,
},
#[error("the value ({value:?}) is subnormal!")]
Subnormal {
value: RealType,
backtrace: Backtrace,
},
}
#[derive(Debug, Error)]
pub enum ComplexValueErrors<RealType, ComplexType> {
#[error("the value ({value:?}) has an invalid real part!")]
InvalidRealPart {
value: ComplexType,
#[source]
#[backtrace]
real_part_error: RealValueErrors<RealType>,
},
#[error("the value ({value:?}) has an invalid imaginary part!")]
InvalidImaginaryPart {
value: ComplexType,
#[source]
#[backtrace]
imaginary_part_error: RealValueErrors<RealType>,
},
}
pub trait RealValueValidator: Sized {
fn validate(self) -> Result<Self, RealValueErrors<Self>>;
}
impl RealValueValidator for f64 {
fn validate(self) -> Result<Self, RealValueErrors<Self>> {
let fp_type = self.classify();
match fp_type {
FpCategory::Infinite => Err(RealValueErrors::Infinite {
backtrace: Backtrace::force_capture(),
}),
FpCategory::Nan => Err(RealValueErrors::NaN {
backtrace: Backtrace::force_capture(),
}),
FpCategory::Subnormal => Err(RealValueErrors::Subnormal {
value: self,
backtrace: Backtrace::force_capture(),
}),
FpCategory::Zero | FpCategory::Normal => Ok(self),
}
}
}
#[cfg(feature = "rug")]
impl RealValueValidator for rug::Float {
fn validate(self) -> Result<Self, RealValueErrors<Self>> {
let fp_type = self.classify();
match fp_type {
FpCategory::Infinite => Err(RealValueErrors::Infinite {
backtrace: Backtrace::force_capture(),
}),
FpCategory::Nan => Err(RealValueErrors::NaN {
backtrace: Backtrace::force_capture(),
}),
FpCategory::Subnormal => Err(RealValueErrors::Subnormal {
value: self,
backtrace: Backtrace::force_capture(),
}),
FpCategory::Zero | FpCategory::Normal => Ok(self),
}
}
}
pub trait ComplexValueValidator: Sized {
type RealType: Sized;
fn validate(self) -> Result<Self, ComplexValueErrors<Self::RealType, Self>>;
}
impl ComplexValueValidator for Complex<f64> {
type RealType = f64;
fn validate(self) -> Result<Self, ComplexValueErrors<Self::RealType, Self>> {
let real_part = match self.re.validate() {
Ok(real_part) => real_part,
Err(real_part_error) => {
return Err(ComplexValueErrors::InvalidRealPart {
value: self,
real_part_error,
})
}
};
let imaginary_part = match self.im.validate() {
Ok(imaginary_part) => imaginary_part,
Err(imaginary_part_error) => {
return Err(ComplexValueErrors::InvalidImaginaryPart {
value: self,
imaginary_part_error,
})
}
};
Ok(Self {
re: real_part,
im: imaginary_part,
})
}
}
#[cfg(feature = "rug")]
impl ComplexValueValidator for rug::Complex {
type RealType = rug::Float;
fn validate(self) -> Result<Self, ComplexValueErrors<Self::RealType, Self>> {
let (re, im) = self.clone().into_real_imag();
let _real_part = match re.validate() {
Ok(real_part) => real_part,
Err(real_part_error) => {
return Err(ComplexValueErrors::InvalidRealPart {
value: self,
real_part_error,
})
}
};
let _imaginary_part = match im.validate() {
Ok(imaginary_part) => imaginary_part,
Err(imaginary_part_error) => {
return Err(ComplexValueErrors::InvalidImaginaryPart {
value: self,
imaginary_part_error,
})
}
};
Ok(self)
}
}
#[derive(Debug, Error)]
pub enum TryFromf64Errors {
#[error("the input value is invalid!")]
ValidationError {
#[from]
source: RealValueErrors<f64>,
},
#[error("the input f64 value ({value:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision.")]
NonRepresentableExactly {
value: f64,
precision: u32,
backtrace: Backtrace,
},
}
#[cfg(feature = "rug")]
#[derive(Debug, Error)]
pub enum TryFromRugFloatErrors {
#[error("the input value is invalid!")]
ValidationError {
#[from]
source: RealValueErrors<f64>,
},
#[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.")]
NonRepresentableExactly {
value: rug::Float,
precision_in: u32,
precision_asked: u32,
value_approx: rug::Float,
backtrace: Backtrace,
},
}
#[cfg(test)]
mod tests {
use super::*;
mod native64 {
use super::*;
mod real {
use super::*;
#[test]
fn test_f64_validate_infinite() {
let value = f64::INFINITY;
let result = value.validate();
assert!(matches!(result, Err(RealValueErrors::Infinite { .. })));
}
#[test]
fn test_f64_validate_nan() {
let value = f64::NAN;
let result = value.validate();
assert!(matches!(result, Err(RealValueErrors::NaN { .. })));
}
#[test]
fn test_f64_validate_subnormal() {
let value = f64::MIN_POSITIVE / 2.0;
let result = value.validate();
assert!(matches!(result, Err(RealValueErrors::Subnormal { .. })));
}
#[test]
fn test_f64_validate_zero() {
let value = 0.0;
let result = value.validate();
assert!(matches!(result, Ok(0.0)));
}
#[test]
fn test_f64_validate_normal() {
let value = 1.0;
let result = value.validate();
assert!(matches!(result, Ok(1.0)));
}
}
mod complex {
use super::*;
#[test]
fn test_complex_f64_validate_invalid_real_part() {
let value = Complex::new(f64::INFINITY, 1.0);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidRealPart { .. })
));
let value = Complex::new(f64::NAN, 1.0);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidRealPart { .. })
));
let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidRealPart { .. })
));
}
#[test]
fn test_complex_f64_validate_invalid_imaginary_part() {
let value = Complex::new(1.0, f64::INFINITY);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidImaginaryPart { .. })
));
let value = Complex::new(1.0, f64::NAN);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidImaginaryPart { .. })
));
let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidImaginaryPart { .. })
));
}
#[test]
fn test_complex_f64_validate_zero() {
let value = Complex::new(0.0, 0.0);
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
#[test]
fn test_complex_f64_validate_normal() {
let value = Complex::new(1.0, 1.0);
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
}
}
#[cfg(feature = "rug")]
mod rug53 {
use super::*;
use rug::Float;
mod real {
use super::*;
#[test]
fn test_rug_float_validate_infinite() {
let value = Float::with_val(53, f64::INFINITY);
let result = value.validate();
assert!(matches!(result, Err(RealValueErrors::Infinite { .. })));
}
#[test]
fn test_rug_float_validate_nan() {
let value = Float::with_val(53, f64::NAN);
let result = value.validate();
assert!(matches!(result, Err(RealValueErrors::NaN { .. })));
}
#[test]
fn test_rug_float_validate_subnormal() {
let value = Float::with_val(53, f64::MIN_POSITIVE);
println!("value: {:?}", value);
let value = Float::with_val(53, f64::MIN_POSITIVE / 2.0);
println!("value: {:?}", value);
let result = value.validate();
println!("result: {:?}", result);
assert!(matches!(result, Ok(..)));
}
#[test]
fn test_rug_float_validate_zero() {
let value = Float::with_val(53, 0.0);
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
#[test]
fn test_rug_float_validate_normal() {
let value = Float::with_val(53, 1.0);
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
}
mod complex {
use super::*;
use rug::Complex;
#[test]
fn test_complex_rug_float_validate_invalid_real_part() {
let value = Complex::with_val(
53,
(Float::with_val(53, f64::INFINITY), Float::with_val(53, 1.0)),
);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidRealPart { .. })
));
let value = Complex::with_val(
53,
(Float::with_val(53, f64::NAN), Float::with_val(53, 1.0)),
);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidRealPart { .. })
));
let value = Complex::with_val(
53,
(
Float::with_val(53, f64::MIN_POSITIVE / 2.0),
Float::with_val(53, 1.0),
),
);
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
#[test]
fn test_complex_rug_float_validate_invalid_imaginary_part() {
let value = Complex::with_val(
53,
(Float::with_val(53, 1.0), Float::with_val(53, f64::INFINITY)),
);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidImaginaryPart { .. })
));
let value = Complex::with_val(
53,
(Float::with_val(53, 1.0), Float::with_val(53, f64::NAN)),
);
let result = value.validate();
assert!(matches!(
result,
Err(ComplexValueErrors::InvalidImaginaryPart { .. })
));
let value = Complex::with_val(
53,
(
Float::with_val(53, 1.0),
Float::with_val(53, f64::MIN_POSITIVE / 2.0),
),
);
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
#[test]
fn test_complex_rug_float_validate_zero() {
let zero =
Complex::with_val(53, (Float::with_val(53, 0.0), Float::with_val(53, 0.0)));
let result = zero.validate();
assert!(matches!(result, Ok(_)));
}
#[test]
fn test_complex_rug_float_validate_normal() {
let value =
Complex::with_val(53, (Float::with_val(53, 1.0), Float::with_val(53, 1.0)));
let result = value.validate();
assert!(matches!(result, Ok(_)));
}
}
}
}