use crate::dfa::{Dfa, DfaState};
use crate::nfa::words::{WordComponentIndices, WordComponents, Words};
use crate::table::Table;
use crate::util::alphabet_equal;
pub use eval::NfaEvaluator;
pub use parse::NfaParseError;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::{iter, mem};
use unicode_segmentation::UnicodeSegmentation;
pub mod eval;
pub mod parse;
pub mod words;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Nfa {
pub(crate) alphabet: Rc<[Rc<str>]>,
pub(crate) states: Vec<NfaState>,
pub(crate) initial_state: usize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NfaState {
pub(crate) name: Rc<str>,
pub(crate) initial: bool,
pub(crate) accepting: bool,
pub(crate) epsilon_transitions: Vec<usize>,
pub(crate) transitions: Vec<Vec<usize>>,
}
impl NfaState {
pub fn name(&self) -> &str {
&self.name
}
pub fn is_initial(&self) -> bool {
self.initial
}
pub fn is_accepting(&self) -> bool {
self.accepting
}
pub fn transitions(&self) -> &[Vec<usize>] {
self.transitions.as_slice()
}
pub fn epsilon_transitions(&self) -> &[usize] {
self.epsilon_transitions.as_slice()
}
}
impl Nfa {
pub fn union(mut self, mut other: Self) -> Result<Self, (Self, Self)> {
if !alphabet_equal(&self.alphabet, &other.alphabet) {
return Err((self, other));
}
let alphabet_translation = other
.alphabet
.iter()
.map(|elem1| {
self.alphabet
.iter()
.enumerate()
.find_map(|(idx, elem2)| (elem1 == elem2).then_some(idx))
.unwrap()
})
.collect::<Vec<usize>>();
if !alphabet_translation.windows(2).all(|v| v[0] < v[1]) {
for state in other.states.iter_mut() {
state.transitions = {
let mut vec = state
.transitions
.drain(..)
.zip(alphabet_translation.iter())
.collect::<Vec<_>>();
vec.sort_by_key(|(_, b)| **b);
vec.into_iter().map(|(a, _)| a).collect()
};
}
}
let a_states = self.states.len();
let remapping = |b_idx| Some(b_idx + a_states);
other.remap_transitions(remapping);
let b_init = remapping(other.initial_state).unwrap();
self.states.extend(other.states);
let names = self
.states
.iter()
.map(|s| s.name.as_ref())
.collect::<HashSet<_>>();
if names.len() != self.states.len() {
let mut iter = 1..;
self.states.iter_mut().for_each(|state| {
state.name = iter
.next()
.map(|i| Rc::from(i.to_string().as_str()))
.unwrap()
});
}
let new_initial_state = NfaState {
name: self.fresh_name("s_new"),
initial: true,
accepting: false,
epsilon_transitions: vec![self.initial_state, b_init],
transitions: vec![vec![]; self.alphabet.len()],
};
self.states[self.initial_state].initial = false;
self.states[b_init].initial = false;
self.initial_state = self.states.len();
self.states.push(new_initial_state);
Ok(self)
}
pub fn intersection(&self, other: &Self) -> Option<Self> {
self.product_construction(other, |s1, s2| {
s1.zip(s2)
.map_or(false, |(s1, s2)| s1.accepting && s2.accepting)
})
}
pub fn product_construction(
&self,
other: &Self,
mut combinator: impl FnMut(Option<&NfaState>, Option<&NfaState>) -> bool,
) -> Option<Self> {
if !alphabet_equal(&self.alphabet, &other.alphabet) {
return None;
}
let alphabet_translation = self
.alphabet
.iter()
.map(|elem1| {
other
.alphabet
.iter()
.enumerate()
.find_map(|(idx, elem2)| (elem1 == elem2).then_some(idx))
.unwrap()
})
.collect::<Vec<usize>>();
let q1 = self.initial_state;
let q2 = other.initial_state;
let mut state_pairs_to_explore = vec![(Some(q1), Some(q2))];
let mut explored_states = HashSet::new();
explored_states.insert((Some(q1), Some(q2)));
let mut state_data = vec![];
while let Some((s1, s2)) = state_pairs_to_explore.pop() {
let mut transition_list = Vec::with_capacity(self.alphabet.len());
let mut eps_transitions = Vec::with_capacity(
s1.map_or(0, |s1| self.states[s1].epsilon_transitions.len())
+ s2.map_or(0, |s2| other.states[s2].epsilon_transitions.len()),
);
for elem in 0..self.alphabet.len() {
let other_elem = alphabet_translation[elem];
let mut elem_transitions = Vec::with_capacity(
s1.map_or(1, |s1| self.states[s1].transitions[elem].len())
* s2.map_or(1, |s2| other.states[s2].transitions[other_elem].len()),
);
match (
s1.filter(|&idx| !self.states[idx].transitions[elem].is_empty()),
s2.filter(|&idx| !other.states[idx].transitions[other_elem].is_empty()),
) {
(Some(s1), Some(s2)) => {
let on_elem1 = &self.states[s1].transitions[elem];
let on_elem2 = &other.states[s2].transitions[other_elem];
for &tr1 in on_elem1 {
for &tr2 in on_elem2 {
let states = (Some(tr1), Some(tr2));
elem_transitions.push(states);
if explored_states.insert(states) {
state_pairs_to_explore.push(states);
}
}
}
}
(Some(s1), None) => {
let on_elem1 = &self.states[s1].transitions[elem];
for &tr1 in on_elem1 {
let states = (Some(tr1), None);
elem_transitions.push(states);
if explored_states.insert(states) {
state_pairs_to_explore.push(states);
}
}
}
(None, Some(s2)) => {
let on_elem2 = &other.states[s2].transitions[other_elem];
for &tr2 in on_elem2 {
let states = (None, Some(tr2));
elem_transitions.push(states);
if explored_states.insert(states) {
state_pairs_to_explore.push(states);
}
}
}
(None, None) => {}
}
transition_list.push(elem_transitions);
}
if let Some(s1) = s1 {
for &eps1 in &self.states[s1].epsilon_transitions {
let states = (Some(eps1), s2);
eps_transitions.push(states);
if explored_states.insert(states) {
state_pairs_to_explore.push(states);
}
}
}
if let Some(s2) = s2 {
for &eps2 in &other.states[s2].epsilon_transitions {
let states = (s1, Some(eps2));
eps_transitions.push(states);
if explored_states.insert(states) {
state_pairs_to_explore.push(states);
}
}
}
state_data.push((
(s1, s2),
combinator(
s1.map(|s1| &self.states[s1]),
s2.map(|s2| &other.states[s2]),
),
transition_list,
eps_transitions,
));
}
let names = {
let mut hm = HashSet::new();
let potential_names = explored_states
.iter()
.map_while(|(s1, s2)| {
let combined_name: Rc<str> = Rc::from(format!(
"({},{})",
s1.map_or("none", |s1| &self.states[s1].name),
s2.map_or("none", |s2| &other.states[s2].name)
));
hm.insert(combined_name.clone())
.then_some(((*s1, *s2), combined_name))
})
.collect::<HashMap<_, _>>();
if potential_names.len() < state_data.len() {
explored_states
.iter()
.enumerate()
.map(|(idx, (s1, s2))| ((*s1, *s2), Rc::from(format!("{idx}"))))
.collect()
} else {
potential_names
}
};
let rev_state_idx_map = state_data
.iter()
.enumerate()
.map(|(idx, ((s1, s2), _, _, _))| ((*s1, *s2), idx))
.collect::<HashMap<_, _>>();
let initial_state = *rev_state_idx_map
.get(&(Some(q1), Some(q2)))
.expect("Initial state should have an index");
let states = state_data
.into_iter()
.map(
|(states, accepting, transitions, epsilon_transitions)| NfaState {
name: names
.get(&states)
.expect("All states should have a name")
.clone(),
initial: states == (Some(q1), Some(q2)),
accepting,
transitions: transitions
.into_iter()
.map(|transition_list| {
transition_list
.iter()
.map(|states| {
*rev_state_idx_map.get(&states).expect(
"Each state pair with transition to it should have a idx",
)
})
.collect()
})
.collect(),
epsilon_transitions: epsilon_transitions
.into_iter()
.map(|states| {
*rev_state_idx_map
.get(&states)
.expect("Each state pair with transition to it should have a idx")
})
.collect(),
},
)
.collect::<Vec<_>>();
Some(Nfa {
alphabet: self.alphabet.clone(),
states,
initial_state,
})
}
pub fn optimize(&mut self) {
self.remove_unreachable_states();
self.remove_epsilon_moves();
}
pub fn remove_epsilon_moves(&mut self) {
if !self.has_epsilon_moves() {
return;
}
let closures = (0..self.states.len())
.filter_map(|idx| self.closure(idx))
.collect::<Vec<_>>();
self.states.iter_mut().for_each(|state| {
state.transitions.iter_mut().for_each(|transition_set| {
*transition_set = transition_set
.iter()
.fold(HashSet::new(), |mut set, transition| {
set.extend(&closures[*transition]);
set
})
.drain()
.collect();
});
state.epsilon_transitions.clear();
});
let mut dead_states = HashSet::new();
let mut added_states = true;
while added_states {
added_states = false;
for (idx, state) in self.states.iter().enumerate() {
if !dead_states.contains(&idx)
&& !state.is_accepting()
&& !state.is_initial()
&& state
.transitions
.iter()
.all(|transitions| transitions.iter().all(|idx| dead_states.contains(idx)))
{
dead_states.insert(idx);
added_states = true;
}
}
}
let init_closure = closures[self.initial_state]
.iter()
.copied()
.filter(|x| !dead_states.contains(x))
.collect::<HashSet<_>>();
if init_closure.len() > 1 {
let old_initial = self.initial_state;
self.states[old_initial].initial = false;
let old_state_dead = !self.states[old_initial].accepting
&& self.states[old_initial]
.transitions
.iter()
.all(|transitions| transitions.iter().all(|idx| dead_states.contains(idx)));
if old_state_dead {
dead_states.insert(old_initial);
let mut added_states = true;
while added_states {
added_states = false;
for (idx, state) in self.states.iter().enumerate() {
if !dead_states.contains(&idx)
&& !state.is_accepting()
&& !state.is_initial()
&& state.transitions.iter().all(|transitions| {
transitions.iter().all(|idx| dead_states.contains(idx))
})
{
dead_states.insert(idx);
added_states = true;
}
}
}
}
let new_state_name = if old_state_dead {
self.states[old_initial].name.clone()
} else {
self.fresh_name("s_new")
};
let transitions = (0..self.alphabet.len())
.map(|elem_idx| {
init_closure
.iter()
.fold(HashSet::new(), |mut set, &state| {
set.extend(self.states[state].transitions[elem_idx].iter().copied());
set
})
.drain()
.filter(|i| !dead_states.contains(i))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let new_state = NfaState {
name: new_state_name,
initial: true,
accepting: init_closure.iter().any(|idx| self.states[*idx].accepting),
epsilon_transitions: vec![],
transitions,
};
self.states[old_initial].initial = false;
self.initial_state = self.states.len();
self.states.push(new_state);
}
self.states.iter_mut().for_each(|state| {
state
.transitions
.iter_mut()
.for_each(|transition| transition.retain(|idx| !dead_states.contains(idx)))
});
self.remove_states(dead_states.drain().collect());
}
fn remove_states(&mut self, mut to_remove: Vec<usize>) {
let mut old_state_idx = (0..self.states.len()).collect::<Vec<_>>();
to_remove.sort();
if let Err(less_than) = to_remove.binary_search(&self.initial_state) {
self.initial_state -= less_than;
} else {
panic!("Cannot remove initial state");
}
to_remove.iter().rev().for_each(|&idx| {
self.states.remove(idx);
old_state_idx.remove(idx);
});
let map = |idx| {
let res = old_state_idx.binary_search(&idx);
if cfg!(debug_assertions) {
Some(res.expect("No transitions to removed state"))
} else {
res.ok()
}
};
self.remap_transitions(map);
}
fn remap_transitions(&mut self, mapper: impl Fn(usize) -> Option<usize>) {
self.states.iter_mut().for_each(|state| {
state.transitions.iter_mut().for_each(|table| {
table
.iter_mut()
.for_each(|trans| *trans = mapper(*trans).unwrap_or(*trans))
});
state
.epsilon_transitions
.iter_mut()
.for_each(|trans| *trans = mapper(*trans).unwrap_or(*trans));
})
}
fn fresh_name(&mut self, wanted: &str) -> Rc<str> {
if self.states.iter().all(|s| s.name.as_ref() != wanted) {
Rc::from(wanted)
} else {
(0..)
.map(|i| Rc::from(i.to_string().as_str()))
.find(|n| self.states.iter().all(|s| &s.name != n))
.unwrap()
}
}
pub fn remove_unreachable_states(&mut self) {
let states = self.unreachable_state_idx().into_iter().collect();
self.remove_states(states);
}
pub fn unreachable_states(&self) -> Vec<&NfaState> {
self.unreachable_state_idx()
.into_iter()
.map(|idx| &self.states[idx])
.collect()
}
pub fn unreachable_state_idx(&self) -> HashSet<usize> {
let reachables = self.reachable_state_idx();
(0..self.states.len())
.filter(|x| !reachables.contains(x))
.collect()
}
pub fn has_reachable_accepting_state(&self) -> bool {
self.reachable_state_idx()
.iter()
.any(|idx| self.states[*idx].accepting)
}
pub fn reachable_states(&self) -> Vec<&NfaState> {
self.reachable_state_idx()
.into_iter()
.map(|idx| &self.states[idx])
.collect()
}
pub fn reachable_state_idx(&self) -> HashSet<usize> {
let mut reachables = HashSet::from([self.initial_state]);
let mut new_states = reachables.clone();
while !new_states.is_empty() {
new_states = new_states
.drain()
.flat_map(|state| {
self.states[state]
.transitions
.iter()
.flatten()
.copied()
.chain(self.closure(state).unwrap().into_iter())
})
.filter(|&state| reachables.insert(state))
.collect();
}
reachables
}
pub fn words(&self) -> Words {
Words::new(self)
}
pub fn word_components(&self) -> WordComponents {
WordComponents::new(self)
}
pub fn word_component_indices(&self) -> WordComponentIndices {
WordComponentIndices::new(self)
}
pub fn to_dfa(&self) -> Dfa {
let mut gen = 0usize..;
let mut map = HashMap::new();
let mut accepting = HashSet::new();
let mut to_explore = vec![self.evaluator()];
let mut transitions = HashMap::new();
{
let key = Self::set_to_vec(to_explore[0].current_states_idx());
let n = gen.next().unwrap(); map.insert(key, n);
if to_explore[0].is_accepting() {
accepting.insert(n);
}
}
while let Some(eval) = to_explore.pop() {
let mut tr = Vec::with_capacity(self.alphabet.len());
for new_evaluator in eval.step_all() {
let is_accepting = new_evaluator.is_accepting();
let key = Self::set_to_vec(new_evaluator.current_states_idx());
if !map.contains_key(&key) {
to_explore.push(new_evaluator);
}
let x = map.entry(key).or_insert_with(|| gen.next().unwrap());
tr.push(*x);
if is_accepting {
accepting.insert(*x);
}
}
transitions.insert(Self::set_to_vec(eval.current_states_idx()), tr);
}
let sorted_keys = {
let mut vec = map.iter().collect::<Vec<_>>();
vec.sort_by_key(|(_, &n)| n);
vec
};
let states = sorted_keys
.into_iter()
.map(|(key, &n)| DfaState {
name: Rc::from(n.to_string()),
initial: n == 0,
accepting: accepting.contains(&n),
transitions: transitions.remove(key).unwrap(),
})
.collect();
Dfa {
alphabet: self.alphabet.clone(), states,
initial_state: 0, }
}
pub fn accepts(&self, string: &[&str]) -> bool {
let mut eval = self.evaluator();
eval.step_multiple(string);
eval.is_accepting()
}
pub fn accepts_graphemes(&self, string: &str) -> bool {
let graphemes = string.graphemes(true).collect::<Vec<_>>();
let mut eval = self.evaluator();
eval.step_multiple(&graphemes);
eval.is_accepting()
}
pub fn graphemes_only(&self) -> bool {
self.alphabet
.iter()
.all(|str| str.graphemes(true).count() == 1)
}
pub fn has_epsilon_moves(&self) -> bool {
self.states
.iter()
.any(|state| !state.epsilon_transitions.is_empty())
}
pub fn evaluator(&self) -> NfaEvaluator<'_> {
self.into()
}
pub fn closure(&self, start: usize) -> Option<HashSet<usize>> {
if start >= self.states.len() {
return None;
}
let mut all = HashSet::new();
all.insert(start);
let mut new = vec![start];
while !new.is_empty() {
let old_new = mem::take(&mut new);
for state in old_new {
for &eps_target in &self.states[state].epsilon_transitions {
if all.insert(eps_target) {
new.push(eps_target)
}
}
}
}
Some(all)
}
pub fn to_table(&self) -> String {
self.gen_table("ε", "→")
}
pub fn ascii_table(&self) -> String {
self.gen_table("eps", "->")
}
fn gen_table(&self, eps: &str, arrow: &str) -> String {
let mut table = Table::default();
let mut alph = vec!["", "", "", eps];
alph.extend(self.alphabet.iter().map(|s| s as &str));
table.push_row(alph);
let trans_strings = &self
.states
.iter()
.map(|state| {
iter::once(&state.epsilon_transitions)
.chain(&state.transitions)
.map(|trans| {
let s = trans
.iter()
.map(|c| self.states[*c].name.clone())
.collect::<Vec<_>>()
.join(" ");
format!("{{{s}}}")
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
for (idx, state) in self.states.iter().enumerate() {
let mut state = vec![
if state.initial { arrow } else { "" },
if state.accepting { "*" } else { "" },
&state.name,
];
state.extend(trans_strings[idx].iter().map(|s| s as &str));
table.push_row(state);
}
table.to_string(" ")
}
pub fn equivalent_to(&self, other: &Nfa) -> bool {
if !alphabet_equal(&self.alphabet, &other.alphabet) {
return false;
}
let mut evaluators_to_explore = vec![(self.evaluator(), other.evaluator())];
let mut explored_states = HashSet::new();
explored_states.insert((
Self::set_to_vec(evaluators_to_explore[0].0.current_states_idx()),
Self::set_to_vec(evaluators_to_explore[0].1.current_states_idx()),
));
while let Some((s1, s2)) = evaluators_to_explore.pop() {
if s1.is_accepting() != s2.is_accepting() {
return false;
}
for elem in self.alphabet.iter() {
let mut d1 = s1.clone();
d1.step(elem);
let mut d2 = s2.clone();
d2.step(elem);
if explored_states.insert((
Self::set_to_vec(d1.current_states_idx()),
Self::set_to_vec(d2.current_states_idx()),
)) {
evaluators_to_explore.push((d1, d2));
}
}
}
true
}
fn set_to_vec<T: Clone + Ord>(set: &HashSet<T>) -> Vec<T> {
let mut vec = set.iter().cloned().collect::<Vec<_>>();
vec.sort();
vec
}
pub fn alphabet(&self) -> &[Rc<str>] {
&self.alphabet
}
pub fn states(&self) -> &[NfaState] {
self.states.as_slice()
}
pub fn initial_state(&self) -> &NfaState {
&self.states[self.initial_state]
}
pub fn initial_state_index(&self) -> usize {
self.initial_state
}
}