use crate::algorithms::compose::IntervalReachVisitor;
use crate::algorithms::compose::IntervalSet;
use crate::algorithms::condense;
use crate::algorithms::dfs_visit::dfs_visit;
use crate::algorithms::tr_filters::AnyTrFilter;
use crate::fst_impls::VectorFst;
use crate::fst_traits::{CoreFst, ExpandedFst};
use crate::StateId;
use crate::fst_properties::FstProperties;
use crate::semirings::Semiring;
use anyhow::Result;
static UNASSIGNED: usize = std::usize::MAX;
pub struct StateReachable {
pub(crate) isets: Vec<IntervalSet>,
pub(crate) state2index: Vec<usize>,
}
impl StateReachable {
pub fn new<W: Semiring, F: ExpandedFst<W>>(fst: &F) -> Result<Self> {
let props = fst.properties_check(FstProperties::ACYCLIC)?;
let acyclic = props.contains(FstProperties::ACYCLIC);
if acyclic {
Ok(Self::new_acyclic(fst))
} else {
Self::new_cyclic(fst)
}
}
pub fn new_cyclic<W: Semiring, F: ExpandedFst<W>>(fst: &F) -> Result<Self> {
let (scc, cfst): (_, VectorFst<_>) = condense(fst).unwrap();
let reachable = StateReachable::new_acyclic(&cfst);
let mut nscc = vec![];
for &c in scc.iter() {
let c = c as usize;
while c >= nscc.len() {
nscc.push(0);
}
nscc[c] += 1;
}
let mut state2index = vec![UNASSIGNED; scc.len()];
let mut isets: Vec<IntervalSet> = vec![];
isets.resize_with(scc.len(), Default::default);
for (s, &c) in scc.iter().enumerate() {
let c = c as StateId;
isets[s] = reachable.isets[c as usize].clone();
state2index[s] = reachable.state2index[c as usize];
if unsafe { cfst.is_final_unchecked(c) } && nscc[c as usize] > 1 {
bail!("StateReachable: Final state contained in a cycle")
}
}
Ok(Self { isets, state2index })
}
pub fn new_acyclic<W: Semiring, F: ExpandedFst<W>>(fst: &F) -> Self {
let mut reach_visitor = IntervalReachVisitor::new(fst);
dfs_visit(fst, &mut reach_visitor, &AnyTrFilter {}, false);
Self {
isets: reach_visitor.isets,
state2index: reach_visitor.state2index,
}
}
#[allow(unused)]
pub fn reach(&self, current_state: StateId, s: StateId) -> Result<bool> {
if let Some(i) = self.state2index.get(s as usize) {
Ok(self.isets[current_state as usize].member(*i))
} else {
bail!("StateReachable: State non-final {}", s)
}
}
}