use scirs2_core::ndarray::{Array1, Array2};
use scirs2_interpolate::resampling::{
resample_scattered_to_grid, Aggregator, GridSpec, ResampleStrategy,
};
#[test]
fn resample_rasterize_mean_on_known_grid() {
let pts = Array2::from_shape_vec((4, 2), vec![0.0_f64, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0])
.expect("shape");
let vals = Array1::from_vec(vec![0.0_f64, 1.0, 2.0, 3.0]);
let grid = GridSpec::uniform(2, &[(0.0, 1.0, 2), (0.0, 1.0, 2)]);
let out = resample_scattered_to_grid(
&pts,
&vals,
&grid,
ResampleStrategy::Rasterize(Aggregator::Mean),
)
.expect("resample");
assert_eq!(out.shape(), &[2, 2]);
assert!(
(out[[0, 0].as_ref()] - 0.0).abs() < 1e-10,
"cell (0,0): {}",
out[[0, 0].as_ref()]
);
assert!(
(out[[1, 0].as_ref()] - 1.0).abs() < 1e-10,
"cell (1,0): {}",
out[[1, 0].as_ref()]
);
assert!(
(out[[0, 1].as_ref()] - 2.0).abs() < 1e-10,
"cell (0,1): {}",
out[[0, 1].as_ref()]
);
assert!(
(out[[1, 1].as_ref()] - 3.0).abs() < 1e-10,
"cell (1,1): {}",
out[[1, 1].as_ref()]
);
}
#[test]
fn resample_handles_empty_cells() {
let pts = Array2::from_shape_vec((1, 2), vec![0.0_f64, 0.0]).expect("shape");
let vals = Array1::from_vec(vec![42.0_f64]);
let grid = GridSpec::uniform(2, &[(0.0, 2.0, 3), (0.0, 2.0, 3)]);
let out = resample_scattered_to_grid(
&pts,
&vals,
&grid,
ResampleStrategy::Rasterize(Aggregator::Mean),
)
.expect("resample");
assert_eq!(out.shape(), &[3, 3]);
let first = out[[0, 0].as_ref()];
assert!(
(first - 42.0).abs() < 1e-10,
"cell (0,0) should be 42, got {first}"
);
let other = out[[2, 2].as_ref()];
assert!(other.is_nan(), "empty cell should be NaN, got {other}");
}
#[test]
fn resample_1d_mean_matches_analytic() {
let n = 100_usize;
let xs: Vec<f64> = (0..n).map(|i| i as f64 / (n - 1) as f64).collect();
let ys: Vec<f64> = xs.clone();
let pts = Array2::from_shape_vec((n, 1), xs).expect("shape");
let vals = Array1::from_vec(ys);
let n_cells = 5_usize;
let grid = GridSpec::uniform(1, &[(0.0, 1.0, n_cells)]);
let out = resample_scattered_to_grid(
&pts,
&vals,
&grid,
ResampleStrategy::Rasterize(Aggregator::Mean),
)
.expect("resample");
assert_eq!(out.shape(), &[n_cells]);
for i in 0..n_cells {
let cell_centre = i as f64 / (n_cells - 1) as f64;
let val = out[[i].as_ref()];
assert!(
val.is_finite(),
"cell {i} (centre={cell_centre}) is not finite: {val}"
);
assert!(
(val - cell_centre).abs() < 0.15,
"cell {i} (centre={cell_centre}) mean={val}, expected ~{cell_centre}"
);
}
}
#[test]
fn resample_conservative_matches_mean_for_uniform() {
let pts = Array2::from_shape_vec((4, 2), vec![0.0_f64, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0])
.expect("shape");
let vals = Array1::from_vec(vec![1.0_f64; 4]);
let grid = GridSpec::uniform(2, &[(0.0, 1.0, 2), (0.0, 1.0, 2)]);
let mean_out = resample_scattered_to_grid(
&pts,
&vals,
&grid,
ResampleStrategy::Rasterize(Aggregator::Mean),
)
.expect("mean resample");
let cons_out = resample_scattered_to_grid(&pts, &vals, &grid, ResampleStrategy::Conservative)
.expect("conservative resample");
assert_eq!(mean_out.shape(), cons_out.shape());
for (m, c) in mean_out.iter().zip(cons_out.iter()) {
assert!(
(m - c).abs() < 1e-10 || (m.is_nan() && c.is_nan()),
"Mean={m}, Conservative={c} should match for uniform single-point-per-cell"
);
}
}
#[test]
fn resample_count_aggregator() {
let pts =
Array2::from_shape_vec((6, 1), vec![0.0_f64, 0.1, 0.2, 1.0, 1.0, 1.0]).expect("shape");
let vals = Array1::from_vec(vec![1.0_f64; 6]);
let grid = GridSpec::uniform(1, &[(0.0, 1.0, 2)]);
let out = resample_scattered_to_grid(
&pts,
&vals,
&grid,
ResampleStrategy::Rasterize(Aggregator::Count),
)
.expect("count resample");
assert_eq!(out.shape(), &[2]);
let c0 = out[[0].as_ref()];
let c1 = out[[1].as_ref()];
assert!((c0 - 3.0).abs() < 1e-10, "cell 0 count = {c0}, expected 3");
assert!((c1 - 3.0).abs() < 1e-10, "cell 1 count = {c1}, expected 3");
}