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).
(** * Concatenation of two regular languages *)
Definition concat_acc_pred B B' := fun (q: B + B') => match q with
| inl q => if decision (Q_acc (@q0 B')) then Q_acc q else False
| inr q => Q_acc q
end.
Instance acc_dec B B' q: dec (@concat_acc_pred B B' q).
Definition concat_acc_decPred B B':= DecPred (@concat_acc_pred B B').
Definition concat_delta B B' (q q': B + B') x:= match q with
| inl q => match q' with
| inl q' => delta_Q q x q'
| inr q' => if decision (@Q_acc B q) then delta_Q q0 x q' else False
end
| inr q => match q' with
| inl q' => False
| inr q' => delta_Q q x q'
end
end.
Instance conact_delta_dec B B' (q: B + B') x q' : dec (concat_delta q q' x).
Definition concat_delta_Q B B':= fun (q: B + B') x => DecPred (fun q' => concat_delta q q' x).
Definition concat B B' := NFA (inl (@q0 B)) (@concat_acc_decPred B B') (@concat_delta_Q B B').
(** * Kleene Operator *)
Definition kleene_acc_pred B := fun q => match q with
| None => True
| Some q => @Q_acc B q end.
Instance kleene_acc_dec B (q: option B): dec (kleene_acc_pred q).
Definition kleene_acc_decPred B:= DecPred (@kleene_acc_pred B).
Definition kleene_delta B (q: option B) x q':= match q' with
| Some q' => match q with
| None => delta_Q q0 x q'
| Some q => delta_Q q x q' \/ (Q_acc q /\ delta_Q q0 x q')
end
| _ => False end.
Instance kleene_delta_dec B (q q': option B) x : dec (kleene_delta q x q').                                                        Proof.
destruct q, q'; auto.                                                                                                                                     Qed.
Definition kleene_star B := NFA (None) (@kleene_acc_decPred B) (fun (q: option B) x => DecPred (fun q' => kleene_delta q x q')).

Lemma nil_kleene B : n_accept (kleene_star B) nil.