use super::{squared_euclidean_distance, validate_dimension_consistency, validate_non_empty};
use crate::error::GaError;
pub fn spread(approx_front: &[Vec<f64>], extreme_points: &[Vec<f64>]) -> Result<f64, GaError> {
validate_non_empty("approx_front", approx_front)?;
validate_non_empty("extreme_points", extreme_points)?;
let dim = validate_dimension_consistency(approx_front)?;
let _ = validate_dimension_consistency(extreme_points)?;
if extreme_points[0].len() != dim {
return Err(GaError::InvalidIndicatorConfiguration(format!(
"Dimension mismatch: approx_front has {} dimensions, extreme_points have {}",
dim,
extreme_points[0].len(),
)));
}
let n = approx_front.len();
if n < 2 {
return Err(GaError::InvalidIndicatorConfiguration(
"Spread requires at least 2 points in approx_front".to_string(),
));
}
let mut sorted: Vec<&[f64]> = approx_front.iter().map(|v| v.as_slice()).collect();
sorted.sort_by(|a, b| a[0].partial_cmp(&b[0]).unwrap_or(std::cmp::Ordering::Equal));
let mut d = Vec::with_capacity(n - 1);
for i in 0..(n - 1) {
let dist = squared_euclidean_distance(sorted[i], sorted[i + 1]).sqrt();
d.push(dist);
}
let d_bar: f64 = d.iter().sum::<f64>() / d.len() as f64;
let df = extreme_points
.iter()
.map(|ep| squared_euclidean_distance(ep, sorted[0]).sqrt())
.fold(f64::INFINITY, f64::min);
let dl = extreme_points
.iter()
.map(|ep| squared_euclidean_distance(ep, sorted[n - 1]).sqrt())
.fold(f64::INFINITY, f64::min);
let sum_deviation: f64 = d.iter().map(|di| (di - d_bar).abs()).sum();
let numerator = df + dl + sum_deviation;
let denominator = df + dl + (n as f64 - 1.0) * d_bar;
if denominator == 0.0 {
Ok(0.0) } else {
Ok(numerator / denominator)
}
}