1use std::collections::BTreeSet;
2
3use derivative::Derivative;
4
5#[derive(Derivative, Clone, PartialEq)]
7#[derivative(Default(new = "true"))]
8#[cfg_attr(feature = "serde", derive(serde::Serialize))]
9#[cfg_attr(feature = "serde", serde(tag = "type", content = "set"))]
10pub enum LitSet {
11 #[derivative(Default)]
13 Match(BTreeSet<&'static str>),
14 Any,
16}
17
18impl std::fmt::Debug for LitSet {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 Self::Match(set) => write!(f, "{set:?}"),
22 Self::Any => write!(f, "*"),
23 }
24 }
25}
26
27impl<T: IntoIterator<Item = &'static str>> From<T> for LitSet {
28 #[inline]
29 fn from(set: T) -> Self {
30 Self::Match(set.into_iter().collect())
31 }
32}
33
34impl LitSet {
35 #[inline]
37 pub fn universe() -> Self {
38 Self::Any
39 }
40
41 #[inline]
43 pub fn is_universe(&self) -> bool {
44 matches!(self, Self::Any)
45 }
46
47 #[inline]
49 pub fn clear(&mut self) {
50 match self {
51 Self::Match(set) => set.clear(),
52 Self::Any => {}
53 }
54 }
55
56 #[inline]
58 pub fn is_empty(&self) -> bool {
59 match self {
60 Self::Match(set) => set.is_empty(),
61 Self::Any => false,
62 }
63 }
64
65 #[inline]
69 pub fn insert(&mut self, lit: &'static str) -> bool {
70 match self {
71 Self::Match(set) => set.insert(lit),
72 Self::Any => false,
73 }
74 }
75
76 #[inline]
78 pub fn contains(&self, lit: &str) -> bool {
79 match self {
80 Self::Match(set) => set.contains(lit),
81 Self::Any => true,
82 }
83 }
84
85 #[inline]
89 pub fn union_universe(&mut self) -> bool {
90 match self {
91 Self::Match(_) => {
92 *self = Self::Any;
93 true
94 }
95 Self::Any => false,
96 }
97 }
98
99 pub fn union(&mut self, other: &Self) -> bool {
101 match (self, other) {
102 (Self::Match(set), Self::Match(other_set)) => {
103 let old_size = set.len();
104 set.extend(other_set.iter());
105 old_size != set.len()
106 }
107 (s, _) => {
108 let is_self_any = matches!(s, Self::Any);
109 *s = Self::Any;
110 !is_self_any
111 }
112 }
113 }
114
115 pub fn intersects(&self, other: &Self) -> bool {
117 match (self, other) {
118 (Self::Match(set), Self::Match(other_set)) => {
119 set.intersection(other_set).next().is_some()
120 }
121 (Self::Any, s) | (s, Self::Any) => !s.is_empty(),
122 }
123 }
124
125 pub fn intersection(&self, other: &Self) -> Self {
127 match (self, other) {
128 (Self::Match(set), Self::Match(other_set)) => {
129 Self::Match(set.intersection(other_set).copied().collect())
130 }
131 (Self::Any, Self::Any) => Self::Any,
132 (Self::Any, s) | (s, Self::Any) => s.clone(),
133 }
134 }
135
136 pub fn iter(&self) -> Option<impl Iterator<Item = &&'static str>> {
140 match self {
141 Self::Match(set) => Some(set.iter()),
142 Self::Any => None,
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 #[test]
151 fn universe_contains_everything() {
152 let set = LitSet::universe();
153 assert!(set.contains(""));
154 assert!(set.contains("a"));
155 assert!(set.contains("b"));
156 assert!(set.contains("c"));
157 }
158
159 #[test]
160 fn contains_static() {
161 let mut set = LitSet::new();
162 set.insert("a");
163 assert!(set.contains("a"));
164 }
165
166 #[test]
167 fn contains_static_not() {
168 let mut set = LitSet::new();
169 set.insert("a");
170 assert!(!set.contains("b"));
171 }
172
173 #[test]
174 fn contains_dynamic() {
175 let a = "a";
176 let b = "a".to_string();
177 assert!(!std::ptr::eq(a, b.as_str()));
178 let mut set = LitSet::new();
179 set.insert(a);
180 assert!(set.contains(&b));
181 }
182
183 #[test]
184 fn union_universe_from_empty() {
185 let mut set = LitSet::new();
186 assert!(set.union_universe());
187 assert_eq!(set, LitSet::universe());
188 }
189
190 #[test]
191 fn union_universe_from_some() {
192 let mut set = LitSet::from(["a"]);
193 assert!(set.union_universe());
194 assert_eq!(set, LitSet::universe());
195 }
196
197 #[test]
198 fn union_universe_from_universe() {
199 let mut set = LitSet::universe();
200 assert!(!set.union_universe());
201 assert_eq!(set, LitSet::universe());
202 }
203
204 #[test]
205 fn union() {
206 let mut set1 = LitSet::from(["a", "b"]);
207 let set2 = LitSet::from(["b", "c"]);
208 let expected = LitSet::from(["a", "b", "c"]);
209 assert!(set1.union(&set2));
210 assert_eq!(set1, expected);
211 }
212
213 #[test]
214 fn union_no_change_empty() {
215 let mut set1 = LitSet::from(["a", "b"]);
216 let set2 = LitSet::new();
217 assert!(!set1.union(&set2));
218 }
219
220 #[test]
221 fn union_no_change_subset() {
222 let mut set1 = LitSet::from(["a", "b"]);
223 let set2 = LitSet::from(["a"]);
224 assert!(!set1.union(&set2));
225 }
226
227 #[test]
228 fn union_no_change_equal() {
229 let mut set1 = LitSet::from(["a", "b"]);
230 let set2 = set1.clone();
231 assert!(!set1.union(&set2));
232 }
233
234 #[test]
235 fn union_disjoint() {
236 let mut set1 = LitSet::from(["a", "b"]);
237 let set2 = LitSet::from(["d", "c"]);
238 let expected = LitSet::from(["a", "b", "c", "d"]);
239 assert!(set1.union(&set2));
240 assert_eq!(set1, expected);
241 }
242
243 #[test]
244 #[allow(clippy::bool_assert_comparison)] fn intersect_empty_universe() {
246 let set1 = LitSet::new();
247 let set2 = LitSet::universe();
248 assert_eq!(set1.intersects(&set1), false);
249 assert_eq!(set1.intersects(&set2), false);
250 assert_eq!(set2.intersects(&set1), false);
251 assert_eq!(set2.intersects(&set2), true);
252 }
253
254 #[test]
255 fn intersect_universe() {
256 let u = LitSet::universe();
257 let set = LitSet::from(["a", "b"]);
258
259 assert!(u.intersects(&set));
260 assert!(set.intersects(&u));
261 assert_eq!(u.intersection(&set), set);
262 assert_eq!(set.intersection(&u), set);
263 }
264
265 #[test]
266 fn intersect_disjoint() {
267 let set1 = LitSet::from(["a", "b"]);
268 let set2 = LitSet::from(["c", "d"]);
269 assert!(!set1.intersects(&set2));
270 assert!(!set2.intersects(&set1));
271 assert_eq!(set2.intersection(&set1), LitSet::new());
272 assert_eq!(set1.intersection(&set2), LitSet::new());
273 }
274
275 #[test]
276 fn intersect_subset() {
277 let set1 = LitSet::from(["a", "b"]);
278 let set2 = LitSet::from(["a"]);
279 assert!(set1.intersects(&set2));
280 assert!(set2.intersects(&set1));
281 assert_eq!(set2.intersection(&set1), set2);
282 assert_eq!(set1.intersection(&set2), set2);
283 }
284
285 #[test]
286 fn intersect_empty() {
287 let set1 = LitSet::from(["a", "b"]);
288 let set2 = LitSet::new();
289 assert!(!set1.intersects(&set2));
290 assert!(!set2.intersects(&set1));
291 assert_eq!(set2.intersection(&set1), set2);
292 assert_eq!(set1.intersection(&set2), set2);
293 }
294
295 #[test]
296 fn intersection() {
297 let set1 = LitSet::from(["a", "b", "c"]);
298 let set2 = LitSet::from(["a", "c", "d"]);
299 let expected = LitSet::from(["a", "c"]);
300 assert_eq!(set1.intersection(&set2), expected);
301 assert_eq!(set2.intersection(&set1), expected);
302 }
303}