use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use machine_check_common::iir::property::ISubpropertyNext;
use machine_check_common::{ExecError, KnownParamValuation, NodeId, ParamValuation, StateId};
use crate::model_check::property_checker::labelling_cacher::LabellingCacher;
use crate::model_check::property_checker::value::{CheckChoice, CheckValue, TimedCheckValue};
use crate::FullMachine;
impl<M: FullMachine> LabellingCacher<'_, M> {
pub fn compute_next_labelling(
&self,
op: &ISubpropertyNext,
node_id: NodeId,
) -> Result<TimedCheckValue, ExecError> {
for successor_id in self.space.direct_successor_iter(node_id) {
self.compute_latest_timed(op.inner, successor_id)?;
}
self.apply_next(op, node_id, &mut BTreeMap::new())
}
pub fn apply_next(
&self,
op: &ISubpropertyNext,
node_id: NodeId,
computed_successors: &mut BTreeMap<StateId, TimedCheckValue>,
) -> Result<TimedCheckValue, ExecError> {
let Some(tail_partition) = self.space.direct_successor_param_partition(node_id) else {
return Ok(TimedCheckValue::new(0, CheckValue::from_bool(op.universal)));
};
let mut can_be_false_at_time: Option<u64> = None;
let mut can_be_true_at_time: Option<u64> = None;
let mut can_be_unknown_at_time: Option<u64> = None;
let mut best_unknown_successor = None;
fn update_flag(flag: &mut Option<u64>, time: u64) {
*flag = Some(flag.map(|flag_time| flag_time.min(time)).unwrap_or(time));
}
for parametric_set in tail_partition.all_sets() {
let (set_valuation, set_valuation_time, best_set_successor) =
self.compute_set_valuation(op, parametric_set, computed_successors)?;
match set_valuation {
ParamValuation::False => {
update_flag(&mut can_be_false_at_time, set_valuation_time);
}
ParamValuation::True => {
update_flag(&mut can_be_true_at_time, set_valuation_time);
}
ParamValuation::Dependent => {
update_flag(&mut can_be_false_at_time, set_valuation_time);
update_flag(&mut can_be_true_at_time, set_valuation_time);
}
ParamValuation::Unknown => {
if can_be_unknown_at_time.is_some() {
best_unknown_successor = None;
} else if let Some(best_set_successor) = best_set_successor {
best_unknown_successor = Some(best_set_successor);
}
update_flag(&mut can_be_unknown_at_time, set_valuation_time);
}
}
}
if let (Some(can_be_false_at_time), Some(can_be_true_at_time)) =
(can_be_false_at_time, can_be_true_at_time)
{
let time = can_be_false_at_time.min(can_be_true_at_time);
return Ok(TimedCheckValue::new(
time,
CheckValue::from_known(KnownParamValuation::Dependent),
));
}
if can_be_unknown_at_time.is_none() {
if let Some(time) = can_be_false_at_time {
return Ok(TimedCheckValue::new(
time,
CheckValue::from_known(KnownParamValuation::False),
));
}
if let Some(time) = can_be_true_at_time {
return Ok(TimedCheckValue::new(
time,
CheckValue::from_known(KnownParamValuation::True),
));
}
}
assert!(can_be_unknown_at_time.is_some());
if let Some(successor_id) = best_unknown_successor {
let successor_timed = computed_successors
.get(&successor_id)
.expect("Successor value should be computed")
.clone();
let CheckValue::Unknown(successor_choice) = successor_timed.value else {
panic!("Successor should be unknown");
};
let value =
CheckValue::Unknown(Box::new(CheckChoice::Next(successor_id, successor_choice)));
return Ok(TimedCheckValue {
time: successor_timed.time,
value,
});
};
let mut successor_sorter = Vec::new();
for successor_id in self.space.direct_successor_iter(node_id) {
let successor_timed = computed_successors
.get(&successor_id)
.expect("Successor value should be computed");
if successor_timed.value.is_unknown() {
successor_sorter.push((
(successor_timed.time, successor_id),
successor_timed.value.clone(),
));
}
}
successor_sorter.sort_by(|(a_key, _), (b_key, _)| a_key.cmp(b_key));
let ((successor_time, successor_id), successor_value) = successor_sorter
.first()
.expect("There should be a first successor");
let successor_timed = TimedCheckValue::new(*successor_time, successor_value.clone());
let CheckValue::Unknown(successor_choice) = successor_timed.value else {
panic!("Successor should be unknown");
};
let value =
CheckValue::Unknown(Box::new(CheckChoice::Next(*successor_id, successor_choice)));
Ok(TimedCheckValue {
time: successor_timed.time,
value,
})
}
fn compute_set_valuation(
&self,
op: &ISubpropertyNext,
parametric_set: partitions::partition_vec::Set<'_, StateId>,
computed_successors: &mut BTreeMap<StateId, TimedCheckValue>,
) -> Result<(ParamValuation, u64, Option<StateId>), ExecError> {
let ground_value = CheckValue::from_bool(op.universal);
let mut current_valuation = ground_value.valuation();
let mut best_successor = None;
let mut valuation_time = 0;
for successor_id in parametric_set.map(|(_, successor_id)| *successor_id) {
if let Entry::Vacant(e) = computed_successors.entry(successor_id) {
e.insert(self.compute_latest_timed(op.inner, successor_id)?);
}
let successor_timed = computed_successors
.get(&successor_id)
.expect("Successor value should be computed");
let successor_valuation = successor_timed.value.valuation();
let is_better = if op.universal {
successor_valuation
.upward_bitand_ordering(¤t_valuation)
.is_gt()
} else {
successor_valuation
.upward_bitor_ordering(¤t_valuation)
.is_gt()
};
if is_better {
current_valuation = successor_valuation;
best_successor = Some(successor_id);
valuation_time = successor_timed.time;
} else if successor_valuation == current_valuation {
best_successor = None;
valuation_time = valuation_time.min(successor_timed.time);
}
}
Ok((current_valuation, valuation_time, best_successor))
}
}