flat/histogram/
api.rs

1use std::ops::{Add, Sub};
2// We use this in the doc strings.
3#[allow(unused_imports)]
4use super::Histogram;
5
6/// Render configuration specific to [`Histogram`]s.
7///
8/// ### Example
9/// ```
10/// # use flat::HistogramConfig;
11/// let histogram_config = HistogramConfig {
12///     ..HistogramConfig::default()
13/// };
14/// ```
15#[derive(Debug, Default)]
16pub struct HistogramConfig {}
17
18/// Allows a type `T` to be used as the *primary* dimension of a [`Histogram`].
19/// Consumers may choose to implement this to bin non-standard types in a histogram.
20pub trait Binnable: PartialEq + PartialOrd + Add + Sub + Sized {
21    /// Multiply this value (`self: T`) by the `rhs: usize`, resulting in another `T`.
22    ///
23    /// **Notice**: For *whole* types `T` (types that belong to ℤ) the resulting value **must** be rounded up.
24    /// In other words, implement `multiply` using `ceil` for integer types.
25    fn multiply(&self, rhs: usize) -> Self;
26
27    /// Divide this value (`self: T`) by the `rhs: usize`, resulting in another `T`.
28    ///
29    /// **Notice**: For *whole* types `T` (types that belong to ℤ) the resulting value **must** be rounded up.
30    /// In other words, implement `divide` using `ceil` for integer types.
31    fn divide(&self, rhs: usize) -> Self;
32}
33
34macro_rules! impl_binnable {
35    ($T:ty) => {
36        impl Binnable for $T {
37            fn multiply(&self, rhs: usize) -> Self {
38                self * (rhs as $T)
39            }
40
41            fn divide(&self, rhs: usize) -> Self {
42                self / (rhs as $T)
43            }
44        }
45    };
46}
47
48impl_binnable!(f64);
49impl_binnable!(f32);
50
51macro_rules! impl_ceil_binnable {
52    ($T:ty) => {
53        impl Binnable for $T {
54            fn multiply(&self, rhs: usize) -> Self {
55                (*self as f64 * (rhs as f64)).ceil() as $T
56            }
57
58            fn divide(&self, rhs: usize) -> Self {
59                (*self as f64 / (rhs as f64)).ceil() as $T
60            }
61        }
62    };
63}
64
65impl_ceil_binnable!(isize);
66impl_ceil_binnable!(i64);
67impl_ceil_binnable!(i32);
68impl_ceil_binnable!(i16);
69impl_ceil_binnable!(i8);
70impl_ceil_binnable!(usize);
71impl_ceil_binnable!(u64);
72impl_ceil_binnable!(u32);
73impl_ceil_binnable!(u16);
74impl_ceil_binnable!(u8);
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn binnable_f64() {
82        assert_eq!(0.0f64.multiply(0), 0.0);
83        assert_eq!(1.0f64.multiply(0), 0.0);
84        assert_eq!(1.5f64.multiply(0), 0.0);
85
86        assert_eq!(0.0f64.multiply(1), 0.0);
87        assert_eq!(1.0f64.multiply(1), 1.0);
88        assert_eq!(1.5f64.multiply(1), 1.5);
89
90        assert_eq!(0.0f64.divide(1), 0.0);
91        assert_eq!(1.0f64.divide(1), 1.0);
92        assert_eq!(1.5f64.divide(1), 1.5);
93
94        assert_eq!(0.0f64.divide(2), 0.0);
95        assert_eq!(1.0f64.divide(2), 0.5);
96        assert_eq!(1.5f64.divide(2), 0.75);
97    }
98
99    #[test]
100    fn binnable_i64() {
101        assert_eq!(0i64.multiply(0), 0);
102        assert_eq!(1i64.multiply(0), 0);
103        assert_eq!(2i64.multiply(0), 0);
104
105        assert_eq!(0i64.multiply(1), 0);
106        assert_eq!(1i64.multiply(1), 1);
107        assert_eq!(2i64.multiply(1), 2);
108
109        assert_eq!(0i64.divide(1), 0);
110        assert_eq!(1i64.divide(1), 1);
111        assert_eq!(2i64.divide(1), 2);
112
113        assert_eq!(0i64.divide(2), 0);
114        assert_eq!(1i64.divide(2), 1);
115        assert_eq!(2i64.divide(2), 1);
116    }
117}