use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use std::collections::{HashMap, HashSet, VecDeque};
use super::types::{
BuchiAutomaton, Cfg, ContextFreeGrammar, CykParser, Dfa, Nfa, PdaSimulator, PushdownAutomaton,
Regex, RegexExt, TapeDir, TmDirection, TuringMachine, TuringMachineSimulator,
};
pub fn app(f: Expr, a: Expr) -> Expr {
Expr::App(Box::new(f), Box::new(a))
}
pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
app(app(f, a), b)
}
pub fn cst(s: &str) -> Expr {
Expr::Const(Name::str(s), vec![])
}
pub fn prop() -> Expr {
Expr::Sort(Level::zero())
}
pub fn type0() -> Expr {
Expr::Sort(Level::succ(Level::zero()))
}
pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
}
pub fn arrow(a: Expr, b: Expr) -> Expr {
pi(BinderInfo::Default, "_", a, b)
}
pub fn bvar(n: u32) -> Expr {
Expr::BVar(n)
}
pub fn nat_ty() -> Expr {
cst("Nat")
}
pub fn bool_ty() -> Expr {
cst("Bool")
}
pub fn string_ty() -> Expr {
cst("String")
}
pub fn list_nat_ty() -> Expr {
app(cst("List"), nat_ty())
}
pub fn alphabet_ty() -> Expr {
type0()
}
pub fn word_ty() -> Expr {
arrow(type0(), type0())
}
pub fn language_ty() -> Expr {
arrow(type0(), prop())
}
pub fn dfa_ty() -> Expr {
type0()
}
pub fn nfa_ty() -> Expr {
type0()
}
pub fn pda_ty() -> Expr {
type0()
}
pub fn cfg_ty() -> Expr {
type0()
}
pub fn turing_machine_ty() -> Expr {
type0()
}
pub fn regular_language_ty() -> Expr {
arrow(type0(), prop())
}
pub fn context_free_language_ty() -> Expr {
arrow(type0(), prop())
}
pub fn recursively_enumerable_ty() -> Expr {
arrow(type0(), prop())
}
pub fn pumping_lemma_regular_ty() -> Expr {
prop()
}
pub fn pumping_lemma_cfl_ty() -> Expr {
prop()
}
pub fn myhill_nerode_ty() -> Expr {
prop()
}
pub fn kleene_theorem_ty() -> Expr {
prop()
}
pub fn chomsky_normal_form_ty() -> Expr {
prop()
}
pub fn halting_undecidable_ty() -> Expr {
prop()
}
pub fn rice_theorem_ty() -> Expr {
prop()
}
pub fn cfl_closed_union_ty() -> Expr {
prop()
}
pub fn regular_closed_complement_ty() -> Expr {
prop()
}
pub fn regex_ty() -> Expr {
type0()
}
pub fn lba_ty() -> Expr {
type0()
}
pub fn buchi_automaton_ty() -> Expr {
type0()
}
pub fn eps_nfa_ty() -> Expr {
type0()
}
pub fn mealy_machine_ty() -> Expr {
type0()
}
pub fn moore_machine_ty() -> Expr {
type0()
}
pub fn two_way_dfa_ty() -> Expr {
type0()
}
pub fn counting_automaton_ty() -> Expr {
type0()
}
pub fn weighted_automaton_ty() -> Expr {
type0()
}
pub fn omega_language_ty() -> Expr {
arrow(type0(), prop())
}
pub fn subset_construction_ty() -> Expr {
prop()
}
pub fn greibach_normal_form_ty() -> Expr {
prop()
}
pub fn cfl_pda_equivalence_ty() -> Expr {
prop()
}
pub fn lba_accepts_csl_ty() -> Expr {
prop()
}
pub fn post_correspondence_undecidable_ty() -> Expr {
prop()
}
pub fn time_hierarchy_theorem_ty() -> Expr {
prop()
}
pub fn space_hierarchy_theorem_ty() -> Expr {
prop()
}
pub fn universal_turing_machine_ty() -> Expr {
prop()
}
pub fn church_turing_thesis_ty() -> Expr {
prop()
}
pub fn earley_parsing_correct_ty() -> Expr {
prop()
}
pub fn cyk_parsing_correct_ty() -> Expr {
prop()
}
pub fn regular_closed_boolean_ty() -> Expr {
prop()
}
pub fn buchi_complementable_ty() -> Expr {
prop()
}
pub fn buchi_closed_union_ty() -> Expr {
prop()
}
pub fn buchi_closed_intersection_ty() -> Expr {
prop()
}
pub fn omega_regular_equals_buchi_ty() -> Expr {
prop()
}
pub fn myhill_nerode_omega_ty() -> Expr {
prop()
}
pub fn pumping_lemma_omega_ty() -> Expr {
prop()
}
pub fn regex_to_dfa_correct_ty() -> Expr {
prop()
}
pub fn dfa_to_regex_correct_ty() -> Expr {
prop()
}
pub fn glushkov_construction_ty() -> Expr {
prop()
}
pub fn empty_word_problem_decidable_ty() -> Expr {
prop()
}
pub fn ambiguity_undecidable_ty() -> Expr {
prop()
}
pub fn intersection_cfl_reg_ty() -> Expr {
prop()
}
pub fn regular_not_closed_arbitrary_ty() -> Expr {
prop()
}
pub fn mealy_moore_equivalent_ty() -> Expr {
prop()
}
pub fn two_way_dfa_equals_dfa_ty() -> Expr {
prop()
}
pub fn p_vs_np_ty() -> Expr {
prop()
}
pub fn build_formal_languages_env(env: &mut Environment) -> Result<(), String> {
for (name, ty) in [
("Alphabet", alphabet_ty()),
("Word", word_ty()),
("FormalLanguage", language_ty()),
("DFA", dfa_ty()),
("NFA", nfa_ty()),
("PDA", pda_ty()),
("CFG", cfg_ty()),
("TuringMachine", turing_machine_ty()),
("RegularLanguage", regular_language_ty()),
("ContextFreeLanguage", context_free_language_ty()),
("RecursivelyEnumerable", recursively_enumerable_ty()),
("RegEx", regex_ty()),
("LBA", lba_ty()),
("BuchiAutomaton", buchi_automaton_ty()),
("EpsNFA", eps_nfa_ty()),
("MealyMachine", mealy_machine_ty()),
("MooreMachine", moore_machine_ty()),
("TwoWayDFA", two_way_dfa_ty()),
("CountingAutomaton", counting_automaton_ty()),
("WeightedAutomaton", weighted_automaton_ty()),
("OmegaLanguage", omega_language_ty()),
] {
env.add(Declaration::Axiom {
name: Name::str(name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
for (name, ty) in [
("DFAAccepts", arrow(dfa_ty(), arrow(string_ty(), prop()))),
("NFAAccepts", arrow(nfa_ty(), arrow(string_ty(), prop()))),
("PDAAccepts", arrow(pda_ty(), arrow(string_ty(), prop()))),
("CFGDerives", arrow(cfg_ty(), arrow(string_ty(), prop()))),
(
"TMAccepts",
arrow(turing_machine_ty(), arrow(string_ty(), prop())),
),
("LBAAccepts", arrow(lba_ty(), arrow(string_ty(), prop()))),
(
"BuchiAccepts",
arrow(
buchi_automaton_ty(),
arrow(arrow(nat_ty(), nat_ty()), prop()),
),
),
(
"RegExMatches",
arrow(regex_ty(), arrow(string_ty(), prop())),
),
("IsRegular", arrow(arrow(string_ty(), prop()), prop())),
("IsContextFree", arrow(arrow(string_ty(), prop()), prop())),
("IsDecidable", arrow(arrow(string_ty(), prop()), prop())),
("IsSemiDecidable", arrow(arrow(string_ty(), prop()), prop())),
(
"IsContextSensitive",
arrow(arrow(string_ty(), prop()), prop()),
),
(
"IsOmegaRegular",
arrow(arrow(nat_ty(), arrow(nat_ty(), prop())), prop()),
),
] {
env.add(Declaration::Axiom {
name: Name::str(name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
for (name, ty) in [
("dfaMinimize", arrow(dfa_ty(), dfa_ty())),
("nfaToDfa", arrow(nfa_ty(), dfa_ty())),
("cfgToCnf", arrow(cfg_ty(), cfg_ty())),
("cfgToGnf", arrow(cfg_ty(), cfg_ty())),
("regexToNfa", arrow(regex_ty(), nfa_ty())),
("dfaToRegex", arrow(dfa_ty(), regex_ty())),
("epsNfaToNfa", arrow(eps_nfa_ty(), nfa_ty())),
(
"mealyToMoore",
arrow(mealy_machine_ty(), moore_machine_ty()),
),
(
"mooreToMealy",
arrow(moore_machine_ty(), mealy_machine_ty()),
),
("dfaProduct", arrow(dfa_ty(), arrow(dfa_ty(), dfa_ty()))),
("dfaComplement", arrow(dfa_ty(), dfa_ty())),
("cfgToPda", arrow(cfg_ty(), pda_ty())),
("pdaToCfg", arrow(pda_ty(), cfg_ty())),
] {
env.add(Declaration::Axiom {
name: Name::str(name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
for (name, ty) in [
("PumpingLemmaRegular", pumping_lemma_regular_ty()),
("PumpingLemmaCFL", pumping_lemma_cfl_ty()),
("MyhillNerode", myhill_nerode_ty()),
("KleeneTheorem", kleene_theorem_ty()),
("ChomskyNormalForm", chomsky_normal_form_ty()),
("HaltingUndecidable", halting_undecidable_ty()),
("RiceTheorem", rice_theorem_ty()),
("CflClosedUnion", cfl_closed_union_ty()),
("RegularClosedComplement", regular_closed_complement_ty()),
("RegularClosedIntersection", prop()),
("RegularClosedUnion", prop()),
("RegularClosedConcatenation", prop()),
("RegularClosedKleeneStar", prop()),
("CflClosedConcatenation", prop()),
("CflClosedKleeneStar", prop()),
("CflNotClosedIntersection", prop()),
("CflNotClosedComplement", prop()),
("RegularSubsetCFL", prop()),
("CFLSubsetCSL", prop()),
("CSLSubsetRE", prop()),
("EmptinessRegularDecidable", prop()),
("MembershipRegularDecidable", prop()),
("MembershipCFLDecidable", prop()),
("EmptinessCFLDecidable", prop()),
("NfaDfaEquivalence", prop()),
("DfaMinimizationCorrect", prop()),
("BarHillellLemma", prop()),
] {
env.add(Declaration::Axiom {
name: Name::str(name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
for (name, ty) in [
("SubsetConstruction", subset_construction_ty()),
("GreibachNormalForm", greibach_normal_form_ty()),
("CflPdaEquivalence", cfl_pda_equivalence_ty()),
("LbaAcceptsCsl", lba_accepts_csl_ty()),
(
"PostCorrespondenceUndecidable",
post_correspondence_undecidable_ty(),
),
("TimeHierarchyTheorem", time_hierarchy_theorem_ty()),
("SpaceHierarchyTheorem", space_hierarchy_theorem_ty()),
(
"UniversalTuringMachineExists",
universal_turing_machine_ty(),
),
("ChurchTuringThesis", church_turing_thesis_ty()),
("EarleyParsingCorrect", earley_parsing_correct_ty()),
("CykParsingCorrect", cyk_parsing_correct_ty()),
("RegularClosedBoolean", regular_closed_boolean_ty()),
("BuchiComplementable", buchi_complementable_ty()),
("BuchiClosedUnion", buchi_closed_union_ty()),
("BuchiClosedIntersection", buchi_closed_intersection_ty()),
("OmegaRegularEqualsBuchi", omega_regular_equals_buchi_ty()),
("MyhillNerodeOmega", myhill_nerode_omega_ty()),
("PumpingLemmaOmega", pumping_lemma_omega_ty()),
("RegExToDfaCorrect", regex_to_dfa_correct_ty()),
("DfaToRegExCorrect", dfa_to_regex_correct_ty()),
("GlushkovConstruction", glushkov_construction_ty()),
(
"EmptyWordProblemDecidable",
empty_word_problem_decidable_ty(),
),
("AmbiguityUndecidable", ambiguity_undecidable_ty()),
("IntersectionCflReg", intersection_cfl_reg_ty()),
(
"RegularNotClosedArbitrary",
regular_not_closed_arbitrary_ty(),
),
("MealyMooreEquivalent", mealy_moore_equivalent_ty()),
("TwoWayDfaEqualsDfa", two_way_dfa_equals_dfa_ty()),
("PvsNP", p_vs_np_ty()),
] {
env.add(Declaration::Axiom {
name: Name::str(name),
univ_params: vec![],
ty: ty.clone(),
})
.ok();
}
Ok(())
}
pub fn dfa_minimize(dfa: &Dfa) -> Dfa {
let n = dfa.states;
let sigma = dfa.alphabet.len();
let mut reachable = vec![false; n];
let mut queue: VecDeque<usize> = VecDeque::new();
reachable[dfa.start] = true;
queue.push_back(dfa.start);
while let Some(q) = queue.pop_front() {
for c in 0..sigma {
let r = dfa.delta[q][c];
if !reachable[r] {
reachable[r] = true;
queue.push_back(r);
}
}
}
let reach_states: Vec<usize> = (0..n).filter(|&q| reachable[q]).collect();
let reach_index: HashMap<usize, usize> = reach_states
.iter()
.enumerate()
.map(|(i, &q)| (q, i))
.collect();
let m = reach_states.len();
let mut dist = vec![vec![false; m]; m];
for i in 0..m {
for j in (i + 1)..m {
if dfa.accept[reach_states[i]] != dfa.accept[reach_states[j]] {
dist[i][j] = true;
dist[j][i] = true;
}
}
}
let mut changed = true;
while changed {
changed = false;
for i in 0..m {
for j in (i + 1)..m {
if dist[i][j] {
continue;
}
'outer: for c in 0..sigma {
let ri = reach_index[&dfa.delta[reach_states[i]][c]];
let rj = reach_index[&dfa.delta[reach_states[j]][c]];
if ri != rj && (dist[ri][rj] || dist[rj][ri]) {
dist[i][j] = true;
dist[j][i] = true;
changed = true;
break 'outer;
}
}
}
}
}
let mut class_of = vec![usize::MAX; m];
let mut num_classes = 0usize;
for i in 0..m {
if class_of[i] == usize::MAX {
class_of[i] = num_classes;
for j in (i + 1)..m {
if !dist[i][j] {
class_of[j] = num_classes;
}
}
num_classes += 1;
}
}
let start_class = class_of[reach_index[&dfa.start]];
let mut new_delta = vec![vec![0usize; sigma]; num_classes];
let mut new_accept = vec![false; num_classes];
for i in 0..m {
let ci = class_of[i];
new_accept[ci] = dfa.accept[reach_states[i]];
for c in 0..sigma {
let ri = reach_index[&dfa.delta[reach_states[i]][c]];
new_delta[ci][c] = class_of[ri];
}
}
Dfa {
states: num_classes,
alphabet: dfa.alphabet.clone(),
delta: new_delta,
start: start_class,
accept: new_accept,
}
}
pub fn nfa_to_dfa(nfa: &Nfa) -> Dfa {
let sigma = nfa.alphabet.len();
let mut subset_to_id: HashMap<Vec<usize>, usize> = HashMap::new();
let mut id_to_subset: Vec<Vec<usize>> = Vec::new();
let mut queue: VecDeque<usize> = VecDeque::new();
let start_set: Vec<usize> = vec![nfa.start];
subset_to_id.insert(start_set.clone(), 0);
id_to_subset.push(start_set);
queue.push_back(0);
let mut dfa_delta: Vec<Vec<usize>> = Vec::new();
while let Some(id) = queue.pop_front() {
let mut row = vec![0usize; sigma];
let subset = id_to_subset[id].clone();
for c in 0..sigma {
let mut next_set: HashSet<usize> = HashSet::new();
for &q in &subset {
for &r in &nfa.delta[q][c] {
next_set.insert(r);
}
}
let mut next_vec: Vec<usize> = next_set.into_iter().collect();
next_vec.sort_unstable();
if next_vec.is_empty() {
row[c] = usize::MAX;
} else {
let next_id = if let Some(&eid) = subset_to_id.get(&next_vec) {
eid
} else {
let eid = id_to_subset.len();
subset_to_id.insert(next_vec.clone(), eid);
id_to_subset.push(next_vec);
queue.push_back(eid);
eid
};
row[c] = next_id;
}
}
dfa_delta.push(row);
}
let has_dead = dfa_delta.iter().any(|row| row.contains(&usize::MAX));
let dead_id = id_to_subset.len();
if has_dead {
for row in &mut dfa_delta {
for s in row.iter_mut() {
if *s == usize::MAX {
*s = dead_id;
}
}
}
dfa_delta.push(vec![dead_id; sigma]);
id_to_subset.push(vec![]);
}
let total = id_to_subset.len();
let accept: Vec<bool> = (0..total)
.map(|id| {
if id == dead_id && has_dead {
false
} else {
id_to_subset[id].iter().any(|&q| nfa.accept[q])
}
})
.collect();
Dfa {
states: total,
alphabet: nfa.alphabet.clone(),
delta: dfa_delta,
start: 0,
accept,
}
}
pub fn check_pumping_lemma_regular(word: &[u8], pump_len: usize) -> bool {
if pump_len == 0 {
return false;
}
if word.len() < pump_len {
return false;
}
true
}
pub fn regex_to_dfa(regex: &Regex) -> Dfa {
fn collect_chars(r: &Regex, out: &mut Vec<char>) {
match r {
Regex::Empty | Regex::Epsilon => {}
Regex::Char(c) => {
if !out.contains(c) {
out.push(*c);
}
}
Regex::Alt(a, b) | Regex::Concat(a, b) => {
collect_chars(a, out);
collect_chars(b, out);
}
Regex::Star(a) => collect_chars(a, out),
}
}
let mut alphabet = Vec::new();
collect_chars(regex, &mut alphabet);
alphabet.sort_unstable();
if alphabet.is_empty() {
let accepts_eps = regex.matches("");
return Dfa {
states: 1,
alphabet: vec![],
delta: vec![vec![]],
start: 0,
accept: vec![accepts_eps],
};
}
const MAX_STATES: usize = 256;
let sigma = alphabet.len();
struct ThompsonNfa {
transitions: Vec<Vec<Vec<usize>>>,
eps: Vec<Vec<usize>>,
}
impl ThompsonNfa {
fn new_state(&mut self) -> usize {
let id = self.transitions.len();
self.transitions.push(vec![]);
self.eps.push(vec![]);
id
}
}
fn build_thompson(r: &Regex, alphabet: &[char], nfa: &mut ThompsonNfa) -> (usize, usize) {
match r {
Regex::Empty => {
let s = nfa.new_state();
let a = nfa.new_state();
nfa.transitions[s].resize(alphabet.len(), vec![]);
nfa.transitions[a].resize(alphabet.len(), vec![]);
(s, a)
}
Regex::Epsilon => {
let s = nfa.new_state();
nfa.transitions[s].resize(alphabet.len(), vec![]);
(s, s)
}
Regex::Char(c) => {
let s = nfa.new_state();
let a = nfa.new_state();
nfa.transitions[s].resize(alphabet.len(), vec![]);
nfa.transitions[a].resize(alphabet.len(), vec![]);
if let Some(idx) = alphabet.iter().position(|&x| x == *c) {
nfa.transitions[s][idx].push(a);
}
(s, a)
}
Regex::Alt(r1, r2) => {
let (s1, a1) = build_thompson(r1, alphabet, nfa);
let (s2, a2) = build_thompson(r2, alphabet, nfa);
let s = nfa.new_state();
let a = nfa.new_state();
nfa.transitions[s].resize(alphabet.len(), vec![]);
nfa.transitions[a].resize(alphabet.len(), vec![]);
nfa.eps[s].push(s1);
nfa.eps[s].push(s2);
nfa.eps[a1].push(a);
nfa.eps[a2].push(a);
(s, a)
}
Regex::Concat(r1, r2) => {
let (s1, a1) = build_thompson(r1, alphabet, nfa);
let (s2, a2) = build_thompson(r2, alphabet, nfa);
nfa.eps[a1].push(s2);
(s1, a2)
}
Regex::Star(r1) => {
let (s1, a1) = build_thompson(r1, alphabet, nfa);
let s = nfa.new_state();
let a = nfa.new_state();
nfa.transitions[s].resize(alphabet.len(), vec![]);
nfa.transitions[a].resize(alphabet.len(), vec![]);
nfa.eps[s].push(s1);
nfa.eps[s].push(a);
nfa.eps[a1].push(s1);
nfa.eps[a1].push(a);
(s, a)
}
}
}
let mut tnfa = ThompsonNfa {
transitions: vec![],
eps: vec![],
};
let (t_start, t_accept) = build_thompson(regex, &alphabet, &mut tnfa);
let n_tnfa = tnfa.transitions.len();
let eps_closure = |states: &HashSet<usize>| -> HashSet<usize> {
let mut closure = states.clone();
let mut stack: Vec<usize> = states.iter().copied().collect();
while let Some(q) = stack.pop() {
if q < tnfa.eps.len() {
for &r in &tnfa.eps[q] {
if closure.insert(r) {
stack.push(r);
}
}
}
}
closure
};
let start_set = eps_closure(&{
let mut s = HashSet::new();
s.insert(t_start);
s
});
let mut subset_map: HashMap<Vec<usize>, usize> = HashMap::new();
let mut id_to_set: Vec<Vec<usize>> = Vec::new();
let mut dfa_delta_build: Vec<Vec<usize>> = Vec::new();
let mut bfs: VecDeque<usize> = VecDeque::new();
let mut start_sorted: Vec<usize> = start_set.into_iter().collect();
start_sorted.sort_unstable();
subset_map.insert(start_sorted.clone(), 0);
id_to_set.push(start_sorted);
bfs.push_back(0);
while let Some(id) = bfs.pop_front() {
if dfa_delta_build.len() > MAX_STATES {
break;
}
let mut row = vec![0usize; sigma];
let set: HashSet<usize> = id_to_set[id].iter().copied().collect();
for c in 0..sigma {
let mut next_set: HashSet<usize> = HashSet::new();
for &q in &set {
if q < n_tnfa && c < tnfa.transitions[q].len() {
for &r in &tnfa.transitions[q][c] {
next_set.insert(r);
}
}
}
let next_closed = eps_closure(&next_set);
let mut next_sorted: Vec<usize> = next_closed.into_iter().collect();
next_sorted.sort_unstable();
let next_id = if let Some(&eid) = subset_map.get(&next_sorted) {
eid
} else {
let eid = id_to_set.len();
subset_map.insert(next_sorted.clone(), eid);
id_to_set.push(next_sorted);
bfs.push_back(eid);
eid
};
row[c] = next_id;
}
dfa_delta_build.push(row);
}
let total = id_to_set.len();
let accept_states: Vec<bool> = (0..total)
.map(|id| id_to_set[id].contains(&t_accept))
.collect();
Dfa {
states: total,
alphabet,
delta: dfa_delta_build,
start: 0,
accept: accept_states,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dfa_even_zeros() {
let alphabet = vec!['0', '1'];
let delta = vec![vec![1, 0], vec![0, 1]];
let dfa = Dfa {
states: 2,
alphabet,
delta,
start: 0,
accept: vec![true, false],
};
assert!(dfa.accepts(""));
assert!(dfa.accepts("1111"));
assert!(dfa.accepts("00"));
assert!(dfa.accepts("010"));
assert!(!dfa.accepts("0"));
assert!(dfa.accepts("001"));
assert!(!dfa.accepts("0111"));
}
#[test]
fn test_nfa_accepts() {
let alphabet = vec!['a', 'b'];
let delta = vec![
vec![vec![0, 1], vec![0]],
vec![vec![], vec![2]],
vec![vec![], vec![]],
];
let nfa = Nfa {
states: 3,
alphabet,
delta,
start: 0,
accept: vec![false, false, true],
};
assert!(nfa.accepts("ab"));
assert!(nfa.accepts("aab"));
assert!(nfa.accepts("bab"));
assert!(nfa.accepts("ababab"));
assert!(!nfa.accepts("a"));
assert!(!nfa.accepts("ba"));
assert!(!nfa.accepts("b"));
assert!(!nfa.accepts(""));
}
#[test]
fn test_regex_matches() {
let r = Regex::Concat(
Box::new(Regex::Char('a')),
Box::new(Regex::Star(Box::new(Regex::Alt(
Box::new(Regex::Char('b')),
Box::new(Regex::Char('c')),
)))),
);
assert!(r.matches("a"));
assert!(r.matches("ab"));
assert!(r.matches("ac"));
assert!(r.matches("abbc"));
assert!(r.matches("abcbc"));
assert!(!r.matches(""));
assert!(!r.matches("b"));
assert!(!r.matches("ba"));
let eps = Regex::Epsilon;
assert!(eps.matches(""));
assert!(!eps.matches("a"));
let empty = Regex::Empty;
assert!(!empty.matches(""));
assert!(!empty.matches("a"));
}
#[test]
fn test_nfa_to_dfa() {
let alphabet = vec!['a', 'b'];
let delta = vec![
vec![vec![0, 1], vec![0]],
vec![vec![], vec![2]],
vec![vec![], vec![]],
];
let nfa = Nfa {
states: 3,
alphabet,
delta,
start: 0,
accept: vec![false, false, true],
};
let dfa = nfa_to_dfa(&nfa);
assert!(dfa.accepts("ab"));
assert!(dfa.accepts("aab"));
assert!(dfa.accepts("bab"));
assert!(!dfa.accepts("a"));
assert!(!dfa.accepts("ba"));
assert!(!dfa.accepts("b"));
assert!(!dfa.accepts(""));
}
#[test]
fn test_cfg_cnf() {
let cnf = Cfg {
variables: vec!["S".into(), "A".into(), "B".into()],
terminals: vec!['a', 'b'],
rules: vec![
("S".into(), vec!["A".into(), "B".into()]),
("A".into(), vec!["a".into()]),
("B".into(), vec!["b".into()]),
],
start: "S".into(),
};
assert!(cnf.is_in_cnf());
let not_cnf = Cfg {
variables: vec!["S".into(), "A".into(), "B".into(), "C".into()],
terminals: vec!['a', 'b', 'c'],
rules: vec![
("S".into(), vec!["A".into(), "B".into(), "C".into()]),
("A".into(), vec!["a".into()]),
("B".into(), vec!["b".into()]),
("C".into(), vec!["c".into()]),
],
start: "S".into(),
};
assert!(!not_cnf.is_in_cnf());
}
#[test]
fn test_build_env() {
let mut env = Environment::new();
let result = build_formal_languages_env(&mut env);
assert!(result.is_ok());
}
#[test]
fn test_cyk_parser() {
let cyk = CykParser {
start: "S".into(),
binary: vec![
("S".into(), "A".into(), "B".into()),
("S".into(), "A".into(), "C".into()),
("C".into(), "S".into(), "B".into()),
],
unit: vec![("A".to_string(), 'a'), ("B".to_string(), 'b')],
derives_empty: false,
};
assert!(cyk.accepts("ab"));
assert!(cyk.accepts("aabb"));
assert!(cyk.accepts("aaabbb"));
assert!(!cyk.accepts("a"));
assert!(!cyk.accepts("b"));
assert!(!cyk.accepts("aab"));
assert!(!cyk.accepts("abb"));
assert!(!cyk.accepts(""));
}
#[test]
fn test_pda_simulator() {
let transitions = vec![
(0, Some(0usize), None, 1, vec![1usize]),
(1, Some(0usize), None, 1, vec![1usize]),
(1, Some(1usize), Some(1usize), 2, vec![]),
(2, Some(1usize), Some(1usize), 2, vec![]),
(2, None, Some(0usize), 3, vec![]),
];
let pda = PdaSimulator {
states: 4,
alphabet: vec!['a', 'b'],
stack_alphabet: vec!["Z".into(), "A".into()],
transitions,
start: 0,
initial_stack: 0,
accept: vec![false, false, false, true],
};
assert!(pda.accepts("ab"));
assert!(pda.accepts("aabb"));
assert!(!pda.accepts("a"));
assert!(!pda.accepts("b"));
assert!(!pda.accepts("aba"));
}
#[test]
fn test_turing_machine_accepts() {
let mut delta: HashMap<(usize, usize), (usize, usize, TmDirection)> = HashMap::new();
delta.insert((0, 1), (0, 1, TmDirection::Right));
delta.insert((0, 0), (1, 0, TmDirection::Stay));
let mut accept_set = HashSet::new();
accept_set.insert(1usize);
let mut reject_set = HashSet::new();
reject_set.insert(2usize);
let tm = TuringMachineSimulator {
states: 3,
tape_alphabet: vec!["_".into(), "a".into()],
delta,
start: 0,
accept: accept_set,
reject: reject_set,
step_limit: 1000,
};
assert!(tm.run(""));
assert!(tm.run("a"));
assert!(tm.run("aaa"));
assert!(tm.run("aaaaaaa"));
}
#[test]
fn test_buchi_accepts_lasso() {
let buchi = BuchiAutomaton {
states: 2,
alphabet: vec!['a', 'b'],
delta: vec![vec![vec![0, 1], vec![0]], vec![vec![1], vec![0]]],
start: 0,
accept: vec![false, true],
};
assert!(buchi.accepts_lasso("ba", "a"));
assert!(buchi.accepts_lasso("", "a"));
assert!(!buchi.accepts_lasso("a", "b"));
assert!(!buchi.accepts_lasso("", "b"));
}
#[test]
fn test_regex_to_dfa() {
let r = Regex::Concat(
Box::new(Regex::Star(Box::new(Regex::Alt(
Box::new(Regex::Char('a')),
Box::new(Regex::Char('b')),
)))),
Box::new(Regex::Char('c')),
);
let dfa = regex_to_dfa(&r);
assert!(dfa.accepts("c"));
assert!(dfa.accepts("ac"));
assert!(dfa.accepts("bc"));
assert!(dfa.accepts("abc"));
assert!(dfa.accepts("bac"));
assert!(!dfa.accepts("a"));
assert!(!dfa.accepts(""));
assert!(!dfa.accepts("ca"));
}
#[test]
fn test_new_theorem_types() {
let prop_expr = prop();
assert_eq!(subset_construction_ty(), prop_expr);
assert_eq!(greibach_normal_form_ty(), prop_expr);
assert_eq!(cfl_pda_equivalence_ty(), prop_expr);
assert_eq!(lba_accepts_csl_ty(), prop_expr);
assert_eq!(post_correspondence_undecidable_ty(), prop_expr);
assert_eq!(time_hierarchy_theorem_ty(), prop_expr);
assert_eq!(space_hierarchy_theorem_ty(), prop_expr);
assert_eq!(universal_turing_machine_ty(), prop_expr);
assert_eq!(church_turing_thesis_ty(), prop_expr);
assert_eq!(buchi_complementable_ty(), prop_expr);
assert_eq!(omega_regular_equals_buchi_ty(), prop_expr);
assert_eq!(regex_to_dfa_correct_ty(), prop_expr);
assert_eq!(ambiguity_undecidable_ty(), prop_expr);
assert_eq!(intersection_cfl_reg_ty(), prop_expr);
assert_eq!(mealy_moore_equivalent_ty(), prop_expr);
assert_eq!(two_way_dfa_equals_dfa_ty(), prop_expr);
}
}
#[cfg(test)]
mod tests_formal_languages_ext {
use super::*;
#[test]
fn test_cfg() {
let mut g = ContextFreeGrammar::new("S");
g.add_terminal("a");
g.add_terminal("b");
g.add_nonterminal("A");
g.add_production("S", vec!["a", "S", "b"]);
g.add_production("S", vec![]);
assert_eq!(g.num_productions(), 2);
assert!(g.generates_empty());
assert!(!g.is_cnf());
}
#[test]
fn test_pda_deterministic() {
let mut pda = PushdownAutomaton::new("q0", 'Z');
pda.add_accept_state("q1");
pda.add_transition("q0", Some('a'), 'Z', "q0", vec!['A', 'Z']);
pda.add_transition("q0", Some('b'), 'A', "q1", vec![]);
assert!(pda.is_deterministic());
assert_eq!(pda.num_states(), 2);
}
#[test]
fn test_regex_accepts() {
let r = RegexExt::star(RegexExt::ch('a'));
assert!(r.accepts(""));
assert!(r.accepts("a"));
assert!(r.accepts("aaa"));
let r2 = RegexExt::concat(RegexExt::ch('a'), RegexExt::ch('b'));
assert!(r2.accepts("ab"));
assert!(!r2.accepts("a"));
assert!(!r2.accepts("b"));
}
#[test]
fn test_regex_union() {
let r = RegexExt::union(RegexExt::ch('a'), RegexExt::ch('b'));
assert!(r.accepts("a"));
assert!(r.accepts("b"));
assert!(!r.accepts("ab"));
assert!(!r.accepts(""));
}
}
#[cfg(test)]
mod tests_formal_languages_ext2 {
use super::*;
#[test]
fn test_turing_machine_accept() {
let mut tm = TuringMachine::new("q0", "qa", "qr", '_');
tm.add_transition("q0", '_', "qa", '_', TapeDir::Stay);
let result = tm.halts_on_empty(10);
assert_eq!(result, Some(true));
assert_eq!(tm.num_transitions(), 1);
}
#[test]
fn test_turing_machine_reject() {
let mut tm = TuringMachine::new("q0", "qa", "qr", '_');
tm.add_transition("q0", '_', "qr", '_', TapeDir::Stay);
let result = tm.halts_on_empty(10);
assert_eq!(result, Some(false));
}
#[test]
fn test_cfg_pumping_bound() {
let mut g = ContextFreeGrammar::new("S");
for _i in 0..5 {
g.add_production("S", vec!["a"]);
}
assert!(g.pumping_length_bound() >= 32);
}
}