#![cfg(any(test, doc))]
use std::fmt::Debug;
use std::fmt::Formatter;
use crate::propagation::EnqueueDecision;
use super::WatchListPropositional;
use crate::basic_types::Inconsistency;
use crate::engine::PropagationStatusCP;
use crate::basic_types::PropositionalConjunction;
use crate::engine::opaque_domain_event::OpaqueDomainEvent;
use crate::engine::predicates::integer_predicate::IntegerPredicate;
use crate::propagation::LocalId;
use crate::propagation::PropagationContext;
use crate::propagation::PropagationContextMut;
use crate::propagation::Propagator;
use crate::propagation::PropagatorId;
use crate::propagation::PropagatorInitialisationContext;
use crate::engine::reason::ReasonStore;
use crate::engine::variables::DomainId;
use crate::engine::variables::IntegerVariable;
use crate::engine::variables::Literal;
use crate::engine::variables::PropositionalVariable;
use crate::engine::AssignmentsInteger;
use crate::engine::AssignmentsPropositional;
use crate::propagation::DomainEvents;
use crate::engine::EmptyDomain;
use crate::engine::WatchListCP;
#[derive(Default, Debug)]
pub(crate) struct TestSolver {
assignments_integer: AssignmentsInteger,
reason_store: ReasonStore,
assignments_propositional: AssignmentsPropositional,
watch_list: WatchListCP,
watch_list_propositional: WatchListPropositional,
next_id: u32,
}
type BoxedPropagator = Box<dyn Propagator>;
impl Debug for BoxedPropagator {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "test_helper::Propagator(<boxed value>)")
}
}
impl TestSolver {
pub(crate) fn new_variable(&mut self, lb: i32, ub: i32) -> DomainId {
self.watch_list.grow();
self.assignments_integer.grow(lb, ub)
}
pub(crate) fn new_literal(&mut self) -> Literal {
let new_variable_index = self.assignments_propositional.num_propositional_variables();
self.watch_list_propositional.grow();
self.assignments_propositional.grow();
Literal::new(PropositionalVariable::new(new_variable_index), true)
}
pub(crate) fn new_propagator(
&mut self,
propagator: impl Propagator + 'static,
) -> Result<BoxedPropagator, Inconsistency> {
let id = PropagatorId(self.next_id);
self.next_id += 1;
let mut propagator: Box<dyn Propagator> = Box::new(propagator);
propagator.initialise_at_root(&mut PropagatorInitialisationContext::new(
&mut self.watch_list,
&mut self.watch_list_propositional,
id,
&self.assignments_integer,
&self.assignments_propositional,
))?;
self.propagate(&mut propagator)?;
Ok(propagator)
}
pub(crate) fn contains<Var: IntegerVariable>(&self, var: Var, value: i32) -> bool {
var.contains(&self.assignments_integer, value)
}
pub(crate) fn lower_bound(&self, var: DomainId) -> i32 {
self.assignments_integer.get_lower_bound(var)
}
pub(crate) fn increase_lower_bound_and_notify(
&mut self,
propagator: &mut BoxedPropagator,
id: i32,
var: DomainId,
value: i32,
) -> EnqueueDecision {
let result = self
.assignments_integer
.tighten_lower_bound(var, value, None);
assert!(result.is_ok(), "The provided value to `increase_lower_bound` caused an empty domain, generally the propagator should not be notified of this change!");
let context =
PropagationContext::new(&self.assignments_integer, &self.assignments_propositional);
propagator.notify(
context,
LocalId::from(id as u32),
OpaqueDomainEvent::from(
DomainEvents::LOWER_BOUND
.get_int_events()
.iter()
.next()
.unwrap(),
),
)
}
pub(crate) fn decrease_upper_bound_and_notify(
&mut self,
propagator: &mut BoxedPropagator,
id: i32,
var: DomainId,
value: i32,
) -> EnqueueDecision {
let result = self
.assignments_integer
.tighten_upper_bound(var, value, None);
assert!(result.is_ok(), "The provided value to `increase_lower_bound` caused an empty domain, generally the propagator should not be notified of this change!");
let context =
PropagationContext::new(&self.assignments_integer, &self.assignments_propositional);
propagator.notify(
context,
LocalId::from(id as u32),
OpaqueDomainEvent::from(
DomainEvents::UPPER_BOUND
.get_int_events()
.iter()
.next()
.unwrap(),
),
)
}
pub(crate) fn set_literal(&mut self, var: Literal, val: bool) {
self.assignments_propositional
.enqueue_decision_literal(if val { var } else { !var });
}
pub(crate) fn is_literal_false(&self, var: Literal) -> bool {
self.assignments_propositional
.is_literal_assigned_false(var)
}
pub(crate) fn upper_bound(&self, var: DomainId) -> i32 {
self.assignments_integer.get_upper_bound(var)
}
pub(crate) fn remove(&mut self, var: DomainId, value: i32) -> Result<(), EmptyDomain> {
self.assignments_integer
.remove_value_from_domain(var, value, None)
}
pub(crate) fn propagate(&mut self, propagator: &mut BoxedPropagator) -> PropagationStatusCP {
let context = PropagationContextMut::new(
&mut self.assignments_integer,
&mut self.reason_store,
&mut self.assignments_propositional,
PropagatorId(0),
);
propagator.propagate(context)
}
pub(crate) fn propagate_until_fixed_point(
&mut self,
propagator: &mut BoxedPropagator,
) -> PropagationStatusCP {
let mut num_trail_entries = self.assignments_integer.num_trail_entries()
+ self.assignments_propositional.num_trail_entries();
self.notify_propagator(propagator);
loop {
{
let context = PropagationContextMut::new(
&mut self.assignments_integer,
&mut self.reason_store,
&mut self.assignments_propositional,
PropagatorId(0),
);
propagator.propagate(context)?;
self.notify_propagator(propagator);
}
if self.assignments_integer.num_trail_entries()
+ self.assignments_propositional.num_trail_entries()
== num_trail_entries
{
break;
}
num_trail_entries = self.assignments_integer.num_trail_entries()
+ self.assignments_propositional.num_trail_entries();
}
Ok(())
}
pub(crate) fn notify_propagator(&mut self, propagator: &mut BoxedPropagator) {
let events = self
.assignments_integer
.drain_domain_events()
.collect::<Vec<_>>();
let context =
PropagationContext::new(&self.assignments_integer, &self.assignments_propositional);
for (event, domain) in events {
for propagator_var in self.watch_list.get_affected_propagators(event, domain) {
let _ = propagator.notify(context, propagator_var.variable, event.into());
}
}
}
pub(crate) fn get_reason_int(
&mut self,
predicate: IntegerPredicate,
) -> &PropositionalConjunction {
let reason_ref = self.assignments_integer.get_reason_for_predicate(predicate);
let context =
PropagationContext::new(&self.assignments_integer, &self.assignments_propositional);
self.reason_store
.get_or_compute(reason_ref, context)
.expect("reason_ref should not be stale")
}
pub(crate) fn get_reason_bool(
&mut self,
literal: Literal,
assignment: bool,
) -> &PropositionalConjunction {
let reason_ref = self
.assignments_propositional
.get_reason_for_assignment(literal, assignment);
let context =
PropagationContext::new(&self.assignments_integer, &self.assignments_propositional);
self.reason_store
.get_or_compute(reason_ref, context)
.expect("reason_ref should not be stale")
}
pub(crate) fn assert_bounds(&self, var: DomainId, lb: i32, ub: i32) {
let actual_lb = self.lower_bound(var);
let actual_ub = self.upper_bound(var);
assert_eq!(
(lb, ub), (actual_lb, actual_ub),
"The expected bounds [{lb}..{ub}] did not match the actual bounds [{actual_lb}..{actual_ub}]"
);
}
}