use std::collections::BTreeSet;
use std::ops::ControlFlow;
use machine_check_common::check::Culprit;
use machine_check_common::ExecStats;
use mck::{abstr::BitvectorDomain, concr::FullMachine};
use crate::model_check::ThreeValuedChecker;
use crate::precision::Precision;
use crate::space::StateSpace;
use crate::{AbstrInput, AbstrPanicState, AbstrParam};
pub struct WorkState<M: FullMachine> {
pub input_precision: Precision<AbstrInput<M>>,
pub param_precision: Precision<AbstrParam<M>>,
pub step_precision: Precision<AbstrPanicState<M>>,
pub space: StateSpace<M>,
pub checker: ThreeValuedChecker,
pub culprit: Option<Culprit>,
pub num_refinements: usize,
pub num_generated_states: usize,
pub num_generated_transitions: usize,
}
impl<M: FullMachine> WorkState<M> {
pub fn new() -> Self {
Self {
input_precision: Precision::new(),
param_precision: Precision::new(),
step_precision: Precision::new(),
space: StateSpace::new(),
checker: ThreeValuedChecker::new(),
culprit: None,
num_refinements: 0,
num_generated_states: 0,
num_generated_transitions: 0,
}
}
pub fn info(&mut self) -> ExecStats {
ExecStats {
num_refinements: self.num_refinements,
num_generated_states: self.num_generated_states,
num_final_states: self.space.num_nodes().saturating_sub(1),
num_generated_transitions: self.num_generated_transitions,
num_final_transitions: self.space.num_transitions(),
inherent_panic_message: self.find_panic_string().map(String::from),
}
}
pub fn find_panic_string(&mut self) -> Option<&'static str> {
let panic_id = self.space.breadth_first_search(|_, state_data| {
if let Some(panic_value) = state_data.panic.concrete_value() {
if panic_value.is_nonzero() {
return ControlFlow::Break(panic_value.to_u64() as u32);
}
}
ControlFlow::Continue(())
});
panic_id.map(|panic_id: u32| M::panic_message(panic_id))
}
pub fn make_compact(&mut self) {
let mut outside_used_ids = if let Some(culprit) = &self.culprit {
BTreeSet::from_iter(culprit.path.iter().copied())
} else {
BTreeSet::new()
};
outside_used_ids.extend(self.input_precision.used_state_ids());
outside_used_ids.extend(self.step_precision.used_state_ids());
let removed_states = self.space.make_compact(outside_used_ids);
self.checker.remove_states(&removed_states);
log::trace!(
"Compacted the state space by removing states {:?}",
removed_states
);
}
pub fn garbage_collect(&mut self) {
if self.space.should_compact() {
self.make_compact();
}
}
}