Skip to content

Commit

Permalink
refactor!: made Evaluate support with_default, checked & return all e…
Browse files Browse the repository at this point in the history
…rr vars

Changed all uses in Expressions and TruthTables.
  • Loading branch information
AurumTheEnd committed Apr 18, 2024
1 parent a307d5e commit 8ecd8ed
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 35 deletions.
51 changes: 37 additions & 14 deletions src/expressions/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ use std::fmt::{Debug, Display, Error, Formatter};
use std::hash::Hash;
use std::ops::{BitAnd, BitOr};

use itertools::Itertools;

use crate::parser::{parse_tokens, tokenize, ParseError};
use crate::traits::{Evaluate, GatherLiterals, Parse, PowerSet, SemanticEq};

Expand Down Expand Up @@ -61,35 +59,60 @@ impl<TLiteral: Debug + Clone + Eq + Hash> PowerSet<TLiteral> for Expression<TLit
}

impl<TLiteral: Debug + Clone + Eq + Hash> Evaluate<TLiteral> for Expression<TLiteral> {
fn evaluate(&self, literal_values: &HashMap<TLiteral, bool>) -> bool {
fn evaluate_with_default(
&self,
literal_values: &HashMap<TLiteral, bool>,
default_value: bool,
) -> bool {
match self.node() {
Literal(t) => *literal_values.get(t).unwrap_or(&false),
Literal(t) => *literal_values.get(t).unwrap_or(&default_value),
Constant(value) => *value,
And(values) => values.iter().all(|e| e.evaluate(literal_values)),
Or(values) => values.iter().any(|e| e.evaluate(literal_values)),
Not(x) => !x.evaluate(literal_values),
}
}

fn evaluate_with_err(
fn evaluate_checked(
&self,
literal_values: &HashMap<TLiteral, bool>,
) -> Result<bool, Vec<TLiteral>> {
let mut errors = vec![];

let ok_result = self.evaluate_checked_rec(&literal_values, &mut errors);

if errors.is_empty() {
Ok(ok_result)
} else {
Err(errors)
}
}
}

impl<TLiteral: Debug + Clone + Eq + Hash> Expression<TLiteral> {
fn evaluate_checked_rec(
&self,
literal_values: &HashMap<TLiteral, bool>,
) -> Result<bool, TLiteral> {
mut err_values: &mut Vec<TLiteral>,
) -> bool {
match self.node() {
Literal(t) => match literal_values.get(t) {
None => Err(t.clone()),
Some(valuation) => Ok(*valuation),
None => {
err_values.push(t.clone());
true // will be unused
}
Some(valuation) => *valuation,
},
Constant(value) => Ok(*value),
Not(inner) => inner.evaluate_with_err(literal_values).map(|value| !value),
Constant(value) => *value,
Not(inner) => inner.evaluate_checked_rec(literal_values, &mut err_values),
And(expressions) => expressions
.iter()
.map(|e| e.evaluate_with_err(literal_values))
.fold_ok(true, BitAnd::bitand),
.map(|e| e.evaluate_checked_rec(literal_values, &mut err_values))
.fold(true, BitAnd::bitand),
Or(expressions) => expressions
.iter()
.map(|e| e.evaluate_with_err(literal_values))
.fold_ok(false, BitOr::bitor),
.map(|e| e.evaluate_checked_rec(literal_values, &mut err_values))
.fold(false, BitOr::bitor),
}
}
}
Expand Down
59 changes: 51 additions & 8 deletions src/table/traits/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,70 @@ use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::hash::Hash;

use crate::table::utils::{values_to_row_index, values_to_row_index_checked};
use crate::table::utils::{values_to_row_index_checked, values_to_row_index_with_default};
use crate::table::TruthTable;
use crate::traits::Evaluate;

impl<TLiteral: Debug + Display + Clone + Eq + Hash + Ord> Evaluate<TLiteral>
for TruthTable<TLiteral>
{
fn evaluate(&self, literal_values: &HashMap<TLiteral, bool>) -> bool {
let index = values_to_row_index(&self.inputs, literal_values);
fn evaluate_with_default(
&self,
literal_values: &HashMap<TLiteral, bool>,
default_value: bool,
) -> bool {
let index = values_to_row_index_with_default(&self.inputs, literal_values, default_value);

self.outputs[index]
}

fn evaluate_with_err(
fn evaluate_checked(
&self,
literal_values: &HashMap<TLiteral, bool>,
) -> Result<bool, TLiteral> {
let index = values_to_row_index_checked(&self.inputs, literal_values)
// TODO change after evaluate trait refactor
.map_err(|not_found| not_found[0].clone())?;
) -> Result<bool, Vec<TLiteral>> {
let index = values_to_row_index_checked(&self.inputs, literal_values)?;

Ok(self.outputs[index])
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_evaluate_variables_match_ok() {
let input_table = TruthTable::new(vec!["a", "b"], vec![true, true, true, false]);

let pairs = [("a", true), ("b", true)];
let mapping = HashMap::<&str, bool>::from_iter(pairs);

assert_eq!(input_table.evaluate(&mapping), false);
assert_eq!(input_table.evaluate_with_default(&mapping, true), false);
assert_eq!(input_table.evaluate_checked(&mapping), Ok(false));
}

#[test]
fn test_evaluate_too_many_variables_ok() {
let input_table = TruthTable::new(vec!["a", "b"], vec![true, true, true, false]);

let pairs = [("a", true), ("b", true), ("c", false)];
let mapping = HashMap::<&str, bool>::from_iter(pairs);

assert_eq!(input_table.evaluate(&mapping), false);
assert_eq!(input_table.evaluate_with_default(&mapping, true), false);
assert_eq!(input_table.evaluate_checked(&mapping), Ok(false));
}

#[test]
fn test_evaluate_too_few_variables_ok() {
let input_table = TruthTable::new(vec!["a", "b"], vec![true, true, true, false]);

let pairs = [("a", true)];
let mapping = HashMap::<&str, bool>::from_iter(pairs);

assert_eq!(input_table.evaluate(&mapping), true);
assert_eq!(input_table.evaluate_with_default(&mapping, true), false);
assert_eq!(input_table.evaluate_checked(&mapping), Err(vec!["b"]));
}
}
4 changes: 3 additions & 1 deletion src/table/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub use row_index_to_valuation::row_index_to_valuation;
pub use string_to_bool::string_to_bool;
pub use valuation_to_row_index::{values_to_row_index, values_to_row_index_checked};
pub use valuation_to_row_index::{
values_to_row_index, values_to_row_index_checked, values_to_row_index_with_default,
};

mod row_index_to_valuation;
mod string_to_bool;
Expand Down
29 changes: 20 additions & 9 deletions src/table/utils/valuation_to_row_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,29 @@ use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::hash::Hash;

// Any errors are ignored
pub fn values_to_row_index<TLiteral: Debug + Clone + Eq + Hash + Ord>(
order: &[TLiteral],
valuation: &HashMap<TLiteral, bool>,
) -> usize {
let (index, _errors) = values_to_row_index_common(order, valuation);
let (index, _errors) = values_to_row_index_common(order, valuation, Some(false));
index
}

pub fn values_to_row_index_with_default<TLiteral: Debug + Clone + Eq + Hash + Ord>(
order: &[TLiteral],
valuation: &HashMap<TLiteral, bool>,
default_value: bool,
) -> usize {
let (index, _errors) = values_to_row_index_common(order, valuation, Some(default_value));
index
}

pub fn values_to_row_index_checked<TLiteral: Debug + Display + Clone + Eq + Hash + Ord>(
order: &[TLiteral],
valuation: &HashMap<TLiteral, bool>,
) -> Result<usize, Vec<TLiteral>> {
let (index, errors) = values_to_row_index_common(order, valuation);
let (index, errors) = values_to_row_index_common(order, valuation, None);

if errors.is_empty() {
Ok(index)
Expand All @@ -26,20 +36,22 @@ pub fn values_to_row_index_checked<TLiteral: Debug + Display + Clone + Eq + Hash
fn values_to_row_index_common<TLiteral: Debug + Clone + Eq + Hash + Ord>(
order: &[TLiteral],
valuation: &HashMap<TLiteral, bool>,
default_value: Option<bool>,
) -> (usize, Vec<TLiteral>) {
let result = order
.iter()
.enumerate()
.rev()
.map(|(order_index, literal)| {
if let Some(literal_is_true) = valuation.get(literal) {
if *literal_is_true {
match (valuation.get(literal), &default_value) {
(Some(true), _) | (None, Some(true)) => {
// found true, so do not use default or not found, but can use default true
Ok(2_usize.pow((order.len() - order_index - 1) as u32))
} else {
Ok(0)
}
} else {
Err(literal)

(Some(false), _) | (None, Some(false)) => Ok(0), // found false or can use default false

(None, None) => Err(literal), // did not find and cannot use default
}
})
.fold(
Expand All @@ -52,7 +64,6 @@ fn values_to_row_index_common<TLiteral: Debug + Clone + Eq + Hash + Ord>(
}
},
);
// .fold_ok(0, std::ops::Add::add);

result
}
31 changes: 28 additions & 3 deletions src/traits/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,33 @@ use std::fmt::Debug;
use std::hash::Hash;

pub trait Evaluate<TLiteral: Debug + Clone + Eq + Hash> {
fn evaluate(&self, literal_values: &HashMap<TLiteral, bool>) -> bool;
/// Evaluates the expression with a given valuation of variable names to their values (i.e. `x_0: true` or `x_0: false`),
/// returning a boolean value.
/// If a variable is found in the expression but not in the `literal_values` parameter, it is treated as false.
///
/// Defaults to `self.evaluate_with_default(literal_values, false)`.
fn evaluate(&self, literal_values: &HashMap<TLiteral, bool>) -> bool {
self.evaluate_with_default(literal_values, false)
}

fn evaluate_with_err(&self, literal_values: &HashMap<TLiteral, bool>)
-> Result<bool, TLiteral>;
/// Evaluates the expression with a given valuation of variable names to their values (i.e. `x_0: true` or `x_0: false`),
/// returning a boolean value.
///
/// If a variable is found in the expression but not in the `literal_values` parameter, the passed `default_value` is used.
fn evaluate_with_default(
&self,
literal_values: &HashMap<TLiteral, bool>,
default_value: bool,
) -> bool;

/// Evaluates the expression with a given valuation of variable names to their values (i.e. `x_0: true` or `x_0: false`)/
///
/// If a variable is found in the expression but not in the `literal_values` parameter,
/// the function returns an `Vector` of all such variables.
///
/// Otherwise, an `Ok(value)` is returned.
fn evaluate_checked(
&self,
literal_values: &HashMap<TLiteral, bool>,
) -> Result<bool, Vec<TLiteral>>;
}

0 comments on commit 8ecd8ed

Please sign in to comment.