numerical-integral 1.0.0

A library for evaluating integrals using numerical methods
Documentation
mod closed;
mod open;

use closed::closed_newton_cotes;
use itertools::Itertools;
use open::open_newton_cotes;
use rayon::iter::{IntoParallelIterator, ParallelBridge, ParallelIterator};

#[derive(Debug, Clone, Copy)]
pub enum PointType {
    Open,
    Closed,
}

pub fn newton_cotes(
    a: f64,
    b: f64,
    f: impl Fn(f64) -> f64 + Send + Sync + Copy,
    n: usize,
    point_type: PointType,
) -> f64 {
    match point_type {
        PointType::Open => open_newton_cotes(a, b, f, n),
        PointType::Closed => closed_newton_cotes(a, b, f, n),
    }
}

pub fn composite_newton_cotes(
    a: f64,
    b: f64,
    f: impl Fn(f64) -> f64 + Send + Sync + Copy,
    n: usize,
    point_type: PointType,
    partition_count: usize,
) -> f64 {
    if a == b {
        return 0.;
    }

    let step = (b - a) / partition_count as f64;
    (0..partition_count + 1)
        .map(|i| a + step * i as f64)
        .tuple_windows::<(_, _)>()
        .par_bridge()
        .map(|(a_i, b_i)| newton_cotes(a_i, b_i, f, n, point_type))
        .sum()
}

pub fn adaptive_newton_cotes(
    a: f64,
    b: f64,
    f: impl Fn(f64) -> f64 + Send + Sync + Copy,
    n: usize,
    point_type: PointType,
    error: f64,
) -> f64 {
    assert!(error > 0.);

    if a == b {
        return 0.;
    }

    let total = newton_cotes(a, b, f, n, point_type);
    let mid_point = (a + b) / 2.;
    let left = newton_cotes(a, mid_point, f, n, point_type);
    let right = newton_cotes(mid_point, b, f, n, point_type);

    let delta = left + right - total;

    if delta.abs() < error {
        left + right
    } else {
        [(a, mid_point), (mid_point, b)]
            .into_par_iter()
            .map(|(left_point, right_point)| {
                adaptive_newton_cotes(left_point, right_point, f, n, point_type, error / 2.)
            })
            .sum()
    }
}