use itertools::Itertools;
use std::collections::{HashMap, HashSet, VecDeque};
use super::types::SymbolType;
use super::{expr::Terminal, Expr};
use super::{Bridge, Symbol, Tristate};
use thiserror::Error;
pub type Assignments = HashMap<String, Tristate>;
#[derive(Debug, Clone)]
pub struct Ambiguity {
pub symbol: String,
pub clauses: Vec<String>,
}
#[derive(Error, Debug, Clone)]
pub enum SolveError {
#[error("the expression is provably unsatisfiable")]
Unsatisfiable,
#[error("complex negated expressions are unsupported")]
ComplexNot,
#[error("expression contains unsupported constructs")]
UnsupportedConstituents,
#[error("expression contains an ambiguous comparison")]
AmbiguousComparison,
#[error("encountered an invalid symbol")]
InvalidSymbol,
#[error("encountered an invalid expression")]
InvalidExpression,
#[error("expression would require Tristate::Mod for boolean symbol {symbol}")]
RequiresModForBoolean { symbol: String },
#[error("solver yielded conflicting assignment for symbol {symbol} (both {a} and {b})")]
ConflictingAssignment { symbol: String, a: Tristate, b: Tristate },
#[error("solution is ambiguous, please satisfy at least one of the expressions for each symbol")]
AmbiguousSolution { symbols: Vec<Ambiguity> },
}
pub trait Solver {
fn satisfy(&self, bridge: &Bridge, expr: &Expr, desired_value: Tristate) -> Result<Assignments, SolveError>;
}
pub struct SolverConfig {
pub solver: Box<dyn Solver>,
pub desired_value: Tristate,
pub recursive: bool,
}
impl Default for SolverConfig {
fn default() -> Self {
SolverConfig {
solver: Box::new(SimpleSolver {}),
desired_value: Tristate::Yes,
recursive: false,
}
}
}
pub fn satisfy(bridge: &Bridge, symbol: String, config: SolverConfig) -> Result<Vec<(String, Tristate)>, SolveError> {
let mut assignments: Vec<(String, Tristate)> = Vec::new();
let mut ambiguities = Vec::new();
let mut dependencies = HashMap::<String, Vec<String>>::new();
let mut solved_symbols = HashMap::new();
let mut done = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(symbol);
while let Some(symbol) = queue.pop_front() {
if !done.insert(symbol.clone()) {
continue;
}
let bridge_symbol = bridge.symbol(&symbol).ok_or(SolveError::InvalidSymbol)?;
let expr = bridge_symbol
.visibility_expression()
.map_err(|_| SolveError::InvalidExpression)?;
let expr = if bridge_symbol.prompt_count() == 0 {
Expr::And(
Box::new(expr),
Box::new({
let expr = bridge_symbol
.reverse_dependencies_bare()
.map_err(|_| SolveError::InvalidExpression)?;
if let Some(expr) = expr {
let clauses = expr.or_clauses();
match clauses.len() {
0 => Expr::Const(true),
1 => clauses[0].clone(),
_ => {
ambiguities.push(Ambiguity {
symbol: symbol.clone(),
clauses: clauses.into_iter().map(|x| x.display(bridge).to_string()).collect_vec(),
});
Expr::Const(true)
}
}
} else {
Expr::Const(true)
}
}),
)
} else {
expr
};
let mut new_assignments = config.solver.satisfy(bridge, &expr, config.desired_value)?;
let depends_on: Vec<String> = new_assignments
.iter()
.filter(|(_, &v)| v != Tristate::No)
.map(|(k, _)| k.clone())
.collect();
new_assignments.retain(|k, _| bridge.symbol(k).unwrap().prompt_count() > 0);
solved_symbols.insert(symbol.clone(), new_assignments);
if !config.recursive {
dependencies.insert(symbol.clone(), vec![]);
break;
}
queue.extend(depends_on.iter().cloned());
dependencies.insert(symbol.clone(), depends_on);
}
let mut merged_assignments = HashMap::new();
for ass in solved_symbols.values() {
merge(&mut merged_assignments, ass.clone())?;
}
let mut already_assigned_symbols = HashSet::new();
while !dependencies.is_empty() {
let (fulfilled_symbols, mut remaining_symbols): (HashMap<String, Vec<String>>, HashMap<String, Vec<String>>) =
dependencies.into_iter().partition(|(_, v)| v.is_empty());
for fs in fulfilled_symbols.keys() {
solved_symbols.get_mut(fs).unwrap().drain().for_each(|e| {
let (k, _) = &e;
if !already_assigned_symbols.contains(k) {
already_assigned_symbols.insert(k.clone());
assignments.push(e);
}
});
}
for v in remaining_symbols.values_mut() {
v.retain(|s| !fulfilled_symbols.contains_key(s))
}
dependencies = remaining_symbols;
}
if !ambiguities.is_empty() {
return Err(SolveError::AmbiguousSolution { symbols: ambiguities });
}
Ok(assignments)
}
pub struct SimpleSolver {}
impl SimpleSolver {
fn satisfy_eq(&self, a: &Symbol, b: Tristate) -> Result<Assignments, SolveError> {
let name = a.name_owned().ok_or(SolveError::InvalidSymbol)?;
if b == Tristate::Mod && a.symbol_type() != SymbolType::Tristate {
return Err(SolveError::RequiresModForBoolean { symbol: name });
}
Ok(HashMap::from([(name, b)]))
}
fn satisfy_neq(&self, a: &Symbol, b: Tristate, desired_value: Tristate) -> Result<Assignments, SolveError> {
let name = a.name_owned().ok_or(SolveError::InvalidSymbol)?;
let value = match b {
Tristate::No => desired_value,
Tristate::Mod => Tristate::Yes,
Tristate::Yes => Tristate::Mod,
};
if value == Tristate::Mod && a.symbol_type() != SymbolType::Tristate {
return Err(SolveError::RequiresModForBoolean { symbol: name });
}
Ok(HashMap::from([(name, value)]))
}
}
impl Solver for SimpleSolver {
fn satisfy(&self, bridge: &Bridge, expr: &Expr, desired_value: Tristate) -> Result<Assignments, SolveError> {
if expr.eval().map_err(|_| SolveError::UnsupportedConstituents)? >= desired_value {
return Ok(HashMap::new());
}
Ok(match expr {
Expr::And(a, b) => {
let mut a = self.satisfy(bridge, a, desired_value)?;
merge(&mut a, self.satisfy(bridge, b, desired_value)?)?;
a
}
Expr::Or(a, b) => {
if let Ok(assignment) = self.satisfy(bridge, a, desired_value) {
assignment
} else {
self.satisfy(bridge, b, desired_value)?
}
}
Expr::Const(false) => return Err(SolveError::Unsatisfiable),
Expr::Const(true) => HashMap::new(),
Expr::Not(a) => match &**a {
Expr::Terminal(Terminal::Eq(a, b)) => {
let a = bridge.wrap_symbol(*a);
let b = bridge.wrap_symbol(*b);
if a.is_const() {
self.satisfy_neq(&b, a.get_tristate_value(), desired_value)?
} else if b.is_const() {
self.satisfy_neq(&a, b.get_tristate_value(), desired_value)?
} else {
return Err(SolveError::AmbiguousComparison);
}
}
Expr::Terminal(Terminal::Neq(a, b)) => {
let a = bridge.wrap_symbol(*a);
let b = bridge.wrap_symbol(*b);
if a.is_const() {
self.satisfy_eq(&b, a.get_tristate_value())?
} else if b.is_const() {
self.satisfy_eq(&a, b.get_tristate_value())?
} else {
return Err(SolveError::AmbiguousComparison);
}
}
Expr::Terminal(Terminal::Symbol(s)) => self.satisfy_eq(&bridge.wrap_symbol(*s), Tristate::No)?,
Expr::Terminal(_) => return Err(SolveError::UnsupportedConstituents),
_ => return Err(SolveError::ComplexNot),
},
Expr::Terminal(Terminal::Eq(a, b)) => {
let a = bridge.wrap_symbol(*a);
let b = bridge.wrap_symbol(*b);
if a.is_const() {
self.satisfy_eq(&b, a.get_tristate_value())?
} else if b.is_const() {
self.satisfy_eq(&a, b.get_tristate_value())?
} else {
return Err(SolveError::AmbiguousComparison);
}
}
Expr::Terminal(Terminal::Neq(a, b)) => {
let a = bridge.wrap_symbol(*a);
let b = bridge.wrap_symbol(*b);
if a.is_const() {
self.satisfy_neq(&b, a.get_tristate_value(), desired_value)?
} else if b.is_const() {
self.satisfy_neq(&a, b.get_tristate_value(), desired_value)?
} else {
return Err(SolveError::AmbiguousComparison);
}
}
Expr::Terminal(Terminal::Symbol(s)) => {
let s = bridge.wrap_symbol(*s);
let desired_value = if s.symbol_type() == SymbolType::Boolean {
Tristate::Yes
} else {
desired_value
};
self.satisfy_neq(&s, Tristate::No, desired_value)?
}
Expr::Terminal(_) => return Err(SolveError::UnsupportedConstituents),
})
}
}
fn merge(a: &mut Assignments, mut b: Assignments) -> Result<(), SolveError> {
let set_a: HashSet<&String> = a.keys().collect();
let set_b: HashSet<&String> = b.keys().collect();
for &k in set_a.intersection(&set_b) {
let va = a[k];
let vb = b[k];
if va != vb {
return Err(SolveError::ConflictingAssignment {
symbol: k.clone(),
a: va,
b: vb,
});
}
}
a.extend(b.drain());
Ok(())
}