use std::{
mem,
ops::{Add, AddAssign},
};
use crate::{
actions::{IntEvent, IntPropCond},
model::{self, ConRef},
solver::engine::{self, PropRef},
};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) enum ActivationAction<A, P> {
Advise(A),
Enqueue(P),
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(crate) struct ActivationActionS(u32);
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(crate) struct ActivationList {
activations: Vec<ActivationActionS>,
lower_bound_idx: u32,
upper_bound_idx: u32,
bounds_idx: u32,
domain_idx: u32,
}
impl From<ActivationActionS> for ActivationAction<engine::AdvRef, PropRef> {
fn from(value: ActivationActionS) -> Self {
if (value.0 & 0b1) == 1 {
Self::Advise(engine::AdvRef::from_raw(value.0 >> 1))
} else {
Self::Enqueue(PropRef::from_raw(value.0 >> 1))
}
}
}
impl From<ActivationActionS> for ActivationAction<model::AdvRef, ConRef> {
fn from(value: ActivationActionS) -> Self {
if (value.0 & 0b1) == 1 {
Self::Advise(model::AdvRef::from_raw(value.0 >> 1))
} else {
Self::Enqueue(ConRef::from_raw(value.0 >> 1))
}
}
}
impl From<ActivationAction<engine::AdvRef, PropRef>> for ActivationActionS {
fn from(value: ActivationAction<engine::AdvRef, PropRef>) -> Self {
Self(match value {
ActivationAction::Advise(advisor) => (advisor.raw() << 1) | 0b1,
ActivationAction::Enqueue(prop) => prop.raw() << 1,
})
}
}
impl From<ActivationAction<model::AdvRef, ConRef>> for ActivationActionS {
fn from(value: ActivationAction<model::AdvRef, ConRef>) -> Self {
Self(match value {
ActivationAction::Advise(advisor) => (advisor.raw() << 1) | 0b1,
ActivationAction::Enqueue(prop) => prop.raw() << 1,
})
}
}
impl ActivationList {
pub(crate) fn add<A, P>(&mut self, action: ActivationAction<A, P>, condition: IntPropCond)
where
ActivationAction<A, P>: Into<ActivationActionS>,
{
assert!(
self.activations.len() < u32::MAX as usize,
"Unable to add more than u32::MAX propagators to the activation list of a single variable."
);
let mut action = action.into();
let mut cond_swap = |idx: u32| {
let idx = idx as usize;
if idx < self.activations.len() {
mem::swap(&mut action, &mut self.activations[idx]);
}
};
match condition {
IntPropCond::Fixed => {
cond_swap(self.lower_bound_idx);
if self.lower_bound_idx < self.upper_bound_idx {
cond_swap(self.upper_bound_idx);
}
if self.upper_bound_idx < self.bounds_idx {
cond_swap(self.bounds_idx);
}
if self.bounds_idx < self.domain_idx {
cond_swap(self.domain_idx);
}
self.lower_bound_idx += 1;
self.upper_bound_idx += 1;
self.bounds_idx += 1;
self.domain_idx += 1;
self.activations.push(action);
}
IntPropCond::LowerBound => {
cond_swap(self.upper_bound_idx);
if self.upper_bound_idx < self.bounds_idx {
cond_swap(self.bounds_idx);
}
if self.bounds_idx < self.domain_idx {
cond_swap(self.domain_idx);
}
self.upper_bound_idx += 1;
self.bounds_idx += 1;
self.domain_idx += 1;
self.activations.push(action);
}
IntPropCond::UpperBound => {
cond_swap(self.bounds_idx);
if self.bounds_idx < self.domain_idx {
cond_swap(self.domain_idx);
}
self.bounds_idx += 1;
self.domain_idx += 1;
self.activations.push(action);
}
IntPropCond::Bounds => {
cond_swap(self.domain_idx);
self.domain_idx += 1;
self.activations.push(action);
}
IntPropCond::Domain => self.activations.push(action),
};
}
pub(crate) fn extend(&mut self, other: Self) {
for (i, act) in other.activations.into_iter().enumerate() {
let i = i as u32;
let act: ActivationAction<engine::AdvRef, PropRef> = act.into();
self.add(
act,
if i < other.lower_bound_idx {
IntPropCond::Fixed
} else if i < other.upper_bound_idx {
IntPropCond::LowerBound
} else if i < other.bounds_idx {
IntPropCond::UpperBound
} else if i < other.domain_idx {
IntPropCond::Bounds
} else {
IntPropCond::Domain
},
);
}
}
pub(crate) fn for_each_activated_by<A, P, F>(&self, event: IntEvent, mut f: F)
where
ActivationAction<A, P>: From<ActivationActionS>,
F: FnMut(ActivationAction<A, P>),
{
if event == IntEvent::LowerBound {
for &act in
&self.activations[self.lower_bound_idx as usize..self.upper_bound_idx as usize]
{
f(act.into());
}
for &act in &self.activations[self.bounds_idx as usize..] {
f(act.into());
}
} else {
let start = match event {
IntEvent::Fixed => 0,
IntEvent::Bounds => self.lower_bound_idx as usize,
IntEvent::UpperBound => self.upper_bound_idx as usize,
IntEvent::LowerBound => unreachable!(),
IntEvent::Domain => self.domain_idx as usize,
};
for &act in &self.activations[start..] {
f(act.into());
}
}
}
}
impl Add<IntEvent> for IntEvent {
type Output = IntEvent;
fn add(self, rhs: IntEvent) -> Self::Output {
use IntEvent::*;
match (self, rhs) {
(Fixed, _) | (_, Fixed) => Fixed,
(Bounds, _) | (_, Bounds) => Bounds,
(LowerBound, UpperBound) | (UpperBound, LowerBound) => Bounds,
(LowerBound, _) | (_, LowerBound) => LowerBound,
(UpperBound, _) | (_, UpperBound) => UpperBound,
(Domain, Domain) => Domain,
}
}
}
impl AddAssign<IntEvent> for IntEvent {
fn add_assign(&mut self, rhs: IntEvent) {
*self = *self + rhs;
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use rustc_hash::FxHashSet;
use crate::{
actions::{IntEvent, IntPropCond},
solver::{
activation_list::{ActivationAction, ActivationList},
engine::PropRef,
},
};
#[test]
fn test_activation_list() {
let props = [
(PropRef::new(0), IntPropCond::Fixed),
(PropRef::new(1), IntPropCond::LowerBound),
(PropRef::new(2), IntPropCond::UpperBound),
(PropRef::new(3), IntPropCond::Bounds),
(PropRef::new(4), IntPropCond::Domain),
];
for list in props.iter().permutations(5) {
let mut activation_list = ActivationList::default();
for (prop, cond) in list.iter() {
activation_list.add(ActivationAction::Enqueue(*prop), *cond);
}
let mut fixed = FxHashSet::default();
activation_list.for_each_activated_by(IntEvent::Fixed, |a: ActivationAction<_, _>| {
fixed.insert(a);
});
assert_eq!(
fixed,
FxHashSet::from_iter([
ActivationAction::Enqueue(PropRef::new(0)),
ActivationAction::Enqueue(PropRef::new(1)),
ActivationAction::Enqueue(PropRef::new(2)),
ActivationAction::Enqueue(PropRef::new(3)),
ActivationAction::Enqueue(PropRef::new(4))
])
);
let mut bounds = FxHashSet::default();
activation_list.for_each_activated_by(IntEvent::Bounds, |a: ActivationAction<_, _>| {
bounds.insert(a);
});
assert_eq!(
bounds,
FxHashSet::from_iter([
ActivationAction::Enqueue(PropRef::new(1)),
ActivationAction::Enqueue(PropRef::new(2)),
ActivationAction::Enqueue(PropRef::new(3)),
ActivationAction::Enqueue(PropRef::new(4))
])
);
let mut lower_bound = FxHashSet::default();
activation_list.for_each_activated_by(
IntEvent::LowerBound,
|a: ActivationAction<_, _>| {
lower_bound.insert(a);
},
);
assert_eq!(
lower_bound,
FxHashSet::from_iter([
ActivationAction::Enqueue(PropRef::new(1)),
ActivationAction::Enqueue(PropRef::new(3)),
ActivationAction::Enqueue(PropRef::new(4))
])
);
let mut upper_bound = FxHashSet::default();
activation_list.for_each_activated_by(
IntEvent::UpperBound,
|a: ActivationAction<_, _>| {
upper_bound.insert(a);
},
);
assert_eq!(
upper_bound,
FxHashSet::from_iter([
ActivationAction::Enqueue(PropRef::new(2)),
ActivationAction::Enqueue(PropRef::new(3)),
ActivationAction::Enqueue(PropRef::new(4))
])
);
let mut domain = FxHashSet::default();
activation_list.for_each_activated_by(IntEvent::Domain, |a: ActivationAction<_, _>| {
domain.insert(a);
});
assert_eq!(
domain,
FxHashSet::from_iter([ActivationAction::Enqueue(PropRef::new(4))])
);
}
}
}