1use crate::eval::{VarMap, truth_table};
20use trit_core::algebra::Algebra;
21use trit_core::ast::Expr;
22use trit_core::value::Ternary;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct SimplifyOptions {
27 pub fold_constants: bool,
29 pub max_vars_for_constant_fold: usize,
31 pub expand_implies: bool,
33}
34
35impl Default for SimplifyOptions {
36 fn default() -> Self {
37 Self {
38 fold_constants: true,
39 max_vars_for_constant_fold: 8,
40 expand_implies: false,
41 }
42 }
43}
44
45pub fn simplify(expr: &Expr, alg: Algebra) -> Expr {
47 simplify_with(expr, alg, SimplifyOptions::default())
48}
49
50pub fn simplify_with(expr: &Expr, alg: Algebra, opts: SimplifyOptions) -> Expr {
52 let step1 = rewrite(expr, alg, opts);
53 if !opts.fold_constants {
54 return step1;
55 }
56 let var_count = step1.variables().len();
57 if var_count > opts.max_vars_for_constant_fold {
58 return step1;
59 }
60 match constant_value(&step1, alg) {
61 Some(v) => Expr::Const(v),
62 None => step1,
63 }
64}
65
66pub fn constant_value(expr: &Expr, alg: Algebra) -> Option<Ternary> {
68 if expr.variables().is_empty() {
69 let asg = crate::eval::Assignment::new();
70 return Some(crate::eval::eval(expr, &asg, alg));
71 }
72 let tt = truth_table(expr, alg);
73 let first = *tt.values.first()?;
74 if tt.values.iter().all(|v| *v == first) {
75 Some(first)
76 } else {
77 None
78 }
79}
80
81fn rewrite(expr: &Expr, alg: Algebra, opts: SimplifyOptions) -> Expr {
83 match expr {
84 Expr::Const(_) | Expr::Var(_) => expr.clone(),
85 Expr::Not(a) => {
86 let inner = rewrite(a, alg, opts);
87 if let Expr::Not(x) = &inner {
89 return (**x).clone();
90 }
91 if let Expr::Const(v) = inner {
93 return Expr::Const(alg.not(v));
94 }
95 Expr::logical_not(inner)
96 }
97 Expr::And(a, b) => {
98 let x = rewrite(a, alg, opts);
99 let y = rewrite(b, alg, opts);
100 rewrite_and(x, y, alg)
101 }
102 Expr::Or(a, b) => {
103 let x = rewrite(a, alg, opts);
104 let y = rewrite(b, alg, opts);
105 rewrite_or(x, y, alg)
106 }
107 Expr::Xor(a, b) => {
108 let x = rewrite(a, alg, opts);
109 let y = rewrite(b, alg, opts);
110 rewrite_xor(x, y, alg)
111 }
112 Expr::Implies(a, b) => {
113 let x = rewrite(a, alg, opts);
114 let y = rewrite(b, alg, opts);
115 if opts.expand_implies && alg == Algebra::Kleene {
116 return rewrite_or(Expr::logical_not(x), y, alg);
118 }
119 rewrite_implies(x, y, alg)
120 }
121 Expr::Equiv(a, b) => {
122 let x = rewrite(a, alg, opts);
123 let y = rewrite(b, alg, opts);
124 rewrite_equiv(x, y, alg)
125 }
126 }
127}
128
129fn ordered(a: Expr, b: Expr) -> (Expr, Expr) {
131 let ka = expr_key(&a);
132 let kb = expr_key(&b);
133 if kb < ka { (b, a) } else { (a, b) }
134}
135
136fn expr_key(e: &Expr) -> String {
138 e.to_infix()
139}
140
141fn rewrite_and(x: Expr, y: Expr, alg: Algebra) -> Expr {
143 if let (Expr::Const(a), Expr::Const(b)) = (&x, &y) {
145 return Expr::Const(alg.and(*a, *b));
146 }
147 if alg.is_poisonous() {
149 if x.is_unknown_const() || y.is_unknown_const() {
150 return Expr::Const(Ternary::Unknown);
151 }
152 if let Expr::Const(Ternary::True) = &x {
153 return y;
154 }
155 if let Expr::Const(Ternary::True) = &y {
156 return x;
157 }
158 let (a, b) = ordered(x, y);
160 return Expr::and(a, b);
161 }
162 if let Expr::Const(Ternary::False) = &x {
164 return Expr::Const(Ternary::False);
165 }
166 if let Expr::Const(Ternary::False) = &y {
167 return Expr::Const(Ternary::False);
168 }
169 if let Expr::Const(Ternary::True) = &x {
170 return y;
171 }
172 if let Expr::Const(Ternary::True) = &y {
173 return x;
174 }
175 if x == y {
177 return x;
178 }
179 if let Some(absorbed) = absorb(&x, &y) {
181 return absorbed;
182 }
183 if let Some(absorbed) = absorb(&y, &x) {
184 return absorbed;
185 }
186 let (a, b) = ordered(x, y);
189 Expr::and(a, b)
190}
191
192fn absorb(x: &Expr, y: &Expr) -> Option<Expr> {
194 if let Expr::Or(p, q) = y
195 && (**p == *x || **q == *x)
196 {
197 return Some(x.clone());
198 }
199 None
200}
201
202fn absorb_or(x: &Expr, y: &Expr) -> Option<Expr> {
204 if let Expr::And(p, q) = y
205 && (**p == *x || **q == *x)
206 {
207 return Some(x.clone());
208 }
209 None
210}
211
212fn rewrite_or(x: Expr, y: Expr, alg: Algebra) -> Expr {
214 if let (Expr::Const(a), Expr::Const(b)) = (&x, &y) {
215 return Expr::Const(alg.or(*a, *b));
216 }
217 if alg.is_poisonous() {
218 if x.is_unknown_const() || y.is_unknown_const() {
219 return Expr::Const(Ternary::Unknown);
220 }
221 if let Expr::Const(Ternary::False) = &x {
222 return y;
223 }
224 if let Expr::Const(Ternary::False) = &y {
225 return x;
226 }
227 let (a, b) = ordered(x, y);
229 return Expr::or(a, b);
230 }
231 if let Expr::Const(Ternary::True) = &x {
232 return Expr::Const(Ternary::True);
233 }
234 if let Expr::Const(Ternary::True) = &y {
235 return Expr::Const(Ternary::True);
236 }
237 if let Expr::Const(Ternary::False) = &x {
238 return y;
239 }
240 if let Expr::Const(Ternary::False) = &y {
241 return x;
242 }
243 if x == y {
244 return x;
245 }
246 if let Some(absorbed) = absorb_or(&x, &y) {
247 return absorbed;
248 }
249 if let Some(absorbed) = absorb_or(&y, &x) {
250 return absorbed;
251 }
252 let (a, b) = ordered(x, y);
253 Expr::or(a, b)
254}
255
256fn rewrite_xor(x: Expr, y: Expr, alg: Algebra) -> Expr {
258 if let (Expr::Const(a), Expr::Const(b)) = (&x, &y) {
259 return Expr::Const(alg.strong_add(*a, *b));
260 }
261 if alg != Algebra::Lukasiewicz && (x.is_unknown_const() || y.is_unknown_const()) {
263 return Expr::Const(Ternary::Unknown);
264 }
265 if alg == Algebra::Lukasiewicz && x.is_unknown_const() && y.is_unknown_const() {
267 return Expr::Const(Ternary::True);
268 }
269 if x == y && alg != Algebra::Lukasiewicz {
271 return x;
272 }
273 let (a, b) = ordered(x, y);
274 Expr::xor(a, b)
275}
276
277fn rewrite_implies(x: Expr, y: Expr, alg: Algebra) -> Expr {
279 if let (Expr::Const(a), Expr::Const(b)) = (&x, &y) {
280 return Expr::Const(alg.implies(*a, *b));
281 }
282 if alg != Algebra::Bochvar
284 && let Expr::Const(Ternary::False) = &x
285 {
286 return Expr::Const(Ternary::True);
287 }
288 Expr::implies(x, y)
289}
290
291fn rewrite_equiv(x: Expr, y: Expr, alg: Algebra) -> Expr {
293 if let (Expr::Const(a), Expr::Const(b)) = (&x, &y) {
294 return Expr::Const(alg.equiv(*a, *b));
295 }
296 if x == y && alg == Algebra::Lukasiewicz {
298 return Expr::Const(Ternary::True);
299 }
300 Expr::equiv(x, y)
301}
302
303pub fn semantic_fingerprint(expr: &Expr, alg: Algebra) -> u64 {
308 let vars = expr.variables();
309 let tt = truth_table(expr, alg);
310 let mut hash = 1469598103934665603u64;
311 for v in &vars {
312 for b in v.as_bytes() {
313 hash ^= u64::from(*b);
314 hash = hash.wrapping_mul(1099511628211);
315 }
316 hash ^= 0xff;
317 hash = hash.wrapping_mul(1099511628211);
318 }
319 for val in &tt.values {
320 hash ^= u64::from(val.as_char() as u8);
321 hash = hash.wrapping_mul(1099511628211);
322 }
323 hash
324}
325
326pub fn semantically_equal(a: &Expr, b: Expr, alg: Algebra) -> bool {
328 crate::eval::equivalent(a, &b, alg).equivalent
329}
330
331#[doc(hidden)]
333pub fn unused_var_map() -> VarMap {
334 VarMap::new()
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use trit_core::parser::Parser;
341
342 fn simp(src: &str, alg: Algebra) -> String {
343 let e = Parser::parse(src).unwrap();
344 simplify(&e, alg).to_infix()
345 }
346
347 #[test]
348 fn folds_constants() {
349 assert_eq!(simp("1 & 0", Algebra::Kleene), "0");
350 assert_eq!(simp("1 | 0", Algebra::Kleene), "1");
351 assert_eq!(simp("!1", Algebra::Kleene), "0");
352 }
353
354 #[test]
355 fn folds_semantic_constants() {
356 assert_eq!(simp("x | 1", Algebra::Kleene), "1");
360 assert_eq!(simp("x & 0", Algebra::Kleene), "0");
361 assert_eq!(simp("1 & 0 | 1", Algebra::Kleene), "1");
363 let e = Parser::parse("a & 1 | b & 0").unwrap();
365 assert_eq!(simplify(&e, Algebra::Kleene), Expr::Var("a".into()));
366 }
367
368 #[test]
369 fn applies_idempotence_and_identity() {
370 assert_eq!(simp("a & a", Algebra::Kleene), "a");
371 assert_eq!(simp("a | a", Algebra::Kleene), "a");
372 assert_eq!(simp("a & 1", Algebra::Kleene), "a");
373 assert_eq!(simp("a | 0", Algebra::Kleene), "a");
374 }
375
376 #[test]
377 fn bochvar_poison_differs_from_kleene() {
378 assert_eq!(simp("0 & x", Algebra::Bochvar), "X");
380 assert_eq!(simp("0 & x", Algebra::Kleene), "0");
381 }
382
383 #[test]
384 fn simplification_preserves_semantics() {
385 for src in [
386 "a & b | a",
387 "!(a & b)",
388 "(a => b) & (b => a)",
389 "a ^ b",
390 "a <=> b",
391 "a & 1 | b & 0",
392 ] {
393 let e = Parser::parse(src).unwrap();
394 let s = simplify(&e, Algebra::Kleene);
395 let r = crate::eval::equivalent(&e, &s, Algebra::Kleene);
396 assert!(r.equivalent, "simplify changed semantics: {src} -> {s}");
397 }
398 }
399
400 #[test]
401 fn simplification_preserves_semantics_in_bochvar() {
402 for src in ["a & b | a", "0 & x", "a | 1", "a & x"] {
403 let e = Parser::parse(src).unwrap();
404 let s = simplify(&e, Algebra::Bochvar);
405 let r = crate::eval::equivalent(&e, &s, Algebra::Bochvar);
406 assert!(
407 r.equivalent,
408 "bochvar simplify changed semantics: {src} -> {s}"
409 );
410 }
411 }
412
413 #[test]
414 fn absorption_only_removes_matching_operator() {
415 let e = Parser::parse("a | (a | b)").unwrap();
417 let s = simplify(&e, Algebra::Kleene);
418 assert!(
419 crate::eval::equivalent(&e, &s, Algebra::Kleene).equivalent,
420 "a | (a | b) -> {s}"
421 );
422 assert_eq!(simp("a | (a & b)", Algebra::Kleene), "a");
423 }
424
425 #[test]
426 fn xor_and_equiv_self_respect_unknown() {
427 assert_eq!(simp("a ^ a", Algebra::Kleene), "a");
429 assert_eq!(simp("a ^ a", Algebra::Bochvar), "a");
430 let e = Parser::parse("a <=> a").unwrap();
431 let s = simplify(&e, Algebra::Kleene);
432 assert!(
433 crate::eval::equivalent(&e, &s, Algebra::Kleene).equivalent,
434 "a ≡ a -> {s}"
435 );
436 assert_eq!(simp("a <=> a", Algebra::Lukasiewicz), "1");
438 }
439
440 #[test]
441 fn semantically_equal_uses_real_equivalence() {
442 let a = Parser::parse("a & b").unwrap();
443 let b = Parser::parse("b & a").unwrap();
444 assert!(semantically_equal(&a, b, Algebra::Kleene));
445
446 let a = Parser::parse("a").unwrap();
447 let b = Parser::parse("a & (b | 1)").unwrap();
448 assert!(semantically_equal(&a, b, Algebra::Kleene));
449
450 let a = Parser::parse("a | (a | b)").unwrap();
451 let b = Parser::parse("a").unwrap();
452 assert!(!semantically_equal(&a, b, Algebra::Kleene));
453 }
454}