use crate::{
lexer::{Token, Tokens},
parse::Expr,
};
pub struct Equation {
root: Expr,
pub idents: Vec<char>,
}
impl Equation {
pub fn from_tokens(tokens: &[Token]) -> Self {
let root = Expr::from_tokens(tokens);
let mut idents = Vec::new();
for token in tokens {
if let Token::Ident(ident) = token {
if !idents.contains(ident) {
idents.push(*ident);
}
}
}
idents.sort_unstable();
Self { root, idents }
}
#[allow(dead_code)]
pub fn from_text(text: &str) -> Self {
let tokens = Tokens::from_text(text);
Self::from_tokens(&tokens)
}
pub fn eval_all(&self) -> Vec<(Vec<(char, bool)>, bool)> {
let combinations: u64 = 1 << self.idents.len();
let mut result = Vec::with_capacity(combinations as usize);
for bits in 0..combinations {
let mut mapping: Vec<(char, bool)> =
Vec::with_capacity(self.idents.len());
for i in (0..(self.idents.len())).rev() {
mapping.push((
self.idents[self.idents.len() - i - 1],
((bits >> i) & 1) != 0,
));
}
let eval_val = eval_expr_with(&self.root, &mapping);
result.push((mapping, eval_val));
}
result
}
}
fn eval_expr_with(expr: &Expr, mapping: &[(char, bool)]) -> bool {
match expr {
Expr::Ident(ident) => mapping
.iter()
.find(|(id, _)| id == ident)
.map(|(_, val)| *val)
.unwrap(), Expr::And(val) => {
eval_expr_with(&val.0, mapping) && eval_expr_with(&val.1, mapping)
}
Expr::Or(val) => {
eval_expr_with(&val.0, mapping) || eval_expr_with(&val.1, mapping)
}
Expr::Equals(val) => {
eval_expr_with(&val.0, mapping) == eval_expr_with(&val.1, mapping)
}
Expr::NotEquals(val) => {
eval_expr_with(&val.0, mapping) != eval_expr_with(&val.1, mapping)
}
Expr::Not(val) => !eval_expr_with(val, mapping),
Expr::Implication(val) => {
if eval_expr_with(&val.0, mapping) {
eval_expr_with(&val.1, mapping)
} else {
true
}
}
Expr::True => true,
Expr::False => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constants() {
let equation = Equation::from_text("1 or 0");
assert_eq!(eval_expr_with(&equation.root, &[]), true);
}
#[test]
fn find_idents() {
let equation = Equation::from_text("a or b and c and not d");
assert_eq!(equation.idents, vec!['a', 'b', 'c', 'd']);
}
#[test]
fn eval_with() {
let equation = Equation::from_text("a or b");
assert_eq!(
eval_expr_with(&equation.root, &vec![('a', true), ('b', false)]),
true
);
assert_eq!(
eval_expr_with(&equation.root, &vec![('a', false), ('b', false)]),
false
);
}
#[test]
fn eval_all() {
let equation = Equation::from_text("a or b");
assert_eq!(
equation.eval_all(),
vec![
(vec![('a', false), ('b', false)], false),
(vec![('a', false), ('b', true)], true),
(vec![('a', true), ('b', false)], true),
(vec![('a', true), ('b', true)], true),
]
)
}
#[test]
fn implication() {
let equation = Equation::from_text("a -> b");
assert_eq!(
equation.eval_all(),
vec![
(vec![('a', false), ('b', false)], true),
(vec![('a', false), ('b', true)], true),
(vec![('a', true), ('b', false)], false),
(vec![('a', true), ('b', true)], true),
]
)
}
#[test]
fn equals() {
let equation = Equation::from_text("a==b");
assert_eq!(
equation.eval_all(),
vec![
(vec![('a', false), ('b', false)], true),
(vec![('a', false), ('b', true)], false),
(vec![('a', true), ('b', false)], false),
(vec![('a', true), ('b', true)], true),
]
)
}
#[test]
fn not_equals() {
let equation = Equation::from_text("a!=b");
assert_eq!(
equation.eval_all(),
vec![
(vec![('a', false), ('b', false)], false),
(vec![('a', false), ('b', true)], true),
(vec![('a', true), ('b', false)], true),
(vec![('a', true), ('b', true)], false),
]
)
}
}