1#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Default)]
9pub struct CharSet {
10 ranges: Vec<(u32, u32)>,
11}
12
13impl CharSet {
14 pub fn empty() -> Self {
16 Self::default()
17 }
18
19 pub fn single(c: u32) -> Self {
21 Self {
22 ranges: vec![(c, c)],
23 }
24 }
25
26 pub fn range(lo: u32, hi: u32) -> Self {
28 if lo > hi {
29 Self::empty()
30 } else {
31 Self {
32 ranges: vec![(lo, hi)],
33 }
34 }
35 }
36
37 pub fn from_ranges<I: IntoIterator<Item = (u32, u32)>>(it: I) -> Self {
39 let mut v: Vec<(u32, u32)> = it.into_iter().filter(|(l, h)| l <= h).collect();
40 v.sort_unstable();
41 let mut out: Vec<(u32, u32)> = Vec::with_capacity(v.len());
42 for (lo, hi) in v {
43 match out.last_mut() {
44 Some(last) if lo <= last.1.saturating_add(1) => last.1 = last.1.max(hi),
45 _ => out.push((lo, hi)),
46 }
47 }
48 Self { ranges: out }
49 }
50
51 pub fn ranges(&self) -> &[(u32, u32)] {
53 &self.ranges
54 }
55
56 pub fn is_empty(&self) -> bool {
58 self.ranges.is_empty()
59 }
60
61 pub fn smallest(&self) -> Option<u32> {
63 self.ranges.first().map(|r| r.0)
64 }
65
66 pub fn contains(&self, c: u32) -> bool {
68 self.ranges
69 .binary_search_by(|&(lo, hi)| {
70 if c < lo {
71 std::cmp::Ordering::Greater
72 } else if c > hi {
73 std::cmp::Ordering::Less
74 } else {
75 std::cmp::Ordering::Equal
76 }
77 })
78 .is_ok()
79 }
80
81 pub fn count(&self) -> u64 {
83 self.ranges.iter().map(|&(l, h)| u64::from(h - l) + 1).sum()
84 }
85
86 pub fn union(&self, o: &CharSet) -> CharSet {
88 CharSet::from_ranges(self.ranges.iter().chain(o.ranges.iter()).copied())
89 }
90
91 pub fn intersect(&self, o: &CharSet) -> CharSet {
93 let (mut i, mut j) = (0, 0);
94 let mut out = Vec::new();
95 while i < self.ranges.len() && j < o.ranges.len() {
96 let (a, b) = (self.ranges[i], o.ranges[j]);
97 let (lo, hi) = (a.0.max(b.0), a.1.min(b.1));
98 if lo <= hi {
99 out.push((lo, hi));
100 }
101 if a.1 < b.1 {
102 i += 1;
103 } else {
104 j += 1;
105 }
106 }
107 Self { ranges: out }
108 }
109
110 pub fn difference(&self, o: &CharSet) -> CharSet {
112 let mut out = Vec::new();
113 let mut j = 0;
114 for &(lo, hi) in &self.ranges {
115 let mut cur = lo;
116 while j < o.ranges.len() && o.ranges[j].1 < cur {
117 j += 1;
118 }
119 let mut k = j;
120 let mut covered = false;
121 while k < o.ranges.len() && o.ranges[k].0 <= hi {
122 let (blo, bhi) = o.ranges[k];
123 if blo > cur {
124 out.push((cur, blo - 1));
125 }
126 if bhi >= hi {
127 covered = true;
128 break;
129 }
130 cur = bhi + 1;
131 k += 1;
132 }
133 if !covered && cur <= hi {
134 out.push((cur, hi));
135 }
136 }
137 Self { ranges: out }
138 }
139}
140
141pub fn refine(p: &[CharSet], s: &CharSet) -> Vec<CharSet> {
143 let mut out = Vec::with_capacity(p.len() + 1);
144 for b in p {
145 let inside = b.intersect(s);
146 if inside.is_empty() {
147 out.push(b.clone());
148 } else {
149 let outside = b.difference(s);
150 out.push(inside);
151 if !outside.is_empty() {
152 out.push(outside);
153 }
154 }
155 }
156 out
157}
158
159pub fn refine_all(p: &[CharSet], q: &[CharSet]) -> Vec<CharSet> {
161 let mut out = Vec::new();
162 for a in p {
163 for b in q {
164 let x = a.intersect(b);
165 if !x.is_empty() {
166 out.push(x);
167 }
168 }
169 }
170 out.sort_by_key(|s| s.smallest());
171 out
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 fn cs(r: &[(u32, u32)]) -> CharSet {
179 CharSet::from_ranges(r.iter().copied())
180 }
181
182 #[test]
183 fn normalisation_merges_overlapping_and_touching() {
184 assert_eq!(cs(&[(5, 9), (1, 3), (4, 4), (8, 12)]).ranges(), &[(1, 12)]);
185 assert_eq!(cs(&[(1, 2), (4, 5)]).ranges(), &[(1, 2), (4, 5)]);
186 assert!(cs(&[(3, 1)]).is_empty());
187 }
188
189 #[test]
190 fn set_algebra_matches_brute_force_on_a_small_universe() {
191 let sets: Vec<CharSet> = (0u32..256)
193 .map(|m| {
194 CharSet::from_ranges(
195 (0..8)
196 .filter(|b| m >> b & 1 == 1)
197 .map(|b| (b * 2, b * 2 + 1)),
198 )
199 })
200 .collect();
201 let bits = |s: &CharSet| (0..16u32).filter(|&c| s.contains(c)).collect::<Vec<_>>();
202 for a in sets.iter().step_by(7) {
203 for b in sets.iter().step_by(5) {
204 let (x, y) = (bits(a), bits(b));
205 let u: Vec<u32> = (0..16).filter(|c| x.contains(c) || y.contains(c)).collect();
206 let i: Vec<u32> = (0..16).filter(|c| x.contains(c) && y.contains(c)).collect();
207 let d: Vec<u32> = (0..16)
208 .filter(|c| x.contains(c) && !y.contains(c))
209 .collect();
210 assert_eq!(bits(&a.union(b)), u);
211 assert_eq!(bits(&a.intersect(b)), i);
212 assert_eq!(bits(&a.difference(b)), d);
213 }
214 }
215 }
216
217 #[test]
218 fn difference_handles_holes_and_full_cover() {
219 assert_eq!(
220 cs(&[(0, 100)])
221 .difference(&cs(&[(10, 20), (30, 40)]))
222 .ranges(),
223 &[(0, 9), (21, 29), (41, 100)]
224 );
225 assert!(cs(&[(5, 6)]).difference(&cs(&[(0, 10)])).is_empty());
226 assert_eq!(
227 cs(&[(1, 3), (5, 9)]).difference(&cs(&[(3, 5)])).ranges(),
228 &[(1, 2), (6, 9)]
229 );
230 }
231
232 #[test]
233 fn membership_count_and_min() {
234 let s = cs(&[(10, 12), (100, 100)]);
235 assert!(s.contains(11) && s.contains(100) && !s.contains(13) && !s.contains(9));
236 assert_eq!((s.count(), s.smallest()), (4, Some(10)));
237 }
238
239 #[test]
240 fn refinement_partitions_exactly() {
241 let all = CharSet::range(0, 99);
242 let p = refine(
243 &refine(std::slice::from_ref(&all), &CharSet::range(10, 30)),
244 &CharSet::range(20, 50),
245 );
246 let mut union = CharSet::empty();
247 for (i, a) in p.iter().enumerate() {
248 for b in &p[i + 1..] {
249 assert!(a.intersect(b).is_empty());
250 }
251 union = union.union(a);
252 }
253 assert_eq!(union, all);
254 assert_eq!(p.len(), 4);
256 }
257}