robust_rs_core/scale/
sn.rs1use crate::error::RobustError;
4use crate::scale::ScaleEstimator;
5use crate::types::Scale;
6
7#[derive(Debug, Clone, Copy)]
17pub struct Sn {
18 pub consistency: f64,
20 pub finite_sample_correction: bool,
22}
23
24impl Default for Sn {
25 fn default() -> Self {
26 Self {
27 consistency: 1.1926,
28 finite_sample_correction: true,
29 }
30 }
31}
32
33impl ScaleEstimator for Sn {
34 fn scale(&self, residuals: &[f64]) -> Result<Scale, RobustError> {
35 let n = residuals.len();
36 if n < 2 {
37 return Err(RobustError::InsufficientData { needed: 2, got: n });
38 }
39
40 let mut inner = vec![0.0_f64; n];
41 let mut a = vec![0.0_f64; n];
42 for (i, &ri) in residuals.iter().enumerate() {
43 for (j, &rj) in residuals.iter().enumerate() {
44 inner[j] = (ri - rj).abs();
45 }
46 let (_, hm, _) = inner.select_nth_unstable_by(n / 2, f64::total_cmp);
48 a[i] = *hm;
49 }
50 let (_, lm, _) = a.select_nth_unstable_by((n - 1) / 2, f64::total_cmp);
52 let med = *lm;
53
54 let dn = if self.finite_sample_correction {
55 sn_correction(n)
56 } else {
57 1.0
58 };
59 let s = self.consistency * dn * med;
60 if !s.is_finite() || s <= 0.0 {
61 return Err(RobustError::DegenerateScale);
62 }
63 Scale::new(s)
64 }
65}
66
67fn sn_correction(n: usize) -> f64 {
70 const SMALL: [f64; 8] = [0.743, 1.851, 0.954, 1.351, 0.993, 1.198, 1.005, 1.131];
71 if n <= 9 {
72 SMALL[n - 2]
73 } else if n % 2 == 1 {
74 1.0
75 } else {
76 n as f64 / (n as f64 - 0.9)
77 }
78}