#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
core::traits::raw::RawScalarValidate, functions::FunctionErrors, kernels::RawScalarTrait,
};
use std::backtrace::Backtrace;
use thiserror::Error;
#[cfg(debug_assertions)]
use crate::core::errors::capture_backtrace;
#[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 RawScalarValidate>::ValidationError,
},
}
pub type ReciprocalErrors<RawScalar> = FunctionErrors<
ReciprocalInputErrors<RawScalar>,
<RawScalar as RawScalarValidate>::ValidationError,
>;
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;
}
impl<T> Reciprocal for T
where
T: RawScalarTrait,
{
type Error = ReciprocalErrors<Self>;
#[inline(always)]
fn try_reciprocal(self) -> Result<Self, <Self as Reciprocal>::Error> {
#[cfg(debug_assertions)]
{
self.validate_raw_scalar()
.map_err(|e| ReciprocalInputErrors::InvalidArgument { source: e }.into())
.and_then(|value| {
if value.is_zero() {
Err(ReciprocalInputErrors::DivisionByZero {
backtrace: capture_backtrace(),
}
.into())
} else {
value
.unchecked_reciprocal()
.validate_raw_scalar()
.map_err(|e| ReciprocalErrors::<Self>::Output { source: e })
}
})
}
#[cfg(not(debug_assertions))]
{
Ok(self.unchecked_reciprocal())
}
}
#[inline(always)]
fn reciprocal(self) -> Self {
#[cfg(debug_assertions)]
{
self.try_reciprocal().unwrap()
}
#[cfg(not(debug_assertions))]
{
self.unchecked_reciprocal()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use num::Complex;
#[cfg(debug_assertions)]
use std::assert_matches;
#[cfg(debug_assertions)]
use crate::core::errors::{ErrorsValidationRawComplex, ErrorsValidationRawReal};
#[cfg(feature = "rug")]
use try_create::TryNew;
mod reciprocal {
use super::*;
mod native {
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]
#[cfg(debug_assertions)]
fn test_f64_reciprocal_zero() {
let value = 0.0;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input(
ReciprocalInputErrors::DivisionByZero { .. }
))
));
}
#[test]
#[cfg(debug_assertions)]
fn test_f64_reciprocal_nan() {
let value = f64::NAN;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawReal::IsNaN { .. }
}
))
));
}
#[test]
#[cfg(debug_assertions)]
fn test_f64_reciprocal_subnormal() {
let value = f64::MIN_POSITIVE / 2.0;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawReal::IsSubnormal { .. }
}
))
));
}
#[test]
#[cfg(debug_assertions)]
fn test_f64_reciprocal_infinite() {
let value = f64::INFINITY;
let result = value.try_reciprocal();
assert!(matches!(
result,
Err(ReciprocalErrors::<f64>::Input(
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]
#[cfg(debug_assertions)]
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(
ReciprocalInputErrors::DivisionByZero { .. }
))
));
}
#[test]
#[cfg(debug_assertions)]
fn test_complex_f64_reciprocal_nan() {
let value = Complex::new(f64::NAN, 0.0);
let err = value.try_reciprocal().unwrap_err();
match err {
ReciprocalErrors::<Complex<f64>>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawComplex::InvalidRealPart { source },
},
) => {
assert_matches!(*source, ErrorsValidationRawReal::IsNaN { .. });
}
_ => {
unreachable!();
}
}
let value = Complex::new(0.0, f64::NAN);
let err = value.try_reciprocal().unwrap_err();
match err {
ReciprocalErrors::<Complex<f64>>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawComplex::InvalidImaginaryPart { source },
},
) => {
assert_matches!(*source, ErrorsValidationRawReal::IsNaN { .. });
}
_ => {
unreachable!();
}
}
}
#[test]
#[cfg(debug_assertions)]
fn test_complex_f64_reciprocal_infinite() {
let value = Complex::new(f64::INFINITY, 0.0);
let err = value.try_reciprocal().unwrap_err();
match err {
ReciprocalErrors::<Complex<f64>>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawComplex::InvalidRealPart { source },
},
) => {
assert_matches!(*source, ErrorsValidationRawReal::IsPosInfinity { .. });
}
_ => {
unreachable!();
}
}
let value = Complex::new(0.0, f64::INFINITY);
let err = value.try_reciprocal().unwrap_err();
match err {
ReciprocalErrors::<Complex<f64>>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawComplex::InvalidImaginaryPart { source },
},
) => {
assert_matches!(*source, ErrorsValidationRawReal::IsPosInfinity { .. });
}
_ => {
unreachable!();
}
}
}
#[test]
#[cfg(debug_assertions)]
fn test_complex_f64_reciprocal_sbnormal() {
let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
let err = value.try_reciprocal().unwrap_err();
match err {
ReciprocalErrors::<Complex<f64>>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawComplex::InvalidRealPart { source },
},
) => {
assert_matches!(*source, ErrorsValidationRawReal::IsSubnormal { .. });
}
_ => {
unreachable!();
}
}
let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
let err = value.try_reciprocal().unwrap_err();
match err {
ReciprocalErrors::<Complex<f64>>::Input(
ReciprocalInputErrors::InvalidArgument {
source: ErrorsValidationRawComplex::InvalidImaginaryPart { source },
},
) => {
assert_matches!(*source, ErrorsValidationRawReal::IsSubnormal { .. });
}
_ => {
unreachable!();
}
}
}
}
#[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(
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(
ReciprocalInputErrors::DivisionByZero { .. }
))
));
}
}
}
}
}
}