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}