Library CoLoR.Term.SimpleType.TermsEnv

CoLoR, a Coq library on rewriting and termination. See the COPYRIGHTS and LICENSE files.
  • Adam Koprowski, 2006-04-27
Operations on environments of terms of simply typed lambda-calculus are introduced in this file.

Set Implicit Arguments.

Require Import RelExtras.
Require Import ListExtras.
Require Import TermsLifting.
Require Import Arith.
Require Import Setoid.

Module TermsEnv (Sig : TermsSig.Signature).

  Module TL := TermsLifting.TermsLifting Sig.
  Export TL.

  Definition emptyEnv E := p, E |= p :! .

  Lemma varD_tail : E A i, E |= S i := A tail E |= i := A.

  Proof.
    intros; destruct E; trivial.
    inversion H.
  Qed.

  Lemma varD_tail_rev : E A i, tail E |= i := A E |= S i := A.

  Proof.
    intros; destruct E; trivial.
    destruct i; trivial.
  Qed.

  Lemma varUD_tail : E i, E |= S i :! tail E |= i :! .

  Proof.
    intros; destruct E; trivial.
    inversion H; destruct i; trivial.
  Qed.

  Lemma varUD_tail_rev : E i, tail E |= i :! E |= S i :! .

  Proof.
    intros; destruct E; trivial.
    destruct i; trivial.
  Qed.

  Lemma varD_UD_absurd : E p A, E |= p := A E |= p :!

    False.
  Proof.
    intros E p A EpA Ep.
    destruct Ep; unfold VarD in *; try_solve.
  Qed.

  Definition equiv E x E' x' := ( A, E |= x := A E' |= x' := A)
    (E |= x :! E' |= x' :!).

  Lemma split_beyond : E k n p, p < k length E p
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p :! .

  Proof.
    intros; unfold VarUD.
    rewrite nth_app_right; autorewrite with datatypes.
    destruct (le_gt_dec n (p - Min.min k (length E))).
    rewrite nth_app_right; autorewrite with datatypes; trivial.
    left; apply nth_beyond; autorewrite with datatypes; trivial.
    simpl; omega.
    rewrite nth_app_left; autorewrite with datatypes; try_solve.
    set (w := Min.le_min_r k (length E)); omega.
  Qed.

  Lemma equiv_copy_split_l : E k p n, p < k
    equiv E p (initialSeg E k ++ copy n None ++ finalSeg E k) p.

  Proof.
    intros; unfold equiv.
    destruct (le_gt_dec (length E) p).
    set (w := split_beyond E n H l).
    split; split; intro; trivial.
    set (ww := nth_some E p H0); elimtype False; omega.
    elimtype False; apply varD_UD_absurd with
      (initialSeg E k ++ copy n None ++ finalSeg E k) p A; trivial.
    left; apply nth_beyond; trivial.
    unfold VarD, VarUD; repeat rewrite nth_app_left.
    rewrite initialSeg_nth; firstorder.
    autorewrite with datatypes.
    apply Min.min_case2; trivial.
  Qed.

  Lemma copy_split_l_varD : E k p n A, p < k E |= p := A
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p := A.

  Proof.
    intros.
    apply (proj1 (proj1 (equiv_copy_split_l E n H) A)); trivial.
  Qed.

  Lemma copy_split_l_varUD : E k p n, p < k E |= p :!
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p :! .

  Proof.
    intros.
    apply (proj1 (proj2 (equiv_copy_split_l E n H))); trivial.
  Qed.

  Lemma copy_split_l_rev_varD : E k p n A, p < k
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p := A E |= p := A.

  Proof.
    intros.
    apply (proj2 (proj1 (equiv_copy_split_l E n H) A)); trivial.
  Qed.

  Lemma copy_split_l_rev_varUD : E k p n, p < k
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p :! E |= p :! .

  Proof.
    intros.
    apply (proj2 (proj2 (equiv_copy_split_l E n H))); trivial.
  Qed.

  Lemma equiv_copy_split_r : E k n p p', p k p' = p + n
    equiv E p (initialSeg E k ++ copy n None ++ finalSeg E k) p'.

  Proof.
    intros; unfold equiv, VarD, VarUD; rewrite H0.
    repeat rewrite nth_app_right; autorewrite with datatypes.
    destruct (le_gt_dec k (length E)).
    replace (p + n - Min.min k (length E) - n) with (p - k).
    split; split; intro; solve
      [ replace (k + (p - k)) with p; [trivial | omega]
      | replace p with (k + (p - k)); [trivial | omega]
      ].
    rewrite Min.min_l; omega.
    replace (p + n - Min.min k (length E) - n) with (p - length E).
    split; split; intro; try solve
      [ left; apply nth_beyond; omega
      | set (w := nth_some E _ H1); elimtype False; omega ].
    rewrite Min.min_r; omega.
    set (w := Min.le_min_l k (length E)); omega.
    set (w := Min.le_min_l k (length E)); omega.
  Qed.

  Lemma copy_split_r_varD : E k n p p' A, p k p' = p + n E |= p := A
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p' := A.

  Proof.
    intros.
    apply (proj1 (proj1 (equiv_copy_split_r E n H H0) A)); trivial.
  Qed.

  Lemma copy_split_r_varUD : E k n p p', p k p' = p + n E |= p :!
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p' :! .

  Proof.
    intros.
    apply (proj1 (proj2 (equiv_copy_split_r E n H H0))); trivial.
  Qed.

  Lemma copy_split_r_rev_varD : E k n p p' A, p k p' = p + n
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p' := A E |= p := A.

  Proof.
    intros.
    apply (proj2 (proj1 (equiv_copy_split_r E n H H0) A)); trivial.
  Qed.

  Lemma copy_split_r_rev_varUD : E k n p p', p k p' = p + n
    (initialSeg E k ++ copy n None ++ finalSeg E k) |= p' :! E |= p :! .

  Proof.
    intros.
    apply (proj2 (proj2 (equiv_copy_split_r E n H H0))); trivial.
  Qed.

  Lemma equiv_hole_l : E k p, p < k equiv E p (initialSeg E k ++ finalSeg E (S k)) p.

  Proof.
    intros; unfold equiv, VarD, VarUD.
    destruct (le_gt_dec (length E) p).
    assert (p length (initialSeg E k ++ finalSeg E (S k))).
    autorewrite with datatypes using simpl.
    omega.
    split; split; intro; try solve [left; apply nth_beyond; trivial].
    set (ww := nth_some E p H1); elimtype False; omega.
    elimtype False; apply varD_UD_absurd with
      (initialSeg E k ++ finalSeg E (S k)) p A; trivial.
    left; apply nth_beyond; trivial.
    unfold VarD, VarUD; repeat rewrite nth_app_left.
    rewrite initialSeg_nth; firstorder.
    autorewrite with datatypes.
    apply Min.min_case2; trivial.
  Qed.

  Lemma hole_l_varD : E k p A, p < k E |= p := A
    (initialSeg E k ++ finalSeg E (S k)) |= p := A.

  Proof.
    intros.
    apply (proj1 (proj1 (equiv_hole_l E H) A)); trivial.
  Qed.

  Lemma hole_l_varUD : E k p, p < k E |= p :!
    (initialSeg E k ++ finalSeg E (S k)) |= p :! .

  Proof.
    intros.
    apply (proj1 (proj2 (equiv_hole_l E H))); trivial.
  Qed.

  Lemma hole_l_rev_varD : E k p A, p < k
    (initialSeg E k ++ finalSeg E (S k)) |= p := A E |= p := A.

  Proof.
    intros.
    apply (proj2 (proj1 (equiv_hole_l E H) A)); trivial.
  Qed.

  Lemma hole_l_rev_varUD : E k p, p < k (initialSeg E k ++ finalSeg E (S k)) |= p :!
    E |= p :! .

  Proof.
    intros.
    apply (proj2 (proj2 (equiv_hole_l E H))); trivial.
  Qed.

  Lemma equiv_hole_r : E k p p', p > k p = S p'
    equiv E p (initialSeg E k ++ finalSeg E (S k)) p'.

  Proof.
    intros; unfold equiv, VarD, VarUD; rewrite H0.
    rewrite nth_app_right; autorewrite with datatypes.
    destruct (le_gt_dec k (length E)).
    replace (p' - Min.min k (length E)) with (p' - k).
    split; split; intro; solve
      [ replace (S k + (p' - k)) with (S p'); [trivial | omega]
      | replace (S p') with (S k + (p' - k)); [trivial | omega]
      ].
    rewrite Min.min_l; omega.
    replace (p' - Min.min k (length E)) with (p' - (length E)).
    split; split; intro; solve
      [ left; apply nth_beyond; omega
      | set (w := nth_some E _ H1); elimtype False; omega
      ].
    rewrite Min.min_r; omega.
    set (w := Min.le_min_l k (length E)); omega.
  Qed.

  Lemma hole_r_varD : E k p p' A, p > k p = S p' E |= p := A
    (initialSeg E k ++ finalSeg E (S k)) |= p' := A.

  Proof.
    intros.
    apply (proj1 (proj1 (equiv_hole_r E H H0) A)); trivial.
  Qed.

  Lemma hole_r_varUD : E k p p', p > k p = S p' E |= p :!
    (initialSeg E k ++ finalSeg E (S k)) |= p' :! .

  Proof.
    intros.
    apply (proj1 (proj2 (equiv_hole_r E H H0))); trivial.
  Qed.

  Lemma hole_r_rev_varD : E k p p' A, p > k p = S p'
    (initialSeg E k ++ finalSeg E (S k)) |= p' := A E |= p := A.

  Proof.
    intros.
    apply (proj2 (proj1 (equiv_hole_r E H H0) A)); trivial.
  Qed.

  Lemma hole_r_rev_varUD : E k p p', p > k p = S p'
    (initialSeg E k ++ finalSeg E (S k)) |= p' :! E |= p :! .

  Proof.
    intros.
    apply (proj2 (proj2 (equiv_hole_r E H H0))); trivial.
  Qed.

  Lemma emptyEnv_empty : emptyEnv EmptyEnv.
  Proof.
    intro p; destruct p; try_solve.
  Qed.

  Lemma emptyEnv_tail : E, emptyEnv (None :: E) emptyEnv E.

  Proof.
    intros; intro p.
    apply (H (S p)).
  Qed.

  Lemma tail_emptyEnv : E, emptyEnv E emptyEnv (tail E).

  Proof.
    intros; intro p.
    destruct (H (S p)); destruct E; try_solve.
  Qed.

  Lemma emptyEnv_cons : E, emptyEnv E emptyEnv (None :: E).

  Proof.
    intros.
    intro p; destruct p.
    right; try_solve.
    apply (H p).
  Qed.

  Lemma emptyEnv_copyNone : k, emptyEnv (copy k None).

  Proof.
    intros; intro p.
    destruct (le_gt_dec k p).
    left; apply nth_beyond; autorewrite with datatypes using trivial.
    right; apply nth_copy_in; trivial.
  Qed.

  Lemma emptyEnv_absurd : a E P, emptyEnv (Some a :: E) P.

  Proof.
    intros.
    elimtype False.
    assert ((Some a :: E) |= 0 := a).
    auto with terms.
    destruct (H 0); try_solve.
  Qed.

  Definition isNotEmpty (e: option SimpleType) := e None.

  Lemma isNotEmpty_dec : e, {isNotEmpty e} + {¬isNotEmpty e}.

  Proof.
    intro e; destruct e.
    left; try_solve.
    right; try_solve.
  Qed.

  Definition dropEmptySuffix (E: Env) : Env :=
    match find_last isNotEmpty isNotEmpty_dec E with
    | NoneEmptyEnv
    | Some iinitialSeg E (S i)
    end.

  Lemma dropSuffix_decl : E x A, E |= x := A dropEmptySuffix E |= x := A.

  Proof.
    intros.
    destruct (find_last_last isNotEmpty isNotEmpty_dec E x H)
      as [q [Eq qx]]; try_solve.
    unfold dropEmptySuffix.
    rewrite Eq.
    unfold VarD; rewrite initialSeg_nth; trivial.
    omega.
  Qed.

  Lemma dropSuffix_decl_rev : E x A,
    dropEmptySuffix E |= x := A E |= x := A.

  Proof.
    intros.
    unfold dropEmptySuffix in H.
    destruct (option_dec (find_last isNotEmpty isNotEmpty_dec E))
      as [Ee | [p Ep]].
    rewrite Ee in H.
    destruct x; try_solve.
    rewrite Ep in H.
    unfold VarD; apply initialSeg_prefix with (S p); trivial.
  Qed.

  Lemma dropSuffix_last : E (E' := dropEmptySuffix E),
    length E' = 0 A, E' |= pred (length E') := A.

  Proof.
    intro E.
    unfold dropEmptySuffix.
    destruct (option_dec (find_last isNotEmpty isNotEmpty_dec E))
      as [Ee | [p Ep]].
    rewrite Ee; left; trivial.
    rewrite Ep; right.
    destruct (find_last_ok isNotEmpty isNotEmpty_dec E Ep) as [w [pEpw wSome]].
    destruct w.
     s.
    autorewrite with datatypes.
    rewrite Min.min_l.
    unfold VarD.
    rewrite initialSeg_nth; trivial.
    auto with arith.
    assert (nth_error E p None); try_solve.
    set (w := proj1 (nth_in E p) H).
    omega.
    destruct wSome; trivial.
  Qed.

  Fixpoint env_compose (E F: Env) {struct E} : Env :=
    match E, F with
    | nil, nilEmptyEnv
    | L, nilL
    | nil, LL
    | _::E', Some a::F'Some a :: env_compose E' F'
    | e1::E', None::F'e1 :: env_compose E' F'
    end.
  Notation "E [+] F" := (env_compose E F)
    (at level 50, left associativity).

  Lemma env_sum_empty_l : E, EmptyEnv [+] E = E.

  Proof.
    induction E; auto.
  Qed.

  Lemma env_sum_empty_r : E, E [+] EmptyEnv = E.
  Proof.
    induction E; auto.
  Qed.

  Hint Rewrite env_sum_empty_l env_sum_empty_r : terms.

  Lemma env_sum_assoc : (E1 E2 E3: Env), E1 [+] E2 [+] E3 = E1 [+] (E2 [+] E3).

  Proof.
    induction E1; intros.
    autorewrite with terms using trivial.
    destruct E2; destruct E3; autorewrite with terms using trivial.
    destruct o; destruct o0; simpl; rewrite IHE1; trivial.
  Qed.

  Lemma tail_distr_sum : E1 E2, tail (E1 [+] E2) = tail E1 [+] tail E2.

  Proof.
    intros.
    destruct E1; destruct E2; try destruct E2; try destruct o0; try_solve.
    rewrite env_sum_empty_r; trivial.
  Qed.

  Lemma env_sum_ln_rn : E1 E2 x, E1 |= x :! E2 |= x :! E1 [+] E2 |= x :!.

  Proof.
    induction E1; destruct E2 as [|a' E2]; trivial.
    destruct x.
    inversion 1; inversion 1; destruct a'; auto.
    unfold VarUD; inversion 1; inversion 1; destruct a';
      simpl in *; apply IHE1; trivial.
  Qed.

  Lemma env_sumn_ln : E1 E2 x, E1 [+] E2 |= x :! E1 |= x :! .

  Proof.
    intros E1 E2 x; generalize x E1 E2; clear E1 E2 x.
    induction x.
    destruct E1; destruct E2; try destruct o0; try_solve.
    inversion 1; try_solve.
    destruct E1; destruct E2; try destruct o0; try_solve; firstorder.
  Qed.

  Lemma env_sumn_rn : E1 E2 x, E1 [+] E2 |= x :! E2 |= x :! .

  Proof.
    intros E1 E2 x; generalize x E1 E2; clear E1 E2 x.
    induction x.
    destruct E1; destruct E2; try destruct o0; try_solve.
    destruct E1; destruct E2; try destruct o0; try_solve; firstorder.
  Qed.

  Lemma env_sum_ry : E1 E2 x A, E2 |= x := A E1 [+] E2 |= x := A.

  Proof.
    induction E1; destruct E2; intros x A E_comp;
      try solve [unfold VarD; destruct x; simpl; intros; discriminate].
    auto.
    unfold VarD; simpl.
    destruct o; destruct x; simpl; intros;
      solve [trivial | discriminate | apply IHE1; trivial].
  Qed.

  Lemma env_sum_right : E1 E2 x A1 A2, E1 |= x := A1 E2 [+] E1 |= x := A2 A1 = A2.

  Proof.
    induction E1; destruct E2; destruct x; unfold VarD; try_solve; try congruence.
    intros A1 A2 aA1.
    inversion aA1; congruence.
    intros A1 A2 E1x.
    destruct a; intro H; apply IHE1 with E2 x; trivial.
  Qed.

  Lemma env_sum_ly_rn : E1 E2 x A, E1 |= x := A E2 |= x :! E1 [+] E2 |= x := A.

  Proof.
    induction E1; destruct E2 as [|a' E2]; trivial.
    inversion 1; destruct x; discriminate.
    inversion 1; inversion 1; destruct a'; destruct x; unfold VarD;
      simpl in *; solve [trivial | discriminate | apply IHE1; trivial].
  Qed.

  Lemma env_sum_length : El Er, length (El [+] Er) = Max.max (length El) (length Er).

  Proof.
    induction El; intros.
    simpl; destruct Er; trivial.
    simpl; destruct Er; trivial.
    destruct o; simpl; rewrite IHEl; trivial.
  Qed.

  Definition env_comp_on E F x : Prop := A B, E |= x := A F |= x := B A = B.

  Lemma env_comp_on_sym : E1 E2 x, env_comp_on E1 E2 x env_comp_on E2 E1 x.

  Proof.
    firstorder.
  Qed.

  Definition env_comp E F : Prop := x, env_comp_on E F x.
  Notation "E [<->] F" := (env_comp E F) (at level 70).

  Lemma env_comp_refl : E, E [<->] E.

  Proof.
    intros E x A B.
    unfold VarD; congruence.
  Qed.

  Lemma env_comp_sym : E1 E2, E1 [<->] E2 E2 [<->] E1.

  Proof.
    intros E1 E2 E1E2 x A B E2x E1x.
    symmetry; apply (E1E2 x); trivial.
  Qed.

  Lemma env_comp_empty : E, E [<->] EmptyEnv.

  Proof.
    intros E x A B.
    destruct x; try_solve.
  Qed.

  Lemma env_comp_empty_r : E, EmptyEnv [<->] E.

  Proof.
    intros; apply env_comp_sym; apply env_comp_empty.
  Qed.

  Lemma env_comp_dropSuffix : E1 E2, E1 [<->] dropEmptySuffix E2 E1 [<->] E2.

  Proof.
    intros; intros p A B Dl Dr.
    apply (H p); trivial.
    apply dropSuffix_decl; trivial.
  Qed.

  Hint Immediate env_comp_refl env_comp_empty env_comp_empty_r : terms.

  Lemma env_comp_tail : e1 e2 E1 E2, (e1::E1) [<->] (e2::E2) E1 [<->] E2.

  Proof.
    unfold env_comp.
    intros e1 e2 E1 E2 E_comp x A B E1_x E2_x.
    apply (E_comp (S x) A B); trivial.
  Qed.

  Lemma env_comp_cons : e1 e2 E1 E2, E1 [<->] E2 (e1 = e2 e1 = None e2 = None)
    (e1::E1) [<->] (e2::E2).

  Proof.
    unfold env_comp.
    intros e1 e2 E1 E2 E_comp e1e2 x A B E1_x E2_x.
    destruct e1e2 as [e1e2 | [e1e2 | e1e2]];
      first [rewrite e1e2 in E1_x | rewrite e1e2 in E2_x];
      destruct x; unfold VarD in *; simpl in *;
      solve [congruence | discriminate | apply E_comp with x; trivial].
  Qed.

  Lemma env_comp_single : A x E, (E |= x :! E |= x := A)
    (copy x None ++ A [#] EmptyEnv) [<->] E.

  Proof.
    intros; intros p B C Dl Dr.
    destruct (lt_eq_lt_dec p x) as [[p_x | p_x] | p_x].
    elimtype False; apply (varD_UD_absurd Dl).
    right; unfold VarUD.
    rewrite nth_app_left.
    apply nth_copy_in; trivial.
    autorewrite with datatypes using trivial.
    inversion H.
    elimtype False; apply (varD_UD_absurd Dr).
    rewrite p_x; trivial.
    rewrite <- p_x in H0.
    inversion Dl.
    assert (A = B).
    cut ((copy x None ++ A[#]EmptyEnv) |= p := B); trivial.
    unfold VarD.
    rewrite nth_app_right; autorewrite with datatypes using try omega.
    rewrite p_x; rewrite <- minus_n_n.
    intro D; inversion D; trivial.
    inversion Dr; inversion H0; try_solve.
    elimtype False; apply (varD_UD_absurd Dl).
    left; unfold VarUD.
    rewrite nth_app_right; autorewrite with datatypes.
    cut (p - x > 0); try omega.
    destruct (p - x); intro; try solve [elimtype False; omega].
    destruct n; trivial.
    omega.
  Qed.

  Hint Resolve env_comp_cons : terms.

  Lemma env_sum_ly : E1 E2 x A, env_comp_on E1 E2 x E1 |= x := A E1 [+] E2 |= x := A.

  Proof.
    intros; destruct (isVarDecl_dec E2 x) as [[B E2x] | E2xn].
    unfold VarD; rewrite (env_sum_ry E1 E2x).
    assert (A = B); [apply H; trivial | congruence].
    apply env_sum_ly_rn; trivial.
  Qed.

  Lemma typing_ext_env_l : E E' Pt A, E |- Pt := A E' [+] E |- Pt := A.

  Proof.
    intros E E' Pt; generalize Pt E E'; clear E E' Pt.
    induction Pt.
    intros E E' A Ex.
    constructor.
    inversion Ex.
    apply env_sum_ry; trivial.
    inversion 1; constructor.
    intros E E' a TypAbs.
    inversion TypAbs.
    constructor.
    change (decl A (E'[+]E)) with (decl A E' [+] decl A E).
    apply IHPt; trivial.
    intros E E' A TypApp.
    inversion TypApp.
    constructor 4 with A0.
    apply IHPt1; trivial.
    apply IHPt2; trivial.
  Qed.

  Lemma typing_ext_env_r : E E' Pt A, E [<->] E' E |- Pt := A E [+] E' |- Pt := A.

  Proof.
    intros E E' Pt; generalize Pt E E'; clear E E' Pt.
    induction Pt.
    intros E E' A envC Ex.
    constructor.
    inversion Ex.
    apply env_sum_ly; trivial.
    inversion 2; constructor.
    intros E E' a EE' TypAbs.
    inversion TypAbs.
    constructor.
    change (decl A (E [+] E')) with (decl A E [+] decl A E').
    apply IHPt; trivial.
    unfold decl; apply env_comp_cons; auto.
    intros E E' A EE' TypApp.
    inversion TypApp.
    constructor 4 with A0.
    apply IHPt1; trivial.
    apply IHPt2; trivial.
  Qed.

  Fixpoint env_subtract (E F: Env) {struct E} : Env :=
  match E, F with
  | nil, _EmptyEnv
  | E, nilE
  | None :: E', _ :: F'None :: env_subtract E' F'
  | Some e :: E', None :: F'Some e :: env_subtract E' F'
  | Some e :: E', Some f :: F'None :: env_subtract E' F'
  end.
  Notation "E [-] F" := (env_subtract E F) (at level 50, left associativity).

  Lemma env_sub_empty : E, E [-] EmptyEnv = E.

  Proof.
    destruct E; trivial.
    destruct o; trivial.
  Qed.

  Hint Rewrite env_sub_empty : terms.

  Lemma env_sub_Empty : E1 E2, emptyEnv E2 E1 [-] E2 = E1.

  Proof.
    induction E1; trivial.
    intros E2 E2_empty; simpl.
    destruct a; destruct E2; try destruct o; trivial; try solve [
      rewrite IHE1; [trivial | apply emptyEnv_tail; trivial]
    | eapply emptyEnv_absurd; eauto
    ].
  Qed.

  Lemma env_sub_ln : E E' x, E |= x :! E [-] E' |= x :!.

  Proof.
    induction E; destruct E' as [|a' E']; intros; trivial.
    destruct a; trivial.
    inversion H; destruct x; destruct a; destruct a'; simpl in *;
      solve [ discriminate
            | auto
            | unfold VarUD; destruct (IHE E' x); auto ].
  Qed.

  Lemma env_sub_ry : E E' x A, E' |= x := A E [-] E' |= x :!.

  Proof.
    induction E; destruct E' as [|a' E']; intros;
      try solve [inversion H; destruct x; simpl in *; discriminate].
    destruct x; unfold VarUD; auto.
    destruct x.
    destruct a; destruct a'; unfold VarUD; simpl; auto.
    inversion H.
    unfold VarD in H; simpl in H.
    simpl; destruct a; destruct a'; unfold VarUD;
      destruct (IHE E' x A); auto.
  Qed.

  Lemma env_sub_ly_rn : E E' x A, E |= x := A E' |= x :! E [-] E' |= x := A.

  Proof.
    induction E; destruct E' as [|a' E']; intros;
      try solve [inversion H; destruct x; simpl in *; discriminate].
    simpl; destruct a; trivial.
    destruct x.
    destruct a; destruct a'; unfold VarD in *; inversion H0;
      simpl in *; solve [discriminate | trivial].
    inversion H.
    simpl; destruct a; destruct a'; unfold VarD;
      simpl; apply IHE; trivial.
  Qed.

  Lemma env_sub_suby_rn : E E' x A, E [-] E' |= x := A E' |= x :! .

  Proof.
    intros E E' x A EE'x.
    destruct (isVarDecl_dec E' x) as [[C E'x] | E'n]; trivial.
    elimtype False; apply varD_UD_absurd with (E [-] E') x A; trivial.
    apply env_sub_ry with C; trivial.
  Qed.

  Lemma env_sub_suby_ly : E E' x A, E [-] E' |= x := A E |= x := A.

  Proof.
    intros E E' x A EE'x.
    destruct (isVarDecl_dec E x) as [[B Ex] | En].
    set (E'n := env_sub_suby_rn E E' EE'x).
    set (w := env_sub_ly_rn Ex E'n).
    assert (A = B).
    unfold VarD in *; congruence.
    rewrite H; trivial.
    set (w := env_sub_ln E' En).
    inversion w; unfold VarD in EE'x; congruence.
  Qed.

  Lemma env_sum_double : E, E [+] E = E.

  Proof.
    induction E; auto.
    destruct a; simpl; congruence.
  Qed.

  Hint Rewrite env_sum_double : terms.

  Lemma env_comp_sum_comp_right : E1 E2 E3, E1 [<->] E2 [+] E3 E1 [<->] E3.

  Proof.
    intros E1 E2 E3 E1_23 p A B E1_pA E3_pB.
    exact (E1_23 p A B E1_pA (env_sum_ry E2 E3_pB)).
  Qed.

  Lemma env_comp_ext_l : E1 E2, E1 [+] E2 [<->] E2.

  Proof.
    intros E1 E2 p A B E1E2_pA E2_pB.
    set (hint := env_sum_ry E1 E2_pB).
    inversion E1E2_pA.
    inversion hint.
    try_solve.
  Qed.

  Lemma env_comp_sub : E1 E2 E3, E1 [<->] E2 E1 [-] E3 [<->] E2.

  Proof.
    induction E1; auto.
    intros E2 E3 E12 p A B.
    destruct p; unfold VarD.
    destruct a; destruct E3; destruct E2;
      try destruct o; try destruct o0; solve
    [
      try_solve
    | intros sA sB;
      inversion sA as [s_A]; rewrite <- s_A;
      inversion sB as [s_B]; rewrite <- s_B;
      apply (E12 0); unfold VarD; trivial
    ].
    destruct a; destruct E3; destruct E2;
      try destruct o; try destruct o0; try solve
    [
      try_solve
    | apply (E12 (S p) A B)
    | apply (IHE1 E2 E3 (env_comp_tail E12) p A B)
    ].
  Qed.

  Lemma env_sub_varDecl : E1 E2 p A, E1 [-] E2 |= p := A E1 |= p := A E2 |= p :!.

  Proof.
    intros E F p A EFp.
    destruct (isVarDecl_dec F p) as [[B Fp] | Fp].
    assert (EFn: E [-] F |= p :!).
    apply env_sub_ry with B; trivial.
    unfold VarD in ×.
    inversion EFn; congruence.
    split; trivial.
    apply env_sub_suby_ly with F; trivial.
  Qed.

  Lemma env_sum_varDecl : E1 E2 p A, E1 [+] E2 |= p := A
    {E1 |= p := A E2 |= p :!} + {E2 |= p := A}.

  Proof.
    intros E F p A EFp.
    destruct (isVarDecl_dec F p) as [[B Fp] | Fn].
    right.
    set (w := env_sum_ry E Fp).
    assert (A = B).
    unfold VarD in *; congruence.
    rewrite H; trivial.
    left; split; trivial.
    destruct (isVarDecl_dec E p) as [[B Ep] | En].
    set (w := env_sum_ly_rn Ep Fn).
    assert (A = B).
    unfold VarD in *; congruence.
    rewrite H; trivial.
    set (w := env_sum_ln_rn En Fn).
    inversion w; unfold VarD in EFp; congruence.
  Qed.

  Lemma env_comp_sum_comm : E1 E2, E1 [<->] E2 E1 [+] E2 = E2 [+] E1.

  Proof.
    induction E1.
    destruct E2; try_solve.
    destruct E2.
    destruct E1; try_solve.
    intro E12c.
    set (IH := IHE1 E2 (env_comp_tail E12c)).
    destruct a; destruct o; simpl; try rewrite IH; trivial.
    rewrite (E12c 0 s s0); unfold VarD; trivial.
  Qed.

  Lemma env_comp_sum : E1 E2 E3, E1 [<->] E2 E1 [<->] E3 E1 [<->] E2 [+] E3.

  Proof.
    induction E1.
    intros; apply env_comp_sym; apply env_comp_empty.
    intros E2 E3 E12 E13.
    destruct E2.
    rewrite env_sum_empty_l; trivial.
    destruct E3.
    rewrite env_sum_empty_r; trivial.
    destruct o0; simpl.
    apply env_comp_cons.
    apply IHE1; eapply env_comp_tail; eauto.
    destruct a; try_solve.
    left; rewrite (E13 0 s0 s); try_solve.
    apply env_comp_cons.
    apply IHE1; eapply env_comp_tail; eauto.
    destruct a; destruct o; try_solve.
    left; rewrite (E12 0 s s0); try_solve.
  Qed.

  Lemma env_sum_disjoint : E1 E2, (copy (length E1) None ++ E2) [+] E1 = E1 ++ E2.

  Proof.
    induction E1; simpl; intros.
    rewrite env_sum_empty_r; trivial.
    destruct a; rewrite IHE1; trivial.
  Qed.

  Lemma env_sub_sub_sum : E1 E2 E3, E1 [-] E2 [-] E3 = E1 [-] (E2 [+] E3).

  Proof.
    induction E1.
    induction E2; trivial.
    destruct E2; destruct E3; destruct a; simpl; trivial.
    rewrite env_sub_empty; trivial.
    destruct o; destruct o0; simpl; rewrite IHE1; congruence.
    destruct o0; rewrite IHE1; trivial.
  Qed.

  Lemma env_sub_disjoint : E F, ( p, E |= p :! F |= p :!) E [-] F = E.

  Proof.
    induction E; auto.
    intros F H.
    simpl.
    destruct a; destruct F; trivial.
    destruct o; trivial.
    destruct (H 0); destruct H0; try_solve.
    rewrite IHE; trivial.
    intro p; exact (H (S p)).
    rewrite IHE; trivial.
    intro p; exact (H (S p)).
  Qed.

  Lemma env_sum_sub : E1 E2 E3, ( p, E2 |= p :! E3 |= p :!)
    E1 [+] E2 [-] E3 = E1 [-] E3 [+] E2.

  Proof.
    induction E1; intros F G H.
    rewrite env_sub_disjoint; trivial.
    rewrite env_sum_empty_l; trivial.
    destruct F; destruct G; destruct a;
      repeat rewrite env_sum_empty_r;
      repeat rewrite env_sub_empty;
        simpl; trivial.
    destruct o; destruct o0; try_solve;
      try solve
        [ rewrite (IHE1 F G); [trivial | intro p; exact (H (S p))]
        | destruct (H 0); destruct H0; try_solve
        ].
    destruct o; destruct o0;
      try solve
        [ simpl; rewrite (IHE1 F G); [trivial | intro p; exact (H (S p))]
        | destruct (H 0); destruct H0; try_solve
        ].
  Qed.

  Lemma env_extend : M A, (copy (length M) None ++ A [#] EmptyEnv) [+] M = M ++ A [#] EmptyEnv.

  Proof.
    induction M; trivial.
    intro A.
    destruct a; simpl; rewrite IHM; trivial.
  Qed.

  Lemma env_comp_sum_l : M N, M [<->] N [+] M.

  Proof.
    intros.
    intros p A B Mp NMp.
    set (w := env_sum_ry N Mp).
    inversion NMp; inversion w; try_solve.
  Qed.

  Lemma env_sum_remove_double : E1 E2, E1 [+] E2 [+] E1 = E2 [+] E1.

  Proof.
    induction E1; intros.
    destruct E2; trivial.
    destruct E2; simpl.
    destruct a; rewrite env_sum_double; trivial.
    destruct o; destruct a; rewrite <- IHE1; trivial.
  Qed.

  Lemma env_sub_move : E1 E2 E3, (E1 [-] E2) [+] (E3 [-] E2) = E1 [+] E3 [-] E2.

  Proof.
    induction E1; intros.
    destruct E3; destruct E2; try destruct o; try destruct o0; trivial.
    destruct E2; destruct E3; destruct a; try destruct o; try destruct o0;
      simpl; trivial; rewrite IHE1; trivial.
  Qed.

  Definition envSubset E F := x A, E |= x := A F |= x := A.

  Lemma env_subset_empty : E, envSubset EmptyEnv E.

  Proof.
    intros; intros p A D.
    inversion D; destruct p; try_solve.
  Qed.

  Lemma env_subset_refl : E, envSubset E E.

  Proof.
    firstorder.
  Qed.

  Lemma env_subset_trans : E1 E2 E3, envSubset E1 E2 envSubset E2 E3 envSubset E1 E3.

  Proof.
    firstorder.
  Qed.

  Lemma env_subset_cons : a E E', envSubset E' E envSubset (a [#] E') (a [#] E).

  Proof.
    intros; intros p A.
    destruct p; firstorder.
  Qed.

  Lemma env_subset_cons_none : E E', envSubset E' E envSubset (None :: E') (None :: E).

  Proof.
    intros; intros p A.
    destruct p; firstorder.
  Qed.

  Lemma env_subset_cons_rev : a b E E', envSubset (a :: E') (b :: E) envSubset E' E.

  Proof.
    intros; intros p A D.
    set (w := H (S p) A D); trivial.
  Qed.

  Lemma env_subset_tail : E' E, envSubset E' E envSubset (tail E') (tail E).

  Proof.
    intros; intros p A D.
    apply varD_tail.
    apply H.
    apply varD_tail_rev; trivial.
  Qed.

  Lemma env_subset_lowered : E E' n, envSubset E E'
    envSubset (loweredEnv E n) (loweredEnv E' n).

  Proof.
    intros; unfold loweredEnv; intros w A D.
    destruct (nth_app (initialSeg E n) (finalSeg E (S n)) w D) as
      [[ED w_len] | [ED w_len]];
      rewrite initialSeg_length in w_len; try rewrite initialSeg_length in ED.
    assert (E' |= w := A).
    apply H; unfold VarD.
    apply initialSeg_prefix with n; trivial.
    assert (w < n).
    set (h := Min.le_min_l n (length E)).
    omega.
    unfold VarD; rewrite nth_app_left; autorewrite with datatypes; trivial.
    apply Min.min_case2; trivial.
    apply nth_some with (Some A); trivial.

    assert (Min.min n (length E) = n).
    apply Min.min_l.
    set (h := finalSeg_nth_idx E (S n) (w - Min.min n (length E)) ED).
    omega.
    rewrite H0 in ED.
    assert (ED': nth_error E (S n + (w - n)) = Some (Some A)).
    rewrite <- nth_finalSeg_nth; trivial.
    set (E'D := H (S n + (w - n)) A ED').
    replace (S n + (w - n)) with (S w) in E'D.
    assert (Min.min n (length E') = n).
    set (ww := nth_some E' (S w) E'D).
    apply Min.min_l.
    omega.
    unfold VarD; rewrite nth_app_right.
    autorewrite with datatypes; rewrite H1.
    replace (S n + (w - n)) with (S w); trivial.
    omega.
    autorewrite with datatypes.
    rewrite H1; rewrite <- H0; trivial.
    omega.
  Qed.

  Lemma env_subset_comp : E E', envSubset E E' E [<->] E'.

  Proof.
    intros; intros x A B D1 D2.
    set (w := H x A D1).
    inversion D2; inversion w; try_solve.
  Qed.

  Lemma env_subset_as_sum_l : E E', length E' length E envSubset E' E E = E' [+] E.

  Proof.
    induction E.
    intros; inversion H; destruct E'; try_solve.
    intros.
    destruct E'; trivial.
    assert (E = E' [+] E).
    apply (IHE E').
    simpl in H; omega.
    apply env_subset_cons_rev with o a; trivial.
    assert ( E E', E = E' a :: E = a :: E').
    intros; replace E0 with E'0; trivial.
    destruct a; destruct o; simpl; try solve [apply H2; trivial].
    elimtype False; apply varD_UD_absurd with (None::E) 0 s.
    apply H0; constructor.
    right; trivial.
  Qed.

  Lemma env_subset_as_sum_r : E E', length E' length E envSubset E' E E = E [+] E'.

  Proof.
    induction E.
    intros; inversion H; destruct E'; try_solve.
    intros.
    destruct E'; trivial.
    assert (E = E [+] E').
    apply (IHE E').
    simpl in H; omega.
    apply env_subset_cons_rev with o a; trivial.
    assert ( E E' (a: option SimpleType), E = E' a :: E = a :: E').
    intros; replace E0 with E'0; trivial.
    destruct a; destruct o; simpl; try solve [apply H2; trivial].
    replace s with s0.
    apply H2; trivial.
    assert ((Some s0 :: E') |= 0 := s0).
    constructor.
    set (w := H0 0 s0 H3); inversion w; trivial.
    elimtype False; apply varD_UD_absurd with (None::E) 0 s.
    apply H0; constructor.
    right; trivial.
  Qed.

  Lemma env_comp_on_subset : El El' Er Er' i, env_comp_on El Er i envSubset El' El
    envSubset Er' Er env_comp_on El' Er' i.

  Proof.
    firstorder.
  Qed.

  Lemma env_comp_subset : El Er El' Er', El [<->] Er envSubset El' El
    envSubset Er' Er El' [<->] Er'.

  Proof.
    firstorder.
  Qed.

  Lemma env_subset_sum_l : E El Er, El [<->] Er envSubset E El envSubset E (El [+] Er).

  Proof.
    intros; intros x A D.
    apply env_sum_ly.
    exact (H x).
    apply H0; trivial.
  Qed.

  Lemma env_subset_sum_r : E El Er, envSubset E Er envSubset E (El [+] Er).

  Proof.
    intros; intros x A D.
    apply env_sum_ry.
    apply H; trivial.
  Qed.

  Lemma env_subset_sum : El El' Er Er', El' [<->] Er' envSubset El El'
    envSubset Er Er' envSubset (El [+] Er) (El' [+] Er').

  Proof.
    intros; intros p A D.
    destruct (env_sum_varDecl El Er D) as [[Elp Ern] | Erp].
    apply env_sum_ly.
    exact (H p).
    apply H0; trivial.
    apply env_sum_ry.
    apply H1; trivial.
  Qed.

  Lemma env_subset_lsum : El Er E, envSubset El E envSubset Er E envSubset (El [+] Er) E.

  Proof.
    intros; intros p A D.
    destruct (env_sum_varDecl El Er D) as [[Elp _] | Erp].
    apply H; trivial.
    apply H0; trivial.
  Qed.

  Lemma env_subset_sum_req : El El' Er, envSubset El El' envSubset (El [+] Er) (El' [+] Er).

  Proof.
    intros; intros p A D.
    destruct (env_sum_varDecl El Er D) as [[Elp Ern] | Erp].
    apply env_sum_ly_rn; trivial.
    apply H; trivial.
    apply env_sum_ry; trivial.
  Qed.

  Lemma env_subset_sum_leq : El Er Er', El [<->] Er' envSubset Er Er'
    envSubset (El [+] Er) (El [+] Er').

  Proof.
    intros.
    apply env_subset_sum; trivial.
    apply env_subset_refl.
  Qed.

  Lemma varDecl_single : x A B w, (copy x None ++ A[#]EmptyEnv) |= w := B A = B x = w.

  Proof.
    intros.
    destruct (lt_eq_lt_dec x w) as [[xw | xw] | xw].
    elimtype False; apply (varD_UD_absurd H).
    left; apply nth_beyond; autorewrite with datatypes using simpl; try omega.
    rewrite xw; split; trivial.
    rewrite xw in H.
    unfold VarD in H.
    rewrite (@nth_app_right (option SimpleType) (copy w None) (A[#]EmptyEnv) w) in H;
      autorewrite with datatypes using auto.
    rewrite copy_length in H.
    replace (w - w) with 0 in H; [inversion H; trivial | omega].
    elimtype False; apply (varD_UD_absurd H).
    right; rewrite nth_app_left; autorewrite with datatypes using trivial.
  Qed.

  Lemma env_subset_single : A x E, E |= x := A envSubset (copy x None ++ A [#] EmptyEnv) E.

  Proof.
    intros; intros w B D.
    destruct (varDecl_single x A D).
    rewrite <- H0; rewrite <- H1; trivial.
  Qed.

  Lemma typing_ext_env : E E' Pt A, envSubset E' E E' |- Pt := A E |- Pt := A.

  Proof.
    intros E E' Pt; generalize Pt E E'; clear E E' Pt.
    induction Pt.
    intros E E' A envC Ex.
    constructor.
    inversion Ex.
    apply envC; trivial.
    inversion 2; constructor.
    intros E E' a EE' TypAbs.
    inversion TypAbs.
    constructor.
    apply IHPt with (A [#] E'); trivial.
    apply env_subset_cons; trivial.
    intros E E' A EE' TypApp.
    inversion TypApp.
    constructor 4 with A0.
    apply IHPt1 with E'; trivial.
    apply IHPt2 with E'; trivial.
  Qed.

  Lemma env_subset_dropSuffix_length : E E', envSubset E' E
    length (dropEmptySuffix E') length E.

  Proof.
    intros.
    destruct (dropSuffix_last E') as [E'e | [A E'A]].
    rewrite E'e; omega.
    set (E'd := dropSuffix_decl_rev E' E'A).
    set (w := H (pred (length (dropEmptySuffix E'))) A E'd).
    assert (Ed: nth_error E (pred (length (dropEmptySuffix E'))) None).
    inversion w; try_solve.
    set (x := proj1 (nth_in E (pred (length (dropEmptySuffix E')))) Ed).
    omega.
  Qed.

  Lemma varUD_hole : E i, (initialSeg E i ++ None :: finalSeg E (S i)) |= i :!.

  Proof.
    intros.
    destruct (lt_eq_lt_dec (length E) i) as [[iE | iE] | iE].
    left.
    apply nth_beyond.
    autorewrite with datatypes using simpl; try omega.
    right.
    rewrite nth_app_right; autorewrite with datatypes; rewrite iE.
    apply Min.min_case2; replace (i - i) with 0; solve [trivial | omega].
    auto with arith.
    right.
    rewrite nth_app_right; autorewrite with datatypes; auto with arith.
    replace (i - Min.min i (length E)) with 0; trivial.
    rewrite Min.min_l; omega.
  Qed.

  Lemma varD_hole : E i j A, i j E |= j := A
    (initialSeg E i ++ None :: finalSeg E (S i)) |= j := A.

  Proof.
    unfold VarD; intros.
    set (iE := nth_some E j H0).
    destruct (lt_eq_lt_dec i j) as [[ij | ij] | ij].
    rewrite nth_app_right; autorewrite with datatypes; rewrite Min.min_l; try omega.
    replace (j - i) with (S (j - S i)); try omega.
    simpl; rewrite nth_finalSeg_nth.
    replace (S i + (j - S i)) with j; [trivial | omega].
    elimtype False; omega.
    rewrite nth_app_left; autorewrite with datatypes; trivial.
    apply Min.min_case2; trivial.
  Qed.

  Lemma varD_hole_env_length : E i j A,
    (initialSeg E i ++ None :: finalSeg E (S i)) |= j := A length E > j.

  Proof.
    unfold VarD; intros.
    destruct (le_lt_dec (Min.min i (length E)) j).
    rewrite (@nth_app_right (option SimpleType) (initialSeg E i)
      (None :: finalSeg E (S i)) j) in H.
    rewrite initialSeg_length in H.
    destruct (le_gt_dec (length E) i); trivial.
    rewrite Min.min_r in H; trivial.
    rewrite (finalSeg_empty E (k := S i)) in H.
    destruct (j - length E); try destruct n; try_solve.
    omega.
    assert ( (A : Type) l (a: A) i,
      i > 0 nth_error (a::l) i = nth_error l (pred i)).
    intros; destruct i0; try_solve.
    elimtype False; omega.
    rewrite H0 in H.
    set (w := finalSeg_nth_idx E (S i) (pred (j - Min.min i (length E))) H).
    rewrite Min.min_l in w; omega.
    destruct (j - Min.min i (length E)); try_solve.
    omega.
    autorewrite with datatypes; trivial.
    rewrite nth_app_left in H; autorewrite with datatypes; trivial.
    set (w := Min.le_min_r i (length E)); omega.
  Qed.

  Lemma varD_hole_env_length_j_ge_i : E i j A, j i
    (initialSeg E i ++ None :: finalSeg E (S i)) |= j := A length E > i.

  Proof.
    intros; set (w := varD_hole_env_length E i H0); omega.
  Qed.

  Lemma varD_hole_rev : E i j A, i j
    (initialSeg E i ++ None :: finalSeg E (S i)) |= j := A E |= j := A.

  Proof.
    unfold VarD; intros.
    destruct (lt_eq_lt_dec i j) as [[ij | ij] | ij].
    assert (length E > i).
    apply varD_hole_env_length_j_ge_i with j A; auto with arith.
    rewrite nth_app_right in H0; autorewrite with datatypes.
    replace (length (initialSeg E i)) with i in H0.
    assert ( (A : Type) l (a: A) i,
      i > 0 nth_error (a::l) i = nth_error l (pred i)).
    intros; destruct i0; try_solve.
    elimtype False; omega.
    rewrite (H2 (option SimpleType) (finalSeg E (S i)) None (j - i)) in H0.
    replace j with (S i + pred (j - i)).
    rewrite <- nth_finalSeg_nth; trivial.
    omega.
    omega.
    autorewrite with datatypes; rewrite Min.min_l; trivial; omega.
    rewrite Min.min_l; trivial; omega.
    elimtype False; omega.
    rewrite nth_app_left in H0; autorewrite with datatypes.
    apply initialSeg_prefix with i; trivial.
    assert (Min.min i (length E) > j).
    rewrite Min.min_comm.
    apply Min.min_case2; trivial.
    apply varD_hole_env_length with i A; trivial.
    omega.
  Qed.

  Lemma env_hole_subset : E i, envSubset (initialSeg E i ++ None :: finalSeg E (S i)) E.

  Proof.
    intros; intros j A D.
    destruct (eq_nat_dec i j).
    rewrite e in D.
    assert (length (initialSeg E j) = j).
    autorewrite with datatypes.
    apply Min.min_l.
    assert (length E > j).
    apply varD_hole_env_length_j_ge_i with j A; trivial.
    omega.
    omega.
    unfold VarD in D.
    rewrite nth_app_right in D.
    replace (j - length (initialSeg E j)) with 0 in D.
    inversion D.
    rewrite H; omega.
    omega.
    apply varD_hole_rev with i; trivial.
  Qed.

  Definition env_eq E1 E2 := envSubset E1 E2 envSubset E2 E1.
  Notation "E1 [=] E2" := (env_eq E1 E2) (at level 70).

  Lemma env_eq_refl : E, E [=] E.

  Proof.
    firstorder.
  Qed.

  Lemma env_eq_sym : E E', E [=] E' E' [=] E.

  Proof.
    firstorder.
  Qed.

  Lemma env_eq_trans : E1 E2 E3, E1 [=] E2 E2 [=] E3 E1 [=] E3.

  Proof.
    firstorder.
  Qed.

  Lemma env_eq_some_none_absurd : E1 E2 A, Some A :: E1 [=] None :: E2 False.

  Proof.
    intros.
    set (w := (proj1 H) 0 A); compute in w.
    set (p := w (refl_equal (Some (Some A)))); inversion p.
  Qed.

  Lemma env_eq_some_nil_absurd : E1 A, Some A :: E1 [=] EmptyEnv False.

  Proof.
    intros.
    set (w := (proj1 H) 0 A); compute in w.
    set (p := w (refl_equal (Some (Some A)))); inversion p.
  Qed.

  Lemma env_eq_tail : E1 E2, E1 [=] E2 tail E1 [=] tail E2.

  Proof.
    intros.
    split; destruct H.
    apply env_subset_tail; trivial.
    apply env_subset_tail; trivial.
  Qed.

  Lemma env_eq_cons : a b E E', a = b E [=] E' a :: E [=] b :: E'.

  Proof.
    intros; split; rewrite H.
    inversion H0.
    destruct b.
    fold (s [#] E); fold (s [#] E'); apply env_subset_cons; trivial.
    apply env_subset_cons_none; trivial.
    inversion H0.
    destruct b.
    fold (s [#] E); fold (s [#] E'); apply env_subset_cons; trivial.
    apply env_subset_cons_none; trivial.
  Qed.

  Lemma env_eq_cons_inv : a b E E', (a :: E) [=] (b :: E') E [=] E'.

  Proof.
    intros.
    change E with (tail (a :: E)).
    change E' with (tail (b :: E')).
    apply env_eq_tail; trivial.
  Qed.

  Lemma env_eq_cons_inv_hd : a b E E', (a :: E) [=] (b :: E') a = b.

  Proof.
    intros.
    destruct a; destruct b; trivial.
    set (hint := (proj1 H) 0 s).
    firstorder; inversion H0; trivial.
    set (hint := (proj1 H) 0 s).
    firstorder; inversion H0; trivial.
    set (hint := (proj2 H) 0 s).
    firstorder; inversion H0; trivial.
  Qed.

  Lemma env_eq_comp : E1 E2, E1 [=] E2 E1 [<->] E2.

  Proof.
    intros.
    intros p A B D1 D2.
    set (D2' := proj1 H p A D1).
    inversion D2; inversion D2'; try_solve.
  Qed.

  Lemma env_eq_empty_none_empty : EmptyEnv [=] None :: EmptyEnv.

  Proof.
    intros; split.
    apply env_subset_empty.
    intros w A wA.
    destruct w; try_solve.
    destruct w; try_solve.
  Qed.

  Lemma env_eq_empty_cons : E, None :: E [=] EmptyEnv E [=] EmptyEnv.

  Proof.
    intros.
    split.
    intros p A Ep.
    set (w := proj1 H (S p) A).
    unfold VarD in w; simpl in w.
    set (ww := w Ep); try_solve.
    apply env_subset_empty.
  Qed.

  Lemma env_eq_empty_cons_rev : E, E [=] EmptyEnv None :: E [=] EmptyEnv.

  Proof.
    intros.
    split.
    intros p A Ep.
    destruct p; try_solve.
    set (w := proj1 H p A Ep); destruct p; try_solve.
    apply env_subset_empty.
  Qed.

  Lemma env_eq_empty_tail : E n, E [=] E ++ copy n None.

  Proof.
    intros; split; intros p A D.
    unfold VarD; rewrite nth_app_left; trivial.
    apply nth_some with (Some A); trivial.
    destruct (nth_app E (copy n None) p D) as [[Dl _] | [Dr _]].
    trivial.
    destruct (le_gt_dec n (p - length E)).
    assert (p - length E length (copy n (None (A:=SimpleType)))).
    autorewrite with datatypes using omega.
    set (w := nth_beyond (copy n None) H).
    try_solve.
    set (w := nth_copy_in (None (A:=SimpleType)) g).
    try_solve.
  Qed.

  Lemma env_eq_empty : E, emptyEnv E E [=] EmptyEnv.

  Proof.
    intros; split.
    intros w A wA.
    elimtype False; apply (varD_UD_absurd wA).
    apply H.
    apply env_subset_empty.
  Qed.

  Lemma env_eq_empty_empty : E E', emptyEnv E emptyEnv E' E [=] E'.

  Proof.
    intros; split; intros p A D.
    destruct (H p); inversion D; try_solve.
    destruct (H0 p); inversion D; try_solve.
  Qed.

  Lemma env_eq_empty_subset : E E', E [=] EmptyEnv envSubset E E'.

  Proof.
    intros.
    intros w A wA.
    set (x := (proj1 H) w A wA).
    destruct w; try_solve.
  Qed.

  Lemma env_eq_def : E1 E2, ( i A, E1 |= i := A E2 |= i := A) E1 [=] E2.

  Proof.
    induction E1.
    induction E2.
    intros; apply env_eq_refl.
    intros.
    destruct a.
    set (w := proj2 (H 0 s)); compute in w.
    set (p := w (refl_equal (Some (Some s)))); try_solve.
    apply env_eq_sym; apply env_eq_empty_cons_rev; apply env_eq_sym.
    apply IHE2; intros.
    split.
    destruct i; try_solve.
    intro D.
    set (w := proj2 (H (S i) A) D); try_solve.
    destruct E2; intros.
    apply env_eq_empty.
    intro j.
    destruct (isVarDecl_dec (a :: E1) j) as [[B D] | X]; trivial.
    set (w := proj1 (H j B) D).
    destruct j; try_solve.
    apply env_eq_cons.
    destruct a; destruct o; trivial.
    set (w := proj1 (H 0 s)); compute in w.
    set (p := w (refl_equal (Some (Some s)))); inversion p; trivial.
    set (w := proj1 (H 0 s)); compute in w.
    set (p := w (refl_equal (Some (Some s)))); inversion p.
    set (w := proj2 (H 0 s)); compute in w.
    set (p := w (refl_equal (Some (Some s)))); inversion p.
    apply IHE1.
    intros.
    set (hint := H (S i) A); trivial.
  Qed.

  Lemma env_equiv_eq : E E', E [=] E' length E = length E' E = E'.

  Proof.
    induction E; destruct E'; intros; try_solve.
    rewrite (IHE E').
    rewrite (env_eq_cons_inv_hd H); trivial.
    apply env_eq_cons_inv with a o; trivial.
    try_solve.
  Qed.

  Hint Immediate env_eq_refl env_eq_sym env_eq_empty_none_empty : terms.

  Definition EnvEqSetoidTheory :=
    Build_Setoid_Theory _ env_eq env_eq_refl env_eq_sym env_eq_trans.

  Add Setoid Env env_eq EnvEqSetoidTheory as EnvSetoid.

  Add Morphism envSubset
    with signature env_eq ==> env_eq ==> iff
      as envSubset_morph.

  Proof.
    firstorder.
  Qed.

  Add Morphism loweredEnv with signature env_eq ==> eq ==> env_eq as loweredEnv_morph.

  Proof.
    intros.
    destruct H; split.
    apply env_subset_lowered; trivial.
    apply env_subset_lowered; trivial.
  Qed.

  Add Morphism env_comp
    with signature env_eq ==> env_eq ==> iff
      as env_comp_morph.

  Proof.
    firstorder.
  Qed.

  Add Morphism VarD with signature env_eq ==> eq ==> eq ==> iff as VarD_morph.

  Proof.
    firstorder.
  Qed.

  Add Morphism VarUD with signature env_eq ==> eq ==> iff as VarUD_morph.

  Proof.
    intros e e0 H n; split; intros.
    destruct (isVarDecl_dec e0 n) as [[A e0n] | e0n]; trivial.
    set (en := proj2 H n A e0n).
    elimtype False; apply varD_UD_absurd with e n A; trivial.
    destruct (isVarDecl_dec e n) as [[A e0n] | e0n]; trivial.
    set (en := proj1 H n A e0n).
    elimtype False; apply varD_UD_absurd with e0 n A; trivial.
  Qed.

  Lemma env_compose_morph_aux0 : El Er El' Er',
    ( El El' Er', El [=] El' Er [=] Er' envSubset (El [+] Er) (El' [+] Er'))
    El [=] El' None :: Er [=] Er' envSubset (El [+] (None :: Er)) (El' [+] Er').

  Proof.
    set (env_subset_cons' := env_subset_cons); unfold decl in env_subset_cons'.
    intros.
    destruct Er'; destruct El; destruct El';
      try destruct o; try destruct o0; try destruct o1;
      simpl; try solve
      [ elimtype False; eapply env_eq_some_nil_absurd; eauto with terms
      | elimtype False; eapply env_eq_some_none_absurd; eauto with terms
      | destruct H1; trivial
      ]; try apply env_subset_cons_none; trivial.
    rewrite (env_eq_empty_cons H1).
    rewrite <- (env_eq_empty_cons (env_eq_sym H0)).
    apply env_subset_refl.
    assert (None :: El[+]Er [=] EmptyEnv).
    apply env_eq_empty_cons_rev.
    split.
    change EmptyEnv with (EmptyEnv [+] EmptyEnv).
    apply H; apply env_eq_empty_cons; trivial.
    apply env_subset_empty.
    fold EmptyEnv; rewrite <- H2; apply env_subset_refl.
    rewrite (env_eq_cons_inv_hd H0).
    apply env_subset_cons'.
    replace El' with (El' [+] EmptyEnv).
    apply H; trivial.
    apply (env_eq_cons_inv H0).
    apply env_eq_empty_cons; trivial.
    rewrite env_sum_empty_r; trivial.
    replace El' with (El' [+] EmptyEnv).
    apply H; trivial.
    apply (env_eq_cons_inv H0).
    apply env_eq_empty_cons; trivial.
    rewrite env_sum_empty_r; trivial.
    apply env_subset_sum_r; destruct (env_eq_cons_inv H1); trivial.
    replace Er' with (nil [+] Er').
    apply H; trivial.
    change (nil (A:=option SimpleType)) with (tail (None (A:=SimpleType) :: nil)) in H0.
    set (w := env_eq_tail H0); trivial.
    apply (env_eq_cons_inv H1).
    rewrite env_sum_empty_l; trivial.
    rewrite (env_eq_cons_inv_hd H0).
    apply env_subset_cons'.
    apply H.
    destruct (env_eq_cons_inv H0); firstorder.
    destruct (env_eq_cons_inv H1); firstorder.
    apply H.
    destruct (env_eq_cons_inv H0); firstorder.
    destruct (env_eq_cons_inv H1); firstorder.
  Qed.

  Lemma env_compose_morph_aux : El Er El' Er', El [=] El' Er [=] Er'
    envSubset (El [+] Er) (El' [+] Er').

  Proof.
    set (env_subset_cons' := env_subset_cons); unfold decl in env_subset_cons'.
    intros El Er; generalize El; induction Er; clear El.
    intros.
    rewrite env_sum_empty_r.
    apply env_subset_sum_l.
    rewrite <- H0.
    apply env_comp_empty.
    destruct H; trivial.

    intros; destruct a; destruct Er'.
    elimtype False; eapply env_eq_some_nil_absurd; eauto.
    destruct o.
    rewrite (env_eq_cons_inv_hd H0); rewrite (env_eq_cons_inv_hd H0) in H0.
    destruct (env_eq_cons_inv H0).
    destruct El; destruct El'; try destruct o; try destruct o0;
      simpl; apply env_subset_cons'; try solve
        [ elimtype False; eapply env_eq_some_nil_absurd; eauto with terms
        | apply IHEr; eapply env_eq_cons_inv; eauto
        | elimtype False; eapply env_eq_some_none_absurd; eauto with terms
        | trivial
        ].
    apply env_subset_sum_r; destruct (env_eq_cons_inv H0); trivial.
    replace Er' with (nil [+] Er').
    apply IHEr; trivial.
    change (nil (A:=option SimpleType)) with (tail (None (A:=SimpleType) :: nil)) in H.
    set (w := env_eq_tail H); trivial.
    firstorder.
    rewrite env_sum_empty_l; trivial.
    elimtype False; eapply env_eq_some_none_absurd; eauto with terms.
    apply env_compose_morph_aux0; trivial.
    apply env_compose_morph_aux0; trivial.
  Qed.

  Add Morphism env_compose
    with signature env_eq ==> env_eq ==> env_eq
      as env_compose_morph.

  Proof.
    intros; split.
    apply env_compose_morph_aux; trivial.
    apply env_compose_morph_aux; auto with terms.
  Qed.

  Lemma env_eq_sum : El El' Er Er', El [=] El' Er [=] Er' El [+] Er [=] El' [+] Er'.

  Proof.
    intros.
    rewrite H; rewrite H0.
    apply env_eq_refl.
  Qed.

  Lemma env_eq_subset_sum_l : E E', envSubset E' E E [=] E' [+] E.

  Proof.
    intros; split.
    apply env_subset_sum_r; apply env_subset_refl.
    intros w A D.
    destruct (env_sum_varDecl E' E D) as [[E'w _] | Ew]; trivial.
    apply H; trivial.
  Qed.

  Lemma env_subset_dropSuffix_eq : E, env_eq (dropEmptySuffix E) E.

  Proof.
    split; intros; intros p A D.
    apply dropSuffix_decl_rev; trivial.
    apply dropSuffix_decl; trivial.
  Qed.

  Lemma typing_drop_suffix : E Pt A, E |- Pt := A dropEmptySuffix E |- Pt := A.

  Proof.
    intros.
    apply typing_ext_env with E; trivial.
    apply (proj2 (env_subset_dropSuffix_eq E)).
  Qed.

  Lemma liftedEnv_empty : E n k, emptyEnv E liftedEnv n E k [=] EmptyEnv.

  Proof.
    intros; split; intros p A D.
    unfold liftedEnv in D.
    destruct (nth_app (initialSeg E k) (copy n None ++ finalSeg E k) p D)
      as [[dec cond] | [dec cond]].
    set (w := initialSeg_prefix E k p dec).
    destruct (H p); try_solve.
    destruct (nth_app (copy n None) (finalSeg E k) (p - length (initialSeg E k)) dec)
      as [[dec' cond'] | [dec' cond']].
    destruct (emptyEnv_copyNone n (p - length (initialSeg E k))); try_solve.
    set (w := nth_finalSeg_nth E k (p - length (initialSeg E k) - length (copy n (A:=option SimpleType) None))).
    destruct (H (k + (p - length (initialSeg E k) - length (copy n (None (A:=SimpleType))))));
      try_solve.
    destruct p; try_solve.
  Qed.

  Lemma liftedEnv_distr_sum_equiv : El Er n k,
    liftedEnv n (El [+] Er) k [=] liftedEnv n El k [+] liftedEnv n Er k.

  Proof.
    unfold liftedEnv; intros; split; intros p A D.
    destruct (nth_app _ _ _ D) as [[Dl cond] | [Dr cond]].
    assert (p < k).
    rewrite initialSeg_length in cond.
    set (w := Min.le_min_l k (length (El[+]Er))); omega.
    set (ElErD := initialSeg_prefix (El[+]Er) k p Dl).
    destruct (env_sum_varDecl El Er ElErD) as [[ElD ErND] | ErD].
    apply env_sum_ly_rn.
    apply copy_split_l_varD; trivial.
    apply copy_split_l_varUD; trivial.
    apply env_sum_ry.
    apply copy_split_l_varD; trivial.
    destruct (nth_app _ _ _ Dr) as [[Drl cond'] | [Drr cond']]; rewrite copy_length in cond'.
    rewrite (@nth_copy_in (option SimpleType) n None (p - length (initialSeg (El[+]Er) k)))
      in Drl; try_solve.
    rewrite copy_length in Drr.
    set (ElErD := Drr); rewrite nth_finalSeg_nth in ElErD.
    assert (length (initialSeg (El[+]Er) k) = k).
    autorewrite with datatypes.
    destruct (le_gt_dec (length (El[+]Er)) k).
    rewrite finalSeg_empty in Drr; trivial.
    destruct (p - length (initialSeg (El[+]Er) k) - n); try_solve.
    apply Min.min_l; omega.
    destruct (env_sum_varDecl El Er ElErD) as [[ElD ErND] | ErD].
    apply env_sum_ly_rn.
    apply copy_split_r_varD with (k + (p - length (initialSeg (El[+]Er) k) - n)); trivial; try omega.
    apply copy_split_r_varUD with (k + (p - length (initialSeg (El[+]Er) k) - n)); trivial; try omega.
    apply env_sum_ry.
    apply copy_split_r_varD with (k + (p - length (initialSeg (El[+]Er) k) - n)); trivial; try omega.

    destruct (env_sum_varDecl _ _ D) as [[DL DRN] | DR].
    destruct (nth_app _ _ _ DL) as [[DLl cond] | [DLr cond]].
    assert (p < k).
    rewrite initialSeg_length in cond.
    set (w := Min.le_min_l k (length El)).
    omega.
    apply copy_split_l_varD; trivial.
    apply env_sum_ly_rn.
    rewrite initialSeg_nth in DLl; trivial.
    apply copy_split_l_rev_varUD with k n; trivial.
    destruct (nth_app _ _ _ DLr) as [[DLrl cond'] | [DLrr cond']];
      rewrite copy_length in cond'.
    rewrite (@nth_copy_in (option SimpleType) n None (p - length (initialSeg El k)))
      in DLrl; try_solve.
    assert (length (initialSeg El k) = k).
    autorewrite with datatypes.
    destruct (le_gt_dec (length El) k).
    rewrite finalSeg_empty in DLrr; trivial.
    rewrite copy_length in DLrr.
    destruct (p - length (initialSeg El k) - n); try_solve.
    apply Min.min_l; omega.
    rewrite copy_length in DLrr.
    set (w := DLrr); rewrite nth_finalSeg_nth in w.
    apply copy_split_r_varD with (k + (p - length (initialSeg El k) - n)); trivial; try omega.
    apply env_sum_ly_rn; trivial.
    apply copy_split_r_rev_varUD with k n p; trivial; try omega.
    destruct (nth_app _ _ _ DR) as [[DRl cond] | [DRr cond]].
    apply copy_split_l_varD; trivial.
    rewrite initialSeg_length in cond.
    set (w := Min.le_min_l k (length Er)); omega.
    apply env_sum_ry.
    unfold VarD; apply initialSeg_prefix with k; trivial.
    destruct (nth_app _ _ _ DRr) as [[DRrl cond'] | [DRrr cond']];
      rewrite copy_length in cond'.
    rewrite (@nth_copy_in (option SimpleType) n None (p - length (initialSeg Er k)))
      in DRrl; try_solve.
    rewrite copy_length in DRrr.
    set (w := DRrr); rewrite nth_finalSeg_nth in w.
    assert (length (initialSeg Er k) = k).
    autorewrite with datatypes.
    destruct (le_gt_dec (length Er) k).
    clear w.
    rewrite finalSeg_empty in DRrr; trivial.
    destruct (p - length (initialSeg Er k) - n); try_solve.
    apply Min.min_l; omega.
    apply copy_split_r_varD with (k + (p - length (initialSeg Er k) - n)); trivial; try omega.
    apply env_sum_ry; trivial.
  Qed.

  Lemma liftedEnv_distr_sum : El Er n k,
    liftedEnv n (El [+] Er) k = liftedEnv n El k [+] liftedEnv n Er k.

  Proof.
    intros.
    apply env_equiv_eq.
    apply liftedEnv_distr_sum_equiv.
    unfold liftedEnv.
    autorewrite with datatypes.
    repeat rewrite env_sum_length.
    autorewrite with datatypes.
    destruct (le_gt_dec (length El) (length Er)).

    rewrite (@Max.max_r (length El) (length Er)); trivial.
    destruct (le_gt_dec k (length Er)).
    rewrite (Min.min_l k (length Er)); trivial.
    destruct (le_gt_dec k (length El)).
    rewrite (Min.min_l k (length El)); trivial.
    rewrite Max.max_r; trivial.
    omega.
    rewrite Min.min_r; try omega.
    rewrite Max.max_r; omega.
    rewrite (Min.min_r k (length Er)); try omega.
    destruct (le_gt_dec k (length El)).
    elimtype False; omega.
    rewrite (Min.min_r k (length El)); try omega.
    rewrite Max.max_r; trivial.
    omega.

    rewrite (@Max.max_l (length El) (length Er)); trivial.
    destruct (le_gt_dec k (length El)).
    rewrite (Min.min_l k (length El)); trivial.
    destruct (le_gt_dec k (length Er)).
    rewrite (Min.min_l k (length Er)); trivial.
    rewrite Max.max_l; trivial.
    omega.
    rewrite Min.min_r; try omega.
    rewrite Max.max_l; omega.
    rewrite (Min.min_r k (length El)); try omega.
    destruct (le_gt_dec k (length Er)).
    elimtype False; omega.
    rewrite (Min.min_r k (length Er)); try omega.
    rewrite Max.max_l; trivial.
    omega.
    omega.
  Qed.

  Lemma loweredEnv_distr_sum_equiv : El Er n,
    loweredEnv (El [+] Er) n [=] loweredEnv El n [+] loweredEnv Er n.

  Proof.
    unfold loweredEnv; intros; split; intros p A D.
    destruct (nth_app _ _ _ D) as [[Dl cond] | [Dr cond]].
    assert (p < n).
    rewrite initialSeg_length in cond.
    set (w := Min.le_min_l n (length (El[+]Er))); omega.
    set (ElErD := initialSeg_prefix (El[+]Er) n p Dl).
    destruct (env_sum_varDecl El Er ElErD) as [[ElD ErND] | ErD].
    apply env_sum_ly_rn.
    apply hole_l_varD; trivial.
    apply hole_l_varUD; trivial.
    apply env_sum_ry.
    apply hole_l_varD; trivial.
    set (ElErD := Dr); rewrite nth_finalSeg_nth in ElErD.
    assert (length (initialSeg (El[+]Er) n) = n).
    autorewrite with datatypes.
    destruct (le_gt_dec (length (El[+]Er)) n).
    clear ElErD.
    rewrite finalSeg_empty in Dr; trivial.
    destruct (p - length (initialSeg (El[+]Er) n)); try_solve.
    omega.
    apply Min.min_l; omega.
    destruct (env_sum_varDecl El Er ElErD) as [[ElD ErND] | ErD].
    apply env_sum_ly_rn.
    apply hole_r_varD with (S n + (p - length (initialSeg (El[+]Er) n)));
      trivial; try omega.
    apply hole_r_varUD with (S n + (p - length (initialSeg (El[+]Er) n)));
      trivial; try omega.
    apply env_sum_ry.
    apply hole_r_varD with (S n + (p - length (initialSeg (El[+]Er) n)));
      trivial; try omega.

    destruct (env_sum_varDecl _ _ D) as [[DL DRN] | DR].
    destruct (nth_app _ _ _ DL) as [[DLl cond] | [DLr cond]].
    assert (p < n).
    rewrite initialSeg_length in cond.
    set (w := Min.le_min_l n (length El)).
    omega.
    apply hole_l_varD; trivial.
    apply env_sum_ly_rn.
    rewrite initialSeg_nth in DLl; trivial.
    apply hole_l_rev_varUD with n; trivial.
    assert (length (initialSeg El n) = n).
    autorewrite with datatypes.
    destruct (le_gt_dec (length El) n).
    rewrite finalSeg_empty in DLr; trivial.
    destruct (p - length (initialSeg El n)); try_solve.
    omega.
    apply Min.min_l; omega.
    rewrite nth_finalSeg_nth in DLr.
    apply hole_r_varD with (S n + (p - length (initialSeg El n))); trivial; try omega.
    apply env_sum_ly_rn; trivial.
    apply hole_r_rev_varUD with n p; trivial; try omega.
    destruct (nth_app _ _ _ DR) as [[DRl cond] | [DRr cond]].
    apply hole_l_varD.
    rewrite initialSeg_length in cond.
    set (w := Min.le_min_l n (length Er)); omega.
    apply env_sum_ry.
    unfold VarD; apply initialSeg_prefix with n; trivial.
    set (w := DRr); rewrite nth_finalSeg_nth in w.
    assert (length (initialSeg Er n) = n).
    autorewrite with datatypes.
    destruct (le_gt_dec (length Er) n).
    rewrite finalSeg_empty in DRr; trivial.
    destruct (p - length (initialSeg Er n)); try_solve.
    omega.
    apply Min.min_l; omega.
    apply hole_r_varD with (S n + (p - length (initialSeg Er n))); trivial; try omega.
    apply env_sum_ry; trivial.
  Qed.

  Lemma loweredEnv_distr_sum : El Er n,
    loweredEnv (El [+] Er) n = loweredEnv El n [+] loweredEnv Er n.

  Proof.
    intros.
    apply env_equiv_eq.
    apply loweredEnv_distr_sum_equiv.
    unfold loweredEnv.
    autorewrite with datatypes.
    repeat rewrite env_sum_length.
    autorewrite with datatypes.
    destruct (le_gt_dec (length El) (length Er)).

    rewrite (@Max.max_r (length El) (length Er)); trivial.
    destruct (le_gt_dec n (length Er)).
    rewrite (Min.min_l n (length Er)); trivial.
    destruct (le_gt_dec n (length El)).
    rewrite (Min.min_l n (length El)); trivial.
    rewrite Max.max_r; trivial.
    omega.
    rewrite Min.min_r; try omega.
    rewrite Max.max_r; omega.
    rewrite (Min.min_r n (length Er)); try omega.
    destruct (le_gt_dec n (length El)).
    elimtype False; omega.
    rewrite (Min.min_r n (length El)); try omega.
    rewrite Max.max_r; trivial.
    omega.

    rewrite (@Max.max_l (length El) (length Er)); trivial.
    destruct (le_gt_dec n (length El)).
    rewrite (Min.min_l n (length El)); trivial.
    destruct (le_gt_dec n (length Er)).
    rewrite (Min.min_l n (length Er)); trivial.
    rewrite Max.max_l; trivial.
    omega.
    rewrite Min.min_r; try omega.
    rewrite Max.max_l; omega.
    rewrite (Min.min_r n (length El)); try omega.
    destruct (le_gt_dec n (length Er)).
    elimtype False; omega.
    rewrite (Min.min_r n (length Er)); try omega.
    rewrite Max.max_l; trivial.
    omega.
    omega.
  Qed.

  Lemma lift_tail : E n k, liftedEnv n (tail E) k [=] tail (liftedEnv n E (S k)).

  Proof.
    induction E; intros.
    unfold liftedEnv, finalSeg; destruct k; simpl;
      repeat rewrite <- app_nil_end.
    apply env_eq_empty_empty.
    apply emptyEnv_copyNone.
    apply tail_emptyEnv.
    apply emptyEnv_copyNone.
    apply env_eq_empty_empty.
    apply emptyEnv_copyNone.
    apply tail_emptyEnv.
    apply emptyEnv_copyNone.
    unfold liftedEnv; simpl.
    change (finalSeg (a::E) (S k)) with (finalSeg E k).
    apply env_eq_refl.
  Qed.

  Lemma lower_tail : E n, loweredEnv (tail E) n = tail (loweredEnv E (S n)).

  Proof.
    induction E; intros.
    destruct n; trivial.
    unfold loweredEnv; simpl.
    change (finalSeg (a::E) (S (S n))) with (finalSeg E (S n)); trivial.
  Qed.

End TermsEnv.