1use std::collections::{HashMap, HashSet};
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};
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 {
63 match expr {
64 Expr::Const(v) => *v,
65 Expr::Var(name) => assignment.get(name).unwrap_or(Ternary::Unknown),
66 Expr::Not(a) => alg.not(eval(a, assignment, alg)),
67 Expr::And(a, b) => alg.and(eval(a, assignment, alg), eval(b, assignment, alg)),
68 Expr::Or(a, b) => alg.or(eval(a, assignment, alg), eval(b, assignment, alg)),
69 Expr::Xor(a, b) => alg.strong_add(eval(a, assignment, alg), eval(b, assignment, alg)),
70 Expr::Implies(a, b) => alg.implies(eval(a, assignment, alg), eval(b, assignment, alg)),
71 Expr::Equiv(a, b) => alg.equiv(eval(a, assignment, alg), eval(b, assignment, alg)),
72 }
73}
74
75pub type VarMap = HashMap<String, usize>;
77
78pub fn eval_bitwise(expr: &Expr, planes: &[TriBit], vars: &VarMap, alg: Algebra) -> TriBit {
80 match expr {
81 Expr::Const(v) => TriBit::constant(*v),
82 Expr::Var(name) => match vars.get(name) {
83 Some(idx) => planes.get(*idx).copied().unwrap_or(TriBit::UNKNOWN),
84 None => TriBit::UNKNOWN,
85 },
86 Expr::Not(a) => eval_bitwise(a, planes, vars, alg).not(),
87 Expr::And(a, b) => {
88 let (x, y) = (
89 eval_bitwise(a, planes, vars, alg),
90 eval_bitwise(b, planes, vars, alg),
91 );
92 x.and(y, alg)
93 }
94 Expr::Or(a, b) => {
95 let (x, y) = (
96 eval_bitwise(a, planes, vars, alg),
97 eval_bitwise(b, planes, vars, alg),
98 );
99 x.or(y, alg)
100 }
101 Expr::Xor(a, b) => {
102 let (x, y) = (
103 eval_bitwise(a, planes, vars, alg),
104 eval_bitwise(b, planes, vars, alg),
105 );
106 x.strong_add(y, alg)
107 }
108 Expr::Implies(a, b) => {
109 let (x, y) = (
110 eval_bitwise(a, planes, vars, alg),
111 eval_bitwise(b, planes, vars, alg),
112 );
113 x.implies(y, alg)
114 }
115 Expr::Equiv(a, b) => {
116 let (x, y) = (
117 eval_bitwise(a, planes, vars, alg),
118 eval_bitwise(b, planes, vars, alg),
119 );
120 x.equiv(y, alg)
121 }
122 }
123}
124
125fn evaluator_for(vars: &[String], alg: Algebra) -> (Evaluator, VarMap) {
127 let mut ev = Evaluator::new(alg);
128 let mut map = VarMap::new();
129 for name in vars {
130 let idx = ev.declare(name.clone());
131 map.insert(name.clone(), idx);
132 }
133 (ev, map)
134}
135
136pub fn truth_table(expr: &Expr, alg: Algebra) -> Result<TruthTable, LimitError> {
141 truth_table_within(expr, alg, Limits::default())
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct EquivalenceResult {
147 pub equivalent: bool,
149 pub counterexample: Option<u64>,
151 pub counterexample_assignment: Option<Assignment>,
153 pub variable_count: usize,
155}
156
157pub fn equivalent(a: &Expr, b: &Expr, alg: Algebra) -> Result<EquivalenceResult, LimitError> {
159 let limits = Limits::default();
160 check_expr(a, limits)?;
161 check_expr(b, limits)?;
162 let vars = union_vars(a, b);
164 equivalent_with_vars(a, b, alg, &vars, limits)
165}
166
167fn union_vars(a: &Expr, b: &Expr) -> Vec<String> {
168 let mut vars = a.variables();
169 for v in b.variables() {
170 if !vars.contains(&v) {
171 vars.push(v);
172 }
173 }
174 vars
175}
176
177fn equivalent_with_vars(
178 a: &Expr,
179 b: &Expr,
180 alg: Algebra,
181 vars: &[String],
182 limits: Limits,
183) -> Result<EquivalenceResult, LimitError> {
184 if vars.len() > limits.max_vars {
185 return Err(LimitError::Vars {
186 limit: limits.max_vars,
187 });
188 }
189 let (ev, map) = evaluator_for(vars, alg);
190 let total = ev.assignment_count()?;
191 if total > limits.max_rows as u64 {
192 return Err(LimitError::Rows {
193 limit: limits.max_rows,
194 });
195 }
196 let mut base = 0u64;
197 while base < total {
198 let planes = ev.chunk_planes(base)?;
199 let va = eval_bitwise(a, &planes, &map, alg);
200 let vb = eval_bitwise(b, &planes, &map, alg);
201 let take = (total - base).min(64) as u32;
202 for bit in 0..take {
203 if va.get(bit) != vb.get(bit) {
204 let index = base + bit as u64;
205 return Ok(EquivalenceResult {
206 equivalent: false,
207 counterexample: Some(index),
208 counterexample_assignment: Some(decode_assignment(index, vars)),
209 variable_count: vars.len(),
210 });
211 }
212 }
213 base += 64;
214 }
215 Ok(EquivalenceResult {
216 equivalent: true,
217 counterexample: None,
218 counterexample_assignment: None,
219 variable_count: vars.len(),
220 })
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
225pub enum AssignmentCodecError {
226 #[error("变量数 {vars} 的赋值编号超出 u64 可表示范围")]
228 Overflow {
229 vars: usize,
231 },
232 #[error("赋值编号 {index} 超出 {vars} 个变量的有效域 3^{vars}")]
234 OutOfDomain {
235 index: u64,
237 vars: usize,
239 },
240 #[error("变量名重复:{0}")]
242 DuplicateVariable(String),
243}
244
245pub fn try_assignment_index(
249 assignment: &Assignment,
250 vars: &[String],
251) -> Result<u64, AssignmentCodecError> {
252 let mut seen = HashSet::new();
253 let mut index = 0u64;
254 let mut power = 1u64;
255 let mut power_overflow = false;
256 for (i, name) in vars.iter().enumerate() {
257 if !seen.insert(name.as_str()) {
258 return Err(AssignmentCodecError::DuplicateVariable(name.clone()));
259 }
260 if i > 0 {
261 match power.checked_mul(3) {
262 Some(next) => power = next,
263 None => power_overflow = true,
264 }
265 }
266 let digit = match assignment.get(name).unwrap_or(Ternary::Unknown) {
267 Ternary::False => 0u64,
268 Ternary::True => 1u64,
269 Ternary::Unknown => 2u64,
270 };
271 if digit == 0 {
272 continue;
273 }
274 if power_overflow {
275 return Err(AssignmentCodecError::Overflow { vars: vars.len() });
276 }
277 let term = digit
278 .checked_mul(power)
279 .ok_or(AssignmentCodecError::Overflow { vars: vars.len() })?;
280 index = index
281 .checked_add(term)
282 .ok_or(AssignmentCodecError::Overflow { vars: vars.len() })?;
283 }
284 Ok(index)
285}
286
287pub fn assignment_index(assignment: &Assignment, vars: &[String]) -> Option<u64> {
291 try_assignment_index(assignment, vars).ok()
292}
293
294pub fn try_decode_assignment(
299 index: u64,
300 vars: &[String],
301) -> Result<Assignment, AssignmentCodecError> {
302 let mut seen = HashSet::new();
303 for name in vars {
304 if !seen.insert(name.as_str()) {
305 return Err(AssignmentCodecError::DuplicateVariable(name.clone()));
306 }
307 }
308 let mut asg = Assignment::new();
309 let mut t = index;
310 for name in vars {
311 let digit = t % 3;
312 t /= 3;
313 let v = match digit {
314 0 => Ternary::False,
315 1 => Ternary::True,
316 _ => Ternary::Unknown,
317 };
318 asg.set(name.clone(), v);
319 }
320 if t != 0 {
321 return Err(AssignmentCodecError::OutOfDomain {
322 index,
323 vars: vars.len(),
324 });
325 }
326 Ok(asg)
327}
328
329pub fn decode_assignment(index: u64, vars: &[String]) -> Assignment {
334 let mut asg = Assignment::new();
335 let mut t = index;
336 for name in vars {
337 let digit = t % 3;
338 t /= 3;
339 let v = match digit {
340 0 => Ternary::False,
341 1 => Ternary::True,
342 _ => Ternary::Unknown,
343 };
344 asg.set(name.clone(), v);
345 }
346 asg
347}
348
349pub fn eval_within(
351 expr: &Expr,
352 assignment: &Assignment,
353 alg: Algebra,
354 limits: Limits,
355) -> Result<Ternary, LimitError> {
356 check_expr(expr, limits)?;
357 Ok(eval(expr, assignment, alg))
358}
359
360pub fn truth_table_within(
362 expr: &Expr,
363 alg: Algebra,
364 limits: Limits,
365) -> Result<TruthTable, LimitError> {
366 check_expr(expr, limits)?;
367 let vars = expr.variables();
368 if vars.len() > limits.max_vars {
369 return Err(LimitError::Vars {
370 limit: limits.max_vars,
371 });
372 }
373 let (ev, map) = evaluator_for(&vars, alg);
374 ev.evaluate_within(|planes| eval_bitwise(expr, planes, &map, alg), limits)
375}
376
377pub fn equivalent_within(
379 a: &Expr,
380 b: &Expr,
381 alg: Algebra,
382 limits: Limits,
383) -> Result<EquivalenceResult, LimitError> {
384 check_expr(a, limits)?;
385 check_expr(b, limits)?;
386 let vars = union_vars(a, b);
387 equivalent_with_vars(a, b, alg, &vars, limits)
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use trit_core::parser::Parser;
394
395 #[test]
396 fn eval_single_assignment() {
397 let e = Parser::parse("a & b").unwrap();
398 let mut asg = Assignment::new();
399 asg.set("a", Ternary::True).set("b", Ternary::False);
400 assert_eq!(eval(&e, &asg, Algebra::Kleene), Ternary::False);
401 }
402
403 #[test]
404 fn unbound_variable_is_unknown() {
405 let e = Parser::parse("a").unwrap();
406 assert_eq!(
407 eval(&e, &Assignment::new(), Algebra::Kleene),
408 Ternary::Unknown
409 );
410 }
411
412 #[test]
413 fn truth_table_covers_all_rows() {
414 let e = Parser::parse("a | b").unwrap();
415 let tt = truth_table(&e, Algebra::Kleene).unwrap();
416 assert_eq!(tt.variables.len(), 2);
417 assert_eq!(tt.values.len(), 9);
418 }
419
420 #[test]
421 fn truth_table_matches_scalar_evaluation() {
422 let e = Parser::parse("(a & b) | (!a & c)").unwrap();
423 let vars = e.variables();
424 let tt = truth_table(&e, Algebra::Kleene).unwrap();
425 for (i, expected) in tt.values.iter().enumerate() {
426 let asg = decode_assignment(i as u64, &vars);
427 assert_eq!(eval(&e, &asg, Algebra::Kleene), *expected, "row {i}");
428 }
429 }
430
431 #[test]
432 fn truth_table_refuses_unbudgeted_variable_count() {
433 let e = Parser::parse(
434 &(0..13)
435 .map(|i| format!("v{i}"))
436 .collect::<Vec<_>>()
437 .join(" & "),
438 )
439 .unwrap();
440 assert_eq!(
441 truth_table(&e, Algebra::Kleene).unwrap_err(),
442 LimitError::Vars { limit: 12 }
443 );
444 }
445
446 #[test]
447 fn equivalent_detects_equal_expressions() {
448 let a = Parser::parse("a & b").unwrap();
449 let b = Parser::parse("b & a").unwrap();
450 let r = equivalent(&a, &b, Algebra::Kleene).unwrap();
451 assert!(r.equivalent);
452 assert!(r.counterexample.is_none());
453 }
454
455 #[test]
456 fn equivalent_finds_counterexample() {
457 let a = Parser::parse("a & b").unwrap();
458 let b = Parser::parse("a | b").unwrap();
459 let r = equivalent(&a, &b, Algebra::Kleene).unwrap();
460 assert!(!r.equivalent);
461 assert!(r.counterexample.is_some());
462 }
463
464 #[test]
465 fn decode_assignment_round_trips() {
466 let vars = vec!["a".to_string(), "b".to_string()];
467 let asg = decode_assignment(0, &vars);
468 assert_eq!(asg.get("a"), Some(Ternary::False));
469 assert_eq!(asg.get("b"), Some(Ternary::False));
470 let asg = decode_assignment(5, &vars);
471 assert_eq!(asg.get("a"), Some(Ternary::Unknown));
473 assert_eq!(asg.get("b"), Some(Ternary::True));
474 }
475
476 #[test]
477 fn checked_codec_rejects_out_of_domain_and_duplicates() {
478 let one = vec!["a".to_string()];
479 assert_eq!(
480 try_decode_assignment(3, &one).unwrap_err(),
481 AssignmentCodecError::OutOfDomain { index: 3, vars: 1 }
482 );
483 assert!(try_decode_assignment(2, &one).is_ok());
484 let dup = vec!["a".to_string(), "a".to_string()];
485 assert!(matches!(
486 try_decode_assignment(0, &dup).unwrap_err(),
487 AssignmentCodecError::DuplicateVariable(_)
488 ));
489 let mut asg = Assignment::new();
490 asg.set("a", Ternary::False);
491 assert!(matches!(
492 try_assignment_index(&asg, &dup).unwrap_err(),
493 AssignmentCodecError::DuplicateVariable(_)
494 ));
495 }
496
497 #[test]
498 fn assignment_index_does_not_over_reject_high_zero_digits() {
499 let vars: Vec<String> = (0..42).map(|i| format!("v{i}")).collect();
501 let mut all_false = Assignment::new();
502 for name in &vars {
503 all_false.set(name.clone(), Ternary::False);
504 }
505 assert_eq!(assignment_index(&all_false, &vars), Some(0));
506 let mut high = all_false.clone();
508 high.set("v40", Ternary::True);
509 assert_eq!(assignment_index(&high, &vars), Some(3u64.pow(40)));
510 let mut over = all_false;
511 over.set("v41", Ternary::True);
512 assert!(matches!(
513 try_assignment_index(&over, &vars).unwrap_err(),
514 AssignmentCodecError::Overflow { .. }
515 ));
516 }
517}