#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
core::policies::StrictFinitePolicy,
functions::FunctionErrors,
kernels::{RawComplexTrait, RawScalarTrait},
};
use num::Complex;
use thiserror::Error;
use try_create::ValidationPolicy;
#[derive(Debug, Error)]
pub enum AbsInputErrors<RawScalar: RawScalarTrait> {
#[error("the input value is invalid!")]
ValidationError {
#[source]
#[backtrace]
source: <RawScalar as RawScalarTrait>::ValidationErrors,
},
}
pub type AbsRealErrors<RawReal> =
FunctionErrors<AbsInputErrors<RawReal>, <RawReal as RawScalarTrait>::ValidationErrors>;
pub type AbsComplexErrors<RawComplex> = FunctionErrors<
AbsInputErrors<RawComplex>,
<<RawComplex as RawComplexTrait>::RawReal as RawScalarTrait>::ValidationErrors,
>;
pub trait Abs: Sized {
type Output: Sized;
type Error: std::error::Error;
#[must_use = "this `Result` may contain an error that should be handled"]
fn try_abs(self) -> Result<Self::Output, Self::Error>;
fn abs(self) -> Self::Output;
}
impl Abs for f64 {
type Output = f64;
type Error = AbsRealErrors<f64>;
#[inline(always)]
fn try_abs(self) -> Result<f64, Self::Error> {
StrictFinitePolicy::<f64, 53>::validate(self)
.map_err(|e| AbsInputErrors::ValidationError { source: e }.into())
.and_then(|v| {
StrictFinitePolicy::<f64, 53>::validate(f64::abs(v))
.map_err(|e| AbsRealErrors::Output { source: e })
})
}
#[inline(always)]
fn abs(self) -> f64 {
#[cfg(debug_assertions)]
{
self.try_abs()
.expect("Error in the computation of the absolute value in debug mode")
}
#[cfg(not(debug_assertions))]
{
f64::abs(self)
}
}
}
impl Abs for Complex<f64> {
type Output = f64;
type Error = AbsComplexErrors<Complex<f64>>;
#[inline(always)]
fn try_abs(self) -> Result<f64, AbsComplexErrors<Complex<f64>>> {
StrictFinitePolicy::<Complex<f64>, 53>::validate(self)
.map_err(|e| AbsInputErrors::ValidationError { source: e }.into())
.and_then(|v| {
let norm = v.norm();
StrictFinitePolicy::<f64, 53>::validate(norm).map_err(|e| AbsComplexErrors::<
Complex<f64>,
>::Output {
source: e,
})
})
}
#[inline(always)]
fn abs(self) -> f64 {
#[cfg(debug_assertions)]
{
self.try_abs()
.expect("Error in the computation of the absolute value in debug mode")
}
#[cfg(not(debug_assertions))]
{
self.norm()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use num::Complex;
mod abs {
use super::*;
mod native64 {
use super::*;
mod real {
use super::*;
#[test]
fn abs_valid() {
let value = 4.0;
assert_eq!(value.try_abs().unwrap(), 4.0);
assert_eq!(value.abs(), 4.0);
}
#[test]
fn abs_negative() {
let value = -4.0;
assert_eq!(value.try_abs().unwrap(), 4.0);
assert_eq!(value.abs(), 4.0);
}
#[test]
fn abs_zero() {
let value = 0.0;
assert_eq!(value.try_abs().unwrap(), 0.0);
assert_eq!(value.abs(), 0.0);
}
#[test]
fn abs_nan() {
let value = f64::NAN;
let result = value.try_abs().unwrap_err();
assert!(matches!(result, AbsRealErrors::Input { .. }));
}
#[test]
fn abs_infinity() {
let value = f64::INFINITY;
assert!(matches!(
value.try_abs().unwrap_err(),
AbsRealErrors::Input { .. }
));
}
#[test]
fn abs_subnormal() {
let value = f64::MIN_POSITIVE / 2.0;
assert!(matches!(
value.try_abs().unwrap_err(),
AbsRealErrors::Input { .. }
));
}
}
mod complex {
use super::*;
#[test]
fn abs_valid() {
let value = Complex::new(4.0, 0.0);
assert_eq!(value.try_abs().unwrap(), 4.0);
assert_eq!(value.abs(), 4.0);
}
#[test]
fn abs_negative() {
let value = Complex::new(-4.0, 0.0);
assert_eq!(value.try_abs().unwrap(), 4.0);
assert_eq!(value.abs(), 4.0);
}
#[test]
fn abs_zero() {
let value = Complex::new(0.0, 0.0);
assert_eq!(value.try_abs().unwrap(), 0.0);
assert_eq!(value.abs(), 0.0);
}
#[test]
fn abs_nan() {
let value = Complex::new(f64::NAN, 0.0);
assert!(matches!(
value.try_abs(),
Err(AbsComplexErrors::<Complex<f64>>::Input { .. })
));
let value = Complex::new(0.0, f64::NAN);
assert!(matches!(
value.try_abs(),
Err(AbsComplexErrors::<Complex<f64>>::Input { .. })
));
}
#[test]
fn abs_infinity() {
let value = Complex::new(f64::INFINITY, 0.0);
assert!(matches!(
value.try_abs(),
Err(AbsComplexErrors::<Complex<f64>>::Input { .. })
));
let value = Complex::new(0.0, f64::INFINITY);
assert!(matches!(
value.try_abs(),
Err(AbsComplexErrors::<Complex<f64>>::Input { .. })
));
}
#[test]
fn abs_subnormal() {
let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
assert!(matches!(
value.try_abs(),
Err(AbsComplexErrors::<Complex<f64>>::Input { .. })
));
let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
assert!(matches!(
value.try_abs(),
Err(AbsComplexErrors::<Complex<f64>>::Input { .. })
));
}
}
}
}
}