use robust_rs_core::error::RobustError;
use robust_rs_core::rho::RhoFunction;
use robust_rs_core::scale::ScaleEstimator;
use robust_rs_core::solver::Control;
use robust_rs_core::types::Scale;
#[derive(Debug, Clone, Copy)]
pub struct LocationFit {
pub estimate: f64,
pub scale: Scale,
pub iters: usize,
}
pub fn m_location(
data: &[f64],
rho: &dyn RhoFunction,
scale: &dyn ScaleEstimator,
ctrl: &Control,
) -> Result<LocationFit, RobustError> {
if data.is_empty() {
return Err(RobustError::InsufficientData { needed: 1, got: 0 });
}
let scale_est = scale.scale(data)?;
let s: f64 = scale_est.get();
let mut buf = data.to_vec();
let mut theta = median(&mut buf);
for i in 1..=ctrl.max_iter {
let (mut sw, mut swx) = (0.0_f64, 0.0_f64);
for &x in data {
let w = rho.weight((x - theta) / s);
sw += w;
swx += w * x;
}
let next = swx / sw;
if !next.is_finite() {
return Err(RobustError::SingularDesign);
}
if (next - theta).abs() <= ctrl.tol * s {
return Ok(LocationFit {
estimate: next,
scale: scale_est,
iters: i,
});
}
theta = next;
}
Err(RobustError::NonConvergence {
iters: ctrl.max_iter,
})
}
fn median(v: &mut [f64]) -> f64 {
v.sort_unstable_by(f64::total_cmp);
let n = v.len();
let mid = n / 2;
if n % 2 == 1 {
v[mid]
} else {
0.5 * (v[mid - 1] + v[mid])
}
}