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
use super::{Axis, BinInterval, Variable};
use std::fmt::{Debug, Display};

/// An axis with variable sized bins and no overflow bins.
///
/// An axis with variable sized bins constructed with a list of bin edges.
/// This axis has (num edges - 1) bins.
///
/// # Example
/// Create a 1D histogram with 3 variable width bin 0.0 and 7.0, plus overflow and underflow bins.
/// ```rust
///    use ndhistogram::{ndhistogram, Histogram};
///    use ndhistogram::axis::{Axis, VariableNoFlow};
///    let mut hist = ndhistogram!(VariableNoFlow::new(vec![0.0, 1.0, 3.0, 7.0]); i32);
///    hist.fill(&-1.0); // will be ignore as there is no underflow bin
///    hist.fill(&1.0);
///    hist.fill(&2.0);
///    assert_eq!(
///        hist.values().copied().collect::<Vec<_>>(),
///        vec![0, 2, 0],
///    );
///
/// ```
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct VariableNoFlow<T> {
    axis: Variable<T>,
}

impl<T: PartialOrd + Copy> VariableNoFlow<T> {
    /// Factory method to create an variable binning from a set of bin edges with no under/overflow bins.
    /// See the documentation for [Variable::new].
    pub fn new<I: IntoIterator<Item = T>>(bin_edges: I) -> Self {
        VariableNoFlow {
            axis: Variable::new(bin_edges),
        }
    }

    /// 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: PartialOrd + Copy> Axis for VariableNoFlow<T> {
    type Coordinate = T;
    type BinInterval = BinInterval<T>;

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

    fn numbins(&self) -> usize {
        self.axis.numbins() - 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: PartialOrd + Copy> IntoIterator for &'a VariableNoFlow<T> {
    type Item = (usize, <VariableNoFlow<T> as Axis>::BinInterval);
    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;

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

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