use super::*;
use crate::{
chernoff::ApplyChernoffExt,
grid::Grid1D,
grid_nd::{GridFnND, GridND},
shift_nd::AnisotropicShiftChernoffND,
ChernoffFunction,
};
type GradAFn2 = alloc::boxed::Box<dyn Fn(&[f64; 2]) -> [f64; 2] + Send + Sync>;
type GradAVec2 = alloc::vec::Vec<GradAFn2>;
fn make_base_kernel(n: usize) -> AnisotropicShiftChernoffND<f64, 2> {
let ax = Grid1D::new(-3.0_f64, 3.0, n).unwrap();
let grid = GridND::<f64, 2>::new([ax, ax]).unwrap();
AnisotropicShiftChernoffND::new(
|_x, a| {
a.set(0, 0, 1.0);
a.set(1, 1, 1.0);
a.set(0, 1, 0.0);
a.set(1, 0, 0.0);
},
|_x, b| {
b[0] = 0.0;
b[1] = 0.0;
},
|_x| 0.0_f64,
grid,
)
.unwrap()
}
fn make_zeta2_zero_grad(n: usize) -> AnisotropicShiftZeta2ND<f64, 2> {
let base = make_base_kernel(n);
let a_ij = |_x: &[f64; 2], a: &mut SquareMatrix<f64, 2>| {
a.set(0, 0, 1.0);
a.set(1, 1, 1.0);
a.set(0, 1, 0.0);
a.set(1, 0, 0.0);
};
let grad_a: GradAVec2 = (0..4)
.map(|_| -> GradAFn2 { alloc::boxed::Box::new(|_x: &[f64; 2]| [0.0_f64; 2]) })
.collect();
AnisotropicShiftZeta2ND::new(base, a_ij, grad_a).unwrap()
}
#[test]
fn order_is_2() {
let kernel = make_zeta2_zero_grad(8);
assert_eq!(kernel.order(), 2, "order() must be 2 for ζ²-corrected kernel");
}
#[test]
fn growth_inherits_from_inner() {
let kernel = make_zeta2_zero_grad(8);
let g = kernel.growth();
assert!(
(g.multiplier - 1.5).abs() < 1e-14,
"expected multiplier=1.5, got {}",
g.multiplier
);
assert!(g.omega.abs() < 1e-14, "expected omega=0, got {}", g.omega);
}
#[test]
fn wrong_grad_a_len_returns_err() {
let base = make_base_kernel(8);
let grad_a: GradAVec2 = (0..3)
.map(|_| -> GradAFn2 { alloc::boxed::Box::new(|_x: &[f64; 2]| [0.0_f64; 2]) })
.collect();
let result = AnisotropicShiftZeta2ND::<f64, 2>::new(
base,
|_x, _a| {},
grad_a,
);
assert!(result.is_err(), "expected Err for wrong grad_a.len()");
}
#[test]
fn grid_matches_inner() {
let base = make_base_kernel(8);
let inner_n = base.grid().len();
let kernel = make_zeta2_zero_grad(8);
assert_eq!(kernel.grid().len(), inner_n);
}
#[test]
fn small_tau_output_is_finite() {
let kernel = make_zeta2_zero_grad(8);
let grid = kernel.grid().clone();
let src = GridFnND::from_fn(grid, |x| x[0].sin() + x[1].cos());
let dst = kernel.apply_chernoff(0.001_f64, &src).unwrap();
for v in &dst.values {
assert!(v.is_finite(), "non-finite output: {v}");
}
}
#[test]
fn constant_datum_gives_finite_output() {
let kernel = make_zeta2_zero_grad(8);
let grid = kernel.grid().clone();
let src = GridFnND::from_fn(grid, |_| 2.0_f64);
let dst = kernel.apply_chernoff(0.1_f64, &src).unwrap();
for v in &dst.values {
assert!(v.is_finite(), "non-finite output: {v}");
}
}