#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
core::{errors::capture_backtrace, policies::StrictFinitePolicy},
functions::FunctionErrors,
kernels::RawScalarTrait,
};
use duplicate::duplicate_item;
use num::Complex;
use std::backtrace::Backtrace;
use thiserror::Error;
use try_create::ValidationPolicy;
#[derive(Debug, Error)]
pub enum ReciprocalInputErrors<RawScalar: RawScalarTrait> {
#[error("division by zero!")]
DivisionByZero {
backtrace: Backtrace,
},
#[error("the input value is invalid according to validation policy")]
InvalidArgument {
#[source]
#[backtrace]
source: <RawScalar as RawScalarTrait>::ValidationErrors,
},
}
pub type ReciprocalErrors<RawScalar> = FunctionErrors<
ReciprocalInputErrors<RawScalar>,
<RawScalar as RawScalarTrait>::ValidationErrors,
>;
pub trait Reciprocal: Sized {
type Error: std::error::Error;
#[must_use = "this `Result` may contain an error that should be handled"]
fn try_reciprocal(self) -> Result<Self, <Self as Reciprocal>::Error>;
fn reciprocal(self) -> Self;
}
#[duplicate_item(
T implementation trait_comment;
[f64] [recip()] ["Implementation of the [`Reciprocal`] trait for [`f64`]."];
[Complex::<f64>] [inv()] ["Implementation of the [`Reciprocal`] trait for [`Complex<f64>`]."];
)]
impl Reciprocal for T {
type Error = ReciprocalErrors<Self>;
#[inline(always)]
fn try_reciprocal(self) -> Result<Self, <Self as Reciprocal>::Error> {
StrictFinitePolicy::<T, 53>::validate(self)
.map_err(|e| ReciprocalInputErrors::InvalidArgument { source: e }.into())
.and_then(|value| {
if RawScalarTrait::is_zero(&value) {
Err(ReciprocalInputErrors::DivisionByZero {
backtrace: capture_backtrace(),
}
.into())
} else {
StrictFinitePolicy::<T, 53>::validate(value.implementation)
.map_err(|e| ReciprocalErrors::<Self>::Output { source: e })
}
})
}
#[inline(always)]
fn reciprocal(self) -> Self {
#[cfg(debug_assertions)]
{
self.try_reciprocal().unwrap()
}
#[cfg(not(debug_assertions))]
{
self.implementation
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::errors::{ErrorsValidationRawComplex, ErrorsValidationRawReal};
use num::Complex;
#[cfg(feature = "rug")]
use try_create::TryNew;
mod reciprocal {
use super::*;
mod native64 {
use super::*;
mod real {
use super::*;
#[test]
fn test_f64_reciprocal_valid() {
let value = 4.0;
assert_eq!(value.try_reciprocal().unwrap(), 0.25);
assert_eq!(value.reciprocal(), 0.25);
}
#[test]
fn test_f64_reciprocal_zero() {
let value = 0.0;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input {
source: ReciprocalInputErrors::DivisionByZero { .. }
})
));
}
#[test]
fn test_f64_reciprocal_nan() {
let value = f64::NAN;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input {
source: ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawReal::IsNaN { .. }
}
})
));
}
#[test]
fn test_f64_reciprocal_subnormal() {
let value = f64::MIN_POSITIVE / 2.0;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input {
source: ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawReal::IsSubnormal { .. }
}
})
));
}
#[test]
fn test_f64_reciprocal_infinite() {
let value = f64::INFINITY;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input {
source: ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawReal::IsPosInfinity { .. }
}
})
));
}
}
mod complex {
use super::*;
#[test]
fn test_complex_f64_reciprocal_valid() {
let value = Complex::new(4.0, 0.0);
assert_eq!(value.try_reciprocal().unwrap(), Complex::new(0.25, 0.0));
assert_eq!(value.reciprocal(), Complex::new(0.25, 0.0));
}
#[test]
fn test_complex_f64_reciprocal_zero() {
let value = Complex::new(0.0, 0.0);
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<Complex<f64>>::Input {
source: ReciprocalInputErrors::DivisionByZero { .. }
})
));
}
#[test]
fn test_complex_f64_reciprocal_nan() {
let value = Complex::new(f64::NAN, 0.0);
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::<Complex<f64>>::Input {
source:
ReciprocalInputErrors::InvalidArgument {
source:
ErrorsValidationRawComplex::InvalidRealPart {
source: box ErrorsValidationRawReal::IsNaN { .. },
},
},
})
));
let value = Complex::new(0.0, f64::NAN);
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::<Complex<f64>>::Input {
source:
ReciprocalInputErrors::InvalidArgument {
source:
ErrorsValidationRawComplex::InvalidImaginaryPart {
source: box ErrorsValidationRawReal::IsNaN { .. },
},
},
})
));
}
#[test]
fn test_complex_f64_reciprocal_infinite() {
let value = Complex::new(f64::INFINITY, 0.0);
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::<Complex<f64>>::Input {
source:
ReciprocalInputErrors::InvalidArgument {
source:
ErrorsValidationRawComplex::InvalidRealPart {
source:
box ErrorsValidationRawReal::IsPosInfinity { .. },
},
},
})
));
let value = Complex::new(0.0, f64::INFINITY);
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::<Complex<f64>>::Input {
source:
ReciprocalInputErrors::InvalidArgument {
source:
ErrorsValidationRawComplex::InvalidImaginaryPart {
source:
box ErrorsValidationRawReal::IsPosInfinity { .. },
},
},
})
));
}
#[test]
fn test_complex_f64_reciprocal_sbnormal() {
let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::<Complex<f64>>::Input {
source:
ReciprocalInputErrors::InvalidArgument {
source:
ErrorsValidationRawComplex::InvalidRealPart {
source: box ErrorsValidationRawReal::IsSubnormal { .. },
},
},
})
));
let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::<Complex<f64>>::Input {
source:
ReciprocalInputErrors::InvalidArgument {
source:
ErrorsValidationRawComplex::InvalidImaginaryPart {
source: box ErrorsValidationRawReal::IsSubnormal { .. },
},
},
})
));
}
}
}
#[cfg(feature = "rug")]
mod rug53 {
use super::*;
use crate::backends::rug::validated::{ComplexRugStrictFinite, RealRugStrictFinite};
mod real {
use super::*;
#[test]
fn test_rug_float_reciprocal_valid() {
let value =
RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 4.0)).unwrap();
let result = value.try_reciprocal();
assert!(result.is_ok());
}
#[test]
fn test_rug_float_reciprocal_zero() {
let value =
RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::Input {
source: ReciprocalInputErrors::DivisionByZero { .. }
})
));
}
}
mod complex {
use super::*;
#[test]
fn test_complex_rug_float_reciprocal_valid() {
let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
53,
(rug::Float::with_val(53, 4.0), rug::Float::with_val(53, 0.0)),
))
.unwrap();
let expected_result =
ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
53,
(
rug::Float::with_val(53, 0.25),
rug::Float::with_val(53, 0.0),
),
))
.unwrap();
assert_eq!(value.clone().try_reciprocal().unwrap(), expected_result);
assert_eq!(value.reciprocal(), expected_result);
}
#[test]
fn test_complex_rug_float_reciprocal_zero() {
let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
53,
(rug::Float::with_val(53, 0.0), rug::Float::with_val(53, 0.0)),
))
.unwrap();
assert!(matches!(
value.try_reciprocal(),
Err(ReciprocalErrors::Input {
source: ReciprocalInputErrors::DivisionByZero { .. }
})
));
}
}
}
}
}