stratifykit_core/
coverage.rs1pub fn bucket_floor(value: u32, bucket_size: u32) -> u32 {
4 (value / bucket_size) * bucket_size
5}
6
7pub fn mean_of(counts: &[usize]) -> f64 {
8 if counts.is_empty() {
9 0.0
10 } else {
11 counts.iter().sum::<usize>() as f64 / counts.len() as f64
12 }
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum BucketStatus {
20 Missing,
21 Under,
22 Ok,
23 Over,
24}
25
26impl std::fmt::Display for BucketStatus {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 f.write_str(match self {
29 BucketStatus::Missing => "MISSING",
30 BucketStatus::Under => "UNDER",
31 BucketStatus::Ok => "OK",
32 BucketStatus::Over => "OVER",
33 })
34 }
35}
36
37pub fn classify_bucket(count: usize, mean: f64, under_ratio: f64, over_ratio: f64) -> BucketStatus {
40 if count == 0 {
41 return BucketStatus::Missing;
42 }
43 let count = count as f64;
44 if count < under_ratio * mean {
45 BucketStatus::Under
46 } else if count > over_ratio * mean {
47 BucketStatus::Over
48 } else {
49 BucketStatus::Ok
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn bucket_floor_rounds_down_to_multiple() {
59 assert_eq!(bucket_floor(45, 10), 40);
60 assert_eq!(bucket_floor(40, 10), 40);
61 assert_eq!(bucket_floor(9, 10), 0);
62 }
63
64 #[test]
65 fn mean_of_empty_is_zero() {
66 assert_eq!(mean_of(&[]), 0.0);
67 }
68
69 #[test]
70 fn mean_of_averages() {
71 assert_eq!(mean_of(&[2, 4, 6]), 4.0);
72 }
73
74 #[test]
75 fn classify_bucket_flags_missing_under_over_ok() {
76 assert_eq!(classify_bucket(0, 10.0, 0.5, 2.0), BucketStatus::Missing);
77 assert_eq!(classify_bucket(2, 10.0, 0.5, 2.0), BucketStatus::Under);
78 assert_eq!(classify_bucket(25, 10.0, 0.5, 2.0), BucketStatus::Over);
79 assert_eq!(classify_bucket(10, 10.0, 0.5, 2.0), BucketStatus::Ok);
80 }
81}