use crate::{BottomUpTa, DetBottomUpTa, StateId, Symbol};
use fixedbitset::FixedBitSet;
use packed_term_arena::tree::{Tree, TreeArena};
use smallvec::SmallVec;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DetRun {
pub states: Vec<StateId>,
pub root_state: StateId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StateSet<S>(SmallVec<[S; 4]>);
impl<S> Default for StateSet<S> {
fn default() -> Self {
Self(SmallVec::new())
}
}
impl<S: Clone + Ord> StateSet<S> {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, s: S) {
match self.0.binary_search(&s) {
Ok(_) => {}
Err(idx) => self.0.insert(idx, s),
}
}
pub fn iter(&self) -> impl Iterator<Item = &S> {
self.0.iter()
}
pub fn as_slice(&self) -> &[S] {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NonDetRun<S> {
pub states: Vec<StateSet<S>>,
pub root_states: StateSet<S>,
}
pub fn run_det<A>(a: &A, arena: &TreeArena<Symbol>, root: Tree) -> DetRun
where
A: DetBottomUpTa<State = StateId>,
{
let mut states = vec![StateId::STUCK; arena.len()];
let mut visited = FixedBitSet::with_capacity(arena.len());
let mut buf: SmallVec<[StateId; 4]> = SmallVec::new();
for node in arena.post_order(root) {
if visited.contains(node.index()) {
continue;
}
visited.set(node.index(), true);
buf.clear();
let mut any_stuck = false;
for &child in arena.get_children(node) {
let cs = states[child.index()];
if cs.is_stuck() {
any_stuck = true;
break;
}
buf.push(cs);
}
if any_stuck {
continue;
}
states[node.index()] = a
.step_det(*arena.get_label(node), &buf)
.unwrap_or(StateId::STUCK);
}
DetRun {
root_state: states[root.index()],
states,
}
}
pub fn run_nondet<A>(a: &A, arena: &TreeArena<Symbol>, root: Tree) -> NonDetRun<A::State>
where
A: BottomUpTa,
A::State: Ord,
{
let mut states = vec![StateSet::new(); arena.len()];
let mut visited = FixedBitSet::with_capacity(arena.len());
for node in arena.post_order(root) {
if visited.contains(node.index()) {
continue;
}
visited.set(node.index(), true);
let local = {
let child_ids: SmallVec<[Tree; 4]> = arena.get_children(node).iter().copied().collect();
let mut pools: SmallVec<[&[A::State]; 4]> = SmallVec::new();
let mut any_empty = false;
for child in child_ids {
let set = &states[child.index()];
if set.is_empty() {
any_empty = true;
break;
}
pools.push(set.as_slice());
}
if any_empty {
None
} else {
let mut local = StateSet::new();
cartesian_product(&pools, |tuple| {
a.step(*arena.get_label(node), tuple, &mut |q| local.insert(q));
});
Some(local)
}
};
if let Some(local) = local {
states[node.index()] = local;
}
}
NonDetRun {
root_states: states[root.index()].clone(),
states,
}
}
pub(crate) fn cartesian_product<T: Clone>(pools: &[&[T]], mut f: impl FnMut(&[T])) {
if pools.iter().any(|pool| pool.is_empty()) {
return;
}
if pools.is_empty() {
f(&[]);
return;
}
let mut indices = vec![0; pools.len()];
let mut tuple: Vec<T> = pools.iter().map(|pool| pool[0].clone()).collect();
loop {
f(&tuple);
let mut pos = pools.len();
loop {
if pos == 0 {
return;
}
pos -= 1;
indices[pos] += 1;
if indices[pos] < pools[pos].len() {
tuple[pos] = pools[pos][indices[pos]].clone();
for reset in pos + 1..pools.len() {
indices[reset] = 0;
tuple[reset] = pools[reset][0].clone();
}
break;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ExplicitBuilder, Symbol};
#[test]
fn cartesian_empty_pools_is_unit() {
let mut tuples = Vec::<Vec<u8>>::new();
cartesian_product::<u8>(&[], |tuple| tuples.push(tuple.to_vec()));
assert_eq!(tuples, vec![vec![]]);
}
#[test]
fn cartesian_empty_member_is_empty() {
let a = [1, 2];
let b: [i32; 0] = [];
let mut count = 0;
cartesian_product(&[&a[..], &b[..]], |_| count += 1);
assert_eq!(count, 0);
}
#[test]
fn deterministic_run_accepts_tree() {
let a = Symbol(0);
let f = Symbol(1);
let mut builder = ExplicitBuilder::new();
let leaf = builder.new_state();
let root_state = builder.new_state();
builder.add_rule(a, vec![], leaf);
builder.add_rule(f, vec![leaf, leaf], root_state);
builder.add_accepting(root_state);
let automaton = builder.build();
let mut arena = TreeArena::new();
let left = arena.add_node(a, vec![]);
let right = arena.add_node(a, vec![]);
let root = arena.add_node(f, vec![left, right]);
let run = run_det(&automaton, &arena, root);
assert_eq!(run.root_state, root_state);
assert!(automaton.is_accepting(&run.root_state));
}
#[test]
fn nondeterministic_run_collects_states() {
let a = Symbol(0);
let mut builder = ExplicitBuilder::new();
let q0 = builder.new_state();
let q1 = builder.new_state();
builder.add_rule(a, vec![], q0);
builder.add_rule(a, vec![], q1);
let automaton = builder.build();
let mut arena = TreeArena::new();
let root = arena.add_node(a, vec![]);
let run = run_nondet(&automaton, &arena, root);
assert_eq!(run.root_states.len(), 2);
}
#[test]
fn deterministic_run_handles_shared_stuck_nodes() {
let leaf_symbol = Symbol(0);
let parent_symbol = Symbol(1);
let builder = ExplicitBuilder::new();
let automaton = builder.build();
let mut arena = TreeArena::new();
let shared = arena.add_node(leaf_symbol, vec![]);
let root = arena.add_node(parent_symbol, vec![shared, shared]);
let run = run_det(&automaton, &arena, root);
assert!(run.states[shared.index()].is_stuck());
assert!(run.root_state.is_stuck());
}
}