#[cfg(feature = "mem_dbg")]
use mem_dbg::{MemDbg, MemSize};
use crate::{
items::{CdclIdx, ENodeIdx, InstIdx, MatchIdx, ProofIdx, StackFrame, StackIdx},
Error, Result, TiVec,
};
use super::Z3Parser;
#[cfg_attr(feature = "mem_dbg", derive(MemSize, MemDbg))]
#[derive(Debug)]
pub struct Stack {
stack: Vec<StackIdx>,
pub(super) stack_frames: TiVec<StackIdx, StackFrame>,
timestamp: u32,
}
impl Default for Stack {
fn default() -> Self {
let mut stack_frames: TiVec<StackIdx, StackFrame> = TiVec::default();
let idx = stack_frames.push_and_get_key(StackFrame::new(0, false));
assert_eq!(idx, Self::ZERO_FRAME);
Self {
stack: vec![idx],
stack_frames,
timestamp: 1,
}
}
}
impl Stack {
pub const ZERO_FRAME: StackIdx = StackIdx::ZERO;
fn height(&self) -> usize {
self.stack.len() - 1
}
fn add_frame(&mut self, from_cdcl: bool) -> Result<()> {
self.stack_frames.raw.try_reserve(1)?;
let idx = self
.stack_frames
.push_and_get_key(StackFrame::new(self.timestamp, from_cdcl));
self.stack.try_reserve(1)?;
self.stack.push(idx);
self.timestamp += 1;
Ok(())
}
fn remove_frame(&mut self, active: bool, from_cdcl: bool) {
let idx = self.stack.pop().unwrap();
assert!(!self.stack.is_empty());
let frame = &mut self.stack_frames[idx];
frame.active.end(self.timestamp, active);
frame.from_cdcl = from_cdcl;
self.timestamp += 1;
}
pub(super) fn ensure_height(&mut self, height: usize) -> Result<()> {
let mut res = Ok(());
while height > self.height() {
res = Err(Error::StackFrameNotPushed);
let from_cdcl = self.is_speculative();
self.add_frame(from_cdcl)?;
}
while height < self.height() {
let from_cdcl = self.is_speculative();
self.remove_frame(true, from_cdcl);
}
res
}
pub(super) fn new_frame(&mut self, idx: usize, from_cdcl: bool) -> Result<()> {
let res = self.ensure_height(idx);
self.add_frame(from_cdcl)?;
res
}
pub(super) fn pop_frames(
&mut self,
count: core::num::NonZeroUsize,
idx: usize,
after_conflict: bool,
) -> Result<bool> {
let count = count.get();
debug_assert!(0 < count && count <= idx);
let result = self.ensure_height(idx);
let from_cdcl = after_conflict
|| (0..count).any(|idx| self[self.stack[self.stack.len() - 1 - idx]].from_cdcl);
if from_cdcl {
for idx in 0..count {
let frame = self.stack[self.stack.len() - 1 - idx];
self.stack_frames[frame].from_cdcl = true;
}
}
debug_assert!(
!from_cdcl
|| (0..count)
.all(|idx| self[self.stack[self.stack.len() - 1 - idx]].from_cdcl == from_cdcl)
);
for _ in 0..count {
self.remove_frame(false, from_cdcl);
}
result.map(|()| from_cdcl)
}
pub(super) fn new_decision(&mut self) -> bool {
debug_assert!(self.stack.len() > 1);
let frame = self.active_frame();
let frame = &mut self.stack_frames[frame];
!core::mem::replace(&mut frame.from_cdcl, true)
}
pub(super) fn active_frame(&self) -> StackIdx {
*self.stack.last().unwrap()
}
pub(super) fn pre_active_frame(&self) -> StackIdx {
self.stack[self.stack.len() - 2]
}
pub(super) fn is_speculative(&self) -> bool {
self.stack_frames[self.active_frame()].from_cdcl
}
pub(super) fn is_alive(&self, frame: StackIdx) -> bool {
self[frame].active.status().is_alive()
}
#[cfg(any())]
pub(super) fn is_global(&self, frame: StackIdx) -> bool {
frame == Self::ZERO_FRAME
}
}
impl std::ops::Index<StackIdx> for Stack {
type Output = StackFrame;
fn index(&self, idx: StackIdx) -> &Self::Output {
&self.stack_frames[idx]
}
}
pub trait HasFrame {
fn frame(self, parser: &Z3Parser) -> StackIdx;
}
macro_rules! impl_has_frame {
($($ty:ty),*) => {
$(impl HasFrame for $ty {
fn frame(self, parser: &Z3Parser) -> StackIdx {
parser[self].frame
}
})*
};
}
impl_has_frame!(ENodeIdx, MatchIdx, InstIdx, ProofIdx, CdclIdx);