1pub fn quantile_from_sorted(sorted: &[f64], q: f64) -> f64 {
3 let n = sorted.len();
4 if n == 0 {
5 return f64::NAN;
6 }
7 if n == 1 {
8 return sorted[0];
9 }
10 let pos = q.clamp(0.0, 1.0) * (n - 1) as f64;
11 let lo = pos.floor() as usize;
12 let hi = (lo + 1).min(n - 1);
13 let frac = pos - lo as f64;
14 sorted[lo] * (1.0 - frac) + sorted[hi] * frac
15}
16
17pub fn order_statistic_from_sorted(sorted: &[f64], rank: usize) -> f64 {
24 if sorted.is_empty() || rank == 0 || rank > sorted.len() {
25 return f64::NAN;
26 }
27 sorted[rank - 1]
28}
29
30pub fn order_statistic(values: &[f64], rank: usize) -> f64 {
35 let mut sorted = values.to_vec();
36 sorted.sort_by(f64::total_cmp);
37 order_statistic_from_sorted(&sorted, rank)
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn order_statistic_returns_nan_on_empty() {
46 assert!(order_statistic(&[], 1).is_nan());
47 assert!(order_statistic_from_sorted(&[], 1).is_nan());
48 }
49
50 #[test]
51 fn order_statistic_returns_nan_on_zero_rank() {
52 let v = [3.0, 1.0, 2.0];
53 assert!(order_statistic(&v, 0).is_nan());
54 let sorted = [1.0, 2.0, 3.0];
55 assert!(order_statistic_from_sorted(&sorted, 0).is_nan());
56 }
57
58 #[test]
59 fn order_statistic_returns_nan_when_rank_exceeds_len() {
60 let v = [3.0, 1.0, 2.0];
61 assert!(order_statistic(&v, 4).is_nan());
62 let sorted = [1.0, 2.0, 3.0];
63 assert!(order_statistic_from_sorted(&sorted, 4).is_nan());
64 }
65
66 #[test]
67 fn order_statistic_hits_mid_rank() {
68 let v = [5.0, 1.0, 4.0, 2.0, 3.0];
70 assert_eq!(order_statistic(&v, 3), 3.0);
71 let sorted = [1.0, 2.0, 3.0, 4.0, 5.0];
72 assert_eq!(order_statistic_from_sorted(&sorted, 3), 3.0);
73 assert_eq!(order_statistic(&v, 1), 1.0);
75 assert_eq!(order_statistic(&v, 5), 5.0);
76 }
77}