Advent of Code solutions
1use std::{fmt::Debug, ops::Range};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
4/// Represents a range of values.
5///
6/// End is exclusive.
7pub struct BetterRange<T: Copy + Clone + Debug> {
8 pub start: T,
9 pub end: T,
10}
11
12impl<T: Copy + Clone + Debug> BetterRange<T> {
13 pub fn new(start: T, end: T) -> Self {
14 Self { start, end }
15 }
16}
17
18pub enum RangeSplitBehavior {
19 IncludeLower,
20 IncludeUpper,
21 Exclude,
22}
23
24impl<T: Copy + Clone + Debug + Ord> BetterRange<T> {
25 /// Checks if the range contains the given value.
26 ///
27 /// # Examples
28 ///
29 /// ```
30 /// use utils::prelude::*;
31 ///
32 /// let range = BetterRange::new(0, 10);
33 /// assert!(range.contains(&5));
34 /// assert!(!range.contains(&10));
35 /// ```
36 ///
37 pub fn contains(&self, value: &T) -> bool {
38 self.start <= *value && *value < self.end
39 }
40
41 /// Checks if the range contains the given range.
42 ///
43 /// # Examples
44 ///
45 /// ```
46 /// use utils::prelude::*;
47 ///
48 /// let range = BetterRange::new(0, 10);
49 /// assert!(range.contains_range(&BetterRange::new(5, 7)));
50 /// assert!(!range.contains_range(&BetterRange::new(5, 15)));
51 /// ```
52 ///
53 pub fn contains_range(&self, other: &Self) -> bool {
54 self.start <= other.start && other.end <= self.end
55 }
56
57 /// Merge this range with another. If the ranges do not intersect, [None] is returned.
58 ///
59 /// # Example
60 ///
61 /// ```
62 /// use utils::prelude::*;
63 ///
64 /// let a = BetterRange::new(1, 5);
65 /// let b = BetterRange::new(6, 11);
66 /// let c = BetterRange::new(4, 8);
67 /// let d = BetterRange::new(3, 4);
68 ///
69 ///
70 /// assert!(a.merge(&b).is_none(), "Should not merge");
71 /// assert!(d.merge(&b).is_none(), "Should not merge");
72 /// assert_eq!(a.merge(&c).unwrap(), BetterRange::new(1, 8), "1-5 & 4-8 = 1-8");
73 /// assert_eq!(c.merge(&a).unwrap(), BetterRange::new(1, 8), "4-8 & 1-5 = 1-8");
74 /// assert_eq!(a.merge(&d).unwrap(), a, "1-5 & 3-4 = 1-5");
75 /// assert_eq!(d.merge(&a).unwrap(), a, "3-4 & 1-5 = 1-5");
76 /// assert_eq!(c.merge(&d).unwrap(), BetterRange::new(3, 8), "4-8 & 3-4 = 3-8");
77 /// assert_eq!(d.merge(&c).unwrap(), BetterRange::new(3, 8), "3-4 & 4-8 = 3-8");
78 /// ```
79 pub fn merge(&self, other: &Self) -> Option<Self> {
80 if self.contains_range(other) {
81 Some(*self)
82 } else if other.contains_range(self) {
83 Some(*other)
84 } else if self.contains(&other.start) || self.end == other.start {
85 Some(Self::new(self.start, other.end))
86 } else if self.contains(&other.end) || other.end == self.start {
87 Some(Self::new(other.start, self.end))
88 } else {
89 None
90 }
91 }
92
93 /// Split the range at the given value.
94 ///
95 /// The behaviour determines if the value is included in the lower range, upper range, or neither.
96 ///
97 /// # Examples
98 ///
99 /// ```
100 /// use utils::prelude::*;
101 ///
102 /// let range = BetterRange::new(0, 10);
103 ///
104 /// let (lower, upper) = range.split(&5, RangeSplitBehavior::IncludeLower);
105 /// assert_eq!(lower, Some(BetterRange::new(0, 6)));
106 /// assert_eq!(upper, Some(BetterRange::new(6, 10)));
107 /// ```
108 ///
109 /// ```
110 /// use utils::prelude::*;
111 ///
112 /// let range = BetterRange::new(0, 10);
113 ///
114 /// let (lower, upper) = range.split(&5, RangeSplitBehavior::IncludeUpper);
115 /// assert_eq!(lower, Some(BetterRange::new(0, 5)));
116 /// assert_eq!(upper, Some(BetterRange::new(5, 10)));
117 /// ```
118 ///
119 /// ```
120 /// use utils::prelude::*;
121 ///
122 /// let range = BetterRange::new(0, 10);
123 ///
124 /// let (lower, upper) = range.split(&5, RangeSplitBehavior::Exclude);
125 /// assert_eq!(lower, Some(BetterRange::new(0, 5)));
126 /// assert_eq!(upper, Some(BetterRange::new(6, 10)));
127 /// ```
128 ///
129 pub fn split(&self, value: &T, behaviour: RangeSplitBehavior) -> (Option<Self>, Option<Self>)
130 where
131 T: std::ops::Add<usize, Output = T> + std::ops::Sub<usize, Output = T>,
132 {
133 if self.contains(value) {
134 match behaviour {
135 RangeSplitBehavior::IncludeLower => (
136 Some(Self::new(self.start, *value + 1)),
137 Some(Self::new(*value + 1, self.end)),
138 ),
139 RangeSplitBehavior::IncludeUpper => (
140 Some(Self::new(self.start, *value)),
141 Some(Self::new(*value, self.end)),
142 ),
143 RangeSplitBehavior::Exclude => (
144 Some(Self::new(self.start, *value)),
145 Some(Self::new(*value + 1, self.end)),
146 ),
147 }
148 } else {
149 (None, None)
150 }
151 }
152}
153
154impl<T: Copy + Clone + Debug + Ord> From<BetterRange<T>> for Range<T> {
155 fn from(val: BetterRange<T>) -> Self {
156 val.start..val.end
157 }
158}
159
160impl<T: Copy + Clone + Debug + Ord> From<Range<T>> for BetterRange<T> {
161 fn from(val: Range<T>) -> Self {
162 BetterRange::new(val.start, val.end)
163 }
164}
165
166impl<T: Copy + Clone + Debug + Ord> std::ops::Add<usize> for BetterRange<T>
167where
168 T: std::ops::Add<usize, Output = T>,
169{
170 type Output = Self;
171
172 fn add(self, rhs: usize) -> Self::Output {
173 Self::new(self.start + rhs, self.end + rhs)
174 }
175}
176
177impl<T: Copy + Clone + Debug + Ord> std::ops::Sub<usize> for BetterRange<T>
178where
179 T: std::ops::Sub<usize, Output = T>,
180{
181 type Output = Self;
182
183 fn sub(self, rhs: usize) -> Self::Output {
184 Self::new(self.start - rhs, self.end - rhs)
185 }
186}