use std::hash::BuildHasherDefault;
use crate::EngineError;
use super::*;
impl FastAutomaton {
fn totalize(&mut self) -> Result<(), EngineError> {
debug_assert!(self.deterministic, "totalize requires a DFA");
let crash_state = self.new_state();
let mut transitions_to_crash_state: IntMap<State, Condition> =
IntMap::with_capacity_and_hasher(
self.number_of_states(),
BuildHasherDefault::default(),
);
let mut ranges = Vec::with_capacity(self.number_of_states());
for from_state in self.states() {
let mut new_condition = Condition::empty(&self.spanning_set);
for (condition, _) in self.transitions_from(from_state) {
new_condition = new_condition.union(condition);
ranges.push(condition.to_range(self.spanning_set())?);
}
new_condition = new_condition.complement();
transitions_to_crash_state.insert(from_state, new_condition);
}
for (from_state, condition) in &transitions_to_crash_state {
self.add_transition(*from_state, crash_state, condition);
ranges.push(condition.to_range(self.spanning_set())?);
}
let new_spanning_set = SpanningSet::compute_spanning_set(&ranges);
self.apply_new_spanning_set(&new_spanning_set)?;
if self.in_degree(crash_state) == 1 {
self.remove_state(crash_state);
}
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, fields(states = self.number_of_states(), deterministic = self.is_deterministic()))]
pub fn complement(&mut self) -> Result<(), EngineError> {
if !self.deterministic {
*self = self.determinize_implicit()?.into_owned();
}
self.totalize()?;
let mut new_accept_states = IntSet::default();
for state in self.states() {
if self.accept_states.contains(&state) {
continue;
}
new_accept_states.insert(state);
}
self.accept_states = new_accept_states;
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, fields(self_states = self.number_of_states(), self_deterministic = self.is_deterministic(), other_states = other.number_of_states(), other_deterministic = other.is_deterministic()))]
pub fn difference(&self, other: &FastAutomaton) -> Result<FastAutomaton, EngineError> {
let mut complement = other.determinize_implicit()?.into_owned();
complement.complement()?;
self.intersection(&complement)
}
}
#[cfg(test)]
mod tests {
use crate::fast_automaton::FastAutomaton;
use crate::regex::RegularExpression;
#[test]
fn complement_of_finite_is_infinite() {
let mut a = RegularExpression::parse("abc", false)
.unwrap()
.to_automaton()
.unwrap();
a.complement().unwrap();
assert!(!a.is_match("abc"), "complement must not match 'abc'");
assert!(a.is_match("x"));
assert!(a.is_match("xx"));
assert!(a.is_match("xxxxxxxxxx"));
}
#[test]
fn complement_of_empty_is_total() {
let mut a = FastAutomaton::new_empty();
a.complement().unwrap();
assert!(a.is_total(), "complement of ∅ must be Σ*");
}
}