use crate::finite::automaton::FiniteAutomaton;
use crate::finite::deterministic::DeterministicFiniteAutomaton;
use crate::general::NonDeterministicAutomaton;
pub trait NonDeterministicFiniteAutomaton: NonDeterministicAutomaton + FiniteAutomaton {
type CorrespondingDFA: DeterministicFiniteAutomaton<State = Self::State, Input = Self::Input, CorrespondingNFA = Self>;
fn to_dfa(&self) -> Self::CorrespondingDFA;
fn union(&self, other: &Self) -> Self;
fn difference(&self, other: &Self) -> Self;
fn concatenate(&self, other: &Self) -> Self;
fn intersection(&self, other: &Self) -> Self;
fn star(&self) -> Self;
fn reverse(&self) -> Self;
fn trimmed(&self) -> Self;
fn complement(&self) -> Self;
fn accessible(&self) -> Self;
fn co_accessible(&self) -> Self;
fn is_subset_of(&self, other: &Self) -> bool;
fn is_equivalent_to(&self, other: &Self) -> bool;
fn is_empty_language(&self) -> bool {
!self.reachable_states().iter().any(|&s| self.is_accepting_state(s))
}
fn union_all(automata: &[Self]) -> Option<Self>
where Self: Clone + Sized
{
clone_reduce(automata, |a, b| a.union(b))
}
fn concatenate_all(automata: &[Self]) -> Option<Self>
where Self: Clone + Sized
{
clone_reduce(automata, |a, b| a.concatenate(b))
}
fn intersect_all(automata: &[Self]) -> Option<Self>
where Self: Clone + Sized
{
clone_reduce(automata, |a, b| a.intersection(b))
}
}
fn clone_reduce<'a, T: Clone>(arr: &[T], f: impl Fn(T, &T) -> T) -> Option<T> {
let mut iter = arr.iter();
let Some(item) = iter.next() else {
return None;
};
Some(iter.fold(item.clone(), |a, b| f(a, &b)))
}