ndhistogram/axis/
uniformnoflow.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::error::AxisError;

use super::{Axis, BinInterval, Uniform};
use std::fmt::{Debug, Display};

use num_traits::{Float, Num, NumCast, NumOps};

/// An axis with equal sized bins and no under/overflow bins.
///
/// An axis with N equally spaced, equal sized, bins between [low, high).
/// Similar to [Uniform] but this axis has no over/underflow bins.
/// Hence it has N bins.
///
/// For floating point types, infinities and NaN do not map to any bin.
///
/// # Example
/// Create a 1D histogram with 10 uniformly spaced bins between -5.0 and 5.0.
/// ```rust
///    use ndhistogram::{ndhistogram, Histogram};
///    use ndhistogram::axis::{Axis, UniformNoFlow, BinInterval};
///    # fn main() -> Result<(), ndhistogram::Error> {
///    let hist = ndhistogram!(UniformNoFlow::new(10, -5.0, 5.0)?);
///    let axis = &hist.axes().as_tuple().0;
///    assert_eq!(axis.bin(0), Some(BinInterval::new(-5.0, -4.0)));
///    assert_eq!(axis.bin(10), None);
///    # Ok(()) }
/// ```
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UniformNoFlow<T = f64> {
    axis: Uniform<T>,
}

impl<T> UniformNoFlow<T>
where
    T: PartialOrd + NumCast + NumOps + Copy,
{
    /// Factory method to create an axis with num uniformly spaced bins in the range [low, high) with no under/overflow bins.
    ///
    /// The parameters have the same constraints as [Uniform::new], otherwise an error is returned.
    pub fn new(num: usize, low: T, high: T) -> Result<Self, AxisError>
    where
        T: Float,
    {
        Ok(Self {
            axis: Uniform::new(num, low, high)?,
        })
    }

    /// Factory method to create an axis with num uniformly spaced bins in the range [low, low+num*step) with no under/overflow bins.
    ///
    /// The parameters have the same constraints as [Uniform::with_step_size], otherwise an error is returned.
    pub fn with_step_size(num: usize, low: T, step: T) -> Result<Self, AxisError>
    where
        T: Num,
    {
        Ok(Self {
            axis: Uniform::with_step_size(num, low, step)?,
        })
    }
}

impl<T> UniformNoFlow<T> {
    /// Return the lowest bin edge.
    pub fn low(&self) -> &T {
        self.axis.low()
    }

    /// Return the highest bin edge.
    pub fn high(&self) -> &T {
        self.axis.high()
    }
}

impl<T> Axis for UniformNoFlow<T>
where
    T: PartialOrd + NumCast + NumOps + Copy,
{
    type Coordinate = T;
    type BinInterval = BinInterval<T>;

    #[inline]
    fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
        let index = self.axis.index(coordinate)?;
        if index == 0 || index + 1 == self.axis.num_bins() {
            return None;
        }
        Some(index - 1)
    }

    fn num_bins(&self) -> usize {
        self.axis.num_bins() - 2
    }

    fn bin(&self, index: usize) -> Option<Self::BinInterval> {
        let bin = self.axis.bin(index + 1)?;
        match bin {
            BinInterval::Underflow { end: _ } => None,
            BinInterval::Overflow { start: _ } => None,
            BinInterval::Bin { start: _, end: _ } => Some(bin),
        }
    }
}

impl<'a, T> IntoIterator for &'a UniformNoFlow<T>
where
    T: PartialOrd + NumCast + NumOps + Copy,
{
    type Item = (usize, <Uniform<T> as Axis>::BinInterval);
    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl<T> Display for UniformNoFlow<T>
where
    T: PartialOrd + NumCast + NumOps + Copy + Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Axis{{# bins={}, range=[{}, {}), class={}}}",
            self.num_bins(),
            self.axis.low(),
            self.axis.high(),
            stringify!(UniformNoFlow)
        )
    }
}