use std::mem;
use std::ops::Index;
use std::ops::IndexMut;
use std::usize;
pub(super) struct Stack {
entries: Vec<StackEntry>,
overflow_depth: usize,
}
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub(super) struct StackDepth {
depth: usize,
}
pub(super) struct StackEntry {
coinductive_goal: bool,
cycle: bool,
}
impl Stack {
pub(super) fn new(
overflow_depth: usize,
) -> Self {
Stack {
entries: vec![],
overflow_depth,
}
}
pub(super) fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub(super) fn push(&mut self, coinductive_goal: bool) -> StackDepth {
let depth = StackDepth {
depth: self.entries.len(),
};
if depth.depth >= self.overflow_depth {
panic!("overflow depth reached")
}
self.entries.push(StackEntry {
coinductive_goal,
cycle: false,
});
depth
}
pub(super) fn pop(&mut self, depth: StackDepth) {
assert_eq!(
depth.depth + 1,
self.entries.len(),
"mismatched stack push/pop"
);
self.entries.pop();
}
pub(super) fn mixed_inductive_coinductive_cycle_from(&self, depth: StackDepth) -> bool {
let coinductive_count = self.entries[depth.depth..]
.iter()
.filter(|entry| entry.coinductive_goal)
.count();
let total_count = self.entries.len() - depth.depth;
let any_coinductive = coinductive_count != 0;
let any_inductive = coinductive_count != total_count;
any_coinductive && any_inductive
}
}
impl StackEntry {
pub(super) fn flag_cycle(&mut self) {
self.cycle = true;
}
pub(super) fn read_and_reset_cycle_flag(&mut self) -> bool {
mem::replace(&mut self.cycle, false)
}
}
impl Index<StackDepth> for Stack {
type Output = StackEntry;
fn index(&self, depth: StackDepth) -> &StackEntry {
&self.entries[depth.depth]
}
}
impl IndexMut<StackDepth> for Stack {
fn index_mut(&mut self, depth: StackDepth) -> &mut StackEntry {
&mut self.entries[depth.depth]
}
}