1use crate::percentiles;
21
22#[derive(Clone, Copy)]
26pub struct SubMsSamples<'a> {
27 raw: &'a [u64],
28}
29
30impl<'a> SubMsSamples<'a> {
31 pub fn new(raw: &'a [u64]) -> Self {
33 Self { raw }
34 }
35
36 pub fn count(&self) -> usize {
38 self.raw.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.raw.is_empty()
44 }
45
46 pub fn raw(&self) -> &'a [u64] {
48 self.raw
49 }
50
51 pub fn p50(&self) -> u64 {
53 self.percentile(0.50)
54 }
55
56 pub fn p90(&self) -> u64 {
58 self.percentile(0.90)
59 }
60
61 pub fn p99(&self) -> u64 {
63 self.percentile(0.99)
64 }
65
66 pub fn p999(&self) -> u64 {
68 self.percentile(0.999)
69 }
70
71 pub fn max(&self) -> u64 {
73 self.raw.iter().copied().max().unwrap_or(0)
74 }
75
76 pub fn mean(&self) -> u64 {
78 percentiles::mean(self.raw)
79 }
80
81 pub fn stddev(&self) -> u64 {
83 percentiles::stddev(self.raw)
84 }
85
86 pub fn percentile(&self, q: f64) -> u64 {
88 let mut sorted = self.raw.to_vec();
89 sorted.sort_unstable();
90 percentiles::percentile(&sorted, q)
91 }
92
93 pub fn percentile_sweep(&self, start: f64, end: f64, step: f64) -> Vec<(f64, u64)> {
95 percentiles::percentile_sweep(self.raw, start, end, step)
96 }
97
98 #[cfg(feature = "histogram")]
101 pub fn cdf_buckets(&self) -> Vec<u64> {
102 crate::histogram::cdf_buckets(self.raw)
103 }
104
105 #[cfg(feature = "jitter")]
108 pub fn jitter_score(&self) -> f64 {
109 crate::jitter::jitter_score(self.raw)
110 }
111
112 #[cfg(feature = "tail")]
114 pub fn conditional_tail_expectation(&self, q: f64) -> u64 {
115 crate::tail::conditional_tail_expectation(self.raw, q)
116 }
117
118 #[cfg(feature = "tail")]
120 pub fn tail_fatness_ratio(&self) -> f64 {
121 crate::tail::tail_fatness_ratio(self.raw)
122 }
123
124 #[cfg(feature = "tail")]
126 pub fn hill_tail_index(&self, k: usize) -> Option<f64> {
127 crate::tail::hill_tail_index(self.raw, k)
128 }
129
130 #[cfg(feature = "robust")]
132 pub fn iqr(&self) -> u64 {
133 crate::robust::iqr(self.raw)
134 }
135
136 #[cfg(feature = "robust")]
138 pub fn median_absolute_deviation(&self) -> u64 {
139 crate::robust::median_absolute_deviation(self.raw)
140 }
141
142 #[cfg(feature = "robust")]
144 pub fn coefficient_of_variation(&self) -> f64 {
145 crate::robust::coefficient_of_variation(self.raw)
146 }
147
148 #[cfg(feature = "robust")]
150 pub fn skewness(&self) -> f64 {
151 crate::robust::skewness(self.raw)
152 }
153
154 #[cfg(feature = "robust")]
156 pub fn kurtosis(&self) -> f64 {
157 crate::robust::kurtosis(self.raw)
158 }
159
160 #[cfg(feature = "bootstrap")]
162 pub fn bootstrap_percentile_ci(
163 &self,
164 q: f64,
165 iters: usize,
166 confidence: f64,
167 seed: u64,
168 ) -> (u64, u64) {
169 crate::bootstrap::bootstrap_percentile_ci(self.raw, q, iters, confidence, seed)
170 }
171}
172
173impl<'a> From<&'a [u64]> for SubMsSamples<'a> {
174 fn from(raw: &'a [u64]) -> Self {
175 Self::new(raw)
176 }
177}
178
179impl<'a> From<&'a Vec<u64>> for SubMsSamples<'a> {
180 fn from(raw: &'a Vec<u64>) -> Self {
181 Self::new(raw.as_slice())
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn empty_samples_returns_zero() {
191 let raw: Vec<u64> = Vec::new();
192 let s = SubMsSamples::new(&raw);
193 assert_eq!(s.count(), 0);
194 assert!(s.is_empty());
195 assert_eq!(s.p99(), 0);
196 assert_eq!(s.mean(), 0);
197 assert_eq!(s.stddev(), 0);
198 assert_eq!(s.max(), 0);
199 }
200
201 #[test]
202 fn known_distribution_percentiles() {
203 let raw: Vec<u64> = (0..100).collect();
204 let s = SubMsSamples::new(&raw);
205 assert_eq!(s.count(), 100);
206 assert_eq!(s.p50(), 50);
207 assert_eq!(s.p99(), 99);
208 assert_eq!(s.max(), 99);
209 }
210
211 #[test]
212 fn from_slice_and_vec_both_work() {
213 let v: Vec<u64> = vec![100, 200, 300];
214 let by_slice: SubMsSamples<'_> = (&v[..]).into();
215 let by_vec: SubMsSamples<'_> = (&v).into();
216 assert_eq!(by_slice.p50(), by_vec.p50());
217 }
218}