use crate::nfa::{Nfa, NfaState};
pub use crate::parser::dfa as parse;
use crate::table::Table;
use crate::util::alphabet_equal;
pub use eval::DfaEvaluator;
pub use parse::DfaParseError;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use unicode_segmentation::UnicodeSegmentation;
pub mod eval;
pub mod parse;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Dfa {
pub(crate) alphabet: Rc<[Rc<str>]>,
pub(crate) states: Vec<DfaState>,
pub(crate) initial_state: usize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DfaState {
pub(crate) name: Rc<str>,
pub(crate) initial: bool,
pub(crate) accepting: bool,
pub(crate) transitions: Vec<usize>,
}
impl DfaState {
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) -> &[usize] {
self.transitions.as_slice()
}
}
impl From<DfaState> for NfaState {
fn from(value: DfaState) -> Self {
let DfaState {
name,
initial,
accepting,
transitions,
} = value;
NfaState {
name,
initial,
accepting,
epsilon_transitions: vec![],
transitions: transitions.into_iter().map(|t| vec![t]).collect(),
}
}
}
impl From<Dfa> for Nfa {
fn from(value: Dfa) -> Self {
value.to_nfa()
}
}
impl Dfa {
pub fn invert(&mut self) {
self.states
.iter_mut()
.for_each(|s| s.accepting = !s.accepting)
}
pub fn union(&self, other: &Self) -> Option<Self> {
self.product_construction(other, |s1, s2| s1.accepting || s2.accepting)
}
pub fn intersection(&self, other: &Self) -> Option<Self> {
self.product_construction(other, |s1, s2| s1.accepting && s2.accepting)
}
pub fn difference(&self, other: &Self) -> Option<Self> {
self.product_construction(other, |s1, s2| s1.accepting && !s2.accepting)
}
pub fn symmetric_difference(&self, other: &Self) -> Option<Self> {
self.product_construction(other, |s1, s2| s1.accepting != s2.accepting)
}
pub fn product_construction(
&self,
other: &Self,
mut combinator: impl FnMut(&DfaState, &DfaState) -> bool,
) -> Option<Self> {
if !alphabet_equal(&self.alphabet, &other.alphabet) {
return None;
}
let mut evaluators_to_explore = vec![(self.evaluator(), other.evaluator())];
let q1 = self.initial_state;
let q2 = other.initial_state;
let mut explored_states = HashSet::new();
explored_states.insert((q1, q2));
let mut state_data = vec![];
while let Some((s1, s2)) = evaluators_to_explore.pop() {
let mut transition_list = Vec::with_capacity(self.alphabet.len());
for elem in self.alphabet.iter() {
let mut d1 = s1.clone();
d1.step(elem);
let mut d2 = s2.clone();
d2.step(elem);
let states = (d1.current_state_idx(), d2.current_state_idx());
transition_list.push(states);
if explored_states.insert(states) {
evaluators_to_explore.push((d1, d2));
}
}
state_data.push((
(s1.current_state_idx(), s2.current_state_idx()),
combinator(s1.current_state().unwrap(), s2.current_state().unwrap()),
transition_list,
));
}
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!(
"({},{})",
self.states[*s1].name, 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(&(q1, q2))
.expect("Initial state should have an index");
let states = state_data
.into_iter()
.map(|(states, accepting, transitions)| DfaState {
name: names
.get(&states)
.expect("All states should have a name")
.clone(),
initial: states == (q1, q2),
accepting,
transitions: 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(Dfa {
alphabet: self.alphabet.clone(),
states,
initial_state,
})
}
pub fn minimize(&mut self) {
self.remove_unreachable_states();
self.merge_nondistinguishable_states();
}
pub fn merge_nondistinguishable_states(&mut self) {
let mapper = self
.state_equivalence_classes_idx()
.into_iter()
.flat_map(|set| {
debug_assert!(!set.is_empty(), "Should not have empty equivalence classes");
let mut iter = set.into_iter();
let new = iter.next();
iter.map(move |old| (old, unsafe { new.unwrap_unchecked() }))
})
.collect::<HashMap<_, _>>();
let map = |idx| mapper.get(&idx).copied();
self.remap_transitions(map);
if let Some(new_initial) = map(self.initial_state) {
self.initial_state = new_initial;
self.states[new_initial].initial = true;
}
let to_remove = mapper.into_keys().collect();
self.remove_states(to_remove);
}
pub fn state_equivalence_classes(&self) -> Vec<Vec<&DfaState>> {
self.state_equivalence_classes_idx()
.into_iter()
.map(|class| {
class
.into_iter()
.map(|state| &self.states[state])
.collect::<Vec<_>>()
})
.collect()
}
pub fn state_equivalence_classes_idx(&self) -> Vec<HashSet<usize>> {
let (finals, nonfinals): (HashSet<usize>, HashSet<usize>) =
(0..self.states.len()).partition(|&idx| self.states[idx].accepting);
if finals.is_empty() {
return vec![nonfinals];
} else if nonfinals.is_empty() {
return vec![finals];
}
let mut p = vec![finals, nonfinals];
let mut w = p.clone();
while let Some(a) = w.pop() {
for c in 0..self.alphabet.len() {
let x: HashSet<usize> = self
.states
.iter()
.enumerate()
.filter(|(_, s)| a.contains(&s.transitions[c]))
.map(|(i, _)| i)
.collect();
p = p
.into_iter()
.map(|y| {
(
x.intersection(&y).copied().collect::<HashSet<_>>(),
y.difference(&x).copied().collect::<HashSet<_>>(),
y,
)
})
.flat_map(|(inters, diff, y)| {
if !inters.is_empty() && !diff.is_empty() {
if let Some(idx) = w.iter().position(|hs| hs == &y) {
w.swap_remove(idx);
w.push(inters.clone());
w.push(diff.clone());
} else if inters.len() <= diff.len() {
w.push(inters.clone());
} else {
w.push(diff.clone());
}
vec![inters, diff].into_iter()
} else {
vec![y].into_iter()
}
})
.collect()
}
}
p
}
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<&DfaState> {
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<&DfaState> {
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().copied())
.filter(|&state| reachables.insert(state))
.collect();
}
reachables
}
fn remap_transitions(&mut self, mapper: impl Fn(usize) -> Option<usize>) {
self.states.iter_mut().for_each(|state| {
state
.transitions
.iter_mut()
.for_each(|trans| *trans = mapper(*trans).unwrap_or(*trans))
})
}
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);
}
pub fn to_nfa(self) -> Nfa {
let Dfa {
alphabet,
states,
initial_state,
} = self;
let states = states.into_iter().map(|s| s.into()).collect();
Nfa {
alphabet,
states,
initial_state,
}
}
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 evaluator(&self) -> DfaEvaluator<'_> {
self.into()
}
pub fn to_table(&self) -> String {
self.gen_table("→")
}
pub fn ascii_table(&self) -> String {
self.gen_table("->")
}
fn gen_table(&self, arrow: &str) -> String {
let mut table = Table::default();
let mut alph = vec!["", "", ""];
alph.extend(self.alphabet.iter().map(|s| s as &str));
table.push_row(alph);
for DfaState {
name,
initial,
accepting,
transitions,
} in &self.states
{
let mut state = vec![
if *initial { arrow } else { "" },
if *accepting { "*" } else { "" },
name,
];
transitions
.iter()
.for_each(|&c| state.push(&self.states[c].name));
table.push_row(state);
}
table.to_string(" ")
}
pub fn equivalent_to(&self, other: &Dfa) -> 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((
evaluators_to_explore[0].0.current_state_idx(),
evaluators_to_explore[0].1.current_state_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((d1.current_state_idx(), d2.current_state_idx())) {
evaluators_to_explore.push((d1, d2));
}
}
}
true
}
pub fn alphabet(&self) -> &[Rc<str>] {
&self.alphabet
}
pub fn states(&self) -> &[DfaState] {
self.states.as_slice()
}
pub fn initial_state(&self) -> &DfaState {
&self.states[self.initial_state]
}
pub fn initial_state_index(&self) -> usize {
self.initial_state
}
}