aoclib_rs/
option_min_max.rs

1use std::{cmp, cmp::Ord};
2
3/// Simple helper type that makes it easier to get mins and maxes when some values may be None.
4/// Typical usage looks something like:
5///
6/// ```
7/// let mut m = aoclib_rs::option_min_max::OptionMinMax::NONE;
8/// for i in [1, 5, 8, 3, 2, 12, 7] {
9///     m = m.max(i);
10/// }
11/// assert_eq!(m.unwrap(), 12);
12/// ```
13#[derive(Copy, Clone, Debug)]
14pub struct OptionMinMax<T: Ord + Copy>(Option<T>);
15
16impl<T: Ord + Copy> OptionMinMax<T> {
17    pub const NONE: Self = Self::new(None);
18
19    pub const fn new(val: Option<T>) -> Self {
20        Self(val)
21    }
22
23    pub fn new_concrete(val: T) -> Self {
24        Self(Some(val))
25    }
26
27    /// Returns a new `OptionMinMax` containing the minimum value, or `other` if `self` contains
28    /// `None`.
29    ///
30    /// ```
31    /// use aoclib_rs::option_min_max::OptionMinMax;
32    /// assert_eq!(OptionMinMax::NONE.min(3).unwrap(), 3);
33    /// assert_eq!(OptionMinMax::new_concrete(2).min(3).unwrap(), 2);
34    /// ```
35    pub fn min(&self, other: T) -> Self {
36        Self(Some(match self.0 {
37            None => other,
38            Some(min) => cmp::min(other, min),
39        }))
40    }
41
42    /// Returns a new `OptionMinMax` containing the maximum value, or `other` if `self` contains
43    /// `None`.
44    ///
45    /// ```
46    /// use aoclib_rs::option_min_max::OptionMinMax;
47    /// assert_eq!(OptionMinMax::NONE.max(3).unwrap(), 3);
48    /// assert_eq!(OptionMinMax::new_concrete(2).max(3).unwrap(), 3);
49    /// ```
50    pub fn max(&self, other: T) -> Self {
51        Self(Some(match self.0 {
52            None => other,
53            Some(max) => cmp::max(other, max),
54        }))
55    }
56
57    /// Returns a new `OptionMinMax` containing the non-`None` value, if one exists. If both values
58    /// are non-`None`, returns the minimum.
59    ///
60    /// ```
61    /// use aoclib_rs::option_min_max::OptionMinMax;
62    /// assert_eq!(OptionMinMax::<i32>::NONE.min_self(OptionMinMax::NONE).get(), None);
63    /// assert_eq!(OptionMinMax::NONE.min_self(OptionMinMax::new_concrete(3)).unwrap(), 3);
64    /// assert_eq!(OptionMinMax::new_concrete(4).min_self(OptionMinMax::NONE).unwrap(), 4);
65    /// assert_eq!(OptionMinMax::new_concrete(4).min_self(OptionMinMax::new_concrete(2)).unwrap(), 2);
66    /// ```
67    pub fn min_self(&self, other: Self) -> Self {
68        let Some(left) = self.0 else {
69            return other;
70        };
71
72        let Some(right) = other.0 else {
73            return *self;
74        };
75
76        Self(Some(cmp::min(left, right)))
77    }
78
79    /// Returns a new `OptionMinMax` containing the non-`None` value, if one exists. If both values
80    /// are non-`None`, returns the maximum.
81    ///
82    /// ```
83    /// use aoclib_rs::option_min_max::OptionMinMax;
84    /// assert_eq!(OptionMinMax::<i32>::NONE.max_self(OptionMinMax::NONE).get(), None);
85    /// assert_eq!(OptionMinMax::NONE.max_self(OptionMinMax::new_concrete(3)).unwrap(), 3);
86    /// assert_eq!(OptionMinMax::new_concrete(4).max_self(OptionMinMax::NONE).unwrap(), 4);
87    /// assert_eq!(OptionMinMax::new_concrete(4).max_self(OptionMinMax::new_concrete(2)).unwrap(), 4);
88    /// ```
89    pub fn max_self(&self, other: Self) -> Self {
90        let Some(left) = self.0 else {
91            return other;
92        };
93
94        let Some(right) = other.0 else {
95            return *self;
96        };
97
98        Self(Some(cmp::max(left, right)))
99    }
100
101    /// Returns a new `OptionMinMax` containing the non-`None` value, if one exists. If both values
102    /// are non-`None`, returns the minimum.
103    ///
104    /// ```
105    /// use aoclib_rs::option_min_max::OptionMinMax;
106    /// assert_eq!(OptionMinMax::<i32>::NONE.min_option(None).get(), None);
107    /// assert_eq!(OptionMinMax::NONE.min_option(Some(3)).unwrap(), 3);
108    /// assert_eq!(OptionMinMax::new_concrete(4).min_option(None).unwrap(), 4);
109    /// assert_eq!(OptionMinMax::new_concrete(4).min_option(Some(2)).unwrap(), 2);
110    /// ```
111    pub fn min_option(&self, other: Option<T>) -> Self {
112        let Some(left) = self.0 else {
113            return Self(other);
114        };
115
116        let Some(right) = other else {
117            return *self;
118        };
119
120        Self(Some(cmp::min(left, right)))
121    }
122
123    /// Returns a new `OptionMinMax` containing the non-`None` value, if one exists. If both values
124    /// are non-`None`, returns the minimum.
125    ///
126    /// ```
127    /// use aoclib_rs::option_min_max::OptionMinMax;
128    /// assert_eq!(OptionMinMax::<i32>::NONE.max_option(None).get(), None);
129    /// assert_eq!(OptionMinMax::NONE.max_option(Some(3)).unwrap(), 3);
130    /// assert_eq!(OptionMinMax::new_concrete(4).max_option(None).unwrap(), 4);
131    /// assert_eq!(OptionMinMax::new_concrete(4).max_option(Some(2)).unwrap(), 4);
132    /// ```
133    pub fn max_option(&self, other: Option<T>) -> Self {
134        let Some(left) = self.0 else {
135            return Self(other);
136        };
137
138        let Some(right) = other else {
139            return *self;
140        };
141
142        Self(Some(cmp::max(left, right)))
143    }
144
145    pub fn get(&self) -> Option<T> {
146        self.0
147    }
148
149    /// can panic
150    pub fn unwrap(&self) -> T {
151        self.0.unwrap()
152    }
153}
154
155impl<T: Ord + Copy> Default for OptionMinMax<T> {
156    fn default() -> Self {
157        Self::NONE
158    }
159}