use crate::{Elementary, Error, Func, Function, Round};
pub trait Integrate {
fn integrate(&self) -> Integral;
fn evaluate_integral(&self, lower_bound: f64, upper_bound: f64) -> f64;
}
const STANDARD_PRECISION: usize = 1000;
pub struct Integral {
function: Func,
lower_bound: Option<f64>,
upper_bound: Option<f64>,
precision: usize,
}
impl Integral {
pub fn vacant(function: Func) -> Self {
Self {
function,
lower_bound: None,
upper_bound: None,
precision: STANDARD_PRECISION,
}
}
pub fn set_lower_bound(&mut self, lower_bound: f64) -> &mut Self {
self.lower_bound = Some(lower_bound);
self
}
pub fn set_upper_bound(&mut self, upper_bound: f64) -> &mut Self {
self.upper_bound = Some(upper_bound);
self
}
pub fn set_precision(&mut self, precision: usize) -> &mut Self {
self.precision = precision;
self
}
pub fn evaluate(&self) -> Result<f64, Error> {
if let (Some(lower_bound), Some(upper_bound)) = (self.lower_bound, self.upper_bound) {
Ok(simpsons_rule(
&self.function,
lower_bound,
upper_bound,
self.precision,
))
} else {
Err(Error::InternalError(String::from(
"Bounds of integration must be set in order to evaluate the integral",
)))
}
}
}
impl Integrate for Elementary {
fn integrate(&self) -> Integral {
Integral::vacant(self.clone().call())
}
fn evaluate_integral(&self, lower_bound: f64, upper_bound: f64) -> f64 {
unsafe {
let mut value = self
.integrate()
.set_lower_bound(lower_bound)
.set_upper_bound(upper_bound)
.evaluate()
.unwrap_unchecked(); value.with_significant_figures(5)
}
}
}
impl Integrate for Function {
fn integrate(&self) -> Integral {
self.elementary().integrate()
}
fn evaluate_integral(&self, lower_bound: f64, upper_bound: f64) -> f64 {
self.elementary()
.evaluate_integral(lower_bound, upper_bound)
}
}
fn simpsons_rule(funciton: &Func, lower_bound: f64, upper_bound: f64, precision: usize) -> f64 {
let n = precision * 2;
let dx = (upper_bound - lower_bound) / n as f64;
let mut sum: f64 = (1..n)
.map(|x| {
if x % 2 == 0 {
2. * funciton(lower_bound + x as f64 * dx)
} else {
4. * funciton(lower_bound + x as f64 * dx)
}
})
.sum();
sum += funciton(lower_bound) + funciton(upper_bound);
sum * dx / 3.
}