(* Higher-order syntax for Quantified Propositional Logic Venanzio Capretta and Amy Felty March 2006 see the article "Combining de Bruijn Indices and Higher-Order Abstract Syntax in Coq" *) Require Import Arith. Require Import Bool. Require Import Max. Require Import deBruijn. Inductive QPLcon:Set := cNOT: QPLcon | cIMP: QPLcon | cAND: QPLcon | cOR: QPLcon | cALL: QPLcon | cEX: QPLcon. Definition oo: Set := expr QPLcon. Fixpoint formula_check (e:oo): bool := match e with (VAR v) => true | (BND b) => true | (APP (CON cNOT) e) => formula_check e | (APP (CON cALL) (ABS e)) => formula_check e | (APP (CON cEX) (ABS e)) => formula_check e | (APP (APP (CON cIMP) e1) e2) => andb (formula_check e1) (formula_check e2) | (APP (APP (CON cAND) e1) e2) => andb (formula_check e1) (formula_check e2) | (APP (APP (CON cOR) e1) e2) => andb (formula_check e1) (formula_check e2) | _ => false end. Definition fcheck: oo -> Prop := fun e => Is_true (formula_check e). Lemma fcheck_ind: forall (P: oo -> Prop), (forall v, P (VAR _ v)) -> (forall i, P (BND _ i)) -> (forall e, P e -> P (APP (CON cNOT) e)) -> (forall e, P e -> P (APP (CON cALL) (ABS e))) -> (forall e, P e -> P (APP (CON cEX) (ABS e))) -> (forall e1 e2, P e1 -> P e2 -> P (APP (APP (CON cIMP) e1) e2)) -> (forall e1 e2, P e1 -> P e2 -> P (APP (APP (CON cAND) e1) e2)) -> (forall e1 e2, P e1 -> P e2 -> P (APP (APP (CON cOR) e1) e2)) -> forall e, fcheck e -> P e. Proof. intros P Hvar Hbnd Hnot Hall Hex Himp Hand Hor e'. apply expr_star_ind with (con := QPLcon) (P := fun e => fcheck e -> P e). intro e; case e. intros q IH h; elim h. intros v IH h; apply Hvar. intros i IH h; apply Hbnd. intros a; case a. intros q; case q. intros b IH h; apply Hnot. inversion_clear IH. apply H1; exact h. intros b IH h; elim h. intros b IH h; elim h. intros b IH h; elim h. (* ALL *) intro b; case b. intros q1 IH h; elim h. intros v IH h; elim h. intros i IH h; elim h. intros b1 b2 IH h; elim h. intros b1 IH h; apply Hall. inversion_clear IH. inversion_clear H2. apply H3; exact h. (* EX *) intro b; case b. intros q1 IH h; elim h. intros v IH h; elim h. intros i IH h; elim h. intros b1 b2 IH h; elim h. intros b1 IH h; apply Hex. inversion_clear IH. inversion_clear H2. apply H3; exact h. intros v b IH h; elim h. intros i b IH h; elim h. intro a1; case a1. intro q; case q. intros a2 b IH h; elim h. (* IMP *) intros a2 b IH h; inversion_clear IH. unfold fcheck in h; simpl in h. apply Himp. inversion_clear H0. apply H5. unfold fcheck; eapply Is_true_conj_left; apply h. apply H1. unfold fcheck; eapply Is_true_conj_right; apply h. (* AND *) intros a2 b IH h; inversion_clear IH. unfold fcheck in h; simpl in h. apply Hand. inversion_clear H0. apply H5. unfold fcheck; eapply Is_true_conj_left; apply h. apply H1. unfold fcheck; eapply Is_true_conj_right; apply h. (* OR *) intros a2 b IH h; inversion_clear IH. unfold fcheck in h; simpl in h. apply Hor. inversion_clear H0. apply H5. unfold fcheck; eapply Is_true_conj_left; apply h. apply H1. unfold fcheck; eapply Is_true_conj_right; apply h. intros a2 b IH h; elim h. intros a2 b IH h; elim h. intros v a2 b IH h; elim h. intros i a2 b IH h; elim h. intros a2 a3 a4 b IH h; elim h. intros a2 a3 b IH h; elim h. intros a2 b IH h; elim h. intros a IH h; elim h. Qed. Record formula: Set := mk_formula { f_expr: oo; f_check: fcheck f_expr }. Lemma bshift_aux_check: forall e, fcheck e -> forall i, fcheck (bshift_aux e i). Proof. intros e' h'; apply fcheck_ind with (P := fun e => forall i, fcheck (bshift_aux e i)). (* Var *) intros v i; red; simpl. exact I. (* Bind *) intros j i; red; simpl. case (le_lt_dec i j); intro; exact I. (* Not *) intros e IH i; red; simpl; apply IH. (* All *) intros e IH i; red; simpl; apply IH. (* Ex *) intros e IH i; red; simpl; apply IH. (* Imp *) intros e1 e2 IH1 IH2 i; red; simpl. apply Is_true_conj; [apply IH1 | apply IH2]. (* And *) intros e1 e2 IH1 IH2 i; red; simpl. apply Is_true_conj; [apply IH1 | apply IH2]. (* Or *) intros e1 e2 IH1 IH2 i; red; simpl. apply Is_true_conj; [apply IH1 | apply IH2]. exact h'. Qed. Lemma bshift_check: forall e, fcheck e -> fcheck (bshift e). Proof. intros e h; unfold bshift; apply bshift_aux_check; exact h. Qed. Lemma bsubst_check: forall e1 j e2, fcheck e1 -> fcheck e2 -> fcheck (bsubst e1 j e2). Proof. intros e1 j e2 h1; generalize e2; generalize j; clear e2; clear j. apply fcheck_ind with (P := fun e1 => forall j e2, fcheck e2 -> fcheck (bsubst e1 j e2)). (* VAR *) intros v j e2 h2; exact I. (* BND *) intros i j e2 h2; simpl. case (bnd_dec i j). intros _; exact h2. intros _; exact I. (* NOT *) intros e IH j e2 h2; simpl. unfold fcheck; simpl. change (fcheck (bsubst e j e2)). apply IH. exact h2. (* ALL *) intros e IH j e2 h2. change (fcheck (bsubst e (S j) (bshift e2))). apply IH. apply bshift_check; exact h2. (* EX *) intros e IH j e2 h2. change (fcheck (bsubst e (S j) (bshift e2))). apply IH. apply bshift_check; exact h2. (* IMP *) intros a b IHa IHb j e2 h2. unfold fcheck; simpl. apply Is_true_conj. apply IHa; exact h2. apply IHb; exact h2. (* AND *) intros a b IHa IHb j e2 h2. unfold fcheck; simpl. apply Is_true_conj. apply IHa; exact h2. apply IHb; exact h2. (* OR *) intros a b IHa IHb j e2 h2. unfold fcheck; simpl. apply Is_true_conj. apply IHa; exact h2. apply IHb; exact h2. exact h1. Qed. Lemma ebind_check: forall e, fcheck e -> forall w j, fcheck (ebind w j e). Proof. intros e' h'. apply fcheck_ind with (P := fun e => forall w j, fcheck (ebind w j e)). (* VAR *) intros v w j; simpl. case (var_dec v w). intros _; exact I. intros _; exact I. (* BND *) intros i w j; simpl. case (bnd_dec i j). intros _; exact I. intros _; exact I. (* NOT *) unfold fcheck; intros e IH w j; simpl. apply IH. (* ALL *) unfold fcheck; intros e IH w j; simpl. apply IH. (* EX *) unfold fcheck; intros e IH w j; simpl. apply IH. (* IMP *) unfold fcheck; intros e1 e2 IH1 IH2 w j; simpl. apply Is_true_conj; [apply IH1 | apply IH2]. (* AND *) unfold fcheck; intros e1 e2 IH1 IH2 w j; simpl. apply Is_true_conj; [apply IH1 | apply IH2]. (* OR *) unfold fcheck; intros e1 e2 IH1 IH2 w j; simpl. apply Is_true_conj; [apply IH1 | apply IH2]. exact h'. Qed. Lemma formula_check_unicity: forall (e:oo)(c1 c2: fcheck e), c1=c2. Proof. intro e; unfold fcheck. case (formula_check e); simpl. intros [] []; trivial. intro c1; case c1. Qed. Lemma mk_formula_unicity: forall (e1 e2:oo), e1=e2 -> forall (c1:fcheck e1)(c2:fcheck e2), mk_formula e1 c1 = mk_formula e2 c2. Proof. intros e1 e2 Heq; rewrite Heq. intros c1 c2; rewrite (formula_check_unicity e2 c1 c2). trivial. Qed. Lemma formula_unicity: forall a1 a2, (f_expr a1)=(f_expr a2) -> a1=a2. Proof. intros [e1 c1] [e2 c2] Heq; simpl in Heq. apply mk_formula_unicity. exact Heq. Qed. Definition Var (v:var): formula := mk_formula (VAR _ v) I. Definition Bind (i:bnd): formula := mk_formula (BND _ i) I. Definition Not (a:formula): formula := mk_formula (APP (CON cNOT) (f_expr a)) (f_check a). Definition fImp (a1 a2:formula): formula := mk_formula (APP (APP (CON cIMP) (f_expr a1)) (f_expr a2)) (Is_true_conj (f_check a1) (f_check a2)). Notation "A 'Imp' B" := (fImp A B) (at level 23, right associativity). Definition fAnd (a1 a2:formula): formula := mk_formula (APP (APP (CON cAND) (f_expr a1)) (f_expr a2)) (Is_true_conj (f_check a1) (f_check a2)). Notation "A 'And' B" := (fAnd A B) (at level 21, left associativity). Definition fOr (a1 a2:formula): formula := mk_formula (APP (APP (CON cOR) (f_expr a1)) (f_expr a2)) (Is_true_conj (f_check a1) (f_check a2)). Notation "A 'Or' B" := (fOr A B) (at level 22, left associativity). Section Quantifiers. Variable f: formula -> formula. Definition nvar := newvar (f_expr (f (Bind 0))). Definition fvar := f (Var nvar). Definition fbind := ebind nvar 0 (f_expr fvar). Lemma fbind_check: fcheck fbind. Proof. unfold fbind. apply ebind_check. exact (f_check (f (Var nvar))). Qed. (* The "function body": f applied to (Bind 0). *) Definition fbody: formula := mk_formula fbind fbind_check. Definition fAll: formula := mk_formula (APP (CON cALL) (ABS fbind)) fbind_check. Definition fEx: formula := mk_formula (APP (CON cEX) (ABS fbind)) fbind_check. End Quantifiers. Notation "'forAll' V , A" := (fAll (fun V => A)) (at level 20). Notation "'thereEx' V , A" := (fEx (fun V => A)) (at level 20). Lemma fbind_extensional: forall f1 f2: formula->formula, (forall x, f1 x = f2 x) -> fbind f1 = fbind f2. Proof. intros f1 f2 H. unfold fbind; simpl. unfold fvar; simpl. unfold nvar; simpl. rewrite H. rewrite H. trivial. Qed. (* The inverse operation: given a quantified expression, build the function. *) Section Quantified_Expression. Variables (e:oo)(h:fcheck e). Variable x:formula. Let xe := f_expr x. Let xh := f_check x. Let esx := bsubst e 0 xe. Lemma esx_check: fcheck esx. Proof. unfold esx. apply bsubst_check; [exact h | exact xh]. Qed. Definition ffun := mk_formula esx esx_check. End Quantified_Expression. Definition fapp (a:formula): formula -> formula := ffun (f_expr a) (f_check a). Lemma fbody_fapp: forall a, fbody (fapp a) = a. Proof. intros [e h]; apply formula_unicity; unfold fbody; unfold fapp; simpl. unfold ffun; unfold fbind; simpl. unfold nvar; simpl. rewrite bsubst_bnd. rewrite ebind_bsubst_eq. trivial. Qed. (* "Canonical form" of a function on formulas. *) Definition funf: (formula -> formula) -> (formula -> formula) := fun f => fapp (fbody f). Section Formula_Recursion_Principle. (* Theorem 4 of article. *) (* Recursion principle on formulas. *) (* Non-dependent recursion. *) Variables (B: Set) (Hvar: var -> B) (Hbind: bnd -> B) (Hnot: formula -> B -> B) (Himp: formula -> formula -> B -> B -> B) (Hand: formula -> formula -> B -> B -> B) (Hor: formula -> formula -> B -> B -> B) (Hall: (formula -> formula) -> B -> B) (Hex: (formula -> formula) -> B -> B). (* In the case of Hall and Hex we have a single recursive argument of type B, instead of a function of type formula->B. This is because we are not really doing higher order recursion. The single argument is the result of the recursive call on the "body" of the function. *) Fixpoint oo_form_rec (e:oo): fcheck e -> B := match e return fcheck e -> B with | (VAR v) => fun h => Hvar v | (BND i) => fun h => Hbind i | (APP (CON cNOT) e1) => fun h => Hnot (mk_formula e1 h) (oo_form_rec e1 h) | (APP (APP (CON cIMP) e1) e2) => fun h => Himp (mk_formula e1 (Is_true_conj_left h)) (mk_formula e2 (Is_true_conj_right h)) (oo_form_rec e1 (Is_true_conj_left h)) (oo_form_rec e2 (Is_true_conj_right h)) | (APP (APP (CON cAND) e1) e2) => fun h => Hand (mk_formula e1 (Is_true_conj_left h)) (mk_formula e2 (Is_true_conj_right h)) (oo_form_rec e1 (Is_true_conj_left h)) (oo_form_rec e2 (Is_true_conj_right h)) | (APP (APP (CON cOR) e1) e2) => fun h => Hor (mk_formula e1 (Is_true_conj_left h)) (mk_formula e2 (Is_true_conj_right h)) (oo_form_rec e1 (Is_true_conj_left h)) (oo_form_rec e2 (Is_true_conj_right h)) | (APP (CON cALL) (ABS e')) => fun h => Hall (ffun e' h) (oo_form_rec e' h) | (APP (CON cEX) (ABS e')) => fun h => Hex (ffun e' h) (oo_form_rec e' h) | _ => fun h => Hvar 0 (* this never happens *) end. (* Recursive function. *) Definition form_rec (a:formula): B:= oo_form_rec (f_expr a) (f_check a). (* Proof of the recursive equations. *) Lemma form_rec_Var: forall v, form_rec (Var v) = Hvar v. Proof. auto. Qed. Lemma form_rec_Bind: forall b, form_rec (Bind b) = Hbind b. Proof. auto. Qed. Lemma form_rec_Not: forall a, form_rec (Not a) = Hnot a (form_rec a). Proof. intros [e h]; auto. Qed. Lemma form_rec_Imp: forall a1 a2, form_rec (a1 Imp a2) = Himp a1 a2 (form_rec a1) (form_rec a2). Proof. intros [e1 h1] [e2 h2]. set (h:= (f_check (mk_formula e1 h1 Imp mk_formula e2 h2))). change (Himp (mk_formula e1 (Is_true_conj_left h)) (mk_formula e2 (Is_true_conj_right h)) (form_rec (mk_formula e1 (Is_true_conj_left h))) (form_rec (mk_formula e2 (Is_true_conj_right h))) = Himp (mk_formula e1 h1) (mk_formula e2 h2) (form_rec (mk_formula e1 h1)) (form_rec (mk_formula e2 h2))). rewrite formula_unicity with (a1 := mk_formula e1 (Is_true_conj_left h)) (a2 := mk_formula e1 h1). rewrite formula_unicity with (a1 := mk_formula e2 (Is_true_conj_right h)) (a2 := mk_formula e2 h2). auto. auto. auto. Qed. Lemma form_rec_And: forall a1 a2, form_rec (a1 And a2) = Hand a1 a2 (form_rec a1) (form_rec a2). Proof. intros [e1 h1] [e2 h2]. set (h:= (f_check (mk_formula e1 h1 Imp mk_formula e2 h2))). change (Hand (mk_formula e1 (Is_true_conj_left h)) (mk_formula e2 (Is_true_conj_right h)) (form_rec (mk_formula e1 (Is_true_conj_left h))) (form_rec (mk_formula e2 (Is_true_conj_right h))) = Hand (mk_formula e1 h1) (mk_formula e2 h2) (form_rec (mk_formula e1 h1)) (form_rec (mk_formula e2 h2))). rewrite formula_unicity with (a1 := mk_formula e1 (Is_true_conj_left h)) (a2 := mk_formula e1 h1). rewrite formula_unicity with (a1 := mk_formula e2 (Is_true_conj_right h)) (a2 := mk_formula e2 h2). auto. auto. auto. Qed. Lemma form_rec_Or: forall a1 a2, form_rec (a1 Or a2) = Hor a1 a2 (form_rec a1) (form_rec a2). Proof. intros [e1 h1] [e2 h2]. set (h:= (f_check (mk_formula e1 h1 Imp mk_formula e2 h2))). change (Hor (mk_formula e1 (Is_true_conj_left h)) (mk_formula e2 (Is_true_conj_right h)) (form_rec (mk_formula e1 (Is_true_conj_left h))) (form_rec (mk_formula e2 (Is_true_conj_right h))) = Hor (mk_formula e1 h1) (mk_formula e2 h2) (form_rec (mk_formula e1 h1)) (form_rec (mk_formula e2 h2))). rewrite formula_unicity with (a1 := mk_formula e1 (Is_true_conj_left h)) (a2 := mk_formula e1 h1). rewrite formula_unicity with (a1 := mk_formula e2 (Is_true_conj_right h)) (a2 := mk_formula e2 h2). auto. auto. auto. Qed. Lemma form_rec_All: forall f: formula -> formula, form_rec (forAll x, f x) = Hall (funf (fun x => f x)) (form_rec (fbody (fun x => f x))). Proof. auto. Qed. Lemma form_rec_Ex: forall f: formula -> formula, form_rec (thereEx x, f x) = Hex (funf (fun x => f x)) (form_rec (fbody (fun x => f x))). Proof. auto. Qed. End Formula_Recursion_Principle. Section Formula_Induction_Principle. (* Theorem 5 of article. *) (* Induction principle on formulas. *) Variables (P: formula -> Prop) (Hvar: forall (v:var), P (Var v)) (Hbind: forall (i:bnd), P (Bind i)) (Hnot: forall (a:formula), P a -> P (Not a)) (Himp: forall (a1 a2:formula), P a1 -> P a2 -> P (a1 Imp a2)) (Hand: forall (a1 a2:formula), P a1 -> P a2 -> P (a1 And a2)) (Hor: forall (a1 a2:formula), P a1 -> P a2 -> P (a1 Or a2)) (Hall: forall (f:formula -> formula), P (fbody (fun x => f x)) -> P (forAll x, f x)) (Hex: forall (f:formula -> formula), P (fbody (fun x => f x)) -> P (thereEx x, f x)). Theorem form_ind: forall a, P a. Proof. intros [e' h']; generalize h'; apply fcheck_ind with (P := fun e => forall h:fcheck e, P (mk_formula e h)). (* Var *) intros v h; rewrite formula_unicity with (a2 := Var v); [apply Hvar | trivial]. (* Bind *) intros i h; rewrite formula_unicity with (a2 := Bind i); [apply Hbind | trivial]. (* Not *) intros e IH h; rewrite formula_unicity with (a2 := Not (mk_formula e h)). apply Hnot; apply IH. trivial. (* All *) intros e IH h; rewrite formula_unicity with (a2 := forAll x, ffun e h x). apply Hall. rewrite formula_unicity with (a2 := mk_formula e h). apply IH. simpl. unfold fbind; unfold nvar; unfold ffun; simpl. unfold nvar; unfold ffun; simpl. rewrite bsubst_bnd. exact (ebind_bsubst_eq e 0). simpl. unfold fbind; unfold nvar; unfold ffun; simpl. unfold nvar; unfold ffun; simpl. rewrite bsubst_bnd. rewrite ebind_bsubst_eq. trivial. (* Ex *) intros e IH h; rewrite formula_unicity with (a2 := thereEx x, ffun e h x). apply Hex. rewrite formula_unicity with (a2 := mk_formula e h). apply IH. simpl. unfold fbind; unfold nvar; unfold ffun; simpl. unfold nvar; unfold ffun; simpl. rewrite bsubst_bnd. exact (ebind_bsubst_eq e 0). simpl. unfold fbind; unfold nvar; unfold ffun; simpl. unfold nvar; unfold ffun; simpl. rewrite bsubst_bnd. rewrite ebind_bsubst_eq. trivial. (* Imp *) intros e1 e2 IH1 IH2 h. unfold fcheck in h; simpl in h. rewrite formula_unicity with (a2 := (mk_formula e1 (Is_true_conj_left h)) Imp (mk_formula e2 (Is_true_conj_right h)) ). apply Himp. apply IH1. apply IH2. trivial. (* And *) intros e1 e2 IH1 IH2 h. unfold fcheck in h; simpl in h. rewrite formula_unicity with (a2 := (mk_formula e1 (Is_true_conj_left h)) And (mk_formula e2 (Is_true_conj_right h)) ). apply Hand. apply IH1. apply IH2. trivial. (* Or *) intros e1 e2 IH1 IH2 h. unfold fcheck in h; simpl in h. rewrite formula_unicity with (a2 := (mk_formula e1 (Is_true_conj_left h)) Or (mk_formula e2 (Is_true_conj_right h)) ). apply Hor. apply IH1. apply IH2. trivial. exact h'. Qed. End Formula_Induction_Principle. (* Definition of negation normal form *) Inductive isNnf: formula -> Prop := isNnf_Var: forall i, isNnf (Var i) | isNnf_Bind: forall i, isNnf (Bind i) | isNnf_nVar: forall i, isNnf (Not (Var i)) | isNnf_nBind: forall i, isNnf (Not (Bind i)) | isNnf_And: forall a1 a2, isNnf a1 -> isNnf a2 -> isNnf (a1 And a2) | isNnf_Or: forall a1 a2, isNnf a1 -> isNnf a2 -> isNnf (a1 Or a2) | isNnf_All: forall f:formula -> formula, isNnf (fbody f) -> isNnf (forAll x, f x) | isNnf_Ex: forall f: formula -> formula, isNnf (fbody f) -> isNnf (thereEx x, f x). (* nnf_aux a = (nnf a, nnf (fNot a). *) Definition nnf_aux: formula -> formula*formula := form_rec (formula*formula)%type (* matching the argument with: *) (* (Var i) *) (fun i => (Var i, Not (Var i))) (* (Bind i) *) (fun i => (Bind i, Not (Bind i))) (* (Not a) *) (fun a na => (snd na, fst na)) (* (a Imp b) *) (fun a b na nb => ((snd na) Or (fst nb), (fst na) And (snd nb))) (* (a And b) *) (fun a b na nb => ((fst na) And (fst nb), (snd na) Or (snd nb))) (* (a Or b) *) (fun a b na nb => ((fst na) Or (fst nb), (snd na) And (snd nb))) (* (forAll x, f x) *) (fun f nb => (forAll x, fapp (fst nb) x, thereEx x, fapp (snd nb) x)) (* (thereEx x, f x) *) (fun f nb => (thereEx x, fapp (fst nb) x, forAll x, fapp (snd nb) x)). Definition nnf: formula -> formula := fun a => fst (nnf_aux a). Lemma nnf_aux_correct: forall a:formula, isNnf (fst (nnf_aux a)) /\ isNnf (snd (nnf_aux a)). Proof. apply form_ind with (P := fun a => isNnf (fst (nnf_aux a)) /\ isNnf (snd (nnf_aux a))). (* Var *) intro v; simpl; split. apply isNnf_Var. apply isNnf_nVar. (* Bind *) intro i; simpl; split. apply isNnf_Bind. apply isNnf_nBind. (* Not *) intros a [IH1 IH2]. unfold nnf_aux; rewrite form_rec_Not; fold nnf_aux; simpl; split. exact IH2. exact IH1. (* Imp *) intros a1 a2 [IH11 IH12] [IH21 IH22]. unfold nnf_aux; rewrite form_rec_Imp; fold nnf_aux; simpl; split. apply isNnf_Or. exact IH12. exact IH21. apply isNnf_And. exact IH11. exact IH22. (* And *) intros a1 a2 [IH11 IH12] [IH21 IH22]. unfold nnf_aux; rewrite form_rec_And; fold nnf_aux; simpl; split. apply isNnf_And. exact IH11. exact IH21. apply isNnf_Or. exact IH12. exact IH22. (* Or *) intros a1 a2 [IH11 IH12] [IH21 IH22]. unfold nnf_aux; rewrite form_rec_Or; fold nnf_aux; simpl; split. apply isNnf_Or. exact IH11. exact IH21. apply isNnf_And. exact IH12. exact IH22. (* All *) intros f [IH1 IH2]. unfold nnf_aux; rewrite form_rec_All; fold nnf_aux; simpl; split. apply isNnf_All; simpl. rewrite fbody_fapp. exact IH1. apply isNnf_Ex; simpl. rewrite fbody_fapp. exact IH2. (* Ex *) intros f [IH1 IH2]. unfold nnf_aux; rewrite form_rec_Ex; fold nnf_aux; simpl; split. apply isNnf_Ex; simpl. rewrite fbody_fapp. exact IH1. apply isNnf_All; simpl. rewrite fbody_fapp. exact IH2. Qed. Theorem nnf_correct: forall a, isNnf (nnf a). Proof. intro a; case (nnf_aux_correct a). intros H1 H2; exact H1. Qed.