ndhistogram/axis/
mod.rs

1//! Axis for ND histograms
2//!
3//! This module contains implementations of [Axis] that are used to represent the axes of
4//! an N-dimensional [Histogram](crate::Histogram).
5//!
6mod bininterval;
7pub use bininterval::bininterval::BinInterval;
8pub use bininterval::singlevaluebininterval::SingleValueBinInterval;
9mod uniformcyclic;
10pub use uniformcyclic::UniformCyclic;
11mod variablecyclic;
12pub use variablecyclic::VariableCyclic;
13mod uniform;
14pub use uniform::Uniform;
15mod uniformnoflow;
16pub use uniformnoflow::UniformNoFlow;
17mod category;
18pub use category::Category;
19mod categorynoflow;
20pub use categorynoflow::CategoryNoFlow;
21mod variable;
22pub use variable::Variable;
23mod variablenoflow;
24pub use variablenoflow::VariableNoFlow;
25
26type Iter<'a, BinInterval> = Box<dyn Iterator<Item = (usize, BinInterval)> + 'a>;
27type Indices = Box<dyn Iterator<Item = usize>>;
28type Bins<'a, BinInterval> = Box<dyn Iterator<Item = BinInterval> + 'a>;
29
30/// An binned axis corresponding to one dimension of an N-dimensional [Histogram](crate::Histogram).
31///
32/// An Axis is composed of a map from some coordinate space to linear bin number, and the inverse map.
33/// For examples see:
34/// - [Uniform],
35/// - [UniformNoFlow],
36/// - [UniformCyclic],
37/// - [Variable],
38/// - [VariableNoFlow],
39/// - [VariableCyclic],
40/// - [Category]
41/// - and [CategoryNoFlow].
42///
43/// Most use cases should be covered by the builtin Axis implementations.
44/// However, you may implement the Axis trait if you have specialist needs.
45///
46/// # Examples
47///
48/// ## Parity Axis
49/// Imagine we wanted an 2-bin axis where even values where mapped to one bin
50/// and odd values to another bin. We could implement this with the following:
51/// ```rust
52/// use ndhistogram::axis::Axis;
53/// use ndhistogram::{ndhistogram, Histogram};
54/// enum Parity {
55///     Even,
56///     Odd
57/// }
58///
59/// struct ParityAxis {}
60///
61/// impl Axis for ParityAxis {
62///     type Coordinate = i32;
63///
64///     type BinInterval = Parity;
65///
66///     fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
67///         if coordinate % 2 == 0 { Some(0) } else { Some(1) }
68///     }
69///
70///    fn num_bins(&self) -> usize {
71///         2
72///     }
73///
74///    fn bin(&self, index: usize) -> Option<Self::BinInterval> {
75///         if index == 0 { Some(Parity::Even) } else { Some(Parity::Odd) }
76///     }
77/// }
78///
79/// let mut hist = ndhistogram!(ParityAxis{}; i32);
80/// hist.fill(&1);
81/// hist.fill_with(&2, 4);
82/// assert_eq!(hist.value(&1), Some(&1));
83/// assert_eq!(hist.value(&2), Some(&4));
84/// ```
85///
86pub trait Axis {
87    /// The type representing a location on this axis.
88    type Coordinate;
89    /// The type of an interval representing the set of Coordinates that correspond to a histogram bin
90    type BinInterval;
91
92    /// Map from coordinate to bin number.
93    /// Returns an option as not all valid coordinates are necessarily contained within a bin.
94    fn index(&self, coordinate: &Self::Coordinate) -> Option<usize>;
95    /// The number of bins in this axis, including underflow and overflow.
96    fn num_bins(&self) -> usize;
97
98    /// Map from bin number to axis to the interval covering the range of coordinates that this bin contains.
99    /// Returns an option in case an index >= [Axis::num_bins] is given.
100    fn bin(&self, index: usize) -> Option<Self::BinInterval>;
101
102    /// An iterator over bin numbers
103    fn indices(&self) -> Indices {
104        Box::new(0..self.num_bins())
105    }
106
107    /// An iterator over bin numbers and bin intervals
108    fn iter(&self) -> Iter<'_, Self::BinInterval> {
109        Box::new(self.indices().map(move |it| {
110            (
111                it,
112                self.bin(it)
113                    .expect("indices() should only produce valid indices"),
114            )
115        }))
116    }
117
118    /// An iterator over bin intervals.
119    fn bins(&self) -> Bins<'_, Self::BinInterval> {
120        Box::new(self.indices().map(move |it| {
121            self.bin(it)
122                .expect("indices() should only produce valid indices")
123        }))
124    }
125
126    /// The number of dimensions that this object corresponds to.
127    /// For most Axis types this will simply be 1.
128    /// However, [Axes](crate::Axes) (i.e. a set of [Axis]) also implement [Axis]
129    /// and should return the number of [Axis] that it contains.
130    fn num_dim(&self) -> usize {
131        1
132    }
133}