Advent of Code solutions
at main 186 lines 5.7 kB view raw
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}