use std::f64::consts::{E, FRAC_PI_2, PI};
use peroxide::fuga::*;
const TOL: f64 = 1e-10;
fn assert_close(actual: f64, expected: f64) {
assert!(
(actual - expected).abs() < TOL,
"actual = {:.17e}, expected = {:.17e}, diff = {:.3e}",
actual,
expected,
(actual - expected).abs()
);
}
fn gk_methods(tol: f64) -> [Integral; 6] {
[
G7K15R(tol, 20),
G10K21R(tol, 20),
G15K31R(tol, 20),
G20K41R(tol, 20),
G25K51R(tol, 20),
G30K61R(tol, 20),
]
}
#[test]
fn test_polynomial() {
for m in gk_methods(1e-8) {
let r: f64 = integrate(|x: f64| x.powi(3), (0.0, 1.0), m);
assert_close(r, 0.25);
}
}
#[test]
fn test_trig() {
for m in gk_methods(1e-8) {
let r: f64 = integrate(|x: f64| x.cos(), (0.0, 1.0), m);
assert_close(r, 1f64.sin());
}
for m in gk_methods(1e-8) {
let r: f64 = integrate(|x: f64| x.sin(), (0.0, PI), m);
assert_close(r, 2.0);
}
}
#[test]
fn test_exponential() {
for m in gk_methods(1e-8) {
let r: f64 = integrate(|x: f64| x.exp(), (0.0, 2.0), m);
assert_close(r, E * E - 1.0);
}
}
#[test]
fn test_gaussian() {
let expected = 0.746_824_132_812_427_f64;
for m in gk_methods(1e-10) {
let r: f64 = integrate(|x: f64| (-x * x).exp(), (0.0, 1.0), m);
assert_close(r, expected);
}
}
#[test]
fn test_rational_symmetric() {
for m in gk_methods(1e-10) {
let r: f64 = integrate(|x: f64| 1.0 / (1.0 + x * x), (-1.0, 1.0), m);
assert_close(r, FRAC_PI_2);
}
}
#[test]
fn test_oscillatory() {
let abs_methods = [
G7K15(1e-10, 20),
G10K21(1e-10, 20),
G15K31(1e-10, 20),
G20K41(1e-10, 20),
G25K51(1e-10, 20),
G30K61(1e-10, 20),
];
for m in abs_methods {
let r: f64 = integrate(|x: f64| (5.0 * x).sin(), (0.0, 2.0 * PI), m);
assert_close(r, 0.0);
}
}
#[test]
fn test_early_exit_on_low_degree_polynomials() {
use std::cell::Cell;
let methods = [
(G7K15(1e-8, 20), 15),
(G10K21(1e-8, 20), 21),
(G15K31(1e-8, 20), 31),
(G20K41(1e-8, 20), 41),
(G25K51(1e-8, 20), 51),
(G30K61(1e-8, 20), 61),
(G7K15R(1e-8, 20), 15),
(G10K21R(1e-8, 20), 21),
(G15K31R(1e-8, 20), 31),
(G20K41R(1e-8, 20), 41),
(G25K51R(1e-8, 20), 51),
(G30K61R(1e-8, 20), 61),
];
for (m, k) in methods {
let count = Cell::new(0u32);
let f = |x: f64| {
count.set(count.get() + 1);
x.powi(3)
};
let r: f64 = integrate(f, (0.0, 1.0), m);
assert_close(r, 0.25);
assert_eq!(
count.get(),
k,
"{:?} did not early-exit on a cubic: {} evaluations instead of {}",
m,
count.get(),
k
);
}
}
#[test]
fn test_optimized_matches_reference_gauss_kronrod() {
let f = |x: f64| (2.0 * x).sin() * (-x).exp();
for m in gk_methods(1e-10) {
let opt: f64 = gauss_kronrod_quadrature_optimized(f, (0.0, 2.0), m);
let reference: f64 = gauss_kronrod_quadrature(f, (0.0, 2.0), m);
assert!(
(opt - reference).abs() < 1e-9,
"{:?}: optimized = {:.17e}, reference = {:.17e}",
m,
opt,
reference
);
}
}
#[test]
fn test_endpoint_independence() {
let m = || G10K21R(1e-10, 20);
let f = |x: f64| (-x).exp() + x.sin();
let full: f64 = integrate(f, (0.0, 3.0), m());
let left: f64 = integrate(f, (0.0, 1.7), m());
let right: f64 = integrate(f, (1.7, 3.0), m());
assert_close(full, left + right);
}