#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
RealScalar,
core::traits::{raw::RawScalarValidate, validation::sealed::NativeReal},
functions::FunctionErrors,
kernels::{RawComplexTrait, RawScalarTrait},
scalars::NonNegativeRealScalar,
};
use duplicate::duplicate_item;
use num::Complex;
use num_traits::Float;
use thiserror::Error;
#[cfg(debug_assertions)]
use try_create::TryNew;
#[derive(Debug, Error)]
pub enum AbsInputErrors<RawScalar: RawScalarTrait> {
#[error("the input value is invalid!")]
ValidationError {
#[source]
#[backtrace]
source: <RawScalar as RawScalarValidate>::ValidationError,
},
}
pub type AbsRealErrors<RawReal> =
FunctionErrors<AbsInputErrors<RawReal>, <RawReal as RawScalarValidate>::ValidationError>;
pub type AbsComplexErrors<RawComplex> = FunctionErrors<
AbsInputErrors<RawComplex>,
<<RawComplex as RawComplexTrait>::RawReal as RawScalarValidate>::ValidationError,
>;
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;
#[must_use = "this `Result` may contain an error that should be handled"]
fn try_abs_squared(self) -> Result<Self::Output, Self::Error>;
fn abs_squared(self) -> Self::Output;
}
#[duplicate_item(
T;
[f64];
[f32];
)]
impl Abs for T
{
type Output = NonNegativeRealScalar<T>;
type Error = AbsRealErrors<T>;
#[inline(always)]
fn try_abs(self) -> Result<NonNegativeRealScalar<T>, Self::Error> {
#[cfg(debug_assertions)]
{
self.validate_raw_scalar()
.map_err(|e| AbsInputErrors::ValidationError { source: e }.into())
.and_then(|v| {
<T as Float>::abs(v)
.validate_raw_scalar()
.map(|abs_v| NonNegativeRealScalar::try_new(abs_v).unwrap())
.map_err(|e| AbsRealErrors::Output { source: e })
})
}
#[cfg(not(debug_assertions))]
{
Ok(NonNegativeRealScalar::new(<T as Float>::abs(self)))
}
}
#[inline(always)]
fn abs(self) -> NonNegativeRealScalar<T> {
#[cfg(debug_assertions)]
{
self.try_abs()
.expect("Error in the computation of the absolute value in debug mode")
}
#[cfg(not(debug_assertions))]
{
NonNegativeRealScalar::new(<T as Float>::abs(self))
}
}
#[inline(always)]
fn try_abs_squared(self) -> Result<NonNegativeRealScalar<T>, Self::Error> {
#[cfg(debug_assertions)]
{
self.validate_raw_scalar()
.map_err(|e| AbsInputErrors::ValidationError { source: e }.into())
.and_then(|v| {
let abs_v = <T as Float>::abs(v);
(abs_v * abs_v)
.validate_raw_scalar()
.map(|abs_sq| NonNegativeRealScalar::try_new(abs_sq).unwrap())
.map_err(|e| AbsRealErrors::Output { source: e })
})
}
#[cfg(not(debug_assertions))]
{
let abs_v = <T as Float>::abs(self);
Ok(NonNegativeRealScalar::new(abs_v * abs_v))
}
}
#[inline(always)]
fn abs_squared(self) -> NonNegativeRealScalar<T> {
#[cfg(debug_assertions)]
{
self.try_abs_squared()
.expect("Error in the computation of the squared absolute value in debug mode")
}
#[cfg(not(debug_assertions))]
{
let abs_v = <T as Float>::abs(self);
NonNegativeRealScalar::new(abs_v * abs_v)
}
}
}
impl<T> Abs for Complex<T>
where
Self: RawComplexTrait<RawReal = T>,
T: NativeReal + RealScalar,
{
type Output = NonNegativeRealScalar<T>;
type Error = AbsComplexErrors<Complex<T>>;
#[inline(always)]
fn try_abs(self) -> Result<NonNegativeRealScalar<T>, AbsComplexErrors<Complex<T>>> {
#[cfg(debug_assertions)]
{
self.validate_raw_scalar()
.map_err(|e| AbsInputErrors::ValidationError { source: e }.into())
.and_then(|v| {
v.norm()
.validate_raw_scalar()
.map(|abs_v| NonNegativeRealScalar::try_new(abs_v).unwrap())
.map_err(|e| AbsComplexErrors::<Complex<T>>::Output { source: e })
})
}
#[cfg(not(debug_assertions))]
{
Ok(NonNegativeRealScalar::new(self.norm()))
}
}
#[inline(always)]
fn abs(self) -> NonNegativeRealScalar<T> {
#[cfg(debug_assertions)]
{
self.try_abs()
.expect("Error in the computation of the absolute value in debug mode")
}
#[cfg(not(debug_assertions))]
{
NonNegativeRealScalar::new(self.norm())
}
}
#[inline(always)]
fn try_abs_squared(self) -> Result<NonNegativeRealScalar<T>, AbsComplexErrors<Complex<T>>> {
#[cfg(debug_assertions)]
{
self.validate_raw_scalar()
.map_err(|e| AbsInputErrors::ValidationError { source: e }.into())
.and_then(|v| {
let norm_sq = v.norm_sqr();
norm_sq
.validate_raw_scalar()
.map(|abs_sq| NonNegativeRealScalar::try_new(abs_sq).unwrap())
.map_err(|e| AbsComplexErrors::<Complex<T>>::Output { source: e })
})
}
#[cfg(not(debug_assertions))]
{
Ok(NonNegativeRealScalar::new(self.norm_sqr()))
}
}
#[inline(always)]
fn abs_squared(self) -> NonNegativeRealScalar<T> {
#[cfg(debug_assertions)]
{
self.try_abs_squared()
.expect("Error in the computation of the squared absolute value in debug mode")
}
#[cfg(not(debug_assertions))]
{
NonNegativeRealScalar::new(self.norm_sqr())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use num::Complex;
use num::Zero;
#[cfg(debug_assertions)]
use std::assert_matches;
mod abs {
use super::*;
mod native {
use super::*;
mod real {
use super::*;
#[test]
fn abs_valid() {
let value = 4.0;
assert_eq!(<f64 as Abs>::try_abs(value).unwrap().into_inner(), 4.0);
assert_eq!(<f64 as Float>::abs(value), 4.0);
assert_eq!(<f64 as Abs>::abs(value).into_inner(), 4.0);
}
#[test]
fn abs_negative() {
let value = -4.0;
assert_eq!(<f64 as Abs>::try_abs(value).unwrap().into_inner(), 4.0);
assert_eq!(<f64 as Float>::abs(value), 4.0);
assert_eq!(<f64 as Abs>::abs(value).into_inner(), 4.0);
}
#[test]
fn abs_zero() {
let value = 0.0;
assert_eq!(<f64 as Abs>::try_abs(value).unwrap().into_inner(), 0.0);
assert_eq!(<f64 as Float>::abs(value), 0.0);
assert_eq!(<f64 as Abs>::abs(value).into_inner(), 0.0);
}
#[test]
#[cfg(debug_assertions)]
fn abs_nan() {
let value = f64::NAN;
let result = value.try_abs().unwrap_err();
assert_matches!(result, AbsRealErrors::Input(..));
}
#[test]
#[cfg(debug_assertions)]
fn abs_infinity() {
let value = f64::INFINITY;
assert_matches!(value.try_abs().unwrap_err(), AbsRealErrors::Input(..));
}
#[test]
#[cfg(debug_assertions)]
fn abs_subnormal() {
let value = f64::MIN_POSITIVE / 2.0;
assert_matches!(value.try_abs().unwrap_err(), AbsRealErrors::Input(..));
}
#[test]
fn abs_squared_positive() {
let value = 4.0;
assert_eq!(
<f64 as Abs>::try_abs_squared(value).unwrap().into_inner(),
16.0
);
assert_eq!(value.abs_squared().into_inner(), 16.0);
}
#[test]
fn abs_squared_negative() {
let value = -4.0;
assert_eq!(
<f64 as Abs>::try_abs_squared(value).unwrap().into_inner(),
16.0
);
assert_eq!(value.abs_squared().into_inner(), 16.0);
}
#[test]
fn abs_squared_zero() {
let value = 0.0;
assert_eq!(
<f64 as Abs>::try_abs_squared(value).unwrap().into_inner(),
0.0
);
assert_eq!(value.abs_squared().into_inner(), 0.0);
}
#[test]
#[cfg(debug_assertions)]
fn abs_squared_nan() {
let value = f64::NAN;
let result = value.try_abs_squared().unwrap_err();
assert_matches!(result, AbsRealErrors::Input(..));
}
#[test]
#[cfg(debug_assertions)]
fn abs_squared_infinity() {
let value = f64::INFINITY;
assert_matches!(
value.try_abs_squared().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().into_inner(), 4.0);
assert_eq!(value.abs().into_inner(), 4.0);
}
#[test]
fn abs_negative() {
let value = Complex::new(-4.0, 0.0);
assert_eq!(value.try_abs().unwrap().into_inner(), 4.0);
assert_eq!(value.abs().into_inner(), 4.0);
}
#[test]
fn abs_zero() {
let value = Complex::new(0.0, 0.0);
assert_eq!(value.try_abs().unwrap(), NonNegativeRealScalar::zero());
assert_eq!(value.abs(), NonNegativeRealScalar::zero());
}
#[test]
#[cfg(debug_assertions)]
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]
#[cfg(debug_assertions)]
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]
#[cfg(debug_assertions)]
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(..))
));
}
#[test]
fn abs_squared_valid() {
let value = Complex::new(3.0, 4.0);
assert_eq!(value.try_abs_squared().unwrap().into_inner(), 25.0);
assert_eq!(value.abs_squared().into_inner(), 25.0);
}
#[test]
fn abs_squared_zero() {
let value = Complex::new(0.0, 0.0);
assert_eq!(
value.try_abs_squared().unwrap(),
NonNegativeRealScalar::zero()
);
assert_eq!(value.abs_squared(), NonNegativeRealScalar::zero());
}
#[test]
#[cfg(debug_assertions)]
fn abs_squared_nan() {
let value = Complex::new(f64::NAN, 0.0);
assert!(matches!(
value.try_abs_squared(),
Err(AbsComplexErrors::<Complex<f64>>::Input(..))
));
let value = Complex::new(0.0, f64::NAN);
assert!(matches!(
value.try_abs_squared(),
Err(AbsComplexErrors::<Complex<f64>>::Input(..))
));
}
#[test]
#[cfg(debug_assertions)]
fn abs_squared_infinity() {
let value = Complex::new(f64::INFINITY, 0.0);
assert!(matches!(
value.try_abs_squared(),
Err(AbsComplexErrors::<Complex<f64>>::Input(..))
));
let value = Complex::new(0.0, f64::INFINITY);
assert!(matches!(
value.try_abs_squared(),
Err(AbsComplexErrors::<Complex<f64>>::Input(..))
));
}
}
}
}
}