use std::rc::Rc;
use veripb_formula::prelude::*;
use veripb_propagator::propagation_engine::PropagationEngine;
use crate::{
context::{CORE, DERIVED},
prelude::*,
};
#[inline]
pub fn new_veripb_propagation_engine() -> PropagationEngine {
let mut propagation_engine = PropagationEngine::default();
propagation_engine.add_set();
propagation_engine.add_set();
propagation_engine.add_set();
propagation_engine.enable_back(CORE);
propagation_engine.enable_back(DERIVED);
propagation_engine
}
#[inline]
pub fn check_solution(
context: &mut Context,
database: &mut Database,
solution: &Vec<Lit>,
) -> Result<Assignment<BooleanVar>, CheckingError> {
let mut assignment = Assignment::from(solution).ok_or(CheckingError::SolutionIsConflicting)?;
assignment.resize(context.var_names.len());
database.update_unique_index(&mut context.propagation_engine)?;
if solution_satisfies_all_constraints(database, &assignment)? {
return Ok(assignment);
}
database.update_propagation_index(&mut context.propagation_engine)?;
let assignment = context.propagation_engine.propagate_solution(
solution,
&context.var_names,
context.args.trace_failed,
)?;
let used_vars = database.get_used_vars();
for var_idx in used_vars {
if assignment.get_value(var_idx) == BoolValue::Unassigned {
return Err(CheckingError::SolutionNotComplete(
context.var_names.get_name(var_idx).to_string(),
));
}
}
Ok(assignment)
}
#[inline]
fn solution_satisfies_all_constraints(
database: &mut Database,
assignment: &Assignment<BooleanVar>,
) -> Result<bool, CheckingError> {
for constraint in database.unique_constraints.iter() {
if !constraint.is_satisfied(assignment) {
if constraint.is_falsified(assignment) {
return Err(CheckingError::SolutionFalsifyingConstraint(
constraint.get_some_id(),
));
}
return Ok(false);
}
}
Ok(true)
}
#[inline]
pub fn check_implication(
prop_engine: &mut PropagationEngine,
database: &mut Database,
target: &Rc<DBConstraint>,
hint: Option<isize>,
) -> Result<isize, CheckingError> {
match hint {
Some(hint) => {
let entry = database.get_entry(hint)?;
if !entry.implies(target) {
return Err(CheckingError::not_implied(target, entry));
}
Ok(entry.get_some_id() as isize)
}
None => {
database.update_unique_index(prop_engine)?;
if let Some(constraint) = database.lookup(target) {
return Ok(constraint.get_some_id() as isize);
}
for constraint in database.unique_constraints.iter() {
if constraint.implies(target) {
return Ok(constraint.get_some_id() as isize);
}
}
Err(CheckingError::not_implied_db(target, false))
}
}
}
#[inline]
pub fn check_implication_strong(
context: &mut Context,
database: &mut Database,
target: &Rc<DBConstraint>,
hint: Option<isize>,
) -> Result<(), CheckingError> {
let mut proof_buf = if let Some(elaborator) = context.elaborator.as_mut() {
Some(&mut elaborator.proof_buf)
} else {
None
};
match hint {
Some(hint) => {
let index = database.normalize_id(hint);
let entry = database.get_entry_usize(index as usize)?;
if context.only_core && !entry.is_core_constraint_id(index as usize) {
return Err(CheckingError::CoreSubproofUsingNonCoreConstraint(hint));
}
if !entry.implies_strong(target, &mut proof_buf, &context.var_names) {
return Err(CheckingError::not_implied(target, entry));
}
Ok(())
}
None => {
database.update_unique_index(&mut context.propagation_engine)?;
if let Some(constraint) = database.lookup(target) {
if !context.only_core || constraint.is_core_constraint() {
if let Some(proof_buf) = proof_buf {
proof_buf.push_str(
&constraint
.get_out_id(constraint.get_some_id())
.unwrap()
.to_string(),
);
}
return Ok(());
}
}
if let Some(proof_buf) = proof_buf {
for constraint in database.unique_constraints.iter() {
if (!context.only_core || constraint.is_core_constraint())
&& constraint.implies_strong(
target,
&mut Some(proof_buf),
&context.var_names,
)
{
return Ok(());
}
proof_buf.clear();
}
} else {
for constraint in database.unique_constraints.iter() {
if (!context.only_core || constraint.is_core_constraint())
&& constraint.implies_strong(target, &mut None, &context.var_names)
{
return Ok(());
}
}
}
Err(CheckingError::not_implied_db(target, context.only_core))
}
}
}