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::limits::{LimitError, Limits, check_expr, check_vars};
9use trit_core::value::Ternary;
10
11#[derive(Debug, Clone, Default, PartialEq, Eq)]
13pub struct Assignment {
14 entries: Vec<(String, Ternary)>,
15}
16
17impl Assignment {
18 pub fn new() -> Self {
20 Self::default()
21 }
22
23 pub fn set(&mut self, name: impl Into<String>, value: Ternary) -> &mut Self {
25 let name = name.into();
26 match self.entries.iter_mut().find(|(n, _)| *n == name) {
27 Some(slot) => slot.1 = value,
28 None => self.entries.push((name, value)),
29 }
30 self
31 }
32
33 pub fn get(&self, name: &str) -> Option<Ternary> {
35 self.entries
36 .iter()
37 .find(|(n, _)| n == name)
38 .map(|(_, v)| *v)
39 }
40
41 pub fn variables(&self) -> Vec<String> {
43 self.entries.iter().map(|(n, _)| n.clone()).collect()
44 }
45
46 pub fn len(&self) -> usize {
48 self.entries.len()
49 }
50
51 pub fn is_empty(&self) -> bool {
53 self.entries.is_empty()
54 }
55}
56
57pub fn eval(expr: &Expr, assignment: &Assignment, alg: Algebra) -> Ternary {
61 match expr {
62 Expr::Const(v) => *v,
63 Expr::Var(name) => assignment.get(name).unwrap_or(Ternary::Unknown),
64 Expr::Not(a) => alg.not(eval(a, assignment, alg)),
65 Expr::And(a, b) => alg.and(eval(a, assignment, alg), eval(b, assignment, alg)),
66 Expr::Or(a, b) => alg.or(eval(a, assignment, alg), eval(b, assignment, alg)),
67 Expr::Xor(a, b) => alg.strong_add(eval(a, assignment, alg), eval(b, assignment, alg)),
68 Expr::Implies(a, b) => alg.implies(eval(a, assignment, alg), eval(b, assignment, alg)),
69 Expr::Equiv(a, b) => alg.equiv(eval(a, assignment, alg), eval(b, assignment, alg)),
70 }
71}
72
73pub type VarMap = HashMap<String, usize>;
75
76pub fn eval_bitwise(expr: &Expr, planes: &[TriBit], vars: &VarMap, alg: Algebra) -> TriBit {
78 match expr {
79 Expr::Const(v) => TriBit::constant(*v),
80 Expr::Var(name) => match vars.get(name) {
81 Some(idx) => planes.get(*idx).copied().unwrap_or(TriBit::UNKNOWN),
82 None => TriBit::UNKNOWN,
83 },
84 Expr::Not(a) => eval_bitwise(a, planes, vars, alg).not(),
85 Expr::And(a, b) => {
86 let (x, y) = (
87 eval_bitwise(a, planes, vars, alg),
88 eval_bitwise(b, planes, vars, alg),
89 );
90 x.and(y, alg)
91 }
92 Expr::Or(a, b) => {
93 let (x, y) = (
94 eval_bitwise(a, planes, vars, alg),
95 eval_bitwise(b, planes, vars, alg),
96 );
97 x.or(y, alg)
98 }
99 Expr::Xor(a, b) => {
100 let (x, y) = (
101 eval_bitwise(a, planes, vars, alg),
102 eval_bitwise(b, planes, vars, alg),
103 );
104 x.strong_add(y, alg)
105 }
106 Expr::Implies(a, b) => {
107 let (x, y) = (
108 eval_bitwise(a, planes, vars, alg),
109 eval_bitwise(b, planes, vars, alg),
110 );
111 x.implies(y, alg)
112 }
113 Expr::Equiv(a, b) => {
114 let (x, y) = (
115 eval_bitwise(a, planes, vars, alg),
116 eval_bitwise(b, planes, vars, alg),
117 );
118 x.equiv(y, alg)
119 }
120 }
121}
122
123fn evaluator_for(vars: &[String], alg: Algebra) -> (Evaluator, VarMap) {
125 let mut ev = Evaluator::new(alg);
126 let mut map = VarMap::new();
127 for name in vars {
128 let idx = ev.declare(name.clone());
129 map.insert(name.clone(), idx);
130 }
131 (ev, map)
132}
133
134pub fn truth_table(expr: &Expr, alg: Algebra) -> TruthTable {
138 let vars = expr.variables();
139 let (ev, map) = evaluator_for(&vars, alg);
140 ev.evaluate(|planes| eval_bitwise(expr, planes, &map, alg))
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct EquivalenceResult {
146 pub equivalent: bool,
148 pub counterexample: Option<u64>,
150 pub counterexample_assignment: Option<Assignment>,
152 pub variable_count: usize,
154}
155
156pub fn equivalent(a: &Expr, b: &Expr, alg: Algebra) -> EquivalenceResult {
158 let mut vars = a.variables();
160 for v in b.variables() {
161 if !vars.contains(&v) {
162 vars.push(v);
163 }
164 }
165 let (ev, map) = evaluator_for(&vars, alg);
166 let total = ev.assignment_count();
167 let mut base = 0u64;
168 while base < total {
169 let planes = ev.chunk_planes(base);
170 let va = eval_bitwise(a, &planes, &map, alg);
171 let vb = eval_bitwise(b, &planes, &map, alg);
172 let take = (total - base).min(64) as u32;
173 for bit in 0..take {
174 if va.get(bit) != vb.get(bit) {
175 let index = base + bit as u64;
176 return EquivalenceResult {
177 equivalent: false,
178 counterexample: Some(index),
179 counterexample_assignment: Some(decode_assignment(index, &vars)),
180 variable_count: vars.len(),
181 };
182 }
183 }
184 base += 64;
185 }
186 EquivalenceResult {
187 equivalent: true,
188 counterexample: None,
189 counterexample_assignment: None,
190 variable_count: vars.len(),
191 }
192}
193
194pub fn assignment_index(assignment: &Assignment, vars: &[String]) -> Option<u64> {
198 let mut index = 0u64;
199 let mut power = 1u64;
200 for name in vars {
201 let digit = match assignment.get(name).unwrap_or(Ternary::Unknown) {
202 Ternary::False => 0u64,
203 Ternary::True => 1u64,
204 Ternary::Unknown => 2u64,
205 };
206 index = index.checked_add(digit.checked_mul(power)?)?;
207 power = power.checked_mul(3)?;
208 }
209 Some(index)
210}
211
212pub fn decode_assignment(index: u64, vars: &[String]) -> Assignment {
214 let mut asg = Assignment::new();
215 let mut t = index;
216 for name in vars {
217 let digit = t % 3;
218 t /= 3;
219 let v = match digit {
220 0 => Ternary::False,
221 1 => Ternary::True,
222 _ => Ternary::Unknown,
223 };
224 asg.set(name.clone(), v);
225 }
226 asg
227}
228
229pub fn eval_within(
231 expr: &Expr,
232 assignment: &Assignment,
233 alg: Algebra,
234 limits: Limits,
235) -> Result<Ternary, LimitError> {
236 check_expr(expr, limits)?;
237 Ok(eval(expr, assignment, alg))
238}
239
240pub fn truth_table_within(
242 expr: &Expr,
243 alg: Algebra,
244 limits: Limits,
245) -> Result<TruthTable, LimitError> {
246 check_expr(expr, limits)?;
247 check_vars(expr, limits)?;
248 Ok(truth_table(expr, alg))
249}
250
251pub fn equivalent_within(
253 a: &Expr,
254 b: &Expr,
255 alg: Algebra,
256 limits: Limits,
257) -> Result<EquivalenceResult, LimitError> {
258 check_expr(a, limits)?;
259 check_expr(b, limits)?;
260 let mut vars = a.variables();
261 for v in b.variables() {
262 if !vars.contains(&v) {
263 vars.push(v);
264 }
265 }
266 if vars.len() > limits.max_vars {
267 return Err(LimitError::Vars {
268 limit: limits.max_vars,
269 });
270 }
271 Ok(equivalent(a, b, alg))
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use trit_core::parser::Parser;
278
279 #[test]
280 fn eval_single_assignment() {
281 let e = Parser::parse("a & b").unwrap();
282 let mut asg = Assignment::new();
283 asg.set("a", Ternary::True).set("b", Ternary::False);
284 assert_eq!(eval(&e, &asg, Algebra::Kleene), Ternary::False);
285 }
286
287 #[test]
288 fn unbound_variable_is_unknown() {
289 let e = Parser::parse("a").unwrap();
290 assert_eq!(
291 eval(&e, &Assignment::new(), Algebra::Kleene),
292 Ternary::Unknown
293 );
294 }
295
296 #[test]
297 fn truth_table_covers_all_rows() {
298 let e = Parser::parse("a | b").unwrap();
299 let tt = truth_table(&e, Algebra::Kleene);
300 assert_eq!(tt.variables.len(), 2);
301 assert_eq!(tt.values.len(), 9);
302 }
303
304 #[test]
305 fn truth_table_matches_scalar_evaluation() {
306 let e = Parser::parse("(a & b) | (!a & c)").unwrap();
307 let vars = e.variables();
308 let tt = truth_table(&e, Algebra::Kleene);
309 for (i, expected) in tt.values.iter().enumerate() {
310 let asg = decode_assignment(i as u64, &vars);
311 assert_eq!(eval(&e, &asg, Algebra::Kleene), *expected, "row {i}");
312 }
313 }
314
315 #[test]
316 fn equivalent_detects_equal_expressions() {
317 let a = Parser::parse("a & b").unwrap();
318 let b = Parser::parse("b & a").unwrap();
319 let r = equivalent(&a, &b, Algebra::Kleene);
320 assert!(r.equivalent);
321 assert!(r.counterexample.is_none());
322 }
323
324 #[test]
325 fn equivalent_finds_counterexample() {
326 let a = Parser::parse("a & b").unwrap();
327 let b = Parser::parse("a | b").unwrap();
328 let r = equivalent(&a, &b, Algebra::Kleene);
329 assert!(!r.equivalent);
330 assert!(r.counterexample.is_some());
331 }
332
333 #[test]
334 fn decode_assignment_round_trips() {
335 let vars = vec!["a".to_string(), "b".to_string()];
336 let asg = decode_assignment(0, &vars);
337 assert_eq!(asg.get("a"), Some(Ternary::False));
338 assert_eq!(asg.get("b"), Some(Ternary::False));
339 let asg = decode_assignment(5, &vars);
340 assert_eq!(asg.get("a"), Some(Ternary::Unknown));
342 assert_eq!(asg.get("b"), Some(Ternary::True));
343 }
344}