1use core::ops::{BitAnd, BitOr, Not};
2use core::fmt;
3
4use alloc::vec::Vec;
5use alloc::boxed::Box;
6use alloc::string::ToString;
7use alloc::collections::BTreeSet;
8
9use netsblox_ast::CompactString;
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub enum RawCondition {
13 Const(bool),
14 Atom(CompactString),
15 Not(Box<RawCondition>),
16 And(Box<RawCondition>, Box<RawCondition>),
17 Or(Box<RawCondition>, Box<RawCondition>),
18}
19
20impl fmt::Display for RawCondition {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 RawCondition::Const(x) => write!(f, "{x}"),
24 RawCondition::Atom(x) => write!(f, "{x}"),
25 RawCondition::And(a, b) => {
26 fn single(f: &mut fmt::Formatter<'_>, v: &RawCondition) -> fmt::Result {
27 match v {
28 RawCondition::Or(_, _) => write!(f, "({v})"),
29 _ => write!(f, "{v}"),
30 }
31 }
32 single(f, a)?;
33 write!(f, " & ")?;
34 single(f, b)
35 }
36 RawCondition::Or(a, b) => {
37 fn single(f: &mut fmt::Formatter<'_>, v: &RawCondition) -> fmt::Result {
38 match v {
39 RawCondition::And(_, _) => write!(f, "({v})"),
40 _ => write!(f, "{v}"),
41 }
42 }
43 single(f, a)?;
44 write!(f, " | ")?;
45 single(f, b)
46 }
47 RawCondition::Not(x) => {
48 let inside = x.to_string();
49 if inside.chars().all(char::is_alphanumeric) {
50 write!(f, "~{inside}")
51 } else {
52 write!(f, "~({inside})")
53 }
54 }
55 }
56 }
57}
58
59impl BitAnd for RawCondition {
60 type Output = Self;
61 fn bitand(self, rhs: Self) -> Self::Output {
62 RawCondition::And(Box::new(self), Box::new(rhs))
63 }
64}
65impl BitOr for RawCondition {
66 type Output = Self;
67 fn bitor(self, rhs: Self) -> Self::Output {
68 RawCondition::Or(Box::new(self), Box::new(rhs))
69 }
70}
71impl Not for RawCondition {
72 type Output = Self;
73 fn not(self) -> Self::Output {
74 RawCondition::Not(Box::new(self))
75 }
76}
77
78#[test]
79fn test_condition() {
80 let a = RawCondition::Atom("a".into());
81 let b = RawCondition::Atom("b".into());
82 let c = RawCondition::Atom("c".into());
83 let d = RawCondition::Atom("d".into());
84 let e = RawCondition::Atom("x < 10".into());
85 let f = RawCondition::Atom("y == x + 10".into());
86
87 let bt = RawCondition::Const(true);
88 let bf = RawCondition::Const(false);
89
90 assert_eq!(a.to_string(), "a");
91 assert_eq!(b.to_string(), "b");
92 assert_eq!(c.to_string(), "c");
93 assert_eq!(d.to_string(), "d");
94 assert_eq!(e.to_string(), "x < 10");
95 assert_eq!(f.to_string(), "y == x + 10");
96 assert_eq!(bt.to_string(), "true");
97 assert_eq!(bf.to_string(), "false");
98
99 assert_eq!((a.clone() & b.clone()).to_string(), "a & b");
100 assert_eq!((a.clone() & b.clone() & c.clone()).to_string(), "a & b & c");
101 assert_eq!((a.clone() & b.clone() & c.clone() & d.clone()).to_string(), "a & b & c & d");
102
103 assert_eq!((a.clone() | b.clone()).to_string(), "a | b");
104 assert_eq!((a.clone() | b.clone() | c.clone()).to_string(), "a | b | c");
105 assert_eq!((a.clone() | b.clone() | c.clone() | d.clone()).to_string(), "a | b | c | d");
106
107 assert_eq!((a.clone() & b.clone() & a.clone()).to_string(), "a & b & a");
108 assert_eq!(((a.clone() & b.clone()) & a.clone()).to_string(), "a & b & a");
109 assert_eq!((a.clone() & (b.clone() & a.clone())).to_string(), "a & b & a");
110
111 assert_eq!(((a.clone() & b.clone()) | c.clone()).to_string(), "(a & b) | c");
112 assert_eq!((c.clone() | (a.clone() & b.clone())).to_string(), "c | (a & b)");
113 assert_eq!(((c.clone() & a.clone()) | (a.clone() & b.clone())).to_string(), "(c & a) | (a & b)");
114
115 assert_eq!(((a.clone() | b.clone()) & c.clone()).to_string(), "(a | b) & c");
116 assert_eq!((c.clone() & (a.clone() | b.clone())).to_string(), "c & (a | b)");
117 assert_eq!(((c.clone() | a.clone()) & (a.clone() | b.clone())).to_string(), "(c | a) & (a | b)");
118
119 assert_eq!(((a.clone() & (c.clone() & b.clone())) & a.clone()).to_string(), "a & c & b & a");
120 assert_eq!(((a.clone() & (c.clone() & b.clone())) & (a.clone() & a.clone() & c.clone())).to_string(), "a & c & b & a & a & c");
121
122 assert_eq!((!a.clone()).to_string(), "~a");
123 assert_eq!((!c.clone()).to_string(), "~c");
124 assert_eq!((!e.clone()).to_string(), "~(x < 10)");
125 assert_eq!((!f.clone()).to_string(), "~(y == x + 10)");
126
127 assert_eq!((!!a.clone()).to_string(), "~(~a)");
128 assert_eq!((!!c.clone()).to_string(), "~(~c)");
129 assert_eq!((!!e.clone()).to_string(), "~(~(x < 10))");
130 assert_eq!((!!f.clone()).to_string(), "~(~(y == x + 10))");
131
132 assert_eq!((bt.clone() & a.clone()).to_string(), "true & a");
133 assert_eq!((a.clone() & bt.clone()).to_string(), "a & true");
134
135 assert_eq!((bf.clone() | a.clone()).to_string(), "false | a");
136 assert_eq!((a.clone() | bf.clone()).to_string(), "a | false");
137
138 assert_eq!((bf.clone() & a.clone()).to_string(), "false & a");
139 assert_eq!((a.clone() & bf.clone()).to_string(), "a & false");
140
141 assert_eq!((bt.clone() | a.clone()).to_string(), "true | a");
142 assert_eq!((a.clone() | bt.clone()).to_string(), "a | true");
143
144 assert_eq!((a.clone() | !a.clone()).to_string(), "a | ~a");
145
146 assert_eq!((!bt.clone()).to_string(), "~true");
147 assert_eq!((!!bt.clone()).to_string(), "~(~true)");
148 assert_eq!((!!!bt.clone()).to_string(), "~(~(~true))");
149 assert_eq!((!!!!bt.clone()).to_string(), "~(~(~(~true)))");
150
151 assert_eq!((!bf.clone()).to_string(), "~false");
152 assert_eq!((!!bf.clone()).to_string(), "~(~false)");
153 assert_eq!((!!!bf.clone()).to_string(), "~(~(~false))");
154 assert_eq!((!!!!bf.clone()).to_string(), "~(~(~(~false)))");
155}
156
157impl RawCondition {
158 fn visit_and<F: FnMut(&RawCondition)>(&self, f: &mut F) {
159 match self {
160 RawCondition::And(x, y) => {
161 x.visit_and(f);
162 y.visit_and(f);
163 }
164 x => f(x),
165 }
166 }
167 fn visit_or<F: FnMut(&RawCondition)>(&self, f: &mut F) {
168 match self {
169 RawCondition::Or(x, y) => {
170 x.visit_or(f);
171 y.visit_or(f);
172 }
173 x => f(x),
174 }
175 }
176 fn simpl(&self) -> Self {
177 macro_rules! hetero_simpl {
178 ($terms:ident : $kind:ident , $visitor:ident) => {{
179 let groups = $terms.iter().filter(|t| if let RawCondition::$kind(_, _) = t { true } else { false }).map(|t| {
180 let mut sub_terms: BTreeSet<RawCondition> = Default::default();
181 t.$visitor(&mut |x| { sub_terms.insert(x.clone()); });
182 (t.clone(), sub_terms)
183 }).collect::<Vec<_>>();
184 for (i, (group, sub_terms)) in groups.iter().enumerate() {
185 if sub_terms.iter().any(|t| $terms.contains(t)) || groups[..i].iter().chain(&groups[i + 1..]).any(|t| t.1.is_subset(&sub_terms)) {
186 assert!($terms.remove(group));
187 }
188 }
189 }};
190 }
191 macro_rules! homo_simpl {
192 ($terms:ident : $kind:ident, $visitor:ident, $identity:literal) => {{
193 let groups = $terms.iter().filter_map(|t| match t { RawCondition::Not(x) => match &**x { RawCondition::$kind(_, _) => Some((t, x)), _ => None }, _ => None }).map(|(src, x)| {
194 let mut sub_terms: BTreeSet<RawCondition> = Default::default();
195 x.$visitor(&mut |x| { sub_terms.insert(x.clone()); });
196 (src.clone(), sub_terms)
197 }).collect::<Vec<_>>();
198 for (src, group) in groups.iter() {
199 assert!($terms.remove(src));
200 if !$terms.iter().any(|t| match t { RawCondition::Not(x) => group.contains(x), _ => false }) && !group.iter().any(|t| match t { RawCondition::Not(x) => $terms.contains(x), _ => false }) {
201 let new_group = group.iter().filter(|t| !$terms.contains(t)).cloned().collect::<BTreeSet<_>>();
202 $terms.insert(new_group.into_iter().reduce(|a, b| RawCondition::$kind(Box::new(a), Box::new(b))).map(|t| !t).unwrap_or(RawCondition::Const(!$identity)));
203 }
204 }
205 }};
206 }
207
208 match self {
209 RawCondition::And(_, _) => {
210 let mut terms: BTreeSet<RawCondition> = Default::default();
211 self.visit_and(&mut |x| { terms.insert(x.simpl()); });
212
213 hetero_simpl!(terms: Or, visit_or);
216
217 homo_simpl!(terms: And, visit_and, true);
221
222 terms.remove(&RawCondition::Const(true));
223 if terms.contains(&RawCondition::Const(false)) || terms.iter().any(|t| if let RawCondition::Not(t) = t { terms.contains(t) } else { false }) {
224 return RawCondition::Const(false);
225 }
226
227 terms.into_iter().reduce(|a, b| a & b).unwrap_or(RawCondition::Const(true))
228 }
229 RawCondition::Or(_, _) => {
230 let mut terms: BTreeSet<RawCondition> = Default::default();
231 self.visit_or(&mut |x| { terms.insert(x.simpl()); });
232
233 hetero_simpl!(terms: And, visit_and);
236
237 homo_simpl!(terms: Or, visit_or, false);
241
242 terms.remove(&RawCondition::Const(false));
243 if terms.contains(&RawCondition::Const(true)) || terms.iter().any(|t| if let RawCondition::Not(t) = t { terms.contains(t) } else { false }) {
244 return RawCondition::Const(true);
245 }
246
247 terms.into_iter().reduce(|a, b| a | b).unwrap_or(RawCondition::Const(false))
248 }
249 RawCondition::Not(x) => match &**x {
250 RawCondition::Const(x) => RawCondition::Const(!x),
251 RawCondition::Not(x) => x.simpl(),
252 x => RawCondition::Not(Box::new(x.simpl())),
253 }
254 x => x.clone(),
255 }
256 }
257}
258
259#[test]
260fn test_simpl() {
261 let a = RawCondition::Atom("a".into());
262 let b = RawCondition::Atom("b".into());
263 let c = RawCondition::Atom("c".into());
264 let d = RawCondition::Atom("d".into());
265 let e = RawCondition::Atom("x < 10".into());
266 let f = RawCondition::Atom("y == x + 10".into());
267
268 let bt = RawCondition::Const(true);
269 let bf = RawCondition::Const(false);
270
271 assert_eq!((a.clone() & a.clone()).simpl().to_string(), "a");
272 assert_eq!((a.clone() & c.clone() & a.clone()).simpl().to_string(), "a & c");
273 assert_eq!((a.clone() & (c.clone() & a.clone())).simpl().to_string(), "a & c");
274 assert_eq!((a.clone() & ((c.clone() | c.clone()) & a.clone())).simpl().to_string(), "a & c");
275 assert_eq!((a.clone() & ((c.clone() | c.clone() | c.clone()) & a.clone())).simpl().to_string(), "a & c");
276
277 assert_eq!((a.clone() | a.clone()).simpl().to_string(), "a");
278 assert_eq!((a.clone() | c.clone() | a.clone()).simpl().to_string(), "a | c");
279 assert_eq!((a.clone() | (c.clone() | a.clone())).simpl().to_string(), "a | c");
280 assert_eq!((a.clone() | ((c.clone() & c.clone()) | a.clone())).simpl().to_string(), "a | c");
281 assert_eq!((a.clone() | ((c.clone() & c.clone() & c.clone()) | a.clone())).simpl().to_string(), "a | c");
282
283 assert_eq!((!f.clone()).simpl().to_string(), "~(y == x + 10)");
284 assert_eq!((!!f.clone()).simpl().to_string(), "y == x + 10");
285 assert_eq!((!!!f.clone()).simpl().to_string(), "~(y == x + 10)");
286 assert_eq!((!!!!f.clone()).simpl().to_string(), "y == x + 10");
287
288 assert_eq!((!(a.clone() | a.clone())).simpl().to_string(), "~a");
289 assert_eq!((!(a.clone() & a.clone())).simpl().to_string(), "~a");
290
291 assert_eq!((bf.clone() & bf.clone()).simpl().to_string(), "false");
292 assert_eq!((bf.clone() & bt.clone()).simpl().to_string(), "false");
293 assert_eq!((bt.clone() & bf.clone()).simpl().to_string(), "false");
294 assert_eq!((bt.clone() & bt.clone()).simpl().to_string(), "true");
295
296 assert_eq!((bf.clone() | bf.clone()).simpl().to_string(), "false");
297 assert_eq!((bf.clone() | bt.clone()).simpl().to_string(), "true");
298 assert_eq!((bt.clone() | bf.clone()).simpl().to_string(), "true");
299 assert_eq!((bt.clone() | bt.clone()).simpl().to_string(), "true");
300
301 assert_eq!((a.clone() & bt.clone()).simpl().to_string(), "a");
302 assert_eq!((bt.clone() & a.clone() & bt.clone()).simpl().to_string(), "a");
303
304 assert_eq!((a.clone() | bf.clone()).simpl().to_string(), "a");
305 assert_eq!((bf.clone() | a.clone() | bf.clone()).simpl().to_string(), "a");
306
307 assert_eq!((a.clone() & bf.clone()).simpl().to_string(), "false");
308 assert_eq!((a.clone() | bt.clone()).simpl().to_string(), "true");
309
310 assert_eq!((a.clone() | (a.clone() & b.clone())).simpl().to_string(), "a");
311 assert_eq!(((a.clone() & b.clone()) | (a.clone() & b.clone())).simpl().to_string(), "a & b");
312 assert_eq!(((a.clone() & b.clone()) | (a.clone() & c.clone() & b.clone())).simpl().to_string(), "a & b");
313 assert_eq!(((a.clone() & c.clone() & b.clone() & c.clone()) | (a.clone() & b.clone())).simpl().to_string(), "a & b");
314 assert_eq!(((a.clone() & c.clone() & b.clone() & c.clone()) | b.clone() | (a.clone() & b.clone())).simpl().to_string(), "b");
315 assert_eq!(((a.clone() & c.clone() & b.clone() & c.clone()) | b.clone() | (a.clone() & d.clone() & b.clone())).simpl().to_string(), "b");
316 assert_eq!(((a.clone() & c.clone() & b.clone() & c.clone()) | (b.clone() & c.clone()) | (a.clone() & d.clone() & b.clone())).simpl().to_string(), "(b & c) | (a & b & d)");
317 assert_eq!(((a.clone() & c.clone() & b.clone() & c.clone()) | (b.clone() & c.clone()) | (a.clone() & c.clone() & b.clone())).simpl().to_string(), "b & c");
318
319 assert_eq!((a.clone() & (a.clone() | b.clone())).simpl().to_string(), "a");
320 assert_eq!(((a.clone() | b.clone()) & (a.clone() | b.clone())).simpl().to_string(), "a | b");
321 assert_eq!(((a.clone() | b.clone()) & (a.clone() | b.clone() | c.clone())).simpl().to_string(), "a | b");
322 assert_eq!(((a.clone() | c.clone() | b.clone() | c.clone()) & (a.clone() | b.clone())).simpl().to_string(), "a | b");
323 assert_eq!(((a.clone() | c.clone() | b.clone() | c.clone()) & b.clone() & (a.clone() | b.clone())).simpl().to_string(), "b");
324 assert_eq!(((a.clone() | c.clone() | b.clone() | c.clone()) & b.clone() & (a.clone() | d.clone() | b.clone())).simpl().to_string(), "b");
325 assert_eq!(((a.clone() | c.clone() | b.clone() | c.clone()) & (b.clone() | c.clone()) & (a.clone() | d.clone() | b.clone())).simpl().to_string(), "(b | c) & (a | b | d)");
326 assert_eq!(((a.clone() | c.clone() | b.clone() | c.clone()) & (b.clone() | c.clone()) & (a.clone() | c.clone() | b.clone())).simpl().to_string(), "b | c");
327
328 assert_eq!((e.clone() | !e.clone()).simpl().to_string(), "true");
329 assert_eq!((!!e.clone() | !e.clone()).simpl().to_string(), "true");
330 assert_eq!((!!e.clone() | !!!e.clone()).simpl().to_string(), "true");
331
332 assert_eq!((e.clone() & !e.clone()).simpl().to_string(), "false");
333 assert_eq!((!!e.clone() & !e.clone()).simpl().to_string(), "false");
334 assert_eq!((!!e.clone() & !!!e.clone()).simpl().to_string(), "false");
335
336 assert_eq!((!bt.clone()).simpl().to_string(), "false");
337 assert_eq!((!!bt.clone()).simpl().to_string(), "true");
338 assert_eq!((!!!bt.clone()).simpl().to_string(), "false");
339 assert_eq!((!!!!bt.clone()).simpl().to_string(), "true");
340
341 assert_eq!((!bf.clone()).simpl().to_string(), "true");
342 assert_eq!((!!bf.clone()).simpl().to_string(), "false");
343 assert_eq!((!!!bf.clone()).simpl().to_string(), "true");
344 assert_eq!((!!!!bf.clone()).simpl().to_string(), "false");
345
346 assert_eq!((a.clone() & !(a.clone() & b.clone())).simpl().to_string(), "a & ~b");
347 assert_eq!((a.clone() | !(a.clone() | b.clone())).simpl().to_string(), "a | ~b");
348
349 assert_eq!((a.clone() & b.clone() & !(a.clone() & b.clone())).simpl().to_string(), "false");
350 assert_eq!((a.clone() | b.clone() | !(a.clone() | b.clone())).simpl().to_string(), "true");
351
352 assert_eq!((!a.clone() & !(a.clone() & b.clone())).simpl().to_string(), "~a");
353 assert_eq!((!a.clone() | !(a.clone() | b.clone())).simpl().to_string(), "~a");
354
355 assert_eq!((a.clone() & !(!a.clone() & b.clone())).simpl().to_string(), "a");
356 assert_eq!((a.clone() | !(!a.clone() | b.clone())).simpl().to_string(), "a");
357}
358
359#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
360pub struct Condition(RawCondition);
361impl Condition {
362 pub fn raw(&self) -> &RawCondition {
363 &self.0
364 }
365 pub fn atom(v: CompactString) -> Self {
366 debug_assert!(v != "true" && v != "false" && !v.is_empty());
367 Condition(RawCondition::Atom(v))
368 }
369 pub fn constant(v: bool) -> Self {
370 Condition(RawCondition::Const(v))
371 }
372}
373impl fmt::Display for Condition {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 write!(f, "{}", self.0)
376 }
377}
378impl fmt::Debug for Condition {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 write!(f, "{}", self.0)
381 }
382}
383impl BitAnd for Condition {
384 type Output = Self;
385 fn bitand(self, rhs: Self) -> Self::Output {
386 let res = (self.0 & rhs.0).simpl();
387 debug_assert_eq!(res, res.simpl());
388 Condition(res)
389 }
390}
391impl BitOr for Condition {
392 type Output = Self;
393 fn bitor(self, rhs: Self) -> Self::Output {
394 let res = (self.0 | rhs.0).simpl();
395 debug_assert_eq!(res, res.simpl());
396 Condition(res)
397 }
398}
399impl Not for Condition {
400 type Output = Self;
401 fn not(self) -> Self::Output {
402 let res = (!self.0).simpl();
403 debug_assert_eq!(res, res.simpl());
404 Condition(res)
405 }
406}