ndhistogram/axis/bininterval/
bininterval.rs

1use std::{
2    convert::TryFrom,
3    error::Error,
4    fmt::{Display, LowerExp, UpperExp},
5    ops::{Range, RangeFrom, RangeTo},
6};
7
8/// BinInterval represents a single bin interval in a 1D axis.
9#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
10pub enum BinInterval<T> {
11    /// The underflow bin covers all values from (-inf, end].
12    /// The interval excludes end.
13    Underflow {
14        /// End of the interval.
15        end: T,
16    },
17    /// The overflow bin covers all values from [start, inf).
18    /// The interval includes start.
19    Overflow {
20        /// Start of the interval.
21        start: T,
22    },
23    /// A finite bin interval from [start, end).
24    /// The interval includes start but excludes end.
25    // TODO: rename to Interval or FiniteInterval?
26    Bin {
27        /// Start of the interval
28        start: T,
29        /// End of the interval
30        end: T,
31    },
32}
33
34impl<T> BinInterval<T> {
35    /// Factory method to create new underflow bin interval.
36    pub fn underflow(end: T) -> Self {
37        Self::Underflow { end }
38    }
39    /// Factory method to create new overflow bin interval.
40    pub fn overflow(start: T) -> Self {
41        Self::Overflow { start }
42    }
43    /// Factory method to create new finite bin interval.
44    pub fn new(start: T, end: T) -> Self {
45        Self::Bin { start, end }
46    }
47}
48
49impl<T: Copy> BinInterval<T> {
50    /// Get start of the interval if it exists for this interval variant.
51    ///
52    /// The underflow bin returns None.
53    pub fn start(&self) -> Option<T> {
54        match self {
55            Self::Underflow { end: _ } => None,
56            Self::Overflow { start } => Some(*start),
57            Self::Bin { start, end: _ } => Some(*start),
58        }
59    }
60
61    /// Get end of the interval if it exists for this interval variant.
62    ///
63    /// The overflow bin returns None.
64    pub fn end(&self) -> Option<T> {
65        match self {
66            Self::Underflow { end } => Some(*end),
67            Self::Overflow { start: _ } => None,
68            Self::Bin { start: _, end } => Some(*end),
69        }
70    }
71}
72
73impl<T> From<Range<T>> for BinInterval<T> {
74    fn from(other: Range<T>) -> Self {
75        Self::Bin {
76            start: other.start,
77            end: other.end,
78        }
79    }
80}
81
82impl<T> From<RangeTo<T>> for BinInterval<T> {
83    fn from(other: RangeTo<T>) -> Self {
84        Self::Underflow { end: other.end }
85    }
86}
87
88impl<T> From<RangeFrom<T>> for BinInterval<T> {
89    fn from(other: RangeFrom<T>) -> Self {
90        Self::Overflow { start: other.start }
91    }
92}
93
94impl<T> TryFrom<BinInterval<T>> for Range<T> {
95    type Error = BinIntervalConversionError;
96
97    fn try_from(value: BinInterval<T>) -> Result<Self, Self::Error> {
98        if let BinInterval::Bin { start, end } = value {
99            return Ok(Self { start, end });
100        }
101        Err(BinIntervalConversionError)
102    }
103}
104
105impl<T> TryFrom<BinInterval<T>> for RangeTo<T> {
106    type Error = BinIntervalConversionError;
107
108    fn try_from(value: BinInterval<T>) -> Result<Self, Self::Error> {
109        if let BinInterval::Underflow { end } = value {
110            return Ok(Self { end });
111        }
112        Err(BinIntervalConversionError)
113    }
114}
115
116impl<T> TryFrom<BinInterval<T>> for RangeFrom<T> {
117    type Error = BinIntervalConversionError;
118
119    fn try_from(value: BinInterval<T>) -> Result<Self, Self::Error> {
120        if let BinInterval::Overflow { start } = value {
121            return Ok(Self { start });
122        }
123        Err(BinIntervalConversionError)
124    }
125}
126
127macro_rules! impl_display {
128    ($Trait:ident) => {
129        impl<T: $Trait> $Trait for BinInterval<T> {
130            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131                match self {
132                    Self::Underflow { end } => {
133                        //write!(f, "(-inf, {})", end)?;
134                        write!(f, "(-inf, ")?;
135                        end.fmt(f)?;
136                        write!(f, ")")?;
137                    }
138                    Self::Overflow { start } => {
139                        //write!(f, "[{}, inf)", start)?;
140                        write!(f, "[")?;
141                        start.fmt(f)?;
142                        write!(f, ", inf)")?;
143                    }
144                    Self::Bin { start, end } => {
145                        //write!(f, "[{}, {})", start, end)?;
146                        write!(f, "[")?;
147                        start.fmt(f)?;
148                        write!(f, ", ")?;
149                        end.fmt(f)?;
150                        write!(f, ")")?;
151                    }
152                }
153                Ok(())
154            }
155        }
156    };
157}
158
159impl_display! {Display}
160impl_display! {LowerExp}
161impl_display! {UpperExp}
162
163#[derive(Debug)]
164pub struct BinIntervalConversionError;
165
166impl Display for BinIntervalConversionError {
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        write!(f, "invalid range to bin interval conversion")
169    }
170}
171
172impl Error for BinIntervalConversionError {}