use itertools::Itertools;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use ultraviolet::DVec3;
use crate::spatial_database::coordinate_system::CoordinateSystem;
use crate::spatial_database::group_provider::GroupProvider;
use crate::{geometry::ellipsoid::Ellipsoid, spatial_database::ConditioningProvider};
use super::ConditioningParams;
#[allow(clippy::too_many_arguments)]
pub fn estimate(
conditioning_data: &impl ConditioningProvider<Data = f64>,
conditioning_params: &ConditioningParams,
coordinate_system: &CoordinateSystem,
scaling: DVec3,
search_ellipsoid: &Ellipsoid,
groups: &GroupProvider,
power: f64,
) -> Vec<f64> {
(0..groups.n_groups())
.into_par_iter()
.map_with(
(coordinate_system, search_ellipsoid.clone()),
|(cs, ellipsoid), group_idx| {
let group = groups.get_group(group_idx);
let center = group.iter().fold(DVec3::zero(), |mut acc, x| {
acc += x.center();
acc
}) / (group.len() as f64);
ellipsoid.translate_to(center);
let (_, cond_values, cond_points, sufficiently_conditioned) =
conditioning_data.query(¢er, ellipsoid, conditioning_params);
if sufficiently_conditioned {
let weights = group
.iter()
.cartesian_product(cond_points.iter())
.map(|(node, cond_point)| {
let distance = node.center() - cond_point.center();
let distance = cs.into_local().rotation * distance;
let distance = distance / scaling;
let distance = distance.mag();
distance.powf(power).recip()
})
.collect::<Vec<_>>();
weights
.chunks(cond_values.len())
.map(|chunk| {
chunk
.iter()
.zip(cond_values.iter())
.map(|(w, v)| w * v)
.sum::<f64>()
/ chunk.iter().sum::<f64>()
})
.collect()
} else {
vec![f64::NAN; group.len()]
}
},
)
.flatten()
.collect::<Vec<_>>()
}