(* Authors Christian Doczkal and Jan-Oliver Kaiser *)
(* Distributed under the terms of CeCILL-B. *)
From mathcomp Require Import all_ssreflect.
From RegLang Require Import misc languages dfa regexp myhill_nerode.
Set Default Proof Using "Type".
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
(* Distributed under the terms of CeCILL-B. *)
From mathcomp Require Import all_ssreflect.
From RegLang Require Import misc languages dfa regexp myhill_nerode.
Set Default Proof Using "Type".
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Two Way Automata
Preliminaries
Inductive ord2_spec n (m : 'I_n.+2) :=
| Ord20 of m == ord0
| Ord2M of m == ord_max
| Ord2C (i : 'I_n) of i.+1 = m.
Arguments Ord20 [n m] _.
Arguments Ord2M [n m] _.
Arguments Ord2C [n m i] _.
Lemma ord2P n (m : 'I_n.+2) : ord2_spec m.
Proof.
case: (unliftP ord0 m) => [j Hj|/eqP]; last exact: Ord20.
case: (unliftP ord_max j) => [i Hi|Hj2]; last apply: Ord2M.
* apply: (@Ord2C _ m i). by rewrite Hj Hi lift0 lift_max.
* rewrite Hj Hj2. apply/eqP. apply/ord_inj. by rewrite lift0.
Qed.
Lemma ord2P0 n : ord2P (@ord0 n.+1) = Ord20 (eqxx _).
Proof. case: (ord2P (@ord0 n.+1)) => //= H. congr Ord20. exact: eq_irrelevance. Qed.
Lemma ord2PM n : ord2P (@ord_max n.+1) = Ord2M (eqxx _).
Proof.
case: (ord2P (@ord_max n.+1)) => //= [H|i Hi].
- apply: f_equal. exact: eq_irrelevance.
- move: (ltn_ord i). move: (Hi) => Hi2. move: Hi => [] ->. by rewrite ltnn.
Qed.
Lemma ord2PC n {i : 'I_n.+2} {i' : 'I_n} (p : i'.+1 = i) : ord2P i = Ord2C p.
Proof.
case: (ord2P i) => [Hi|Hi|j' p'].
- exfalso. move/eqP: Hi => Hi. by rewrite Hi in p.
- exfalso. move:Hi. apply/negP. apply: contra_eqN p => /eqP->.
rewrite eqn_leq negb_and -[~~ (ord_max <= _)]ltnNge [_.+1 < _](_ : _ = true) ?orbT //.
exact: leq_ltn_trans (ltn_ord _) _.
- have ?: i' = j'. apply: ord_inj. apply/eqP. by rewrite -eqSS p p'. subst.
by rewrite (eq_irrelevance p p').
Qed.
Definition of 2NFAs and their languages.
Section NFA2.
Variable char : finType.
Definition dir := bool.
Definition L := true.
Definition R := false.
Record nfa2 := Nfa2 {
nfa2_state :> finType;
nfa2_s : nfa2_state;
nfa2_f : {set nfa2_state};
nfa2_trans : nfa2_state -> char -> {set nfa2_state * dir};
nfa2_transL : nfa2_state -> {set nfa2_state};
nfa2_transR : nfa2_state -> {set nfa2_state}}.
Variables (A : nfa2) (x : word char).
Definition tape := in_tuple x.
Definition pos := ('I_(size x).+2)%type.
Definition nfa2_config := (A * pos)%type.
Definition read (q:A) (n : pos) : {set (A * dir)} :=
match ord2P n with
| Ord20 _ => setX (nfa2_transL q) [set R]
| Ord2M _ => setX (nfa2_transR q) [set L]
| Ord2C m' _ => nfa2_trans q (tnth tape m')
end.
Definition step (c d : nfa2_config) :=
let: ((p,i),(q,j)) := (c,d) in
((q,R) \in read p i) && (j == i.+1 :> nat)
|| ((q,L) \in read p i) && (j.+1 == i :> nat).
Definition nfa2_lang := [exists (q | q \in nfa2_f A), connect step (nfa2_s A,ord1) (q,ord_max)].
End NFA2.
Arguments step [char] A x c d.
Prenex Implicits step.
Section DFA2.
Variable char : finType.
Record deterministic (M : nfa2 char) : Prop :=
{ detC : forall (p:M) a, #|nfa2_trans p a| <= 1;
detL : forall (p:M), #|nfa2_transL p| <= 1;
detR : forall (p:M), #|nfa2_transR p| <= 1}.
Record dfa2 := DFA2 { nfa2_of :> nfa2 char ; dfa2_det : deterministic nfa2_of }.
Definition dfa2_lang := nfa2_lang.
Variable M : dfa2.
Lemma cards_lt1 (T : finType) (A : {set T}) : #|A| <= 1 -> A = set0 \/ exists x, A = [set x].
Proof.
move => H. case (posnP #|A|) => H'.
- left. exact:cards0_eq.
- right. apply/cards1P. by rewrite eqn_leq H H'.
Qed.
Lemma read1 x (p:M) (j:pos x) : read p j = set0 \/ exists s : M * dir, read p j = [set s].
Proof.
rewrite /read.
case: (ord2P _) => [||i] _;apply cards_lt1; rewrite ?cardsX ?cards1 ?muln1;
by auto using detL, detC, detR, dfa2_det.
Qed.
Lemma step_fun x : functional (step M x).
Proof.
have lr: ((R == L = false)*(L == R = false))%type by done.
move => [p i] [q j] [r k]. rewrite /step.
case: (read1 p i) => [ -> |[[q' [|]] -> ]]; first by rewrite !inE.
- rewrite !inE !xpair_eqE -/L -/R !lr !eqxx !andbT !andbF /=.
move => /andP [/eqP -> /eqP A] /andP [/eqP -> /eqP B].
f_equal. apply ord_inj. apply/eqP. by rewrite -eqSS A B.
- rewrite !inE !xpair_eqE -/L -/R !lr !eqxx !andbT !andbF !orbF /=.
move => /andP [/eqP -> /eqP A] /andP [/eqP -> /eqP B].
f_equal. apply ord_inj. apply/eqP. by rewrite -eqSS A B.
Qed.
End DFA2.
Section DFAtoDFA2.
Variables (char : finType) (A : dfa char).
Definition nfa2_of_dfa : nfa2 char :=
{| nfa2_s := dfa_s A;
nfa2_f := [set q in dfa_fin A];
nfa2_trans q a := [set (dfa_trans q a,R)];
nfa2_transL q := [set dfa_s A]; (* Never reached *)
nfa2_transR q := set0
|}.
Lemma drop_accept (w : word char) (i : 'I_(size w)) (q : A) :
drop i w \in dfa_accept q = (drop i.+1 w \in dfa_accept (dfa_trans q (tnth (tape w) i))).
Proof.
case: w i q => [[//]|a w [m Hm] q]. rewrite [drop]lock (tnth_nth a) /= -lock.
elim: {w} (a :: w) m Hm q => [|b w IHw [|m] Hm q]; first by case.
by rewrite drop0 drop1. exact: IHw.
Qed.
Variable (w : word char).
Let n := size w.
Lemma nfa2_of_aux (q:A) i : i < (size w).+1 ->
((drop i w) \in dfa_accept q) ->
[exists f in nfa2_f nfa2_of_dfa, connect (step nfa2_of_dfa w) (q,inord i.+1) (f,ord_max)].
Proof.
move eq_m : (n - i) => m. elim: m q i eq_m => [|m IHm] q i /eqP H1 H2.
- have/eqP -> : i == (size w). by rewrite eqn_leq -ltnS H2 -subn_eq0 H1.
rewrite drop_size unfold_in -inord_max /= => F. apply/existsP;exists q. rewrite inE F. exact: connect0.
- move => H. have Hi : i < size w.
{ rewrite ltn_neqAle -ltnS H2 andbT. apply: contraTN H1 => /eqP->. by rewrite subnn. }
have Hm : n - i.+1 = m by apply/eqP;rewrite subnS (eqP H1).
move/(_ (dfa_trans q (tnth (tape w) (Ordinal Hi))) _ Hm Hi) : IHm.
rewrite -[i.+1]/(Ordinal Hi).+1 -drop_accept. move => /(_ H).
case/exists_inP => f f1 f2. apply/exists_inP;exists f => //. apply: connect_trans (connect1 _) f2.
rewrite /step /read (ord2PC (i' := (Ordinal Hi))) ?inordK //= => _.
by rewrite inE ?eqxx.
Qed.
Lemma nfa2_of_aux2 (q f:A) (i : pos w) : i != ord0 ->
f \in nfa2_f nfa2_of_dfa -> connect (step nfa2_of_dfa w) (q,i) (f,ord_max) ->
((drop i.-1 w) \in dfa_accept q).
Proof.
move => H fin_f. case/connectP => p. elim: p i H q => //= [|[q' j] p IHp i Hi q].
- move => i Hi q _ [<- <-]. rewrite drop_size -topredE /= accept_nil. by rewrite inE in fin_f.
- rewrite {1}/step /read. case: (ord2P i) => /= [|/eqP->|i' Hi']; try by rewrite ?inE ?(negbTE Hi).
rewrite !inE !xpair_eqE (_ : L == R = false) ?eqxx ?andbT ?andbF ?orbF -?andbA //=.
case/and3P => /eqP -> /eqP E. rewrite -Hi' drop_accept.
have -> : i'.+1 = j.-1 by rewrite E. apply IHp.
apply/negP. move/eqP/(f_equal (@nat_of_ord _)). by rewrite E.
Qed.
Lemma nfa2_of_correct : (w \in dfa_lang A) = (w \in nfa2_lang nfa2_of_dfa).
Proof.
apply/idP/idP; rewrite -![_ \in _ A]topredE /=.
- rewrite -{1}[w]drop0 /nfa2_lang -topredE /= inord1 => H. exact: nfa2_of_aux.
- rewrite -{2}[w]drop0 -[0]/((@ord1 n).-1). case/exists_inP => p. exact: nfa2_of_aux2.
Qed.
Lemma nfa2_of_dfa_det : deterministic (nfa2_of_dfa).
Proof. split => [p a|p|p]; by rewrite ?cards1 ?cards0. Qed.
Definition dfa2_of_dfa := DFA2 nfa2_of_dfa_det.
Lemma dfa2_of_correct : (w \in dfa_lang A) = (w \in dfa2_lang dfa2_of_dfa).
Proof. exact: nfa2_of_correct. Qed.
End DFAtoDFA2.