use crate::scalar::Numeric;
use crate::utils::error_codes::CalcError;
pub(crate) enum Domain<T: Numeric> {
Finite(T, T),
LowerToInf(T),
UpperToInf(T),
BothInf,
}
pub(crate) fn classify<T: Numeric>(limit: &[T; 2]) -> Result<Domain<T>, CalcError> {
let (a, b) = (limit[0], limit[1]);
if a.is_nan() || b.is_nan() {
return Err(CalcError::IntegrationLimitsIllDefined);
}
match (a.is_finite(), b.is_finite()) {
(true, true) if a < b => Ok(Domain::Finite(a, b)),
(true, false) if b > T::ZERO => Ok(Domain::LowerToInf(a)), (false, true) if a < T::ZERO => Ok(Domain::UpperToInf(b)), (false, false) if a < T::ZERO && b > T::ZERO => Ok(Domain::BothInf), _ => Err(CalcError::IntegrationLimitsIllDefined),
}
}
pub(crate) fn t_bounds<T: Numeric>(d: &Domain<T>) -> (T, T) {
match d {
Domain::Finite(a, b) => (*a, *b),
Domain::LowerToInf(_) => (T::ZERO, T::ONE - T::EPSILON), Domain::UpperToInf(_) => (T::ZERO, T::ONE - T::EPSILON), Domain::BothInf => (T::EPSILON, T::ONE - T::EPSILON),
}
}
pub(crate) fn map_sample<T: Numeric>(d: &Domain<T>, t: T) -> (T, T) {
match *d {
Domain::Finite(_, _) => (t, T::ONE),
Domain::LowerToInf(a) => {
let q = T::ONE - t;
(a + t / q, T::ONE / (q * q))
}
Domain::UpperToInf(b) => {
let q = T::ONE - t;
(b - t / q, T::ONE / (q * q))
}
Domain::BothInf => {
let u = T::PI * (t - T::HALF);
let c = u.cos();
(u.tan(), T::PI / (c * c))
}
}
}
pub trait IntegratorSingleVariable {
type Scalar: Numeric;
fn get<F: Fn(Self::Scalar) -> Self::Scalar, const NUM_INTEGRATIONS: usize>(
&self,
func: &F,
integration_limit: &[[Self::Scalar; 2]; NUM_INTEGRATIONS],
) -> Result<Self::Scalar, CalcError>;
fn get_single<F: Fn(Self::Scalar) -> Self::Scalar>(
&self,
func: &F,
integration_limit: &[Self::Scalar; 2],
) -> Result<Self::Scalar, CalcError> {
self.get(func, &[*integration_limit])
}
fn get_double<F: Fn(Self::Scalar) -> Self::Scalar>(
&self,
func: &F,
integration_limit: &[[Self::Scalar; 2]; 2],
) -> Result<Self::Scalar, CalcError> {
self.get(func, integration_limit)
}
}
pub trait IntegratorMultiVariable {
type Scalar: Numeric;
fn get<
F: Fn(&[Self::Scalar; NUM_VARS]) -> Self::Scalar,
const NUM_VARS: usize,
const NUM_INTEGRATIONS: usize,
>(
&self,
idx_to_integrate: [usize; NUM_INTEGRATIONS],
func: &F,
integration_limits: &[[Self::Scalar; 2]; NUM_INTEGRATIONS],
point: &[Self::Scalar; NUM_VARS],
) -> Result<Self::Scalar, CalcError>;
fn get_single_partial<
F: Fn(&[Self::Scalar; NUM_VARS]) -> Self::Scalar,
const NUM_VARS: usize,
>(
&self,
func: &F,
idx_to_integrate: usize,
integration_limits: &[Self::Scalar; 2],
point: &[Self::Scalar; NUM_VARS],
) -> Result<Self::Scalar, CalcError> {
self.get([idx_to_integrate], func, &[*integration_limits], point)
}
fn get_double_partial<
F: Fn(&[Self::Scalar; NUM_VARS]) -> Self::Scalar,
const NUM_VARS: usize,
>(
&self,
func: &F,
idx_to_integrate: [usize; 2],
integration_limits: &[[Self::Scalar; 2]; 2],
point: &[Self::Scalar; NUM_VARS],
) -> Result<Self::Scalar, CalcError> {
self.get(idx_to_integrate, func, integration_limits, point)
}
}