use crate::utils::error_codes::CalcError;
use core::f64::consts::PI;
const EPSILON: f64 = f64::EPSILON;
pub(crate) enum Domain {
Finite(f64, f64),
LowerToInf(f64),
UpperToInf(f64),
BothInf,
}
pub(crate) fn classify(limit: &[f64; 2]) -> Result<Domain, 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 > 0.0 => Ok(Domain::LowerToInf(a)), (false, true) if a < 0.0 => Ok(Domain::UpperToInf(b)), (false, false) if a < 0.0 && b > 0.0 => Ok(Domain::BothInf), _ => Err(CalcError::IntegrationLimitsIllDefined),
}
}
pub(crate) fn t_bounds(d: &Domain) -> (f64, f64) {
match d {
Domain::Finite(a, b) => (*a, *b),
Domain::LowerToInf(_) => (0.0, 1.0 - EPSILON), Domain::UpperToInf(_) => (0.0, 1.0 - EPSILON), Domain::BothInf => (EPSILON, 1.0 - EPSILON),
}
}
pub(crate) fn map_sample(d: &Domain, t: f64) -> (f64, f64) {
match *d {
Domain::Finite(_, _) => (t, 1.0),
Domain::LowerToInf(a) => {
let q = 1.0 - t;
(a + t / q, 1.0 / (q * q))
}
Domain::UpperToInf(b) => {
let q = 1.0 - t;
(b - t / q, 1.0 / (q * q))
}
Domain::BothInf => {
let u = PI * (t - 0.5);
let c = libm::cos(u);
(libm::tan(u), PI / (c * c))
}
}
}
pub trait IntegratorSingleVariable {
fn get<F: Fn(f64) -> f64, const NUM_INTEGRATIONS: usize>(
&self,
func: &F,
integration_limit: &[[f64; 2]; NUM_INTEGRATIONS],
) -> Result<f64, CalcError>;
fn get_single<F: Fn(f64) -> f64>(
&self,
func: &F,
integration_limit: &[f64; 2],
) -> Result<f64, CalcError> {
self.get(func, &[*integration_limit])
}
fn get_double<F: Fn(f64) -> f64>(
&self,
func: &F,
integration_limit: &[[f64; 2]; 2],
) -> Result<f64, CalcError> {
self.get(func, integration_limit)
}
}
pub trait IntegratorMultiVariable {
fn get<F: Fn(&[f64; NUM_VARS]) -> f64, const NUM_VARS: usize, const NUM_INTEGRATIONS: usize>(
&self,
idx_to_integrate: [usize; NUM_INTEGRATIONS],
func: &F,
integration_limits: &[[f64; 2]; NUM_INTEGRATIONS],
point: &[f64; NUM_VARS],
) -> Result<f64, CalcError>;
fn get_single_partial<F: Fn(&[f64; NUM_VARS]) -> f64, const NUM_VARS: usize>(
&self,
func: &F,
idx_to_integrate: usize,
integration_limits: &[f64; 2],
point: &[f64; NUM_VARS],
) -> Result<f64, CalcError> {
self.get([idx_to_integrate], func, &[*integration_limits], point)
}
fn get_double_partial<F: Fn(&[f64; NUM_VARS]) -> f64, const NUM_VARS: usize>(
&self,
func: &F,
idx_to_integrate: [usize; 2],
integration_limits: &[[f64; 2]; 2],
point: &[f64; NUM_VARS],
) -> Result<f64, CalcError> {
self.get(idx_to_integrate, func, integration_limits, point)
}
}