1use std::collections::HashMap;
4
5use trit_core::algebra::Algebra;
6use trit_core::ast::Expr;
7use trit_core::bits::{Evaluator, TriBit, TruthTable};
8use trit_core::value::Ternary;
9
10#[derive(Debug, Clone, Default, PartialEq, Eq)]
12pub struct Assignment {
13 entries: Vec<(String, Ternary)>,
14}
15
16impl Assignment {
17 pub fn new() -> Self {
19 Self::default()
20 }
21
22 pub fn set(&mut self, name: impl Into<String>, value: Ternary) -> &mut Self {
24 let name = name.into();
25 match self.entries.iter_mut().find(|(n, _)| *n == name) {
26 Some(slot) => slot.1 = value,
27 None => self.entries.push((name, value)),
28 }
29 self
30 }
31
32 pub fn get(&self, name: &str) -> Option<Ternary> {
34 self.entries
35 .iter()
36 .find(|(n, _)| n == name)
37 .map(|(_, v)| *v)
38 }
39
40 pub fn variables(&self) -> Vec<String> {
42 self.entries.iter().map(|(n, _)| n.clone()).collect()
43 }
44
45 pub fn len(&self) -> usize {
47 self.entries.len()
48 }
49
50 pub fn is_empty(&self) -> bool {
52 self.entries.is_empty()
53 }
54}
55
56pub fn eval(expr: &Expr, assignment: &Assignment, alg: Algebra) -> Ternary {
60 match expr {
61 Expr::Const(v) => *v,
62 Expr::Var(name) => assignment.get(name).unwrap_or(Ternary::Unknown),
63 Expr::Not(a) => alg.not(eval(a, assignment, alg)),
64 Expr::And(a, b) => alg.and(eval(a, assignment, alg), eval(b, assignment, alg)),
65 Expr::Or(a, b) => alg.or(eval(a, assignment, alg), eval(b, assignment, alg)),
66 Expr::Xor(a, b) => alg.strong_add(eval(a, assignment, alg), eval(b, assignment, alg)),
67 Expr::Implies(a, b) => alg.implies(eval(a, assignment, alg), eval(b, assignment, alg)),
68 Expr::Equiv(a, b) => alg.equiv(eval(a, assignment, alg), eval(b, assignment, alg)),
69 }
70}
71
72pub type VarMap = HashMap<String, usize>;
74
75pub fn eval_bitwise(expr: &Expr, planes: &[TriBit], vars: &VarMap, alg: Algebra) -> TriBit {
77 match expr {
78 Expr::Const(v) => TriBit::constant(*v),
79 Expr::Var(name) => match vars.get(name) {
80 Some(idx) => planes.get(*idx).copied().unwrap_or(TriBit::UNKNOWN),
81 None => TriBit::UNKNOWN,
82 },
83 Expr::Not(a) => eval_bitwise(a, planes, vars, alg).not(),
84 Expr::And(a, b) => {
85 let (x, y) = (
86 eval_bitwise(a, planes, vars, alg),
87 eval_bitwise(b, planes, vars, alg),
88 );
89 x.and(y, alg)
90 }
91 Expr::Or(a, b) => {
92 let (x, y) = (
93 eval_bitwise(a, planes, vars, alg),
94 eval_bitwise(b, planes, vars, alg),
95 );
96 x.or(y, alg)
97 }
98 Expr::Xor(a, b) => {
99 let (x, y) = (
100 eval_bitwise(a, planes, vars, alg),
101 eval_bitwise(b, planes, vars, alg),
102 );
103 x.strong_add(y, alg)
104 }
105 Expr::Implies(a, b) => {
106 let (x, y) = (
107 eval_bitwise(a, planes, vars, alg),
108 eval_bitwise(b, planes, vars, alg),
109 );
110 x.implies(y, alg)
111 }
112 Expr::Equiv(a, b) => {
113 let (x, y) = (
114 eval_bitwise(a, planes, vars, alg),
115 eval_bitwise(b, planes, vars, alg),
116 );
117 x.equiv(y, alg)
118 }
119 }
120}
121
122fn evaluator_for(vars: &[String], alg: Algebra) -> (Evaluator, VarMap) {
124 let mut ev = Evaluator::new(alg);
125 let mut map = VarMap::new();
126 for name in vars {
127 let idx = ev.declare(name.clone());
128 map.insert(name.clone(), idx);
129 }
130 (ev, map)
131}
132
133pub fn truth_table(expr: &Expr, alg: Algebra) -> TruthTable {
137 let vars = expr.variables();
138 let (ev, map) = evaluator_for(&vars, alg);
139 ev.evaluate(|planes| eval_bitwise(expr, planes, &map, alg))
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
144pub struct EquivalenceResult {
145 pub equivalent: bool,
147 pub counterexample: Option<u64>,
149 pub counterexample_assignment: Option<Assignment>,
151 pub variable_count: usize,
153}
154
155pub fn equivalent(a: &Expr, b: &Expr, alg: Algebra) -> EquivalenceResult {
157 let mut vars = a.variables();
159 for v in b.variables() {
160 if !vars.contains(&v) {
161 vars.push(v);
162 }
163 }
164 let (ev, map) = evaluator_for(&vars, alg);
165 let total = ev.assignment_count();
166 let mut base = 0u64;
167 while base < total {
168 let planes = ev.chunk_planes(base);
169 let va = eval_bitwise(a, &planes, &map, alg);
170 let vb = eval_bitwise(b, &planes, &map, alg);
171 let take = (total - base).min(64) as u32;
172 for bit in 0..take {
173 if va.get(bit) != vb.get(bit) {
174 let index = base + bit as u64;
175 return EquivalenceResult {
176 equivalent: false,
177 counterexample: Some(index),
178 counterexample_assignment: Some(decode_assignment(index, &vars)),
179 variable_count: vars.len(),
180 };
181 }
182 }
183 base += 64;
184 }
185 EquivalenceResult {
186 equivalent: true,
187 counterexample: None,
188 counterexample_assignment: None,
189 variable_count: vars.len(),
190 }
191}
192
193pub fn assignment_index(assignment: &Assignment, vars: &[String]) -> Option<u64> {
197 let mut index = 0u64;
198 let mut power = 1u64;
199 for name in vars {
200 let digit = match assignment.get(name).unwrap_or(Ternary::Unknown) {
201 Ternary::False => 0u64,
202 Ternary::True => 1u64,
203 Ternary::Unknown => 2u64,
204 };
205 index = index.checked_add(digit.checked_mul(power)?)?;
206 power = power.checked_mul(3)?;
207 }
208 Some(index)
209}
210
211pub fn decode_assignment(index: u64, vars: &[String]) -> Assignment {
213 let mut asg = Assignment::new();
214 let mut t = index;
215 for name in vars {
216 let digit = t % 3;
217 t /= 3;
218 let v = match digit {
219 0 => Ternary::False,
220 1 => Ternary::True,
221 _ => Ternary::Unknown,
222 };
223 asg.set(name.clone(), v);
224 }
225 asg
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use trit_core::parser::Parser;
232
233 #[test]
234 fn eval_single_assignment() {
235 let e = Parser::parse("a & b").unwrap();
236 let mut asg = Assignment::new();
237 asg.set("a", Ternary::True).set("b", Ternary::False);
238 assert_eq!(eval(&e, &asg, Algebra::Kleene), Ternary::False);
239 }
240
241 #[test]
242 fn unbound_variable_is_unknown() {
243 let e = Parser::parse("a").unwrap();
244 assert_eq!(
245 eval(&e, &Assignment::new(), Algebra::Kleene),
246 Ternary::Unknown
247 );
248 }
249
250 #[test]
251 fn truth_table_covers_all_rows() {
252 let e = Parser::parse("a | b").unwrap();
253 let tt = truth_table(&e, Algebra::Kleene);
254 assert_eq!(tt.variables.len(), 2);
255 assert_eq!(tt.values.len(), 9);
256 }
257
258 #[test]
259 fn truth_table_matches_scalar_evaluation() {
260 let e = Parser::parse("(a & b) | (!a & c)").unwrap();
261 let vars = e.variables();
262 let tt = truth_table(&e, Algebra::Kleene);
263 for (i, expected) in tt.values.iter().enumerate() {
264 let asg = decode_assignment(i as u64, &vars);
265 assert_eq!(eval(&e, &asg, Algebra::Kleene), *expected, "row {i}");
266 }
267 }
268
269 #[test]
270 fn equivalent_detects_equal_expressions() {
271 let a = Parser::parse("a & b").unwrap();
272 let b = Parser::parse("b & a").unwrap();
273 let r = equivalent(&a, &b, Algebra::Kleene);
274 assert!(r.equivalent);
275 assert!(r.counterexample.is_none());
276 }
277
278 #[test]
279 fn equivalent_finds_counterexample() {
280 let a = Parser::parse("a & b").unwrap();
281 let b = Parser::parse("a | b").unwrap();
282 let r = equivalent(&a, &b, Algebra::Kleene);
283 assert!(!r.equivalent);
284 assert!(r.counterexample.is_some());
285 }
286
287 #[test]
288 fn decode_assignment_round_trips() {
289 let vars = vec!["a".to_string(), "b".to_string()];
290 let asg = decode_assignment(0, &vars);
291 assert_eq!(asg.get("a"), Some(Ternary::False));
292 assert_eq!(asg.get("b"), Some(Ternary::False));
293 let asg = decode_assignment(5, &vars);
294 assert_eq!(asg.get("a"), Some(Ternary::Unknown));
296 assert_eq!(asg.get("b"), Some(Ternary::True));
297 }
298}