use numra_core::Scalar;
use crate::adaptive::{quad, QuadOptions, QuadResult};
use crate::error::IntegrationError;
pub fn dblquad<S, F, G1, G2>(
f: F,
xa: S,
xb: S,
ya: G1,
yb: G2,
opts: &QuadOptions<S>,
) -> Result<QuadResult<S>, IntegrationError>
where
S: Scalar,
F: Fn(S, S) -> S,
G1: Fn(S) -> S,
G2: Fn(S) -> S,
{
let mut total_inner_evals = 0usize;
let outer_result = quad(
|x: S| {
let y_lo = ya(x);
let y_hi = yb(x);
match quad(|y: S| f(x, y), y_lo, y_hi, opts) {
Ok(inner) => {
total_inner_evals += inner.n_evaluations;
inner.value
}
Err(_) => S::NAN,
}
},
xa,
xb,
opts,
)?;
Ok(QuadResult {
value: outer_result.value,
error_estimate: outer_result.error_estimate,
n_evaluations: outer_result.n_evaluations + total_inner_evals,
n_subdivisions: outer_result.n_subdivisions,
})
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_dblquad_unit_square() {
let result = dblquad(
|_x: f64, _y: f64| 1.0,
0.0,
1.0,
|_| 0.0,
|_| 1.0,
&QuadOptions::default(),
)
.unwrap();
assert_relative_eq!(result.value, 1.0, epsilon = 1e-10);
}
#[test]
fn test_dblquad_xy() {
let result = dblquad(
|x: f64, y: f64| x * y,
0.0,
1.0,
|_| 0.0,
|_| 1.0,
&QuadOptions::default(),
)
.unwrap();
assert_relative_eq!(result.value, 0.25, epsilon = 1e-10);
}
#[test]
fn test_dblquad_triangular_region() {
let result = dblquad(
|_x: f64, _y: f64| 1.0,
0.0,
1.0,
|_| 0.0,
|x| x,
&QuadOptions::default(),
)
.unwrap();
assert_relative_eq!(result.value, 0.5, epsilon = 1e-10);
}
#[test]
fn test_dblquad_x_plus_y() {
let result = dblquad(
|x: f64, y: f64| x + y,
0.0,
1.0,
|_| 0.0,
|_| 1.0,
&QuadOptions::default(),
)
.unwrap();
assert_relative_eq!(result.value, 1.0, epsilon = 1e-10);
}
}