Section Product_automaton.
Variable op: Prop -> Prop -> Prop.
Variable op_dec: forall P Q, dec P -> dec Q -> dec (op P Q).
Definition prod_delta: A (x) A' -> Sig -> A (x) A' :=
fun P x  => match P with (q1,q2) => (delta_S q1 x, delta_S q2 x) end.
Definition prod_pred := (fun P => match P with (q1,q2) => op (@F A q1) (@F A' q2) end).
Global Instance prod_pred_dec P: dec (prod_pred P).
Definition prod_F  := DecPred prod_pred.
Definition prod := DFA (s,s) prod_F prod_delta.
End Product_automaton.
Arguments prod op {op_dec}.
Definition intersect := prod and.
Definition U :=  prod or.
Definition diff := prod (fun P Q => P /\ ~ Q).
Definition lang_incl := forall w, in_lang A w -> in_lang A' w.
End Operations.
Definition lang_equiv A A':= lang_incl A A' /\ lang_incl A' A.
Instance lang_sub_dec A A' : dec (lang_incl A A').
Instance equiv_eq_dec A A': dec (lang_equiv A A').
(** * Nondeterministic finite automata (NFA)*)
Record nfa := NFA {
Q :> finType;
q0:Q;
Q_acc: decPred Q;
delta_Q: Q -> Sig -> decPred Q
}.
Implicit Type B : nfa.
Implicit Type A: dfa.
Fixpoint delta_Q_star B q w: B -> Prop  :=
match w with
| nil => fun q' => q' = q
|x::w' => fun q'' => exists q', delta_Q q x q' /\ delta_Q_star q' w' q''
end.
Arguments delta_Q_star {B} q w q'.
Instance delta_Q_star_dec B (q q': B) w: dec (delta_Q_star q w q').
Definition n_accept B w := exists (q:B), Q_acc q /\ delta_Q_star q0 w q.
Definition toNFA A := @NFA (S A) s F (fun q x => DecPred (fun q' => delta_S q x = q')).
Definition toDFA_F B := fun f: B --> bool => exists q, f q /\ Q_acc q.
Definition toDFA_delta B := fun (f: B --> bool) x => vectorise (fun q => toBool (exists q':B, f q' /\ delta_Q q' x q)).
Definition onestate B q:= (vectorise (fun q':B => toBool (q' = q) )).
Definition toDFA B := DFA (onestate q0) (DecPred (@toDFA_F B)) (@toDFA_delta B).

Lemma toDFA_correct B w: n_accept B w <-> accept (toDFA B) w.