Advent of Code solutions
1use std::{fmt::Debug, ops::Range};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, 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 /// Split the range at the given value.
58 ///
59 /// The behaviour determines if the value is included in the lower range, upper range, or neither.
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// use utils::prelude::*;
65 ///
66 /// let range = BetterRange::new(0, 10);
67 ///
68 /// let (lower, upper) = range.split(&5, RangeSplitBehavior::IncludeLower);
69 /// assert_eq!(lower, Some(BetterRange::new(0, 6)));
70 /// assert_eq!(upper, Some(BetterRange::new(6, 10)));
71 /// ```
72 ///
73 /// ```
74 /// use utils::prelude::*;
75 ///
76 /// let range = BetterRange::new(0, 10);
77 ///
78 /// let (lower, upper) = range.split(&5, RangeSplitBehavior::IncludeUpper);
79 /// assert_eq!(lower, Some(BetterRange::new(0, 5)));
80 /// assert_eq!(upper, Some(BetterRange::new(5, 10)));
81 /// ```
82 ///
83 /// ```
84 /// use utils::prelude::*;
85 ///
86 /// let range = BetterRange::new(0, 10);
87 ///
88 /// let (lower, upper) = range.split(&5, RangeSplitBehavior::Exclude);
89 /// assert_eq!(lower, Some(BetterRange::new(0, 5)));
90 /// assert_eq!(upper, Some(BetterRange::new(6, 10)));
91 /// ```
92 ///
93 pub fn split(&self, value: &T, behaviour: RangeSplitBehavior) -> (Option<Self>, Option<Self>)
94 where
95 T: std::ops::Add<usize, Output = T> + std::ops::Sub<usize, Output = T>,
96 {
97 if self.contains(value) {
98 match behaviour {
99 RangeSplitBehavior::IncludeLower => (
100 Some(Self::new(self.start, *value + 1)),
101 Some(Self::new(*value + 1, self.end)),
102 ),
103 RangeSplitBehavior::IncludeUpper => (
104 Some(Self::new(self.start, *value)),
105 Some(Self::new(*value, self.end)),
106 ),
107 RangeSplitBehavior::Exclude => (
108 Some(Self::new(self.start, *value)),
109 Some(Self::new(*value + 1, self.end)),
110 ),
111 }
112 } else {
113 (None, None)
114 }
115 }
116}
117
118impl<T: Copy + Clone + Debug + Ord> From<BetterRange<T>> for Range<T> {
119 fn from(val: BetterRange<T>) -> Self {
120 val.start..val.end
121 }
122}
123
124impl<T: Copy + Clone + Debug + Ord> From<Range<T>> for BetterRange<T> {
125 fn from(val: Range<T>) -> Self {
126 BetterRange::new(val.start, val.end)
127 }
128}
129
130impl<T: Copy + Clone + Debug + Ord> std::ops::Add<usize> for BetterRange<T>
131where
132 T: std::ops::Add<usize, Output = T>,
133{
134 type Output = Self;
135
136 fn add(self, rhs: usize) -> Self::Output {
137 Self::new(self.start + rhs, self.end + rhs)
138 }
139}
140
141impl<T: Copy + Clone + Debug + Ord> std::ops::Sub<usize> for BetterRange<T>
142where
143 T: std::ops::Sub<usize, Output = T>,
144{
145 type Output = Self;
146
147 fn sub(self, rhs: usize) -> Self::Output {
148 Self::new(self.start - rhs, self.end - rhs)
149 }
150}
151
152impl<T: Copy + Clone + Debug + Ord> std::ops::BitAnd for BetterRange<T>
153where
154 T: std::ops::BitAnd<Output = T>,
155{
156 type Output = Self;
157
158 fn bitand(self, rhs: Self) -> Self::Output {
159 Self::new(self.start.max(rhs.start), self.end.min(rhs.end))
160 }
161}
162
163impl<T: Copy + Clone + Debug + Ord> std::ops::BitOr for BetterRange<T>
164where
165 T: std::ops::BitOr<Output = T>,
166{
167 type Output = Self;
168
169 fn bitor(self, rhs: Self) -> Self::Output {
170 Self::new(self.start.min(rhs.start), self.end.max(rhs.end))
171 }
172}