use super::scratch::SgsScratch;
use crate::foundation::Lattice;
use crate::geostat::local_kriging::{simple_kriging_with, SkNeighbour};
use crate::geostat::neighbourhood::Neighbourhood;
use crate::geostat::nscore::NormalScore;
use crate::gridding::kriging::SpatialVariogram;
use crate::sampling::seeded_rng;
use ndarray::Array2;
use rand::RngExt;
use rand_distr::{Distribution, Normal};
#[allow(clippy::too_many_arguments)]
pub(super) fn simulate_scores(
lattice: &Lattice,
variogram: &SpatialVariogram,
max_neighbours: usize,
radius: f64,
secondary: Option<&(Array2<f64>, f64)>,
seed: u64,
fixed: &[(usize, usize, f64)],
scratch: &mut SgsScratch,
) -> Array2<f64> {
let (ncol, nrow) = (lattice.ncol, lattice.nrow);
let mut sim = Array2::from_elem((ncol, nrow), f64::NAN);
scratch.inf_pos.clear();
scratch.inf_val.clear();
scratch.path.clear();
scratch.nb = Neighbourhood::new();
for &(i, j, score) in fixed {
if sim[[i, j]].is_nan() {
let (x, y) = lattice.node_xy(i, j);
scratch.inf_pos.push([x, y]);
scratch.inf_val.push(score);
scratch.nb.insert([x, y], scratch.inf_pos.len() - 1);
}
sim[[i, j]] = score;
}
for j in 0..nrow {
for i in 0..ncol {
if sim[[i, j]].is_nan() {
scratch.path.push((i, j));
}
}
}
let mut rng = seeded_rng(seed);
for k in (1..scratch.path.len()).rev() {
let swap = rng.random_range(0..=k);
scratch.path.swap(k, swap);
}
let standard_normal = Normal::new(0.0, 1.0).expect("standard normal");
for idx in 0..scratch.path.len() {
let (i, j) = scratch.path[idx];
let (x, y) = lattice.node_xy(i, j);
scratch
.nb
.nearest_into([x, y], max_neighbours, radius, &mut scratch.near);
scratch.neighbours.clear();
for &(pidx, _) in &scratch.near {
let pos = scratch.inf_pos[pidx];
scratch.neighbours.push(SkNeighbour {
pos,
value: scratch.inf_val[pidx],
offset_to_target: [pos[0] - x, pos[1] - y],
});
}
let collocated = secondary.and_then(|(sec, rho)| {
let s = sec[[i, j]];
if s.is_finite() {
Some((s, *rho))
} else {
None
}
});
let (mean, var) =
simple_kriging_with(&scratch.neighbours, variogram, collocated, &mut scratch.sk);
let score = mean + var.sqrt() * standard_normal.sample(&mut rng);
sim[[i, j]] = score;
scratch.inf_pos.push([x, y]);
scratch.inf_val.push(score);
scratch.nb.insert([x, y], scratch.inf_pos.len() - 1);
}
sim
}
pub(super) fn snap_fixed(
coords: &[[f64; 3]],
lattice: &Lattice,
ns: &NormalScore,
) -> Vec<(usize, usize, f64)> {
let (ncol, nrow) = (lattice.ncol, lattice.nrow);
let mut fixed: Vec<(usize, usize, f64)> = Vec::new();
for c in coords {
if let Some((fi, fj)) = lattice.xy_to_ij(c[0], c[1]) {
let (i, j) = (fi.round(), fj.round());
if i < 0.0 || j < 0.0 {
continue;
}
let (i, j) = (i as usize, j as usize);
if i >= ncol || j >= nrow {
continue;
}
fixed.push((i, j, ns.forward(c[2])));
}
}
fixed
}