Library CoLoR.Term.SimpleType.TermsManip

CoLoR, a Coq library on rewriting and termination. See the COPYRIGHTS and LICENSE files.
  • Adam Koprowski, 2006-04-27
In this file some basic operations on terms of simply typed lambda-calculus are defined.

Set Implicit Arguments.

Require Import RelExtras.
Require Import ListExtras.
Require Import TermsTyping.

Module TermsManip (Sig : TermsSig.Signature).

  Module TS := TermsTyping.TermsTyping Sig.
  Export TS.

  Implicit Types M N : Term.

  Definition isVar M : Prop :=
  let (_, _, _, typ) := M in
  match typ with
  | TVar _ _ _ _True
  | _False
  end.

  Lemma var_is_var : M x, term M = %xisVar M.

  Proof.
    intro M; term_inv M.
  Qed.

  Definition isFunS M : Prop :=
  let (_, _, _, typ) := M in
  match typ with
  | TFun _ _True
  | _False
  end.

  Lemma funS_is_funS : M f, term M = ^fisFunS M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma funS_fun : M, isFunS M f, term M = ^f.

  Proof.
    intro M; term_inv M.
    intro; f; trivial.
  Qed.

  Definition isAbs M : Prop :=
  let (_, _, _, typ) := M in
  match typ with
  | TAbs _ _ _ _ _True
  | _False
  end.

  Hint Unfold isVar isFunS isAbs : terms.

  Ltac assert_abs hypName M :=
     assert (hypName : isAbs M);
     [try solve [eauto with terms | term_inv M]
     | idtac
     ].

  Definition absBody M : isAbs MTerm :=
  match M return (isAbs MTerm) with
  | buildT _ _ _ typ
      match typ return (isAbs (buildT typ) → Term) with
      | TAbs _ _ _ _ absTfun _ : TruebuildT absT
      | _fun notAbs: FalseFalse_rect Term notAbs
      end
  end.
  Implicit Arguments absBody [M].

  Definition absType M : isAbs MSimpleType :=
  match M return (isAbs MSimpleType) with
  | buildT _ _ _ typ
      match typ return (isAbs (buildT typ) → SimpleType) with
      | TAbs _ A B _ _fun _ : TrueA
      | _fun notAbs : FalseFalse_rect SimpleType notAbs
      end
  end.
  Implicit Arguments absType [M].

  Lemma abs_isAbs : M A Pt, term M = \A PtisAbs M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma absBody_eq_env : M N (MAbs: isAbs M) (NAbs: isAbs N),
    env M = env Ntype M = type Nenv (absBody MAbs) = env (absBody NAbs).

  Proof.
    intros M N; term_inv M; term_inv N.
  Qed.

  Lemma absBody_equiv_type : M N (MAbs: isAbs M) (NAbs: isAbs N),
    type M == type Ntype (absBody MAbs) == type (absBody NAbs).

  Proof.
    intros M N; term_inv M; term_inv N.
  Qed.

  Lemma absBody_eq_type : M N (MAbs: isAbs M) (NAbs: isAbs N),
    type M = type Ntype (absBody MAbs) = type (absBody NAbs).

  Proof.
    intros M N; term_inv M; term_inv N.
  Qed.

  Lemma absBody_term : M (Mabs: isAbs M) A Pt, term M = \A Ptterm (absBody Mabs) = Pt.

  Proof.
    intro M; term_inv M; congruence.
  Qed.

  Lemma absBody_env : M (Mabs: isAbs M), env (absBody Mabs) = decl (absType Mabs) (env M).

  Proof.
    intro M; term_inv M; congruence.
  Qed.

  Lemma abs_type : M (Mabs: isAbs M) A B, type M = A --> BabsType Mabs = A.

  Proof.
    intro M; term_inv M; congruence.
  Qed.

  Hint Rewrite absBody_term absBody_env abs_type using solve [auto with terms] : terms.

  Lemma type_eq_absType_eq : M N (Mabs: isAbs M) (Nabs: isAbs N), type M = type N
    absType Mabs = absType Nabs.

  Proof.
    intros M N; term_inv M; term_inv N.
  Qed.

  Lemma absBody_type : M (Mabs: isAbs M) A B, type M = A --> Btype (absBody Mabs) = B.

  Proof.
    intros M Mabs A B MAB.
    term_inv M; congruence.
  Qed.

  Lemma abs_type_eq : M N (Mabs: isAbs M) (Nabs: isAbs N), absType Mabs = absType Nabs
    type (absBody Mabs) = type (absBody Nabs) → type M = type N.

  Proof.
    intros M N Mabs Nabs absTypeEq absBodyEq.
    term_inv M; term_inv N; congruence.
  Qed.

  Lemma abs_proof_irr : M (Mabs Mabs': isAbs M), Mabs = Mabs'.

  Proof.
    intro M; term_inv M.
    dependent inversion Mabs; dependent inversion Mabs'; trivial.
  Qed.

  Lemma absBody_eq : M N (Mabs: isAbs M) (Nabs: isAbs N), M = NabsBody Mabs = absBody Nabs.

  Proof.
    intros; term_inv M; term_inv N.
    injection H; intros.
    apply term_eq; try_solve.
  Qed.

  Lemma abs_term : M (Mabs: isAbs M), term M = \ (absType Mabs) (term (absBody Mabs)).

  Proof.
    intros; term_inv M.
  Qed.

  Hint Resolve absBody absType abs_isAbs absBody_eq_env funS_is_funS
    absBody_equiv_type absBody_eq_type : terms.
  Hint Rewrite absBody_env : terms.

  Definition isApp M : Prop :=
  let (_, _, _, typ) := M in
  match typ with
  | TApp _ _ _ _ _ _ _True
  | _False
  end.

  Ltac assert_app hypName M :=
     assert (hypName : isApp M);
     [try solve [eauto with terms | term_inv M]
     | idtac
     ].

  Definition appBodyL M : isApp MTerm :=
  match M return (isApp MTerm) with
  | buildT _ _ _ typ
      match typ return (isApp (buildT typ) → Term) with
      | TApp _ _ _ _ _ L Rfun _ : TruebuildT L
      | _fun notApp: FalseFalse_rect Term notApp
      end
  end.
  Implicit Arguments appBodyL [M].

  Definition appBodyR M : isApp MTerm :=
  match M return (isApp MTerm) with
  | buildT _ _ _ typ
      match typ return (isApp (buildT typ) → Term) with
      | TApp _ _ _ _ _ L Rfun _ : TruebuildT R
      | _fun notApp: FalseFalse_rect Term notApp
      end
  end.
  Implicit Arguments appBodyR [M].

  Lemma app_isApp : M Pt0 Pt1, term M = Pt0 @@ Pt1isApp M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma app_eq : M N (Mapp: isApp M) (Napp: isApp N),
    appBodyL Mapp = appBodyL NappappBodyR Mapp = appBodyR NappM = N.

  Proof.
    intros M N; term_inv M; term_inv N.
    intros Mapp Napp Leq Req.
    inversion Leq; destruct H4.
    inversion Req; destruct H7.
    apply deriv_uniq; auto.
    simpl; congruence.
  Qed.

  Lemma app_Req_LtypeEq : M N (Mapp: isApp M) (Napp: isApp N),
    term (appBodyR Mapp) = term (appBodyR Napp) → env M = env Ntype M = type N
    type (appBodyL Mapp) = type (appBodyL Napp).

  Proof.
    intros M N; term_inv M; term_inv N; intros; simpl in × |-.
    assert (Aeq: type (buildT T2) = type (buildT T4)).
    auto with terms.
    simpl in *; congruence.
  Qed.

  Lemma app_Leq_RtypeEq : M N (Mapp: isApp M) (Napp: isApp N),
    term (appBodyL Mapp) = term (appBodyL Napp) → env M = env Ntype M = type N
    type (appBodyR Mapp) = type (appBodyR Napp).

  Proof.
    intros M N; term_inv M; term_inv N; intros; simpl in × |-.
    assert (ABeq: type (buildT T1) = type (buildT T3)).
    auto with terms.
    simpl in *; inversion ABeq; trivial.
  Qed.

  Lemma appBodyL_env : M (Mapp: isApp M), env (appBodyL Mapp) = env M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma appBodyR_env : M (Mapp: isApp M), env (appBodyR Mapp) = env M.

  Proof.
    intro M; term_inv M.
  Qed.

  Hint Rewrite appBodyL_env appBodyR_env : terms.

  Lemma appBodyL_term : M PtL PtR (Mterm: term M = PtL @@ PtR) (Mapp: isApp M),
    term (appBodyL Mapp) = PtL.

  Proof.
    intros; term_inv M.
  Qed.

  Lemma appBodyR_term : M PtL PtR (Mterm: term M = PtL @@ PtR) (Mapp: isApp M),
    term (appBodyR Mapp) = PtR.

  Proof.
    intros; term_inv M.
  Qed.

  Lemma term_appBodyL_eq : M N (Mapp: isApp M) (Napp: isApp N),
    term M = term Nterm (appBodyL Mapp) = term (appBodyL Napp).

  Proof.
    intros M N; term_inv M; term_inv N.
  Qed.

  Lemma term_appBodyR_eq : M N (Mapp: isApp M) (Napp: isApp N),
    term M = term Nterm (appBodyR Mapp) = term (appBodyR Napp).

  Proof.
    intros M N; term_inv M; term_inv N.
  Qed.

  Hint Rewrite appBodyL_term appBodyR_term using solve [auto with terms] : terms.

  Lemma app_typeL_type : M A B (Mapp: isApp M), type (appBodyL Mapp) = A --> Btype M = B.

  Proof.
    intros M A B Mapp Ml.
    term_inv M.
  Qed.

  Lemma app_typeR : M A B (Mapp: isApp M), type (appBodyL Mapp) = A --> B
    type (appBodyR Mapp) = A.

  Proof.
    intros.
    term_inv M.
  Qed.

  Hint Rewrite app_typeL_type app_typeR using solve [auto with terms] : terms.

  Lemma app_type_eq : M N (Mapp: isApp M) (Napp: isApp N),
    type (appBodyL Mapp) = type (appBodyL Napp) →
    type (appBodyR Mapp) = type (appBodyR Napp) →
    type M = type N.

  Proof.
    intros; term_inv M; term_inv N.
  Qed.

  Lemma app_proof_irr : M (Mapp Mapp': isApp M), Mapp = Mapp'.

  Proof.
    intro M; term_inv M.
    dependent inversion Mapp; dependent inversion Mapp'; trivial.
  Qed.

  Lemma app_term : M (Mapp: isApp M),term M = term (appBodyL Mapp) @@ term (appBodyR Mapp).

  Proof.
    intro M; term_inv M.
  Qed.

  Hint Resolve appBodyL appBodyR app_isApp app_proof_irr app_Req_LtypeEq app_Leq_RtypeEq : terms.
  Hint Unfold isApp : terms.
  Hint Rewrite appBodyL_env appBodyR_env : terms.

  Lemma isVar_dec : M, {isVar M} + {¬isVar M}.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma isAbs_dec : M, {isAbs M} + {¬isAbs M}.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma isFunS_dec : M, {isFunS M} + {¬isFunS M}.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma isApp_dec : M, {isApp M} + {¬isApp M}.

  Proof.
    intro M; term_inv M.
  Qed.

  Definition appUnits M : list Term :=
  let
    fix appUnits_rec M (Mt: TermTyping M) {struct Mt} : list Term :=
    match Mt with
    | (TApp _ _ _ _ _ Ltyp Rtyp) ⇒ (buildT Rtyp) ::
                                    (@appUnits_rec (buildT Ltyp) Ltyp)
    | _(buildT Mt)::nil
    end
  in rev (appUnits_rec M M.(typing)).

  Lemma appUnits_not_nil : M, appUnits M nil.

  Proof.
    destruct M as [E Pt A TypM].
    induction TypM;
       solve [unfold appUnits; simpl; auto with datatypes].
  Qed.

  Definition appHead M : Term := proj1_sig (head_notNil (appUnits_not_nil M)).

  Definition appArgs M := tail (appUnits M).

  Definition isArg M' M : Prop := In M' (appArgs M).

  Definition isAppUnit M' M : Prop := In M' (appUnits M).

  Definition isNeutral M : Prop := ¬ isAbs M.

  Definition isFunApp M : Prop := isFunS (appHead M).

  Definition isPartialFlattening Args M : Prop :=
  match Args with
  | nilFalse
  | hd::nilFalse
  | hd::tlappUnits hd ++ tl = appUnits M
  end.

  Hint Resolve appUnits appHead appArgs appUnits_not_nil : terms.
  Hint Unfold isPartialFlattening isFunApp isNeutral : terms.

  Lemma appUnits_notApp : M, ¬isApp MappUnits M = M::nil.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma appUnit_notApp : M Mu, isAppUnit Mu M¬isApp MMu = M.

  Proof.
    intros.
    unfold isAppUnit in H.
    rewrite appUnits_notApp in H; trivial.
    inversion H; try_solve.
  Qed.

  Lemma appUnit_head_or_arg : M M', In M' (appUnits M) → M' = appHead M In M' (appArgs M).

  Proof.
    intros.
    destruct (in_head_tail M' (appUnits M) H).
    left; symmetry.
    unfold appHead; apply head_of_notNil; auto.
    right; trivial.
  Qed.

  Lemma appUnits_app : M (Mapp: isApp M),
    appUnits M = appUnits (appBodyL Mapp) ++ (appBodyR Mapp)::nil.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma appUnit_app : Mu M (Mapp: isApp M), isAppUnit Mu M
    (Mu = appBodyR Mapp isAppUnit Mu (appBodyL Mapp)).

  Proof.
    intros.
    unfold isAppUnit in H.
    rewrite (appUnits_app M Mapp) in H.
    destruct (in_app_or H).
    right; trivial.
    inversion H0; try_solve.
  Qed.

  Lemma appHead_notApp : M, ¬isApp MappHead M = M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma appHead_app_aux : M (Mapp: isApp M),
    head (appUnits M) = head (appUnits (appBodyL Mapp)).

  Proof.
    intro M; term_inv M.
    rewrite (appUnits_app Tr I).
    simpl.
    rewrite head_app.
    trivial.
    apply appUnits_not_nil.
  Qed.

  Lemma appHead_app : M (Mapp: isApp M), appHead M = appHead (appBodyL Mapp).

  Proof.
    intro M; term_inv M.
    unfold appHead.
    destruct (head_notNil (appUnits_not_nil Tr)).
    destruct (head_notNil (appUnits_not_nil (buildT T1))).
    simpl.
    assert (head (appUnits Tr) = head (appUnits (buildT T1))).
    change (buildT T1) with (@appBodyL Tr I).
    apply appHead_app_aux.
    congruence.
  Qed.

  Lemma appHead_app_explicit : E PtL PtR A B (L: E |- PtL := A --> B) (R: E |- PtR := A),
    appHead (buildT (TApp L R)) = appHead (buildT L).

  Proof.
    intros.
    rewrite (appHead_app (buildT (TApp L R)) I).
    trivial.
  Qed.

  Lemma appArgs_notApp : M, ¬isApp MappArgs M = nil.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma appArgs_app : M (Mapp: isApp M),
    appArgs M = appArgs (appBodyL Mapp) ++ (appBodyR Mapp)::nil.

  Proof.
    intros M Mapp; unfold appArgs.
    rewrite (appUnits_app M Mapp).
    symmetry; auto with terms datatypes.
  Qed.

  Lemma app_units_app : M (Mapp: isApp M),
    appUnits M = appHead M :: nil ++ appArgs M.

  Proof.
    destruct M as [E Pt T M]; induction M; try_solve.
    simpl.
    rewrite (appUnits_app (buildT (TApp M1 M2)) I).
    rewrite (appArgs_app (buildT (TApp M1 M2)) I).
    rewrite (appHead_app (buildT (TApp M1 M2)) I).
    simpl.
    destruct (isApp_dec (buildT M1)).
    rewrite IHM1; trivial.
    rewrite (appUnits_notApp (buildT M1) n).
    rewrite (appArgs_notApp (buildT M1) n).
    rewrite (appHead_notApp (buildT M1) n).
    trivial.
  Qed.

  Hint Rewrite appUnits_notApp appUnits_app appHead_notApp appHead_app
    appArgs_notApp appArgs_app : terms.

  Lemma appArg_inv : M N (Mapp: isApp M), isArg N M
    (isArg N (appBodyL Mapp) N = (appBodyR Mapp)).

  Proof.
    destruct M as [E Pt A typM].
    induction typM; simpl; intros; try contradiction.
    unfold isArg in H.
    rewrite (appArgs_app (buildT (TApp typM1 typM2)) Mapp) in H.
    case (@in_app_or _ (appArgs (buildT typM1)) (buildT typM2::nil) N); auto.
    destruct 1; solve [auto | contradiction].
  Qed.

  Lemma appArg_is_appUnit : M N, isArg M NisAppUnit M N.

  Proof.
    unfold isArg, isAppUnit; auto with datatypes terms.
  Qed.

  Lemma appUnit_left : M N (Mapp: isApp M), isAppUnit N (appBodyL Mapp) → isAppUnit N M.

  Proof.
    intro M; term_inv M.
    unfold isAppUnit.
    intros N Mapp.
    rewrite (appUnits_app Tr Mapp).
    simpl.
    auto with datatypes.
  Qed.

  Lemma appUnit_right : M N (Mapp: isApp M), N = appBodyR MappisAppUnit N M.

  Proof.
    intros.
    unfold isAppUnit.
    rewrite H.
    rewrite (appUnits_app M Mapp).
    auto with datatypes.
  Qed.

  Lemma appArg_left : M N (Mapp: isApp M), isArg N (appBodyL Mapp) → isArg N M.

  Proof.
    intros M N Mapp Narg.
    unfold isArg in ×.
    rewrite (appArgs_app M Mapp).
    auto with datatypes.
  Qed.

  Lemma appArg_right : M N (Mapp: isApp M), N = appBodyR MappisArg N M.

  Proof.
    intros M N Mapp Narg.
    unfold isArg in ×.
    rewrite (appArgs_app M Mapp).
    rewrite Narg.
    auto with datatypes.
  Qed.

  Lemma appArg_app : M Marg, isArg Marg MisApp M.

  Proof.
    intro M.
    destruct (isApp_dec M); trivial.
    unfold isArg; rewrite appArgs_notApp; try_solve.
  Qed.

  Lemma funS_neutral : M, isFunS MisNeutral M.

  Proof.
    intros; term_inv M.
  Qed.

  Lemma app_neutral : M, isApp MisNeutral M.

  Proof.
    intros; term_inv M.
  Qed.

  Lemma var_neutral : M, isVar MisNeutral M.

  Proof.
    intros; term_inv M.
  Qed.

  Lemma abs_not_neutral : M, isAbs MisNeutral MFalse.

  Proof.
    intros M; term_inv M.
  Qed.

  Lemma abs_isnot_app : M, isAbs M¬isApp M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma app_isnot_abs : M, isApp M¬isAbs M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma term_case : M, {isVar M} + {isFunS M} + {isAbs M} + {isApp M}.

  Proof.
    intro M; term_inv M.
  Qed.

  Hint Resolve appArg_inv appArg_is_appUnit abs_isnot_app app_isnot_abs
               appUnits_notApp appHead_notApp appHead_app appArgs_notApp
               appArgs_app appUnit_left: terms.

  Inductive subterm : TermTermProp :=
  | AppLsub: M N (Mapp: isApp M),
      subterm_le N (appBodyL Mapp) →
      subterm N M
  | AppRsub: M N (Mapp: isApp M),
      subterm_le N (appBodyR Mapp) →
      subterm N M
  | Abs_sub: M N (Mabs: isAbs M),
      subterm_le N (absBody Mabs) →
      subterm N M
   with subterm_le: TermTermProp :=
  | subterm_lt: M N,
      subterm N M
      subterm_le N M
  | subterm_eq: M,
      subterm_le M M.

  Definition subterm_gt := transp subterm.

  Module Subterm_Ord <: Ord.

    Module TermSet <: SetA.
      Definition A := Term.
    End TermSet.
    Module S := Eqset_def TermSet.

    Definition A := Term.
    Definition gtA := subterm_gt.
    Definition gtA_eqA_compat := Eqset_def_gtA_eqA_compat subterm_gt.

  End Subterm_Ord.

  Lemma subterm_wf : well_founded subterm.

  Proof.
    intros M; destruct M as [E Pt A TypM].
    induction TypM; constructor; inversion 1; simpl in *;
      try contradiction.
    inversion H0; trivial.
    apply Acc_inv with (buildT TypM); trivial.
    inversion H0; trivial.
    apply Acc_inv with (buildT TypM1); trivial.
    inversion H0; trivial.
    apply Acc_inv with (buildT TypM2); trivial.
  Qed.

  Lemma appBodyL_subterm : M (Mapp: isApp M), subterm (appBodyL Mapp) M.

  Proof.
    intros.
    constructor 1 with Mapp; constructor 2.
  Qed.

  Lemma appBodyR_subterm : M (Mapp: isApp M), subterm (appBodyR Mapp) M.

  Proof.
    intros.
    constructor 2 with Mapp; constructor 2.
  Qed.

  Lemma absBody_subterm : M (Mabs: isAbs M), subterm (absBody Mabs) M.

  Proof.
    intros.
    constructor 3 with Mabs; constructor 2.
  Qed.

  Lemma appUnit_subterm : M N, isApp MisAppUnit N Msubterm N M.

  Proof.
    destruct M as [E Pt A TypM].
    induction TypM; try contradiction.
    intros N M1M2app Narg.
    case (isApp_dec (buildT TypM1)).
    intro M1app.
    unfold isAppUnit in Narg.
    rewrite (appUnits_app (buildT (TApp TypM1 TypM2)) M1M2app) in Narg.
    simpl in Narg.
    case (@in_app_or _ (appUnits (buildT TypM1)) (buildT TypM2::nil) N).
    trivial.
    constructor 1 with M1M2app; constructor.
    apply IHTypM1; trivial.
    intro N_M2.
    constructor 2 with M1M2app.
    inversion N_M2.
    rewrite <- H; constructor 2.
    contradiction.
    intro M1notApp.
    unfold isAppUnit, appUnits in Narg.
    simpl in Narg.
    change TypM1 at 2 with (typing (buildT TypM1)) in Narg.
    fold (appUnits (buildT TypM1)) in Narg.
    case (@in_app_or _ (appUnits (buildT TypM1)) (buildT TypM2::nil) N).
    trivial.
    intro N_M1; constructor 1 with M1M2app.
    rewrite appUnits_notApp in N_M1; trivial.
    destruct (in_inv N_M1) as [NM1 | Nnil].
    rewrite <- NM1; constructor 2.
    absurd (In N nil); auto with datatypes.
    intro N_M2; constructor 2 with M1M2app.
    destruct (in_inv N_M2) as [NM2 | Nnil].
    rewrite <- NM2; constructor 2.
    absurd (In N nil); auto with datatypes.
  Qed.

  Lemma arg_subterm : M Ma, isArg Ma Msubterm Ma M.

  Proof.
    unfold isArg, appArgs.
    intros.
    destruct (isApp_dec M).
    apply appUnit_subterm; trivial.
    unfold isAppUnit; auto with datatypes.
    rewrite appUnits_notApp in H; try_solve.
  Qed.

  Lemma appUnits_notEmpty : M hd tl el, appUnits M ++ hd::tl = el::nilFalse.

  Proof.
    intros M hd tl el Eq.
    destruct (app_eq_unit (appUnits M) (hd::tl) Eq)
      as [[unitsL argR] | [unitsL argR]].
    absurd (appUnits M = nil); auto with terms.
    discriminate.
  Qed.

  Lemma partialFlattening_app : M Ms, isPartialFlattening Ms MisApp M.

  Proof.
    intros M Ms Ms_pflat_M.
    unfold isPartialFlattening in ×.
    repeat (destruct Ms; [contradiction | idtac]).
    case (isApp_dec M).
    auto.
    intro Mnapp.
    rewrite (appUnits_notApp M) in Ms_pflat_M.
    elimtype False.
    eapply appUnits_notEmpty; eexact Ms_pflat_M.
    trivial.
  Qed.

  Lemma app_notApp_diffUnits : M N, isApp M¬isApp NappUnits M = appUnits NFalse.

  Proof.
    intros M N Mapp Nnapp eqUnits.
    rewrite (appUnits_app M Mapp) in eqUnits.
    rewrite (appUnits_notApp N Nnapp) in eqUnits.
    eapply appUnits_notEmpty; eexact eqUnits.
  Qed.

  Lemma eq_unitTypes_eq_types : M N, length (appUnits M) = length (appUnits N) →
    ( p Ma Na,
      nth_error (appUnits M) p = Some Ma
      nth_error (appUnits N) p = Some Na
      type Ma = type Na
    ) →
    type M = type N.

  Proof.
    intro M.
    apply well_founded_ind with
      (R := subterm)
      (P := fun M
          N,
           length (appUnits M) = length (appUnits N) →
           ( p Ma Na,
             nth_error (appUnits M) p = Some Ma
             nth_error (appUnits N) p = Some Na
             type Ma = type Na
           ) →
           type M = type N).
    apply subterm_wf.
    clear M; intros M IH N MNunits H.
    destruct (isApp_dec M) as [Mapp | Mnapp].
    destruct (isApp_dec N) as [Napp | Nnapp].
    assert (type (appBodyL Mapp) = type (appBodyL Napp)).
    apply IH.
    apply appBodyL_subterm.
    rewrite (appUnits_app M Mapp) in MNunits.
    rewrite (appUnits_app N Napp) in MNunits.
    repeat rewrite length_app in MNunits.
    simpl in MNunits; omega.
    intros; apply (H p Ma Na).
    rewrite (appUnits_app M Mapp).
    rewrite nth_app_left; trivial.
    apply nth_some with Ma; trivial.
    rewrite (appUnits_app N Napp).
    rewrite nth_app_left; trivial.
    apply nth_some with Na; trivial.
    term_inv M; term_inv N.
    absurd (length (appUnits M) = length (appUnits N)); trivial.
    rewrite (appUnits_app M Mapp).
    rewrite (appUnits_notApp N Nnapp).
    rewrite length_app; try_solve.
    term_inv (appBodyL Mapp).
    rewrite (appUnits_app Tr I).
    rewrite length_app; simpl; omega.
    destruct (isApp_dec N) as [Napp | Nnapp].
    absurd (length (appUnits M) = length (appUnits N)); trivial.
    rewrite (appUnits_app N Napp).
    rewrite (appUnits_notApp M Mnapp).
    rewrite length_app; try_solve.
    term_inv (appBodyL Napp).
    rewrite (appUnits_app Tr I).
    rewrite length_app; simpl; omega.
    apply (H 0 M N).
    rewrite (appUnits_notApp M Mnapp); trivial.
    rewrite (appUnits_notApp N Nnapp); trivial.
  Qed.

  Lemma eq_units_eq_terms : M N, appUnits M = appUnits NM = N.

  Proof.
    intro M.
    apply well_founded_ind with
      (R := subterm)
      (P := fun M
          N,
           appUnits M = appUnits N
           M = N
      ).
    apply subterm_wf.
    clear M; intros M IH N unitsEq.
    case (isApp_dec M); case (isApp_dec N).
    intros Napp Mapp.
    rewrite (appUnits_app M Mapp) in unitsEq.
    rewrite (appUnits_app N Napp) in unitsEq.
    destruct (app_inj_tail (appUnits (appBodyL Mapp))
      (appUnits (appBodyL Napp)) (appBodyR Mapp) (appBodyR Napp));
      trivial.
    apply app_eq with Mapp Napp; simpl.
    apply IH; trivial.
    constructor 1 with Mapp; constructor 2.
    trivial.
    intros; elimtype False.
    apply app_notApp_diffUnits with M N; trivial.
    intros; elimtype False.
    apply app_notApp_diffUnits with N M; auto.
    intros Nnapp Mnapp.
    do 2 rewrite appUnits_notApp in unitsEq; trivial.
    congruence.
  Qed.

  Lemma partialFlattening_subterm_aux : L M N, L nilappUnits M ++ L = appUnits N
    subterm M N.

  Proof.
    intros L M N; generalize L M; clear L M.
    apply well_founded_ind with
      (R := subterm)
      (P := fun N
          L M,
           L nil
           appUnits M ++ L = appUnits N
           subterm M N
      ).
    apply subterm_wf.
    clear N; intros N IH L M L_neq_nil units.
    destruct L.
    tauto.
    case (isApp_dec N).
    intro Napp.
    rewrite (appUnits_app N Napp) in units; trivial.
    constructor 1 with Napp.
    destruct L.
    assert (Meq: M = appBodyL Napp).
    destruct (app_inj_tail (appUnits M) (appUnits (appBodyL Napp))
      t (appBodyR Napp)) as [unitsEq _]; trivial.
    apply eq_units_eq_terms; trivial.
    rewrite Meq; constructor 2.
    rewrite <- list_app_first_last in units.
    destruct (@list_drop_last Term (appUnits M ++ t::nil) (t0::L)
      (appUnits (appBodyL Napp)) (appBodyR Napp)); auto with datatypes.
    constructor 1.
    apply IH with (t::x); auto with datatypes.
    constructor 1 with Napp; constructor 2.
    rewrite <- list_app_first_last; trivial.
    intro Nnapp.
    rewrite (appUnits_notApp N) in units; trivial.
    elimtype False; eapply appUnits_notEmpty; eexact units.
  Qed.

  Lemma partialFlattening_subterm : M Ms m, isPartialFlattening Ms MIn m Mssubterm m M.

  Proof.
    intros M Ms m Ms_pflat m_Ms.
    assert (Mapp: isApp M).
    apply partialFlattening_app with Ms; trivial.
    unfold isPartialFlattening in ×.
    repeat (destruct Ms; [contradiction | idtac]).
    destruct m_Ms as [m_t | [m_t0 | m_Ms]].
    apply partialFlattening_subterm_aux with (t0::Ms).
    auto with datatypes.
    rewrite <- m_t; trivial.
    apply appUnit_subterm; trivial.
    unfold isAppUnit; rewrite <- m_t0; rewrite <- Ms_pflat.
    auto with datatypes.
    apply appUnit_subterm; trivial.
    unfold isAppUnit; rewrite <- Ms_pflat; auto with datatypes.
  Qed.

  Lemma funApp_head : M, isFunApp M{ f: FunctionSymbol | term (appHead M) = ^f }.

  Proof.
    unfold isFunApp; intros M MfunApp.
    term_inv (appHead M).
     f; trivial.
  Qed.

  Lemma funApp : M f, term (appHead M) = ^fisApp MisFunApp M.

  Proof.
    unfold isFunApp; intros M f Mhead Mapp.
    term_inv (appHead M).
  Qed.

  Lemma funS_funApp : M, isFunS MisFunApp M.

  Proof.
    intros.
    term_inv M.
  Qed.

  Lemma appHead_term : (M N: Term), term M = term Nterm (appHead M) = term (appHead N).

  Proof.
    intros M.
    apply well_founded_ind with
      (R := subterm)
      (P := fun (M: Term) ⇒
          (N: Term),
           term M = term N
           term (appHead M) = term (appHead N)
      ).
    apply subterm_wf.
    clear M; intros M IH N M_N.
    term_inv M; term_inv N.
    unfold Tr, Tr0.
    repeat rewrite appHead_app_explicit.
    apply IH.
    constructor 1 with I; constructor 2.
    simpl; congruence.
  Qed.

  Lemma appHead_left : M (Mapp: isApp M), isFunS (appHead M) →
    isFunS (appHead (appBodyL Mapp)).

  Proof.
    intro M; term_inv M.
    rewrite (appHead_app Tr I); trivial.
  Qed.

  Lemma headFunS_dec : M, {isFunS (appHead M)} + {¬isFunS (appHead M)}.

  Proof.
    destruct M as [E Pt A M]; induction M.
    firstorder.
    firstorder.
    firstorder.
    rewrite (appHead_app (buildT (TApp M1 M2)) I).
    destruct IHM1; firstorder.
  Qed.

  Lemma isFunApp_dec : M, {isFunApp M} + {¬isFunApp M}.

  Proof.
    destruct M as [E Pt A M]; destruct M.
    firstorder.
    firstorder.
    firstorder.
    destruct (headFunS_dec (buildT (TApp M1 M2))) as [headF | headNF]; firstorder.
  Qed.

  Lemma isFunApp_left : M (Mapp: isApp M), isFunApp MisFunApp (appBodyL Mapp).

  Proof.
    unfold isFunApp; intros.
    rewrite (appHead_app M Mapp) in H; trivial.
  Qed.

  Lemma notFunApp_left: M (Mapp: isApp M), ¬isFunApp (appBodyL Mapp) → ¬isFunApp M.

  Proof.
    intros.
    intro.
    absurd (isFunApp (appBodyL Mapp)); trivial.
    apply isFunApp_left; trivial.
  Qed.

  Lemma notFunApp_left_inv : M (Mapp: isApp M), ¬isFunApp M¬isFunApp (appBodyL Mapp).

  Proof.
    unfold isFunApp; intros; intro MLfa.
    absurd (isFunApp M); trivial.
    term_inv M.
    unfold isFunApp; rewrite (appHead_app Tr I); trivial.
  Qed.

  Lemma notFunApp_left_eq : M (Mapp: isApp M) N (Napp: isApp N), ¬isFunApp M
    appBodyL Mapp = appBodyL Napp¬isFunApp N.

  Proof.
    intro M; term_inv M.
    intros _ N; term_inv N.
    intros.
    apply notFunApp_left with I.
    simpl; rewrite <- H0.
    change (buildT T1) with (appBodyL (M:=buildT (TApp T1 T2)) I).
    apply notFunApp_left_inv; trivial.
  Qed.

  Lemma funAbs_notFunApp : M, isAbs (appHead M) → ¬isFunApp M.

  Proof.
    unfold isFunApp; intros.
    term_inv M.
    intro Tr_fa.
    term_inv (appHead Tr).
  Qed.

  Lemma notFunApp_notFunS : M, ¬isFunApp M¬isFunS M.

  Proof.
    intro M; term_inv M.
  Qed.

  Lemma appUnit_env : Mu M, isAppUnit Mu Menv Mu = env M.

  Proof.
    destruct M as [E Pt T M].
    induction M; intros;
      try solve [inversion H; try contradiction; rewrite <- H0; trivial].
    unfold isAppUnit in H.
    rewrite (appUnits_app (buildT (TApp M1 M2)) I) in H.
    simpl in H.
    destruct (in_app_or H).
    apply IHM1; trivial.
    inversion H0; try_solve.
    rewrite <- H1; trivial.
  Qed.

  Lemma partialFlattening_env : M Ms, isPartialFlattening Ms M
     Min, In Min Msenv Min = env M.

  Proof.
    intros.
    destruct Ms; try_solve.
    destruct Ms; try_solve.
    destruct H0.
    rewrite <- H0.
    destruct (head_notNil (appUnits_not_nil t)).
    destruct (head_notNil (appUnits_not_nil M)).
    rewrite <- (appUnit_env x t).
    rewrite <- (appUnit_env x0 M).
    rewrite (list_decompose_head (appUnits_not_nil t) e) in H.
    rewrite (list_decompose_head (appUnits_not_nil M) e0) in H.
    inversion H; trivial.
    unfold isAppUnit; destruct (appUnits M); inversion e0; try_solve.
    unfold isAppUnit; destruct (appUnits t); inversion e; try_solve.
    apply appUnit_env.
    unfold isAppUnit; rewrite <- H.
    destruct H0.
    rewrite <- H0; firstorder.
    firstorder.
  Qed.

  Lemma partialFlattening_inv : M (Mapp: isApp M) Ms, isPartialFlattening Ms M
    Ms = appBodyL Mapp :: appBodyR Mapp :: nil
     Ns, isPartialFlattening Ns (appBodyL Mapp) Ms = Ns ++ appBodyR Mapp :: nil.

  Proof.
    destruct M as [E Pt T M]; induction M; intros.
    set (w := partialFlattening_app (buildT (TVar v)) Ms H); try_solve.
    set (w := partialFlattening_app (buildT (TFun E f)) Ms H); try_solve.
    set (w := partialFlattening_app (buildT (TAbs M)) Ms H); try_solve.
    destruct Ms.
    inversion H.
    destruct Ms.
    inversion H.
    unfold isPartialFlattening in H.
    rewrite (appUnits_app (buildT (TApp M1 M2)) I) in H.
    destruct Ms.
    left.
    destruct (app_inj_tail (appUnits t) (appUnits (appBodyL Mapp))
      t0 (appBodyR Mapp)); trivial.
    rewrite H1.
    rewrite (eq_units_eq_terms t (appBodyL Mapp) H0); trivial.
    right.
    assert (MLapp: isApp (appBodyL Mapp)).
    destruct (isApp_dec (appBodyL Mapp)); trivial.
    rewrite (appUnits_notApp (appBodyL (M:=buildT (TApp M1 M2)) I)) in H; trivial.
    simpl in H.
    elimtype False; cut (appUnits t nil).
    destruct (appUnits t); auto.
    inversion H.
    cut (length (l ++ t0 :: t1 :: Ms) = length (buildT M2 :: nil)).
    autorewrite with datatypes; simpl; omega.
    rewrite H2; trivial.
    apply appUnits_not_nil.
     (t :: t0 :: dropLast (t1 :: Ms)); split.
    set (w := dropLast_eq H).
    rewrite dropLast_last in w.
    set (ww := dropLast_app).
    rewrite dropLast_app in w.
    simpl in w; trivial.
    simpl; rewrite (appUnits_app (buildT M1) MLapp); auto with datatypes.
    set (w := last_eq H).
    simpl in w.
    rewrite last_app in w.
    rewrite last_app in w.
    change ((t :: t0 :: dropLast (t1 :: Ms)) ++ appBodyR Mapp :: nil) with
      (t :: t0 :: (dropLast (t1 :: Ms) ++ buildT M2 :: nil)).
    rewrite dropLast_plus_last; trivial.
  Qed.

  Lemma partialFlattening_desc : M (Mapp: isApp M) ML,
    isPartialFlattening ML (appBodyL Mapp) → isPartialFlattening (ML ++ appBodyR Mapp :: nil) M.

  Proof.
    unfold isPartialFlattening.
    intros.
    destruct ML; try_solve.
    destruct ML; try_solve.
    rewrite (appUnits_app M Mapp).
    rewrite <- H.
    rewrite app_ass.
    auto with datatypes.
  Qed.

  Lemma allPartialFlattenings : M, isApp M
    { MF: list (list Term) | Mpf, isPartialFlattening Mpf M In Mpf MF }.

  Proof.
    destruct M as [E Pt T M]; induction M; try_solve.
    intros _.
    destruct (isApp_dec (buildT M1)).
    destruct IHM1; trivial.
    set (F0 := buildT M1::buildT M2::nil).
    set (fp := (fun ll ++ buildT M2::nil)).
    set (Fr := map fp x).
     (F0::Fr); split; intro.
    destruct (partialFlattening_inv (buildT (TApp M1 M2)) I Mpf H).
    rewrite H0; unfold F0; try_solve.
    destruct H0 as [Ns [NsL MpfNs]].
    rewrite MpfNs.
    right; unfold Fr.
    set (Nsx := proj1 (i0 Ns) NsL).
    destruct (list_In_nth x Ns Nsx) as [p xp].
    apply nth_some_in with p.
    simpl.
    change (Ns ++ buildT M2::nil) with (fp Ns).
    apply nth_map_some; trivial.
    inversion H.
    rewrite <- H0.
    unfold F0; simpl.
    rewrite (appUnits_app (buildT (TApp M1 M2)) I); trivial.
    destruct (list_In_nth Fr Mpf H0) as [p xp].
    unfold Fr in xp.
    destruct (nth_map_some_rev x p fp xp) as [w [xpw fpw]].
    rewrite <- fpw; unfold fp.
    change (buildT M2) with (appBodyR (M:=buildT (TApp M1 M2)) I).
    apply partialFlattening_desc.
    apply (proj2 (i0 w)).
    apply nth_some_in with p; trivial.
     ((buildT M1::buildT M2::nil)::nil).
    split; intros.
    destruct (partialFlattening_inv (buildT (TApp M1 M2)) I Mpf H).
    try_solve.
    destruct H0 as [Ns [NsL _]].
    absurd (isApp (buildT M1)); trivial.
    apply partialFlattening_app with Ns; trivial.
    unfold isPartialFlattening.
    inversion H; try_solve.
    rewrite <- H0; try_solve.
  Qed.

  Lemma term_neq_type : M N, type M type NM N.

  Proof.
    intros; intro MN.
    absurd (type M = type N); trivial.
    rewrite MN; trivial.
  Qed.

  Hint Resolve appUnit_subterm partialFlattening_app eq_units_eq_terms
               partialFlattening_subterm funApp_head funApp : terms.

  Module TermSet <: SetA.
     Definition A := Term.
  End TermSet.
  Module TermEqSet := Eqset_def TermSet.

End TermsManip.