#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
core::policies::StrictFinitePolicy, functions::FunctionErrors, kernels::RawScalarTrait,
};
use duplicate::duplicate_item;
use num::Complex;
use thiserror::Error;
use try_create::ValidationPolicy;
#[derive(Debug, Error)]
pub enum ExpInputErrors<RawScalar: RawScalarTrait> {
#[error("the input exponent is invalid according to validation policy")]
InvalidExponent {
#[source]
#[backtrace]
source: <RawScalar as RawScalarTrait>::ValidationErrors,
},
}
pub type ExpErrors<RawScalar> =
FunctionErrors<ExpInputErrors<RawScalar>, <RawScalar as RawScalarTrait>::ValidationErrors>;
pub trait Exp: Sized {
type Error: std::error::Error;
#[must_use = "this `Result` may contain an error that should be handled"]
fn try_exp(self) -> Result<Self, <Self as Exp>::Error>;
fn exp(self) -> Self;
}
#[duplicate_item(
T;
[f64];
[Complex::<f64>];
)]
impl Exp for T {
type Error = ExpErrors<Self>;
#[inline(always)]
fn try_exp(self) -> Result<Self, Self::Error> {
StrictFinitePolicy::<Self, 53>::validate(self)
.map_err(|e| ExpInputErrors::InvalidExponent { source: e }.into())
.and_then(|v| {
StrictFinitePolicy::<Self, 53>::validate(T::exp(v))
.map_err(|e| Self::Error::Output { source: e })
})
}
#[inline(always)]
fn exp(self) -> Self {
#[cfg(debug_assertions)]
{
self.try_exp().unwrap()
}
#[cfg(not(debug_assertions))]
{
T::exp(self)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use num::Complex;
mod native64 {
use super::*;
mod real {
use super::*;
#[test]
fn test_f64_exp_valid() {
let value = 4.0;
let expected_result = 54.598150033144236;
assert_eq!(value.try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
#[test]
fn test_f64_exp_negative() {
let value = -4.0;
let expected_result = 1.831563888873418e-02;
assert_eq!(value.try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
#[test]
fn test_f64_exp_zero() {
let value = 0.0;
assert_eq!(value.try_exp().unwrap(), 1.0);
assert_eq!(value.exp(), 1.0);
}
#[test]
fn test_f64_exp_nan() {
let value = f64::NAN;
let result = value.try_exp();
assert!(matches!(result, Err(ExpErrors::<f64>::Input { .. })));
}
#[test]
fn test_f64_exp_infinity() {
let value = f64::INFINITY;
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<f64>::Input { .. })
));
}
#[test]
fn test_f64_exp_subnormal() {
let value = f64::MIN_POSITIVE / 2.0;
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<f64>::Input { .. })
));
}
#[test]
fn test_f64_exp_output_overflow() {
let value = 710.0; let result = value.try_exp();
assert!(matches!(result, Err(ExpErrors::<f64>::Output { .. })));
}
}
mod complex {
use super::*;
#[test]
fn test_complex_f64_exp_valid() {
let value = Complex::new(4.0, 1.0);
let expected_result = Complex::new(29.49950635904248, 45.94275907707917);
assert_eq!(value.try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
#[test]
fn test_complex_f64_exp_zero() {
let value = Complex::new(0.0, 0.0);
let expected_result = Complex::new(1.0, 0.0);
assert_eq!(value.try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
#[test]
fn test_complex_f64_exp_nan() {
let value = Complex::new(f64::NAN, 0.0);
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<Complex<f64>>::Input { .. })
));
let value = Complex::new(0.0, f64::NAN);
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<Complex<f64>>::Input { .. })
));
}
#[test]
fn test_complex_f64_exp_infinity() {
let value = Complex::new(f64::INFINITY, 0.0);
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<Complex<f64>>::Input { .. })
));
let value = Complex::new(0.0, f64::INFINITY);
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<Complex<f64>>::Input { .. })
));
}
#[test]
fn test_complex_f64_exp_subnormal() {
let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<Complex<f64>>::Input { .. })
));
let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
assert!(matches!(
value.try_exp(),
Err(ExpErrors::<Complex<f64>>::Input { .. })
));
}
#[test]
fn test_complex_f64_exp_output_overflow_real() {
let value = Complex::new(710.0, 0.0); let result = value.try_exp();
assert!(matches!(
result,
Err(ExpErrors::<Complex<f64>>::Output { .. })
));
}
}
}
#[cfg(feature = "rug")]
mod rug53 {
use super::*;
use crate::backends::rug::validated::{ComplexRugStrictFinite, RealRugStrictFinite};
use try_create::TryNew;
mod real {
use super::*;
#[test]
fn test_rug_float_exp_valid() {
let value =
RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, -4.0)).unwrap();
let expected_result = RealRugStrictFinite::<53>::try_new(rug::Float::with_val(
53,
1.831563888873418e-2,
))
.unwrap();
assert_eq!(value.clone().try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
#[test]
fn test_rug_float_exp_zero() {
let value =
RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
let expected_result =
RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 1.0)).unwrap();
assert_eq!(value.clone().try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
}
mod complex {
use super::*;
#[test]
fn test_complex_rug_float_exp_valid() {
let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
53,
(rug::Float::with_val(53, 4.0), rug::Float::with_val(53, 1.0)),
))
.unwrap();
let expected_result =
ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
53,
(
rug::Float::with_val(53, 29.49950635904248),
rug::Float::with_val(53, 45.94275907707917),
),
))
.unwrap();
assert_eq!(value.clone().try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
#[test]
fn test_complex_rug_float_exp_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();
let expected_result =
ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
53,
(rug::Float::with_val(53, 1.), rug::Float::with_val(53, 0.)),
))
.unwrap();
assert_eq!(value.clone().try_exp().unwrap(), expected_result);
assert_eq!(value.exp(), expected_result);
}
}
}
}