ndhistogram/axis/
uniformnoflow.rs

1use crate::error::AxisError;
2
3use super::{Axis, BinInterval, Uniform};
4use std::fmt::{Debug, Display};
5
6use num_traits::{Float, Num, NumCast, NumOps};
7
8/// An axis with equal sized bins and no under/overflow bins.
9///
10/// An axis with N equally spaced, equal sized, bins between [low, high).
11/// Similar to [Uniform] but this axis has no over/underflow bins.
12/// Hence it has N bins.
13///
14/// For floating point types, infinities and NaN do not map to any bin.
15///
16/// # Example
17/// Create a 1D histogram with 10 uniformly spaced bins between -5.0 and 5.0.
18/// ```rust
19///    use ndhistogram::{ndhistogram, Histogram};
20///    use ndhistogram::axis::{Axis, UniformNoFlow, BinInterval};
21///    # fn main() -> Result<(), ndhistogram::Error> {
22///    let hist = ndhistogram!(UniformNoFlow::new(10, -5.0, 5.0)?);
23///    let axis = &hist.axes().as_tuple().0;
24///    assert_eq!(axis.bin(0), Some(BinInterval::new(-5.0, -4.0)));
25///    assert_eq!(axis.bin(10), None);
26///    # Ok(()) }
27/// ```
28#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct UniformNoFlow<T = f64> {
31    axis: Uniform<T>,
32}
33
34impl<T> UniformNoFlow<T>
35where
36    T: PartialOrd + NumCast + NumOps + Copy,
37{
38    /// Factory method to create an axis with num uniformly spaced bins in the range [low, high) with no under/overflow bins.
39    ///
40    /// The parameters have the same constraints as [Uniform::new], otherwise an error is returned.
41    pub fn new(num: usize, low: T, high: T) -> Result<Self, AxisError>
42    where
43        T: Float,
44    {
45        Ok(Self {
46            axis: Uniform::new(num, low, high)?,
47        })
48    }
49
50    /// Factory method to create an axis with num uniformly spaced bins in the range [low, low+num*step) with no under/overflow bins.
51    ///
52    /// The parameters have the same constraints as [Uniform::with_step_size], otherwise an error is returned.
53    pub fn with_step_size(num: usize, low: T, step: T) -> Result<Self, AxisError>
54    where
55        T: Num,
56    {
57        Ok(Self {
58            axis: Uniform::with_step_size(num, low, step)?,
59        })
60    }
61}
62
63impl<T> UniformNoFlow<T> {
64    /// Return the lowest bin edge.
65    pub fn low(&self) -> &T {
66        self.axis.low()
67    }
68
69    /// Return the highest bin edge.
70    pub fn high(&self) -> &T {
71        self.axis.high()
72    }
73}
74
75impl<T> Axis for UniformNoFlow<T>
76where
77    T: PartialOrd + NumCast + NumOps + Copy,
78{
79    type Coordinate = T;
80    type BinInterval = BinInterval<T>;
81
82    #[inline]
83    fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
84        let index = self.axis.index(coordinate)?;
85        if index == 0 || index + 1 == self.axis.num_bins() {
86            return None;
87        }
88        Some(index - 1)
89    }
90
91    fn num_bins(&self) -> usize {
92        self.axis.num_bins() - 2
93    }
94
95    fn bin(&self, index: usize) -> Option<Self::BinInterval> {
96        let bin = self.axis.bin(index + 1)?;
97        match bin {
98            BinInterval::Underflow { end: _ } => None,
99            BinInterval::Overflow { start: _ } => None,
100            BinInterval::Bin { start: _, end: _ } => Some(bin),
101        }
102    }
103}
104
105impl<'a, T> IntoIterator for &'a UniformNoFlow<T>
106where
107    T: PartialOrd + NumCast + NumOps + Copy,
108{
109    type Item = (usize, <Uniform<T> as Axis>::BinInterval);
110    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
111
112    fn into_iter(self) -> Self::IntoIter {
113        self.iter()
114    }
115}
116
117impl<T> Display for UniformNoFlow<T>
118where
119    T: PartialOrd + NumCast + NumOps + Copy + Display,
120{
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(
123            f,
124            "Axis{{# bins={}, range=[{}, {}), class={}}}",
125            self.num_bins(),
126            self.axis.low(),
127            self.axis.high(),
128            stringify!(UniformNoFlow)
129        )
130    }
131}