use crate::numerical_integration::integrator::*;
use crate::numerical_integration::mode::IterativeMethod;
use crate::utils::error_codes::CalcError;
pub const DEFAULT_TOTAL_ITERATIONS: u64 = 120;
#[derive(Debug, Clone, Copy)]
pub struct IterativeConfig {
pub total_iterations: u64,
pub integration_method: IterativeMethod,
}
impl Default for IterativeConfig {
fn default() -> Self {
IterativeConfig {
total_iterations: DEFAULT_TOTAL_ITERATIONS,
integration_method: IterativeMethod::Booles,
}
}
}
impl IterativeConfig {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeConfig {
total_iterations,
integration_method,
}
}
fn check_for_errors<const NUM_INTEGRATIONS: usize>(
&self,
integration_limit: &[[f64; 2]; NUM_INTEGRATIONS],
) -> Result<(), CalcError> {
if self.total_iterations == 0 {
return Err(CalcError::IterationsZero);
}
for limit in integration_limit {
classify(limit)?;
}
Ok(())
}
}
fn integrate_rule<G: FnMut(f64) -> f64>(
method: IterativeMethod,
iterations: u64,
lo: f64,
hi: f64,
g: G,
) -> f64 {
match method {
IterativeMethod::Booles => booles(iterations, lo, hi, g),
IterativeMethod::Simpsons => simpsons(iterations, lo, hi, g),
IterativeMethod::Trapezoidal => trapezoidal(iterations, lo, hi, g),
}
}
fn booles<G: FnMut(f64) -> f64>(iterations: u64, lo: f64, hi: f64, mut g: G) -> f64 {
let delta = (hi - lo) / iterations as f64;
let mut point = lo;
let mut ans = 7.0 * g(point);
let mut multiplier = 32.0;
for iter in 0..iterations - 1 {
point += delta;
ans += multiplier * g(point);
if (iter + 2) % 2 != 0 {
multiplier = 32.0;
} else if (iter + 2) % 4 == 0 {
multiplier = 14.0;
} else {
multiplier = 12.0;
}
}
ans += 7.0 * g(hi);
2.0 * delta * ans / 45.0
}
fn simpsons<G: FnMut(f64) -> f64>(iterations: u64, lo: f64, hi: f64, mut g: G) -> f64 {
let delta = (hi - lo) / iterations as f64;
let mut point = lo;
let mut ans = g(point);
let mut multiplier = 3.0;
for iter in 0..iterations - 1 {
point += delta;
ans += multiplier * g(point);
if (iter + 2) % 3 == 0 {
multiplier = 2.0;
} else {
multiplier = 3.0;
}
}
ans += g(hi);
3.0 * delta * ans / 8.0
}
fn trapezoidal<G: FnMut(f64) -> f64>(iterations: u64, lo: f64, hi: f64, mut g: G) -> f64 {
let delta = (hi - lo) / iterations as f64;
let mut point = lo;
let mut ans = g(point);
for _ in 0..iterations - 1 {
point += delta;
ans += 2.0 * g(point);
}
ans += g(hi);
0.5 * delta * ans
}
#[derive(Debug, Clone, Copy, Default)]
pub struct IterativeSingle {
pub config: IterativeConfig,
}
impl IterativeSingle {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeSingle {
config: IterativeConfig::from_parameters(total_iterations, integration_method),
}
}
fn integrate<F: Fn(f64) -> f64, const NUM_INTEGRATIONS: usize>(
&self,
level: usize,
func: &F,
integration_limit: &[[f64; 2]; NUM_INTEGRATIONS],
) -> f64 {
let method = self.config.integration_method;
let iterations = self.config.total_iterations;
let domain = match classify(&integration_limit[level - 1]) {
Ok(d) => d,
Err(_) => return f64::NAN, };
if level == 1 {
return match domain {
Domain::Finite(a, b) => integrate_rule(method, iterations, a, b, func),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (x, jacobian) = map_sample(&domain, t);
func(x) * jacobian
})
}
};
}
let inner = self.integrate(level - 1, func, integration_limit);
match domain {
Domain::Finite(a, b) => integrate_rule(method, iterations, a, b, |_| inner),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (_, jacobian) = map_sample(&domain, t);
inner * jacobian
})
}
}
}
}
impl IntegratorSingleVariable for IterativeSingle {
fn get<F: Fn(f64) -> f64, const NUM_INTEGRATIONS: usize>(
&self,
func: &F,
integration_limit: &[[f64; 2]; NUM_INTEGRATIONS],
) -> Result<f64, CalcError> {
self.config.check_for_errors(integration_limit)?;
Ok(self.integrate(NUM_INTEGRATIONS, func, integration_limit))
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct IterativeMulti {
pub config: IterativeConfig,
}
impl IterativeMulti {
pub fn from_parameters(total_iterations: u64, integration_method: IterativeMethod) -> Self {
IterativeMulti {
config: IterativeConfig::from_parameters(total_iterations, integration_method),
}
}
fn integrate<
F: Fn(&[f64; NUM_VARS]) -> f64,
const NUM_VARS: usize,
const NUM_INTEGRATIONS: usize,
>(
&self,
level: usize,
idx_to_integrate: [usize; NUM_INTEGRATIONS],
func: &F,
integration_limits: &[[f64; 2]; NUM_INTEGRATIONS],
point: &[f64; NUM_VARS],
) -> f64 {
let method = self.config.integration_method;
let iterations = self.config.total_iterations;
let domain = match classify(&integration_limits[level - 1]) {
Ok(d) => d,
Err(_) => return f64::NAN, };
let var = idx_to_integrate[level - 1];
if level == 1 {
let mut current = *point;
return match domain {
Domain::Finite(a, b) => integrate_rule(method, iterations, a, b, |x| {
current[var] = x;
func(¤t)
}),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (x, jacobian) = map_sample(&domain, t);
current[var] = x;
func(¤t) * jacobian
})
}
};
}
let mut current = *point;
match domain {
Domain::Finite(a, b) => integrate_rule(method, iterations, a, b, |x| {
current[var] = x;
self.integrate(
level - 1,
idx_to_integrate,
func,
integration_limits,
¤t,
)
}),
_ => {
let (lo, hi) = t_bounds(&domain);
integrate_rule(method, iterations, lo, hi, |t| {
let (x, jacobian) = map_sample(&domain, t);
current[var] = x;
let inner = self.integrate(
level - 1,
idx_to_integrate,
func,
integration_limits,
¤t,
);
inner * jacobian
})
}
}
}
}
impl IntegratorMultiVariable for IterativeMulti {
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> {
self.config.check_for_errors(integration_limits)?;
Ok(self.integrate(
NUM_INTEGRATIONS,
idx_to_integrate,
func,
integration_limits,
point,
))
}
}