1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum InvalidRangeError {
4 NotEnoughRanges,
6 NotSorted,
9 NaN,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct SampleOutOfRangeError;
16
17#[doc(hidden)]
18#[macro_export]
19macro_rules! define_histogram_common {
20 ($LEN:expr) => {
21 use $crate::Histogram as Trait;
22
23 const LEN: usize = $LEN;
25
26 impl ::core::fmt::Debug for Histogram {
27 fn fmt(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
28 formatter.write_str("Histogram {{ range: ")?;
29 self.range[..].fmt(formatter)?;
30 formatter.write_str(", bins: ")?;
31 self.bin[..].fmt(formatter)?;
32 formatter.write_str(" }}")
33 }
34 }
35
36 impl Histogram {
37 #[inline]
39 pub fn with_const_width(start: f64, end: f64) -> Self {
40 let step = (end - start) / (LEN as f64);
41 let mut range = [0.; LEN + 1];
42 for (i, r) in range.iter_mut().enumerate() {
43 *r = start + step * (i as f64);
44 }
45
46 Self {
47 range,
48 bin: [0; LEN],
49 }
50 }
51
52 #[inline]
61 pub fn from_ranges<T>(ranges: T) -> Result<Self, $crate::InvalidRangeError>
62 where
63 T: IntoIterator<Item = f64>,
64 {
65 let mut range = [0.; LEN + 1];
66 let mut last_i = 0;
67 for (i, r) in ranges.into_iter().enumerate() {
68 if i > LEN {
69 break;
70 }
71 if r.is_nan() {
72 return Err($crate::InvalidRangeError::NaN);
73 }
74 if i > 0 && range[i - 1] > r {
75 return Err($crate::InvalidRangeError::NotSorted);
76 }
77 range[i] = r;
78 last_i = i;
79 }
80 if last_i != LEN {
81 return Err($crate::InvalidRangeError::NotEnoughRanges);
82 }
83 Ok(Self {
84 range,
85 bin: [0; LEN],
86 })
87 }
88
89 #[inline]
93 pub fn find(&self, x: f64) -> Result<usize, $crate::SampleOutOfRangeError> {
94 match self.range.binary_search_by(|p| p.partial_cmp(&x).unwrap()) {
97 Ok(i) if i < LEN => Ok(i),
98 Err(i) if i > 0 && i < LEN + 1 => Ok(i - 1),
99 _ => Err($crate::SampleOutOfRangeError),
100 }
101 }
102
103 #[inline]
107 pub fn add(&mut self, x: f64) -> Result<(), $crate::SampleOutOfRangeError> {
108 if let Ok(i) = self.find(x) {
109 self.bin[i] += 1;
110 Ok(())
111 } else {
112 Err($crate::SampleOutOfRangeError)
113 }
114 }
115
116 #[inline]
118 pub fn ranges(&self) -> &[f64] {
119 &self.range[..]
120 }
121
122 #[inline]
125 pub fn iter(&self) -> IterHistogram<'_> {
126 self.into_iter()
127 }
128
129 #[inline]
131 pub fn reset(&mut self) {
132 self.bin = [0; LEN];
133 }
134
135 #[inline]
139 pub fn range_min(&self) -> f64 {
140 self.range[0]
141 }
142
143 #[inline]
147 pub fn range_max(&self) -> f64 {
148 self.range[LEN]
149 }
150 }
151
152 #[derive(Debug, Clone)]
154 pub struct IterHistogram<'a> {
155 remaining_bin: &'a [u64],
156 remaining_range: &'a [f64],
157 }
158
159 impl<'a> ::core::iter::Iterator for IterHistogram<'a> {
160 type Item = ((f64, f64), u64);
161 fn next(&mut self) -> Option<((f64, f64), u64)> {
162 if let Some((&bin, rest)) = self.remaining_bin.split_first() {
163 let left = self.remaining_range[0];
164 let right = self.remaining_range[1];
165 self.remaining_bin = rest;
166 self.remaining_range = &self.remaining_range[1..];
167 return Some(((left, right), bin));
168 }
169 None
170 }
171 }
172
173 impl<'a> ::core::iter::IntoIterator for &'a Histogram {
174 type Item = ((f64, f64), u64);
175 type IntoIter = IterHistogram<'a>;
176 fn into_iter(self) -> IterHistogram<'a> {
177 IterHistogram {
178 remaining_bin: self.bins(),
179 remaining_range: self.ranges(),
180 }
181 }
182 }
183
184 impl $crate::Histogram for Histogram {
185 #[inline]
186 fn bins(&self) -> &[u64] {
187 &self.bin[..]
188 }
189 }
190
191 impl<'a> ::core::ops::AddAssign<&'a Self> for Histogram {
192 #[inline]
193 fn add_assign(&mut self, other: &Self) {
194 for (a, b) in self.range.iter().zip(other.range.iter()) {
195 assert_eq!(a, b, "Both histograms must have the same ranges");
196 }
197 for (x, y) in self.bin.iter_mut().zip(other.bin.iter()) {
198 *x += y;
199 }
200 }
201 }
202
203 impl ::core::ops::MulAssign<u64> for Histogram {
204 #[inline]
205 fn mul_assign(&mut self, other: u64) {
206 for x in &mut self.bin[..] {
207 *x *= other;
208 }
209 }
210 }
211
212 impl $crate::Merge for Histogram {
213 fn merge(&mut self, other: &Self) {
214 assert_eq!(self.bin.len(), other.bin.len());
215 for (a, b) in self.range.iter().zip(other.range.iter()) {
216 assert_eq!(a, b, "Both histograms must have the same ranges");
217 }
218 for (a, b) in self.bin.iter_mut().zip(other.bin.iter()) {
219 *a += *b;
220 }
221 }
222 }
223 };
224}
225
226#[cfg(feature = "serde")]
227#[doc(hidden)]
228#[macro_export]
229macro_rules! define_histogram_inner {
230 ($name:ident, $LEN:expr) => {
231 mod $name {
232 $crate::define_histogram_common!($LEN);
233
234 use ::serde::{Deserialize, Serialize};
235 use serde_big_array::BigArray;
236
237 #[derive(Clone, Serialize, Deserialize)]
239 pub struct Histogram {
240 #[serde(with = "BigArray")]
242 range: [f64; LEN + 1],
243 #[serde(with = "BigArray")]
245 bin: [u64; LEN],
246 }
247 }
248 };
249}
250
251#[cfg(not(feature = "serde"))]
252#[doc(hidden)]
253#[macro_export]
254macro_rules! define_histogram_inner {
255 ($name:ident, $LEN:expr) => {
256 mod $name {
257 $crate::define_histogram_common!($LEN);
258
259 #[derive(Clone)]
261 pub struct Histogram {
262 range: [f64; LEN + 1],
264 bin: [u64; LEN],
266 }
267 }
268 };
269}
270
271#[macro_export]
293macro_rules! define_histogram {
294 ($name:ident, $LEN:expr) => {
295 $crate::define_histogram_inner!($name, $LEN);
296 };
297}