Require Import Coq.Arith.Wf_nat.
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Program.Equality.

Require Export Metatheory.
Require Export LibLNgen.

Require Export k_ott.

(** NOTE: Auxiliary theorems are hidden in generated documentation.
    In general, there is a [_rec] version of every lemma involving
    [open] and [close]. *)

(* *********************************************************************** *)
(** * Induction principles for nonterminals *)

Scheme t_ind' := Induction for t Sort Prop.

Definition t_mutind :=
  fun H1 H2 H3 H4 =>
  t_ind' H1 H2 H3 H4.

Scheme t_rec' := Induction for t Sort Set.

Definition t_mutrec :=
  fun H1 H2 H3 H4 =>
  t_rec' H1 H2 H3 H4.

Scheme p_ind' := Induction for p Sort Prop.

Definition p_mutind :=
  fun H1 H2 H3 =>
  p_ind' H1 H2 H3.

Scheme p_rec' := Induction for p Sort Set.

Definition p_mutrec :=
  fun H1 H2 H3 =>
  p_rec' H1 H2 H3.

Scheme e_ind' := Induction for e Sort Prop
  with u_ind' := Induction for u Sort Prop.

Definition e_u_mutind :=
  fun H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12 =>
  (conj (e_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12)
  (u_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12)).

Scheme e_rec' := Induction for e Sort Set
  with u_rec' := Induction for u Sort Set.

Definition e_u_mutrec :=
  fun H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12 =>
  (pair (e_rec' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12)
  (u_rec' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12)).

(* *********************************************************************** *)
(** * Close *)

Fixpoint close_e_wrt_u_rec (n1 : nat) (x1 : x) (e1 : e) {struct e1} : e :=
  match e1 with
    | e_ann u1 t1 => e_ann (close_u_wrt_u_rec n1 x1 u1) t1
  end

with close_u_wrt_u_rec (n1 : nat) (x1 : x) (u1 : u) {struct u1} : u :=
  match u1 with
    | u_var_f x2 => if (x1 == x2) then (u_var_b n1) else (u_var_f x2)
    | u_var_b n2 => if (lt_ge_dec n2 n1) then (u_var_b n2) else (u_var_b (S n2))
    | u_int i1 => u_int i1
    | u_lam t1 e1 => u_lam t1 (close_e_wrt_u_rec (S n1) x1 e1)
    | u_app e1 e2 => u_app (close_e_wrt_u_rec n1 x1 e1) (close_e_wrt_u_rec n1 x1 e2)
    | u_prim e1 p1 e2 => u_prim (close_e_wrt_u_rec n1 x1 e1) p1 (close_e_wrt_u_rec n1 x1 e2)
    | u_if0 e1 e2 e3 => u_if0 (close_e_wrt_u_rec n1 x1 e1) (close_e_wrt_u_rec n1 x1 e2) (close_e_wrt_u_rec n1 x1 e3)
    | u_let e1 u2 => u_let (close_e_wrt_u_rec n1 x1 e1) (close_u_wrt_u_rec (S n1) x1 u2)
    | u_halt e1 => u_halt (close_e_wrt_u_rec n1 x1 e1)
  end.

Definition close_e_wrt_u x1 e1 := close_e_wrt_u_rec 0 x1 e1.

Definition close_u_wrt_u x1 u1 := close_u_wrt_u_rec 0 x1 u1.

(* *********************************************************************** *)
(** * Size *)

Fixpoint size_t (t1 : t) {struct t1} : nat :=
  match t1 with
    | t_int => 1
    | t_void => 1
    | t_arr t2 t3 => 1 + (size_t t2) + (size_t t3)
  end.

Fixpoint size_p (p1 : p) {struct p1} : nat :=
  match p1 with
    | p_plus => 1
    | p_minus => 1
  end.

Fixpoint size_e (e1 : e) {struct e1} : nat :=
  match e1 with
    | e_ann u1 t1 => 1 + (size_u u1) + (size_t t1)
  end

with size_u (u1 : u) {struct u1} : nat :=
  match u1 with
    | u_var_f x1 => 1
    | u_var_b n1 => 1
    | u_int i1 => 1
    | u_lam t1 e1 => 1 + (size_t t1) + (size_e e1)
    | u_app e1 e2 => 1 + (size_e e1) + (size_e e2)
    | u_prim e1 p1 e2 => 1 + (size_e e1) + (size_p p1) + (size_e e2)
    | u_if0 e1 e2 e3 => 1 + (size_e e1) + (size_e e2) + (size_e e3)
    | u_let e1 u2 => 1 + (size_e e1) + (size_u u2)
    | u_halt e1 => 1 + (size_e e1)
  end.

(* *********************************************************************** *)
(** * Degree *)

(** These define only an upper bound, not a strict upper bound. *)

Inductive degree_e_wrt_u : nat -> e -> Prop :=
  | degree_wrt_u_e_ann : forall n1 u1 t1,
    degree_u_wrt_u n1 u1 ->
    degree_e_wrt_u n1 (e_ann u1 t1)

with degree_u_wrt_u : nat -> u -> Prop :=
  | degree_wrt_u_u_var_f : forall n1 x1,
    degree_u_wrt_u n1 (u_var_f x1)
  | degree_wrt_u_u_var_b : forall n1 n2,
    lt n2 n1 ->
    degree_u_wrt_u n1 (u_var_b n2)
  | degree_wrt_u_u_int : forall n1 i1,
    degree_u_wrt_u n1 (u_int i1)
  | degree_wrt_u_u_lam : forall n1 t1 e1,
    degree_e_wrt_u (S n1) e1 ->
    degree_u_wrt_u n1 (u_lam t1 e1)
  | degree_wrt_u_u_app : forall n1 e1 e2,
    degree_e_wrt_u n1 e1 ->
    degree_e_wrt_u n1 e2 ->
    degree_u_wrt_u n1 (u_app e1 e2)
  | degree_wrt_u_u_prim : forall n1 e1 p1 e2,
    degree_e_wrt_u n1 e1 ->
    degree_e_wrt_u n1 e2 ->
    degree_u_wrt_u n1 (u_prim e1 p1 e2)
  | degree_wrt_u_u_if0 : forall n1 e1 e2 e3,
    degree_e_wrt_u n1 e1 ->
    degree_e_wrt_u n1 e2 ->
    degree_e_wrt_u n1 e3 ->
    degree_u_wrt_u n1 (u_if0 e1 e2 e3)
  | degree_wrt_u_u_let : forall n1 e1 u1,
    degree_e_wrt_u n1 e1 ->
    degree_u_wrt_u (S n1) u1 ->
    degree_u_wrt_u n1 (u_let e1 u1)
  | degree_wrt_u_u_halt : forall n1 e1,
    degree_e_wrt_u n1 e1 ->
    degree_u_wrt_u n1 (u_halt e1).

Scheme degree_e_wrt_u_ind' := Induction for degree_e_wrt_u Sort Prop
  with degree_u_wrt_u_ind' := Induction for degree_u_wrt_u Sort Prop.

Definition degree_e_wrt_u_degree_u_wrt_u_mutind :=
  fun H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12 =>
  (conj (degree_e_wrt_u_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12)
  (degree_u_wrt_u_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 H12)).

Hint Constructors degree_e_wrt_u : core lngen.

Hint Constructors degree_u_wrt_u : core lngen.

(* *********************************************************************** *)
(** * Local closure (version in [Set], induction principles) *)

Inductive lc_set_e : e -> Set :=
  | lc_set_e_ann : forall u1 t1,
    lc_set_u u1 ->
    lc_set_e (e_ann u1 t1)

with lc_set_u : u -> Set :=
  | lc_set_u_var_f : forall x1,
    lc_set_u (u_var_f x1)
  | lc_set_u_int : forall i1,
    lc_set_u (u_int i1)
  | lc_set_u_lam : forall t1 e1,
    (forall x1 : x, lc_set_e (open_e_wrt_u e1 (u_var_f x1))) ->
    lc_set_u (u_lam t1 e1)
  | lc_set_u_app : forall e1 e2,
    lc_set_e e1 ->
    lc_set_e e2 ->
    lc_set_u (u_app e1 e2)
  | lc_set_u_prim : forall e1 p1 e2,
    lc_set_e e1 ->
    lc_set_e e2 ->
    lc_set_u (u_prim e1 p1 e2)
  | lc_set_u_if0 : forall e1 e2 e3,
    lc_set_e e1 ->
    lc_set_e e2 ->
    lc_set_e e3 ->
    lc_set_u (u_if0 e1 e2 e3)
  | lc_set_u_let : forall e1 u1,
    lc_set_e e1 ->
    (forall x1 : x, lc_set_u (open_u_wrt_u u1 (u_var_f x1))) ->
    lc_set_u (u_let e1 u1)
  | lc_set_u_halt : forall e1,
    lc_set_e e1 ->
    lc_set_u (u_halt e1).

Scheme lc_e_ind' := Induction for lc_e Sort Prop
  with lc_u_ind' := Induction for lc_u Sort Prop.

Definition lc_e_lc_u_mutind :=
  fun H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 =>
  (conj (lc_e_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11)
  (lc_u_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11)).

Scheme lc_set_e_ind' := Induction for lc_set_e Sort Prop
  with lc_set_u_ind' := Induction for lc_set_u Sort Prop.

Definition lc_set_e_lc_set_u_mutind :=
  fun H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 =>
  (conj (lc_set_e_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11)
  (lc_set_u_ind' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11)).

Scheme lc_set_e_rec' := Induction for lc_set_e Sort Set
  with lc_set_u_rec' := Induction for lc_set_u Sort Set.

Definition lc_set_e_lc_set_u_mutrec :=
  fun H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 =>
  (pair (lc_set_e_rec' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11)
  (lc_set_u_rec' H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11)).

Hint Constructors lc_e : core lngen.

Hint Constructors lc_u : core lngen.

Hint Constructors lc_set_e : core lngen.

Hint Constructors lc_set_u : core lngen.

(* *********************************************************************** *)
(** * Body *)

Definition body_e_wrt_u e1 := forall x1, lc_e (open_e_wrt_u e1 (u_var_f x1)).

Definition body_u_wrt_u u1 := forall x1, lc_u (open_u_wrt_u u1 (u_var_f x1)).

Hint Unfold body_e_wrt_u.

Hint Unfold body_u_wrt_u.

(* *********************************************************************** *)
(** * Tactic support *)

(** Additional hint declarations. *)

Hint Resolve @plus_le_compat : lngen.

(** Redefine some tactics. *)

Ltac default_case_split ::=
  first
    [ progress destruct_notin
    | progress destruct_sum
    | progress safe_f_equal
    ].

(* *********************************************************************** *)
(** * Theorems about [size] *)

Ltac default_auto ::= auto with arith lngen; tauto.
Ltac default_autorewrite ::= fail.

(* begin hide *)

Lemma size_t_min_mutual :
(forall t1, 1 <= size_t t1).
Proof. Admitted.

(* end hide *)

Lemma size_t_min :
forall t1, 1 <= size_t t1.
Proof. Admitted.

Hint Resolve size_t_min : lngen.

(* begin hide *)

Lemma size_p_min_mutual :
(forall p1, 1 <= size_p p1).
Proof. Admitted.

(* end hide *)

Lemma size_p_min :
forall p1, 1 <= size_p p1.
Proof. Admitted.

Hint Resolve size_p_min : lngen.

(* begin hide *)

Lemma size_e_min_size_u_min_mutual :
(forall e1, 1 <= size_e e1) /\
(forall u1, 1 <= size_u u1).
Proof. Admitted.

(* end hide *)

Lemma size_e_min :
forall e1, 1 <= size_e e1.
Proof. Admitted.

Hint Resolve size_e_min : lngen.

Lemma size_u_min :
forall u1, 1 <= size_u u1.
Proof. Admitted.

Hint Resolve size_u_min : lngen.

(* begin hide *)

Lemma size_e_close_e_wrt_u_rec_size_u_close_u_wrt_u_rec_mutual :
(forall e1 x1 n1,
  size_e (close_e_wrt_u_rec n1 x1 e1) = size_e e1) /\
(forall u1 x1 n1,
  size_u (close_u_wrt_u_rec n1 x1 u1) = size_u u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma size_e_close_e_wrt_u_rec :
forall e1 x1 n1,
  size_e (close_e_wrt_u_rec n1 x1 e1) = size_e e1.
Proof. Admitted.

Hint Resolve size_e_close_e_wrt_u_rec : lngen.
Hint Rewrite size_e_close_e_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma size_u_close_u_wrt_u_rec :
forall u1 x1 n1,
  size_u (close_u_wrt_u_rec n1 x1 u1) = size_u u1.
Proof. Admitted.

Hint Resolve size_u_close_u_wrt_u_rec : lngen.
Hint Rewrite size_u_close_u_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

Lemma size_e_close_e_wrt_u :
forall e1 x1,
  size_e (close_e_wrt_u x1 e1) = size_e e1.
Proof. Admitted.

Hint Resolve size_e_close_e_wrt_u : lngen.
Hint Rewrite size_e_close_e_wrt_u using solve [auto] : lngen.

Lemma size_u_close_u_wrt_u :
forall u1 x1,
  size_u (close_u_wrt_u x1 u1) = size_u u1.
Proof. Admitted.

Hint Resolve size_u_close_u_wrt_u : lngen.
Hint Rewrite size_u_close_u_wrt_u using solve [auto] : lngen.

(* begin hide *)

Lemma size_e_open_e_wrt_u_rec_size_u_open_u_wrt_u_rec_mutual :
(forall e1 u1 n1,
  size_e e1 <= size_e (open_e_wrt_u_rec n1 u1 e1)) /\
(forall u1 u2 n1,
  size_u u1 <= size_u (open_u_wrt_u_rec n1 u2 u1)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma size_e_open_e_wrt_u_rec :
forall e1 u1 n1,
  size_e e1 <= size_e (open_e_wrt_u_rec n1 u1 e1).
Proof. Admitted.

Hint Resolve size_e_open_e_wrt_u_rec : lngen.

(* end hide *)

(* begin hide *)

Lemma size_u_open_u_wrt_u_rec :
forall u1 u2 n1,
  size_u u1 <= size_u (open_u_wrt_u_rec n1 u2 u1).
Proof. Admitted.

Hint Resolve size_u_open_u_wrt_u_rec : lngen.

(* end hide *)

Lemma size_e_open_e_wrt_u :
forall e1 u1,
  size_e e1 <= size_e (open_e_wrt_u e1 u1).
Proof. Admitted.

Hint Resolve size_e_open_e_wrt_u : lngen.

Lemma size_u_open_u_wrt_u :
forall u1 u2,
  size_u u1 <= size_u (open_u_wrt_u u1 u2).
Proof. Admitted.

Hint Resolve size_u_open_u_wrt_u : lngen.

(* begin hide *)

Lemma size_e_open_e_wrt_u_rec_var_size_u_open_u_wrt_u_rec_var_mutual :
(forall e1 x1 n1,
  size_e (open_e_wrt_u_rec n1 (u_var_f x1) e1) = size_e e1) /\
(forall u1 x1 n1,
  size_u (open_u_wrt_u_rec n1 (u_var_f x1) u1) = size_u u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma size_e_open_e_wrt_u_rec_var :
forall e1 x1 n1,
  size_e (open_e_wrt_u_rec n1 (u_var_f x1) e1) = size_e e1.
Proof. Admitted.

Hint Resolve size_e_open_e_wrt_u_rec_var : lngen.
Hint Rewrite size_e_open_e_wrt_u_rec_var using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma size_u_open_u_wrt_u_rec_var :
forall u1 x1 n1,
  size_u (open_u_wrt_u_rec n1 (u_var_f x1) u1) = size_u u1.
Proof. Admitted.

Hint Resolve size_u_open_u_wrt_u_rec_var : lngen.
Hint Rewrite size_u_open_u_wrt_u_rec_var using solve [auto] : lngen.

(* end hide *)

Lemma size_e_open_e_wrt_u_var :
forall e1 x1,
  size_e (open_e_wrt_u e1 (u_var_f x1)) = size_e e1.
Proof. Admitted.

Hint Resolve size_e_open_e_wrt_u_var : lngen.
Hint Rewrite size_e_open_e_wrt_u_var using solve [auto] : lngen.

Lemma size_u_open_u_wrt_u_var :
forall u1 x1,
  size_u (open_u_wrt_u u1 (u_var_f x1)) = size_u u1.
Proof. Admitted.

Hint Resolve size_u_open_u_wrt_u_var : lngen.
Hint Rewrite size_u_open_u_wrt_u_var using solve [auto] : lngen.

(* *********************************************************************** *)
(** * Theorems about [degree] *)

Ltac default_auto ::= auto with lngen; tauto.
Ltac default_autorewrite ::= fail.

(* begin hide *)

Lemma degree_e_wrt_u_S_degree_u_wrt_u_S_mutual :
(forall n1 e1,
  degree_e_wrt_u n1 e1 ->
  degree_e_wrt_u (S n1) e1) /\
(forall n1 u1,
  degree_u_wrt_u n1 u1 ->
  degree_u_wrt_u (S n1) u1).
Proof. Admitted.

(* end hide *)

Lemma degree_e_wrt_u_S :
forall n1 e1,
  degree_e_wrt_u n1 e1 ->
  degree_e_wrt_u (S n1) e1.
Proof. Admitted.

Hint Resolve degree_e_wrt_u_S : lngen.

Lemma degree_u_wrt_u_S :
forall n1 u1,
  degree_u_wrt_u n1 u1 ->
  degree_u_wrt_u (S n1) u1.
Proof. Admitted.

Hint Resolve degree_u_wrt_u_S : lngen.

Lemma degree_e_wrt_u_O :
forall n1 e1,
  degree_e_wrt_u O e1 ->
  degree_e_wrt_u n1 e1.
Proof. Admitted.

Hint Resolve degree_e_wrt_u_O : lngen.

Lemma degree_u_wrt_u_O :
forall n1 u1,
  degree_u_wrt_u O u1 ->
  degree_u_wrt_u n1 u1.
Proof. Admitted.

Hint Resolve degree_u_wrt_u_O : lngen.

(* begin hide *)

Lemma degree_e_wrt_u_close_e_wrt_u_rec_degree_u_wrt_u_close_u_wrt_u_rec_mutual :
(forall e1 x1 n1,
  degree_e_wrt_u n1 e1 ->
  degree_e_wrt_u (S n1) (close_e_wrt_u_rec n1 x1 e1)) /\
(forall u1 x1 n1,
  degree_u_wrt_u n1 u1 ->
  degree_u_wrt_u (S n1) (close_u_wrt_u_rec n1 x1 u1)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma degree_e_wrt_u_close_e_wrt_u_rec :
forall e1 x1 n1,
  degree_e_wrt_u n1 e1 ->
  degree_e_wrt_u (S n1) (close_e_wrt_u_rec n1 x1 e1).
Proof. Admitted.

Hint Resolve degree_e_wrt_u_close_e_wrt_u_rec : lngen.

(* end hide *)

(* begin hide *)

Lemma degree_u_wrt_u_close_u_wrt_u_rec :
forall u1 x1 n1,
  degree_u_wrt_u n1 u1 ->
  degree_u_wrt_u (S n1) (close_u_wrt_u_rec n1 x1 u1).
Proof. Admitted.

Hint Resolve degree_u_wrt_u_close_u_wrt_u_rec : lngen.

(* end hide *)

Lemma degree_e_wrt_u_close_e_wrt_u :
forall e1 x1,
  degree_e_wrt_u 0 e1 ->
  degree_e_wrt_u 1 (close_e_wrt_u x1 e1).
Proof. Admitted.

Hint Resolve degree_e_wrt_u_close_e_wrt_u : lngen.

Lemma degree_u_wrt_u_close_u_wrt_u :
forall u1 x1,
  degree_u_wrt_u 0 u1 ->
  degree_u_wrt_u 1 (close_u_wrt_u x1 u1).
Proof. Admitted.

Hint Resolve degree_u_wrt_u_close_u_wrt_u : lngen.

(* begin hide *)

Lemma degree_e_wrt_u_close_e_wrt_u_rec_inv_degree_u_wrt_u_close_u_wrt_u_rec_inv_mutual :
(forall e1 x1 n1,
  degree_e_wrt_u (S n1) (close_e_wrt_u_rec n1 x1 e1) ->
  degree_e_wrt_u n1 e1) /\
(forall u1 x1 n1,
  degree_u_wrt_u (S n1) (close_u_wrt_u_rec n1 x1 u1) ->
  degree_u_wrt_u n1 u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma degree_e_wrt_u_close_e_wrt_u_rec_inv :
forall e1 x1 n1,
  degree_e_wrt_u (S n1) (close_e_wrt_u_rec n1 x1 e1) ->
  degree_e_wrt_u n1 e1.
Proof. Admitted.

Hint Immediate degree_e_wrt_u_close_e_wrt_u_rec_inv : lngen.

(* end hide *)

(* begin hide *)

Lemma degree_u_wrt_u_close_u_wrt_u_rec_inv :
forall u1 x1 n1,
  degree_u_wrt_u (S n1) (close_u_wrt_u_rec n1 x1 u1) ->
  degree_u_wrt_u n1 u1.
Proof. Admitted.

Hint Immediate degree_u_wrt_u_close_u_wrt_u_rec_inv : lngen.

(* end hide *)

Lemma degree_e_wrt_u_close_e_wrt_u_inv :
forall e1 x1,
  degree_e_wrt_u 1 (close_e_wrt_u x1 e1) ->
  degree_e_wrt_u 0 e1.
Proof. Admitted.

Hint Immediate degree_e_wrt_u_close_e_wrt_u_inv : lngen.

Lemma degree_u_wrt_u_close_u_wrt_u_inv :
forall u1 x1,
  degree_u_wrt_u 1 (close_u_wrt_u x1 u1) ->
  degree_u_wrt_u 0 u1.
Proof. Admitted.

Hint Immediate degree_u_wrt_u_close_u_wrt_u_inv : lngen.

(* begin hide *)

Lemma degree_e_wrt_u_open_e_wrt_u_rec_degree_u_wrt_u_open_u_wrt_u_rec_mutual :
(forall e1 u1 n1,
  degree_e_wrt_u (S n1) e1 ->
  degree_u_wrt_u n1 u1 ->
  degree_e_wrt_u n1 (open_e_wrt_u_rec n1 u1 e1)) /\
(forall u1 u2 n1,
  degree_u_wrt_u (S n1) u1 ->
  degree_u_wrt_u n1 u2 ->
  degree_u_wrt_u n1 (open_u_wrt_u_rec n1 u2 u1)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma degree_e_wrt_u_open_e_wrt_u_rec :
forall e1 u1 n1,
  degree_e_wrt_u (S n1) e1 ->
  degree_u_wrt_u n1 u1 ->
  degree_e_wrt_u n1 (open_e_wrt_u_rec n1 u1 e1).
Proof. Admitted.

Hint Resolve degree_e_wrt_u_open_e_wrt_u_rec : lngen.

(* end hide *)

(* begin hide *)

Lemma degree_u_wrt_u_open_u_wrt_u_rec :
forall u1 u2 n1,
  degree_u_wrt_u (S n1) u1 ->
  degree_u_wrt_u n1 u2 ->
  degree_u_wrt_u n1 (open_u_wrt_u_rec n1 u2 u1).
Proof. Admitted.

Hint Resolve degree_u_wrt_u_open_u_wrt_u_rec : lngen.

(* end hide *)

Lemma degree_e_wrt_u_open_e_wrt_u :
forall e1 u1,
  degree_e_wrt_u 1 e1 ->
  degree_u_wrt_u 0 u1 ->
  degree_e_wrt_u 0 (open_e_wrt_u e1 u1).
Proof. Admitted.

Hint Resolve degree_e_wrt_u_open_e_wrt_u : lngen.

Lemma degree_u_wrt_u_open_u_wrt_u :
forall u1 u2,
  degree_u_wrt_u 1 u1 ->
  degree_u_wrt_u 0 u2 ->
  degree_u_wrt_u 0 (open_u_wrt_u u1 u2).
Proof. Admitted.

Hint Resolve degree_u_wrt_u_open_u_wrt_u : lngen.

(* begin hide *)

Lemma degree_e_wrt_u_open_e_wrt_u_rec_inv_degree_u_wrt_u_open_u_wrt_u_rec_inv_mutual :
(forall e1 u1 n1,
  degree_e_wrt_u n1 (open_e_wrt_u_rec n1 u1 e1) ->
  degree_e_wrt_u (S n1) e1) /\
(forall u1 u2 n1,
  degree_u_wrt_u n1 (open_u_wrt_u_rec n1 u2 u1) ->
  degree_u_wrt_u (S n1) u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma degree_e_wrt_u_open_e_wrt_u_rec_inv :
forall e1 u1 n1,
  degree_e_wrt_u n1 (open_e_wrt_u_rec n1 u1 e1) ->
  degree_e_wrt_u (S n1) e1.
Proof. Admitted.

Hint Immediate degree_e_wrt_u_open_e_wrt_u_rec_inv : lngen.

(* end hide *)

(* begin hide *)

Lemma degree_u_wrt_u_open_u_wrt_u_rec_inv :
forall u1 u2 n1,
  degree_u_wrt_u n1 (open_u_wrt_u_rec n1 u2 u1) ->
  degree_u_wrt_u (S n1) u1.
Proof. Admitted.

Hint Immediate degree_u_wrt_u_open_u_wrt_u_rec_inv : lngen.

(* end hide *)

Lemma degree_e_wrt_u_open_e_wrt_u_inv :
forall e1 u1,
  degree_e_wrt_u 0 (open_e_wrt_u e1 u1) ->
  degree_e_wrt_u 1 e1.
Proof. Admitted.

Hint Immediate degree_e_wrt_u_open_e_wrt_u_inv : lngen.

Lemma degree_u_wrt_u_open_u_wrt_u_inv :
forall u1 u2,
  degree_u_wrt_u 0 (open_u_wrt_u u1 u2) ->
  degree_u_wrt_u 1 u1.
Proof. Admitted.

Hint Immediate degree_u_wrt_u_open_u_wrt_u_inv : lngen.

(* *********************************************************************** *)
(** * Theorems about [open] and [close] *)

Ltac default_auto ::= auto with lngen brute_force; tauto.
Ltac default_autorewrite ::= fail.

(* begin hide *)

Lemma close_e_wrt_u_rec_inj_close_u_wrt_u_rec_inj_mutual :
(forall e1 e2 x1 n1,
  close_e_wrt_u_rec n1 x1 e1 = close_e_wrt_u_rec n1 x1 e2 ->
  e1 = e2) /\
(forall u1 u2 x1 n1,
  close_u_wrt_u_rec n1 x1 u1 = close_u_wrt_u_rec n1 x1 u2 ->
  u1 = u2).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma close_e_wrt_u_rec_inj :
forall e1 e2 x1 n1,
  close_e_wrt_u_rec n1 x1 e1 = close_e_wrt_u_rec n1 x1 e2 ->
  e1 = e2.
Proof. Admitted.

Hint Immediate close_e_wrt_u_rec_inj : lngen.

(* end hide *)

(* begin hide *)

Lemma close_u_wrt_u_rec_inj :
forall u1 u2 x1 n1,
  close_u_wrt_u_rec n1 x1 u1 = close_u_wrt_u_rec n1 x1 u2 ->
  u1 = u2.
Proof. Admitted.

Hint Immediate close_u_wrt_u_rec_inj : lngen.

(* end hide *)

Lemma close_e_wrt_u_inj :
forall e1 e2 x1,
  close_e_wrt_u x1 e1 = close_e_wrt_u x1 e2 ->
  e1 = e2.
Proof. Admitted.

Hint Immediate close_e_wrt_u_inj : lngen.

Lemma close_u_wrt_u_inj :
forall u1 u2 x1,
  close_u_wrt_u x1 u1 = close_u_wrt_u x1 u2 ->
  u1 = u2.
Proof. Admitted.

Hint Immediate close_u_wrt_u_inj : lngen.

(* begin hide *)

Lemma close_e_wrt_u_rec_open_e_wrt_u_rec_close_u_wrt_u_rec_open_u_wrt_u_rec_mutual :
(forall e1 x1 n1,
  x1 `notin` fv_e e1 ->
  close_e_wrt_u_rec n1 x1 (open_e_wrt_u_rec n1 (u_var_f x1) e1) = e1) /\
(forall u1 x1 n1,
  x1 `notin` fv_u u1 ->
  close_u_wrt_u_rec n1 x1 (open_u_wrt_u_rec n1 (u_var_f x1) u1) = u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma close_e_wrt_u_rec_open_e_wrt_u_rec :
forall e1 x1 n1,
  x1 `notin` fv_e e1 ->
  close_e_wrt_u_rec n1 x1 (open_e_wrt_u_rec n1 (u_var_f x1) e1) = e1.
Proof. Admitted.

Hint Resolve close_e_wrt_u_rec_open_e_wrt_u_rec : lngen.
Hint Rewrite close_e_wrt_u_rec_open_e_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma close_u_wrt_u_rec_open_u_wrt_u_rec :
forall u1 x1 n1,
  x1 `notin` fv_u u1 ->
  close_u_wrt_u_rec n1 x1 (open_u_wrt_u_rec n1 (u_var_f x1) u1) = u1.
Proof. Admitted.

Hint Resolve close_u_wrt_u_rec_open_u_wrt_u_rec : lngen.
Hint Rewrite close_u_wrt_u_rec_open_u_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

Lemma close_e_wrt_u_open_e_wrt_u :
forall e1 x1,
  x1 `notin` fv_e e1 ->
  close_e_wrt_u x1 (open_e_wrt_u e1 (u_var_f x1)) = e1.
Proof. Admitted.

Hint Resolve close_e_wrt_u_open_e_wrt_u : lngen.
Hint Rewrite close_e_wrt_u_open_e_wrt_u using solve [auto] : lngen.

Lemma close_u_wrt_u_open_u_wrt_u :
forall u1 x1,
  x1 `notin` fv_u u1 ->
  close_u_wrt_u x1 (open_u_wrt_u u1 (u_var_f x1)) = u1.
Proof. Admitted.

Hint Resolve close_u_wrt_u_open_u_wrt_u : lngen.
Hint Rewrite close_u_wrt_u_open_u_wrt_u using solve [auto] : lngen.

(* begin hide *)

Lemma open_e_wrt_u_rec_close_e_wrt_u_rec_open_u_wrt_u_rec_close_u_wrt_u_rec_mutual :
(forall e1 x1 n1,
  open_e_wrt_u_rec n1 (u_var_f x1) (close_e_wrt_u_rec n1 x1 e1) = e1) /\
(forall u1 x1 n1,
  open_u_wrt_u_rec n1 (u_var_f x1) (close_u_wrt_u_rec n1 x1 u1) = u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma open_e_wrt_u_rec_close_e_wrt_u_rec :
forall e1 x1 n1,
  open_e_wrt_u_rec n1 (u_var_f x1) (close_e_wrt_u_rec n1 x1 e1) = e1.
Proof. Admitted.

Hint Resolve open_e_wrt_u_rec_close_e_wrt_u_rec : lngen.
Hint Rewrite open_e_wrt_u_rec_close_e_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma open_u_wrt_u_rec_close_u_wrt_u_rec :
forall u1 x1 n1,
  open_u_wrt_u_rec n1 (u_var_f x1) (close_u_wrt_u_rec n1 x1 u1) = u1.
Proof. Admitted.

Hint Resolve open_u_wrt_u_rec_close_u_wrt_u_rec : lngen.
Hint Rewrite open_u_wrt_u_rec_close_u_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

Lemma open_e_wrt_u_close_e_wrt_u :
forall e1 x1,
  open_e_wrt_u (close_e_wrt_u x1 e1) (u_var_f x1) = e1.
Proof. Admitted.

Hint Resolve open_e_wrt_u_close_e_wrt_u : lngen.
Hint Rewrite open_e_wrt_u_close_e_wrt_u using solve [auto] : lngen.

Lemma open_u_wrt_u_close_u_wrt_u :
forall u1 x1,
  open_u_wrt_u (close_u_wrt_u x1 u1) (u_var_f x1) = u1.
Proof. Admitted.

Hint Resolve open_u_wrt_u_close_u_wrt_u : lngen.
Hint Rewrite open_u_wrt_u_close_u_wrt_u using solve [auto] : lngen.

(* begin hide *)

Lemma open_e_wrt_u_rec_inj_open_u_wrt_u_rec_inj_mutual :
(forall e2 e1 x1 n1,
  x1 `notin` fv_e e2 ->
  x1 `notin` fv_e e1 ->
  open_e_wrt_u_rec n1 (u_var_f x1) e2 = open_e_wrt_u_rec n1 (u_var_f x1) e1 ->
  e2 = e1) /\
(forall u2 u1 x1 n1,
  x1 `notin` fv_u u2 ->
  x1 `notin` fv_u u1 ->
  open_u_wrt_u_rec n1 (u_var_f x1) u2 = open_u_wrt_u_rec n1 (u_var_f x1) u1 ->
  u2 = u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma open_e_wrt_u_rec_inj :
forall e2 e1 x1 n1,
  x1 `notin` fv_e e2 ->
  x1 `notin` fv_e e1 ->
  open_e_wrt_u_rec n1 (u_var_f x1) e2 = open_e_wrt_u_rec n1 (u_var_f x1) e1 ->
  e2 = e1.
Proof. Admitted.

Hint Immediate open_e_wrt_u_rec_inj : lngen.

(* end hide *)

(* begin hide *)

Lemma open_u_wrt_u_rec_inj :
forall u2 u1 x1 n1,
  x1 `notin` fv_u u2 ->
  x1 `notin` fv_u u1 ->
  open_u_wrt_u_rec n1 (u_var_f x1) u2 = open_u_wrt_u_rec n1 (u_var_f x1) u1 ->
  u2 = u1.
Proof. Admitted.

Hint Immediate open_u_wrt_u_rec_inj : lngen.

(* end hide *)

Lemma open_e_wrt_u_inj :
forall e2 e1 x1,
  x1 `notin` fv_e e2 ->
  x1 `notin` fv_e e1 ->
  open_e_wrt_u e2 (u_var_f x1) = open_e_wrt_u e1 (u_var_f x1) ->
  e2 = e1.
Proof. Admitted.

Hint Immediate open_e_wrt_u_inj : lngen.

Lemma open_u_wrt_u_inj :
forall u2 u1 x1,
  x1 `notin` fv_u u2 ->
  x1 `notin` fv_u u1 ->
  open_u_wrt_u u2 (u_var_f x1) = open_u_wrt_u u1 (u_var_f x1) ->
  u2 = u1.
Proof. Admitted.

Hint Immediate open_u_wrt_u_inj : lngen.

(* *********************************************************************** *)
(** * Theorems about [lc] *)

Ltac default_auto ::= auto with lngen brute_force; tauto.
Ltac default_autorewrite ::= autorewrite with lngen.

(* begin hide *)

Lemma degree_e_wrt_u_of_lc_e_degree_u_wrt_u_of_lc_u_mutual :
(forall e1,
  lc_e e1 ->
  degree_e_wrt_u 0 e1) /\
(forall u1,
  lc_u u1 ->
  degree_u_wrt_u 0 u1).
Proof. Admitted.

(* end hide *)

Lemma degree_e_wrt_u_of_lc_e :
forall e1,
  lc_e e1 ->
  degree_e_wrt_u 0 e1.
Proof. Admitted.

Hint Resolve degree_e_wrt_u_of_lc_e : lngen.

Lemma degree_u_wrt_u_of_lc_u :
forall u1,
  lc_u u1 ->
  degree_u_wrt_u 0 u1.
Proof. Admitted.

Hint Resolve degree_u_wrt_u_of_lc_u : lngen.

(* begin hide *)

Lemma lc_e_of_degree_lc_u_of_degree_size_mutual :
forall i1,
(forall e1,
  size_e e1 = i1 ->
  degree_e_wrt_u 0 e1 ->
  lc_e e1) /\
(forall u1,
  size_u u1 = i1 ->
  degree_u_wrt_u 0 u1 ->
  lc_u u1).
Proof. Admitted.

(* end hide *)

Lemma lc_e_of_degree :
forall e1,
  degree_e_wrt_u 0 e1 ->
  lc_e e1.
Proof. Admitted.

Hint Resolve lc_e_of_degree : lngen.

Lemma lc_u_of_degree :
forall u1,
  degree_u_wrt_u 0 u1 ->
  lc_u u1.
Proof. Admitted.

Hint Resolve lc_u_of_degree : lngen.

Ltac t_lc_exists_tac :=
  repeat (match goal with
            | H : _ |- _ =>
              fail 1
          end).

Ltac p_lc_exists_tac :=
  repeat (match goal with
            | H : _ |- _ =>
              fail 1
          end).

Ltac e_u_lc_exists_tac :=
  repeat (match goal with
            | H : _ |- _ =>
              let J1 := fresh in pose proof H as J1; apply degree_e_wrt_u_of_lc_e in J1; clear H
            | H : _ |- _ =>
              let J1 := fresh in pose proof H as J1; apply degree_u_wrt_u_of_lc_u in J1; clear H
          end).

Lemma lc_u_lam_exists :
forall x1 t1 e1,
  lc_e (open_e_wrt_u e1 (u_var_f x1)) ->
  lc_u (u_lam t1 e1).
Proof. Admitted.

Lemma lc_u_let_exists :
forall x1 e1 u1,
  lc_e e1 ->
  lc_u (open_u_wrt_u u1 (u_var_f x1)) ->
  lc_u (u_let e1 u1).
Proof. Admitted.

Hint Extern 1 (lc_u (u_lam _ _)) =>
  let x1 := fresh in
  pick_fresh x1;
  apply (lc_u_lam_exists x1).

Hint Extern 1 (lc_u (u_let _ _)) =>
  let x1 := fresh in
  pick_fresh x1;
  apply (lc_u_let_exists x1).

Lemma lc_body_e_wrt_u :
forall e1 u1,
  body_e_wrt_u e1 ->
  lc_u u1 ->
  lc_e (open_e_wrt_u e1 u1).
Proof. Admitted.

Hint Resolve lc_body_e_wrt_u : lngen.

Lemma lc_body_u_wrt_u :
forall u1 u2,
  body_u_wrt_u u1 ->
  lc_u u2 ->
  lc_u (open_u_wrt_u u1 u2).
Proof. Admitted.

Hint Resolve lc_body_u_wrt_u : lngen.

Lemma lc_body_u_lam_2 :
forall t1 e1,
  lc_u (u_lam t1 e1) ->
  body_e_wrt_u e1.
Proof. Admitted.

Hint Resolve lc_body_u_lam_2 : lngen.

Lemma lc_body_u_let_2 :
forall e1 u1,
  lc_u (u_let e1 u1) ->
  body_u_wrt_u u1.
Proof. Admitted.

Hint Resolve lc_body_u_let_2 : lngen.

(* begin hide *)

Lemma lc_e_unique_lc_u_unique_mutual :
(forall e1 (proof2 proof3 : lc_e e1), proof2 = proof3) /\
(forall u1 (proof2 proof3 : lc_u u1), proof2 = proof3).
Proof. Admitted.

(* end hide *)

Lemma lc_e_unique :
forall e1 (proof2 proof3 : lc_e e1), proof2 = proof3.
Proof. Admitted.

Hint Resolve lc_e_unique : lngen.

Lemma lc_u_unique :
forall u1 (proof2 proof3 : lc_u u1), proof2 = proof3.
Proof. Admitted.

Hint Resolve lc_u_unique : lngen.

(* begin hide *)

Lemma lc_e_of_lc_set_e_lc_u_of_lc_set_u_mutual :
(forall e1, lc_set_e e1 -> lc_e e1) /\
(forall u1, lc_set_u u1 -> lc_u u1).
Proof. Admitted.

(* end hide *)

Lemma lc_e_of_lc_set_e :
forall e1, lc_set_e e1 -> lc_e e1.
Proof. Admitted.

Hint Resolve lc_e_of_lc_set_e : lngen.

Lemma lc_u_of_lc_set_u :
forall u1, lc_set_u u1 -> lc_u u1.
Proof. Admitted.

Hint Resolve lc_u_of_lc_set_u : lngen.

(* begin hide *)

Lemma lc_set_e_of_lc_e_lc_set_u_of_lc_u_size_mutual :
forall i1,
(forall e1,
  size_e e1 = i1 ->
  lc_e e1 ->
  lc_set_e e1) *
(forall u1,
  size_u u1 = i1 ->
  lc_u u1 ->
  lc_set_u u1).
Proof. Admitted.

(* end hide *)

Lemma lc_set_e_of_lc_e :
forall e1,
  lc_e e1 ->
  lc_set_e e1.
Proof. Admitted.

Hint Resolve lc_set_e_of_lc_e : lngen.

Lemma lc_set_u_of_lc_u :
forall u1,
  lc_u u1 ->
  lc_set_u u1.
Proof. Admitted.

Hint Resolve lc_set_u_of_lc_u : lngen.

(* *********************************************************************** *)
(** * More theorems about [open] and [close] *)

Ltac default_auto ::= auto with lngen; tauto.
Ltac default_autorewrite ::= fail.

(* begin hide *)

Lemma close_e_wrt_u_rec_degree_e_wrt_u_close_u_wrt_u_rec_degree_u_wrt_u_mutual :
(forall e1 x1 n1,
  degree_e_wrt_u n1 e1 ->
  x1 `notin` fv_e e1 ->
  close_e_wrt_u_rec n1 x1 e1 = e1) /\
(forall u1 x1 n1,
  degree_u_wrt_u n1 u1 ->
  x1 `notin` fv_u u1 ->
  close_u_wrt_u_rec n1 x1 u1 = u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma close_e_wrt_u_rec_degree_e_wrt_u :
forall e1 x1 n1,
  degree_e_wrt_u n1 e1 ->
  x1 `notin` fv_e e1 ->
  close_e_wrt_u_rec n1 x1 e1 = e1.
Proof. Admitted.

Hint Resolve close_e_wrt_u_rec_degree_e_wrt_u : lngen.
Hint Rewrite close_e_wrt_u_rec_degree_e_wrt_u using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma close_u_wrt_u_rec_degree_u_wrt_u :
forall u1 x1 n1,
  degree_u_wrt_u n1 u1 ->
  x1 `notin` fv_u u1 ->
  close_u_wrt_u_rec n1 x1 u1 = u1.
Proof. Admitted.

Hint Resolve close_u_wrt_u_rec_degree_u_wrt_u : lngen.
Hint Rewrite close_u_wrt_u_rec_degree_u_wrt_u using solve [auto] : lngen.

(* end hide *)

Lemma close_e_wrt_u_lc_e :
forall e1 x1,
  lc_e e1 ->
  x1 `notin` fv_e e1 ->
  close_e_wrt_u x1 e1 = e1.
Proof. Admitted.

Hint Resolve close_e_wrt_u_lc_e : lngen.
Hint Rewrite close_e_wrt_u_lc_e using solve [auto] : lngen.

Lemma close_u_wrt_u_lc_u :
forall u1 x1,
  lc_u u1 ->
  x1 `notin` fv_u u1 ->
  close_u_wrt_u x1 u1 = u1.
Proof. Admitted.

Hint Resolve close_u_wrt_u_lc_u : lngen.
Hint Rewrite close_u_wrt_u_lc_u using solve [auto] : lngen.

(* begin hide *)

Lemma open_e_wrt_u_rec_degree_e_wrt_u_open_u_wrt_u_rec_degree_u_wrt_u_mutual :
(forall e1 u1 n1,
  degree_e_wrt_u n1 e1 ->
  open_e_wrt_u_rec n1 u1 e1 = e1) /\
(forall u2 u1 n1,
  degree_u_wrt_u n1 u2 ->
  open_u_wrt_u_rec n1 u1 u2 = u2).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma open_e_wrt_u_rec_degree_e_wrt_u :
forall e1 u1 n1,
  degree_e_wrt_u n1 e1 ->
  open_e_wrt_u_rec n1 u1 e1 = e1.
Proof. Admitted.

Hint Resolve open_e_wrt_u_rec_degree_e_wrt_u : lngen.
Hint Rewrite open_e_wrt_u_rec_degree_e_wrt_u using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma open_u_wrt_u_rec_degree_u_wrt_u :
forall u2 u1 n1,
  degree_u_wrt_u n1 u2 ->
  open_u_wrt_u_rec n1 u1 u2 = u2.
Proof. Admitted.

Hint Resolve open_u_wrt_u_rec_degree_u_wrt_u : lngen.
Hint Rewrite open_u_wrt_u_rec_degree_u_wrt_u using solve [auto] : lngen.

(* end hide *)

Lemma open_e_wrt_u_lc_e :
forall e1 u1,
  lc_e e1 ->
  open_e_wrt_u e1 u1 = e1.
Proof. Admitted.

Hint Resolve open_e_wrt_u_lc_e : lngen.
Hint Rewrite open_e_wrt_u_lc_e using solve [auto] : lngen.

Lemma open_u_wrt_u_lc_u :
forall u2 u1,
  lc_u u2 ->
  open_u_wrt_u u2 u1 = u2.
Proof. Admitted.

Hint Resolve open_u_wrt_u_lc_u : lngen.
Hint Rewrite open_u_wrt_u_lc_u using solve [auto] : lngen.

(* *********************************************************************** *)
(** * Theorems about [fv] *)

Ltac default_auto ::= auto with set lngen; tauto.
Ltac default_autorewrite ::= autorewrite with lngen.

(* begin hide *)

Lemma fv_e_close_e_wrt_u_rec_fv_u_close_u_wrt_u_rec_mutual :
(forall e1 x1 n1,
  fv_e (close_e_wrt_u_rec n1 x1 e1) [=] remove x1 (fv_e e1)) /\
(forall u1 x1 n1,
  fv_u (close_u_wrt_u_rec n1 x1 u1) [=] remove x1 (fv_u u1)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma fv_e_close_e_wrt_u_rec :
forall e1 x1 n1,
  fv_e (close_e_wrt_u_rec n1 x1 e1) [=] remove x1 (fv_e e1).
Proof. Admitted.

Hint Resolve fv_e_close_e_wrt_u_rec : lngen.
Hint Rewrite fv_e_close_e_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

(* begin hide *)

Lemma fv_u_close_u_wrt_u_rec :
forall u1 x1 n1,
  fv_u (close_u_wrt_u_rec n1 x1 u1) [=] remove x1 (fv_u u1).
Proof. Admitted.

Hint Resolve fv_u_close_u_wrt_u_rec : lngen.
Hint Rewrite fv_u_close_u_wrt_u_rec using solve [auto] : lngen.

(* end hide *)

Lemma fv_e_close_e_wrt_u :
forall e1 x1,
  fv_e (close_e_wrt_u x1 e1) [=] remove x1 (fv_e e1).
Proof. Admitted.

Hint Resolve fv_e_close_e_wrt_u : lngen.
Hint Rewrite fv_e_close_e_wrt_u using solve [auto] : lngen.

Lemma fv_u_close_u_wrt_u :
forall u1 x1,
  fv_u (close_u_wrt_u x1 u1) [=] remove x1 (fv_u u1).
Proof. Admitted.

Hint Resolve fv_u_close_u_wrt_u : lngen.
Hint Rewrite fv_u_close_u_wrt_u using solve [auto] : lngen.

(* begin hide *)

Lemma fv_e_open_e_wrt_u_rec_lower_fv_u_open_u_wrt_u_rec_lower_mutual :
(forall e1 u1 n1,
  fv_e e1 [<=] fv_e (open_e_wrt_u_rec n1 u1 e1)) /\
(forall u1 u2 n1,
  fv_u u1 [<=] fv_u (open_u_wrt_u_rec n1 u2 u1)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma fv_e_open_e_wrt_u_rec_lower :
forall e1 u1 n1,
  fv_e e1 [<=] fv_e (open_e_wrt_u_rec n1 u1 e1).
Proof. Admitted.

Hint Resolve fv_e_open_e_wrt_u_rec_lower : lngen.

(* end hide *)

(* begin hide *)

Lemma fv_u_open_u_wrt_u_rec_lower :
forall u1 u2 n1,
  fv_u u1 [<=] fv_u (open_u_wrt_u_rec n1 u2 u1).
Proof. Admitted.

Hint Resolve fv_u_open_u_wrt_u_rec_lower : lngen.

(* end hide *)

Lemma fv_e_open_e_wrt_u_lower :
forall e1 u1,
  fv_e e1 [<=] fv_e (open_e_wrt_u e1 u1).
Proof. Admitted.

Hint Resolve fv_e_open_e_wrt_u_lower : lngen.

Lemma fv_u_open_u_wrt_u_lower :
forall u1 u2,
  fv_u u1 [<=] fv_u (open_u_wrt_u u1 u2).
Proof. Admitted.

Hint Resolve fv_u_open_u_wrt_u_lower : lngen.

(* begin hide *)

Lemma fv_e_open_e_wrt_u_rec_upper_fv_u_open_u_wrt_u_rec_upper_mutual :
(forall e1 u1 n1,
  fv_e (open_e_wrt_u_rec n1 u1 e1) [<=] fv_u u1 `union` fv_e e1) /\
(forall u1 u2 n1,
  fv_u (open_u_wrt_u_rec n1 u2 u1) [<=] fv_u u2 `union` fv_u u1).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma fv_e_open_e_wrt_u_rec_upper :
forall e1 u1 n1,
  fv_e (open_e_wrt_u_rec n1 u1 e1) [<=] fv_u u1 `union` fv_e e1.
Proof. Admitted.

Hint Resolve fv_e_open_e_wrt_u_rec_upper : lngen.

(* end hide *)

(* begin hide *)

Lemma fv_u_open_u_wrt_u_rec_upper :
forall u1 u2 n1,
  fv_u (open_u_wrt_u_rec n1 u2 u1) [<=] fv_u u2 `union` fv_u u1.
Proof. Admitted.

Hint Resolve fv_u_open_u_wrt_u_rec_upper : lngen.

(* end hide *)

Lemma fv_e_open_e_wrt_u_upper :
forall e1 u1,
  fv_e (open_e_wrt_u e1 u1) [<=] fv_u u1 `union` fv_e e1.
Proof. Admitted.

Hint Resolve fv_e_open_e_wrt_u_upper : lngen.

Lemma fv_u_open_u_wrt_u_upper :
forall u1 u2,
  fv_u (open_u_wrt_u u1 u2) [<=] fv_u u2 `union` fv_u u1.
Proof. Admitted.

Hint Resolve fv_u_open_u_wrt_u_upper : lngen.

(* begin hide *)

Lemma fv_e_subst_e_fresh_fv_u_subst_u_fresh_mutual :
(forall e1 u1 x1,
  x1 `notin` fv_e e1 ->
  fv_e (subst_e u1 x1 e1) [=] fv_e e1) /\
(forall u1 u2 x1,
  x1 `notin` fv_u u1 ->
  fv_u (subst_u u2 x1 u1) [=] fv_u u1).
Proof. Admitted.

(* end hide *)

Lemma fv_e_subst_e_fresh :
forall e1 u1 x1,
  x1 `notin` fv_e e1 ->
  fv_e (subst_e u1 x1 e1) [=] fv_e e1.
Proof. Admitted.

Hint Resolve fv_e_subst_e_fresh : lngen.
Hint Rewrite fv_e_subst_e_fresh using solve [auto] : lngen.

Lemma fv_u_subst_u_fresh :
forall u1 u2 x1,
  x1 `notin` fv_u u1 ->
  fv_u (subst_u u2 x1 u1) [=] fv_u u1.
Proof. Admitted.

Hint Resolve fv_u_subst_u_fresh : lngen.
Hint Rewrite fv_u_subst_u_fresh using solve [auto] : lngen.

(* begin hide *)

Lemma fv_e_subst_e_lower_fv_u_subst_u_lower_mutual :
(forall e1 u1 x1,
  remove x1 (fv_e e1) [<=] fv_e (subst_e u1 x1 e1)) /\
(forall u1 u2 x1,
  remove x1 (fv_u u1) [<=] fv_u (subst_u u2 x1 u1)).
Proof. Admitted.

(* end hide *)

Lemma fv_e_subst_e_lower :
forall e1 u1 x1,
  remove x1 (fv_e e1) [<=] fv_e (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve fv_e_subst_e_lower : lngen.

Lemma fv_u_subst_u_lower :
forall u1 u2 x1,
  remove x1 (fv_u u1) [<=] fv_u (subst_u u2 x1 u1).
Proof. Admitted.

Hint Resolve fv_u_subst_u_lower : lngen.

(* begin hide *)

Lemma fv_e_subst_e_notin_fv_u_subst_u_notin_mutual :
(forall e1 u1 x1 x2,
  x2 `notin` fv_e e1 ->
  x2 `notin` fv_u u1 ->
  x2 `notin` fv_e (subst_e u1 x1 e1)) /\
(forall u1 u2 x1 x2,
  x2 `notin` fv_u u1 ->
  x2 `notin` fv_u u2 ->
  x2 `notin` fv_u (subst_u u2 x1 u1)).
Proof. Admitted.

(* end hide *)

Lemma fv_e_subst_e_notin :
forall e1 u1 x1 x2,
  x2 `notin` fv_e e1 ->
  x2 `notin` fv_u u1 ->
  x2 `notin` fv_e (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve fv_e_subst_e_notin : lngen.

Lemma fv_u_subst_u_notin :
forall u1 u2 x1 x2,
  x2 `notin` fv_u u1 ->
  x2 `notin` fv_u u2 ->
  x2 `notin` fv_u (subst_u u2 x1 u1).
Proof. Admitted.

Hint Resolve fv_u_subst_u_notin : lngen.

(* begin hide *)

Lemma fv_e_subst_e_upper_fv_u_subst_u_upper_mutual :
(forall e1 u1 x1,
  fv_e (subst_e u1 x1 e1) [<=] fv_u u1 `union` remove x1 (fv_e e1)) /\
(forall u1 u2 x1,
  fv_u (subst_u u2 x1 u1) [<=] fv_u u2 `union` remove x1 (fv_u u1)).
Proof. Admitted.

(* end hide *)

Lemma fv_e_subst_e_upper :
forall e1 u1 x1,
  fv_e (subst_e u1 x1 e1) [<=] fv_u u1 `union` remove x1 (fv_e e1).
Proof. Admitted.

Hint Resolve fv_e_subst_e_upper : lngen.

Lemma fv_u_subst_u_upper :
forall u1 u2 x1,
  fv_u (subst_u u2 x1 u1) [<=] fv_u u2 `union` remove x1 (fv_u u1).
Proof. Admitted.

Hint Resolve fv_u_subst_u_upper : lngen.

(* *********************************************************************** *)
(** * Theorems about [subst] *)

Ltac default_auto ::= auto with lngen brute_force; tauto.
Ltac default_autorewrite ::= autorewrite with lngen.

(* begin hide *)

Lemma subst_e_close_e_wrt_u_rec_subst_u_close_u_wrt_u_rec_mutual :
(forall e1 u1 x1 x2 n1,
  degree_u_wrt_u n1 u1 ->
  x1 <> x2 ->
  x2 `notin` fv_u u1 ->
  subst_e u1 x1 (close_e_wrt_u_rec n1 x2 e1) = close_e_wrt_u_rec n1 x2 (subst_e u1 x1 e1)) /\
(forall u2 u1 x1 x2 n1,
  degree_u_wrt_u n1 u1 ->
  x1 <> x2 ->
  x2 `notin` fv_u u1 ->
  subst_u u1 x1 (close_u_wrt_u_rec n1 x2 u2) = close_u_wrt_u_rec n1 x2 (subst_u u1 x1 u2)).
Proof. Admitted.

(* end hide *)

Lemma subst_e_close_e_wrt_u_rec :
forall e1 u1 x1 x2 n1,
  degree_u_wrt_u n1 u1 ->
  x1 <> x2 ->
  x2 `notin` fv_u u1 ->
  subst_e u1 x1 (close_e_wrt_u_rec n1 x2 e1) = close_e_wrt_u_rec n1 x2 (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_close_e_wrt_u_rec : lngen.

Lemma subst_u_close_u_wrt_u_rec :
forall u2 u1 x1 x2 n1,
  degree_u_wrt_u n1 u1 ->
  x1 <> x2 ->
  x2 `notin` fv_u u1 ->
  subst_u u1 x1 (close_u_wrt_u_rec n1 x2 u2) = close_u_wrt_u_rec n1 x2 (subst_u u1 x1 u2).
Proof. Admitted.

Hint Resolve subst_u_close_u_wrt_u_rec : lngen.

Lemma subst_e_close_e_wrt_u :
forall e1 u1 x1 x2,
  lc_u u1 ->  x1 <> x2 ->
  x2 `notin` fv_u u1 ->
  subst_e u1 x1 (close_e_wrt_u x2 e1) = close_e_wrt_u x2 (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_close_e_wrt_u : lngen.

Lemma subst_u_close_u_wrt_u :
forall u2 u1 x1 x2,
  lc_u u1 ->  x1 <> x2 ->
  x2 `notin` fv_u u1 ->
  subst_u u1 x1 (close_u_wrt_u x2 u2) = close_u_wrt_u x2 (subst_u u1 x1 u2).
Proof. Admitted.

Hint Resolve subst_u_close_u_wrt_u : lngen.

(* begin hide *)

Lemma subst_e_degree_e_wrt_u_subst_u_degree_u_wrt_u_mutual :
(forall e1 u1 x1 n1,
  degree_e_wrt_u n1 e1 ->
  degree_u_wrt_u n1 u1 ->
  degree_e_wrt_u n1 (subst_e u1 x1 e1)) /\
(forall u1 u2 x1 n1,
  degree_u_wrt_u n1 u1 ->
  degree_u_wrt_u n1 u2 ->
  degree_u_wrt_u n1 (subst_u u2 x1 u1)).
Proof. Admitted.

(* end hide *)

Lemma subst_e_degree_e_wrt_u :
forall e1 u1 x1 n1,
  degree_e_wrt_u n1 e1 ->
  degree_u_wrt_u n1 u1 ->
  degree_e_wrt_u n1 (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_degree_e_wrt_u : lngen.

Lemma subst_u_degree_u_wrt_u :
forall u1 u2 x1 n1,
  degree_u_wrt_u n1 u1 ->
  degree_u_wrt_u n1 u2 ->
  degree_u_wrt_u n1 (subst_u u2 x1 u1).
Proof. Admitted.

Hint Resolve subst_u_degree_u_wrt_u : lngen.

(* begin hide *)

Lemma subst_e_fresh_eq_subst_u_fresh_eq_mutual :
(forall e1 u1 x1,
  x1 `notin` fv_e e1 ->
  subst_e u1 x1 e1 = e1) /\
(forall u2 u1 x1,
  x1 `notin` fv_u u2 ->
  subst_u u1 x1 u2 = u2).
Proof. Admitted.

(* end hide *)

Lemma subst_e_fresh_eq :
forall e1 u1 x1,
  x1 `notin` fv_e e1 ->
  subst_e u1 x1 e1 = e1.
Proof. Admitted.

Hint Resolve subst_e_fresh_eq : lngen.
Hint Rewrite subst_e_fresh_eq using solve [auto] : lngen.

Lemma subst_u_fresh_eq :
forall u2 u1 x1,
  x1 `notin` fv_u u2 ->
  subst_u u1 x1 u2 = u2.
Proof. Admitted.

Hint Resolve subst_u_fresh_eq : lngen.
Hint Rewrite subst_u_fresh_eq using solve [auto] : lngen.

(* begin hide *)

Lemma subst_e_fresh_same_subst_u_fresh_same_mutual :
(forall e1 u1 x1,
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_e (subst_e u1 x1 e1)) /\
(forall u2 u1 x1,
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_u (subst_u u1 x1 u2)).
Proof. Admitted.

(* end hide *)

Lemma subst_e_fresh_same :
forall e1 u1 x1,
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_e (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_fresh_same : lngen.

Lemma subst_u_fresh_same :
forall u2 u1 x1,
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_u (subst_u u1 x1 u2).
Proof. Admitted.

Hint Resolve subst_u_fresh_same : lngen.

(* begin hide *)

Lemma subst_e_fresh_subst_u_fresh_mutual :
(forall e1 u1 x1 x2,
  x1 `notin` fv_e e1 ->
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_e (subst_e u1 x2 e1)) /\
(forall u2 u1 x1 x2,
  x1 `notin` fv_u u2 ->
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_u (subst_u u1 x2 u2)).
Proof. Admitted.

(* end hide *)

Lemma subst_e_fresh :
forall e1 u1 x1 x2,
  x1 `notin` fv_e e1 ->
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_e (subst_e u1 x2 e1).
Proof. Admitted.

Hint Resolve subst_e_fresh : lngen.

Lemma subst_u_fresh :
forall u2 u1 x1 x2,
  x1 `notin` fv_u u2 ->
  x1 `notin` fv_u u1 ->
  x1 `notin` fv_u (subst_u u1 x2 u2).
Proof. Admitted.

Hint Resolve subst_u_fresh : lngen.

Lemma subst_e_lc_e :
forall e1 u1 x1,
  lc_e e1 ->
  lc_u u1 ->
  lc_e (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_lc_e : lngen.

Lemma subst_u_lc_u :
forall u1 u2 x1,
  lc_u u1 ->
  lc_u u2 ->
  lc_u (subst_u u2 x1 u1).
Proof. Admitted.

Hint Resolve subst_u_lc_u : lngen.

(* begin hide *)

Lemma subst_e_open_e_wrt_u_rec_subst_u_open_u_wrt_u_rec_mutual :
(forall e1 u1 u2 x1 n1,
  lc_u u1 ->
  subst_e u1 x1 (open_e_wrt_u_rec n1 u2 e1) = open_e_wrt_u_rec n1 (subst_u u1 x1 u2) (subst_e u1 x1 e1)) /\
(forall u3 u1 u2 x1 n1,
  lc_u u1 ->
  subst_u u1 x1 (open_u_wrt_u_rec n1 u2 u3) = open_u_wrt_u_rec n1 (subst_u u1 x1 u2) (subst_u u1 x1 u3)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma subst_e_open_e_wrt_u_rec :
forall e1 u1 u2 x1 n1,
  lc_u u1 ->
  subst_e u1 x1 (open_e_wrt_u_rec n1 u2 e1) = open_e_wrt_u_rec n1 (subst_u u1 x1 u2) (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_open_e_wrt_u_rec : lngen.

(* end hide *)

(* begin hide *)

Lemma subst_u_open_u_wrt_u_rec :
forall u3 u1 u2 x1 n1,
  lc_u u1 ->
  subst_u u1 x1 (open_u_wrt_u_rec n1 u2 u3) = open_u_wrt_u_rec n1 (subst_u u1 x1 u2) (subst_u u1 x1 u3).
Proof. Admitted.

Hint Resolve subst_u_open_u_wrt_u_rec : lngen.

(* end hide *)

Lemma subst_e_open_e_wrt_u :
forall e1 u1 u2 x1,
  lc_u u1 ->
  subst_e u1 x1 (open_e_wrt_u e1 u2) = open_e_wrt_u (subst_e u1 x1 e1) (subst_u u1 x1 u2).
Proof. Admitted.

Hint Resolve subst_e_open_e_wrt_u : lngen.

Lemma subst_u_open_u_wrt_u :
forall u3 u1 u2 x1,
  lc_u u1 ->
  subst_u u1 x1 (open_u_wrt_u u3 u2) = open_u_wrt_u (subst_u u1 x1 u3) (subst_u u1 x1 u2).
Proof. Admitted.

Hint Resolve subst_u_open_u_wrt_u : lngen.

Lemma subst_e_open_e_wrt_u_var :
forall e1 u1 x1 x2,
  x1 <> x2 ->
  lc_u u1 ->
  open_e_wrt_u (subst_e u1 x1 e1) (u_var_f x2) = subst_e u1 x1 (open_e_wrt_u e1 (u_var_f x2)).
Proof. Admitted.

Hint Resolve subst_e_open_e_wrt_u_var : lngen.

Lemma subst_u_open_u_wrt_u_var :
forall u2 u1 x1 x2,
  x1 <> x2 ->
  lc_u u1 ->
  open_u_wrt_u (subst_u u1 x1 u2) (u_var_f x2) = subst_u u1 x1 (open_u_wrt_u u2 (u_var_f x2)).
Proof. Admitted.

Hint Resolve subst_u_open_u_wrt_u_var : lngen.

(* begin hide *)

Lemma subst_e_spec_rec_subst_u_spec_rec_mutual :
(forall e1 u1 x1 n1,
  subst_e u1 x1 e1 = open_e_wrt_u_rec n1 u1 (close_e_wrt_u_rec n1 x1 e1)) /\
(forall u1 u2 x1 n1,
  subst_u u2 x1 u1 = open_u_wrt_u_rec n1 u2 (close_u_wrt_u_rec n1 x1 u1)).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma subst_e_spec_rec :
forall e1 u1 x1 n1,
  subst_e u1 x1 e1 = open_e_wrt_u_rec n1 u1 (close_e_wrt_u_rec n1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_spec_rec : lngen.

(* end hide *)

(* begin hide *)

Lemma subst_u_spec_rec :
forall u1 u2 x1 n1,
  subst_u u2 x1 u1 = open_u_wrt_u_rec n1 u2 (close_u_wrt_u_rec n1 x1 u1).
Proof. Admitted.

Hint Resolve subst_u_spec_rec : lngen.

(* end hide *)

Lemma subst_e_spec :
forall e1 u1 x1,
  subst_e u1 x1 e1 = open_e_wrt_u (close_e_wrt_u x1 e1) u1.
Proof. Admitted.

Hint Resolve subst_e_spec : lngen.

Lemma subst_u_spec :
forall u1 u2 x1,
  subst_u u2 x1 u1 = open_u_wrt_u (close_u_wrt_u x1 u1) u2.
Proof. Admitted.

Hint Resolve subst_u_spec : lngen.

(* begin hide *)

Lemma subst_e_subst_e_subst_u_subst_u_mutual :
(forall e1 u1 u2 x2 x1,
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  subst_e u1 x1 (subst_e u2 x2 e1) = subst_e (subst_u u1 x1 u2) x2 (subst_e u1 x1 e1)) /\
(forall u1 u2 u3 x2 x1,
  x2 `notin` fv_u u2 ->
  x2 <> x1 ->
  subst_u u2 x1 (subst_u u3 x2 u1) = subst_u (subst_u u2 x1 u3) x2 (subst_u u2 x1 u1)).
Proof. Admitted.

(* end hide *)

Lemma subst_e_subst_e :
forall e1 u1 u2 x2 x1,
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  subst_e u1 x1 (subst_e u2 x2 e1) = subst_e (subst_u u1 x1 u2) x2 (subst_e u1 x1 e1).
Proof. Admitted.

Hint Resolve subst_e_subst_e : lngen.

Lemma subst_u_subst_u :
forall u1 u2 u3 x2 x1,
  x2 `notin` fv_u u2 ->
  x2 <> x1 ->
  subst_u u2 x1 (subst_u u3 x2 u1) = subst_u (subst_u u2 x1 u3) x2 (subst_u u2 x1 u1).
Proof. Admitted.

Hint Resolve subst_u_subst_u : lngen.

(* begin hide *)

Lemma subst_e_close_e_wrt_u_rec_open_e_wrt_u_rec_subst_u_close_u_wrt_u_rec_open_u_wrt_u_rec_mutual :
(forall e1 u1 x1 x2 n1,
  x2 `notin` fv_e e1 ->
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  degree_u_wrt_u n1 u1 ->
  subst_e u1 x1 e1 = close_e_wrt_u_rec n1 x2 (subst_e u1 x1 (open_e_wrt_u_rec n1 (u_var_f x2) e1))) *
(forall u2 u1 x1 x2 n1,
  x2 `notin` fv_u u2 ->
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  degree_u_wrt_u n1 u1 ->
  subst_u u1 x1 u2 = close_u_wrt_u_rec n1 x2 (subst_u u1 x1 (open_u_wrt_u_rec n1 (u_var_f x2) u2))).
Proof. Admitted.

(* end hide *)

(* begin hide *)

Lemma subst_e_close_e_wrt_u_rec_open_e_wrt_u_rec :
forall e1 u1 x1 x2 n1,
  x2 `notin` fv_e e1 ->
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  degree_u_wrt_u n1 u1 ->
  subst_e u1 x1 e1 = close_e_wrt_u_rec n1 x2 (subst_e u1 x1 (open_e_wrt_u_rec n1 (u_var_f x2) e1)).
Proof. Admitted.

Hint Resolve subst_e_close_e_wrt_u_rec_open_e_wrt_u_rec : lngen.

(* end hide *)

(* begin hide *)

Lemma subst_u_close_u_wrt_u_rec_open_u_wrt_u_rec :
forall u2 u1 x1 x2 n1,
  x2 `notin` fv_u u2 ->
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  degree_u_wrt_u n1 u1 ->
  subst_u u1 x1 u2 = close_u_wrt_u_rec n1 x2 (subst_u u1 x1 (open_u_wrt_u_rec n1 (u_var_f x2) u2)).
Proof. Admitted.

Hint Resolve subst_u_close_u_wrt_u_rec_open_u_wrt_u_rec : lngen.

(* end hide *)

Lemma subst_e_close_e_wrt_u_open_e_wrt_u :
forall e1 u1 x1 x2,
  x2 `notin` fv_e e1 ->
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  lc_u u1 ->
  subst_e u1 x1 e1 = close_e_wrt_u x2 (subst_e u1 x1 (open_e_wrt_u e1 (u_var_f x2))).
Proof. Admitted.

Hint Resolve subst_e_close_e_wrt_u_open_e_wrt_u : lngen.

Lemma subst_u_close_u_wrt_u_open_u_wrt_u :
forall u2 u1 x1 x2,
  x2 `notin` fv_u u2 ->
  x2 `notin` fv_u u1 ->
  x2 <> x1 ->
  lc_u u1 ->
  subst_u u1 x1 u2 = close_u_wrt_u x2 (subst_u u1 x1 (open_u_wrt_u u2 (u_var_f x2))).
Proof. Admitted.

Hint Resolve subst_u_close_u_wrt_u_open_u_wrt_u : lngen.

Lemma subst_u_u_lam :
forall x2 t1 e1 u1 x1,
  lc_u u1 ->
  x2 `notin` fv_u u1 `union` fv_e e1 `union` singleton x1 ->
  subst_u u1 x1 (u_lam t1 e1) = u_lam (t1) (close_e_wrt_u x2 (subst_e u1 x1 (open_e_wrt_u e1 (u_var_f x2)))).
Proof. Admitted.

Hint Resolve subst_u_u_lam : lngen.

Lemma subst_u_u_let :
forall x2 e1 u2 u1 x1,
  lc_u u1 ->
  x2 `notin` fv_u u1 `union` fv_u u2 `union` singleton x1 ->
  subst_u u1 x1 (u_let e1 u2) = u_let (subst_e u1 x1 e1) (close_u_wrt_u x2 (subst_u u1 x1 (open_u_wrt_u u2 (u_var_f x2)))).
Proof. Admitted.

Hint Resolve subst_u_u_let : lngen.

(* begin hide *)

Lemma subst_e_intro_rec_subst_u_intro_rec_mutual :
(forall e1 x1 u1 n1,
  x1 `notin` fv_e e1 ->
  open_e_wrt_u_rec n1 u1 e1 = subst_e u1 x1 (open_e_wrt_u_rec n1 (u_var_f x1) e1)) /\
(forall u1 x1 u2 n1,
  x1 `notin` fv_u u1 ->
  open_u_wrt_u_rec n1 u2 u1 = subst_u u2 x1 (open_u_wrt_u_rec n1 (u_var_f x1) u1)).
Proof. Admitted.

(* end hide *)

Lemma subst_e_intro_rec :
forall e1 x1 u1 n1,
  x1 `notin` fv_e e1 ->
  open_e_wrt_u_rec n1 u1 e1 = subst_e u1 x1 (open_e_wrt_u_rec n1 (u_var_f x1) e1).
Proof. Admitted.

Hint Resolve subst_e_intro_rec : lngen.
Hint Rewrite subst_e_intro_rec using solve [auto] : lngen.

Lemma subst_u_intro_rec :
forall u1 x1 u2 n1,
  x1 `notin` fv_u u1 ->
  open_u_wrt_u_rec n1 u2 u1 = subst_u u2 x1 (open_u_wrt_u_rec n1 (u_var_f x1) u1).
Proof. Admitted.

Hint Resolve subst_u_intro_rec : lngen.
Hint Rewrite subst_u_intro_rec using solve [auto] : lngen.

Lemma subst_e_intro :
forall x1 e1 u1,
  x1 `notin` fv_e e1 ->
  open_e_wrt_u e1 u1 = subst_e u1 x1 (open_e_wrt_u e1 (u_var_f x1)).
Proof. Admitted.

Hint Resolve subst_e_intro : lngen.

Lemma subst_u_intro :
forall x1 u1 u2,
  x1 `notin` fv_u u1 ->
  open_u_wrt_u u1 u2 = subst_u u2 x1 (open_u_wrt_u u1 (u_var_f x1)).
Proof. Admitted.

Hint Resolve subst_u_intro : lngen.

(* *********************************************************************** *)
(** * "Restore" tactics *)

Ltac default_auto ::= auto; tauto.
Ltac default_autorewrite ::= fail.


(** * Undecidability of the halting problem of 2 Counter Machines a la Pierce *)

Require Import Undecidability.Synthetic.Undecidability.

From Undecidability.CounterMachines
  Require Import CM2 CM2_undec.

Require Import CM2 Reductions.CM2_HALT_to_CM2_HALT.

Lemma CM2_HALT_undec : undecidable CM2_HALT.
Proof.
  apply (undecidability_from_reducibility CM2_HALT_undec).
  exact CM2_HALT_to_CM2_HALT.reduction.
Qed.


(*
 This is the definition of formal syntax for Dan Grossman's Thesis,
  "SAFE PROGRAMMING AT THE C LEVEL OF ABSTRACTION".

 This defines the dynamic semantics of heap objects, pg. 60.

*)

Set Implicit Arguments.
Require Export List.
Require Export Cyclone_Formal_Syntax Cyclone_LN_Types_In_Terms.
Open Scope env_scope.
Open Scope list_scope.
Import ListNotations.

Inductive get' : E -> Path -> E -> Prop :=
 | get'_pdot: forall (v : E),
               Value v ->
               get' v [] v
 | get'_l:    forall (v v0 v1 : E) (p : Path),
               Value v  ->
               Value v0 ->
               Value v1 ->
               get' v0 p v ->
               get' (cpair v0 v1) ((i_pe zero_pe) :: p) v
 | get'_r:    forall (v v0 v1 : E) (p : Path),
               Value v  ->
               Value v0 ->
               Value v1 ->
               get' v1 p v ->
               get' (cpair v0 v1) ((i_pe one_pe) :: p) v
 | get'_pack: forall (v v1 : E) (p : Path) (tau tau' : Tau) (alpha : var)
                    (k : Kappa),
               Value v  ->
               Value v1 ->
               get' v1 p v ->
               get' (pack tau' v1 (etype aliases k tau)) (u_pe :: p) v.
Hint Constructors get'.

Inductive set' : E -> Path -> E -> E -> Prop :=
  | set'_ip: forall (v v' : E),
                Value v  ->
                Value v' ->
                set' v' [] v v

  | set'_l:    forall (v v' v0 v1 : E) (p : Path),
                Value v  ->
                Value v' ->
                Value v0 ->
                set' v0 p v v' ->
                set' (cpair v0 v1)  ((i_pe zero_pe) :: p) v (cpair v' v1)

  | set'_r:    forall (v v' v0 v1 : E) (p : Path),
                Value v  ->
                Value v' ->
                Value v0 ->
                set' v1 p v v' ->
                set' (cpair v0 v1)  ((i_pe one_pe) :: p) v (cpair v0 v')

  | set'_pack: forall (v v' v1 : E) (p : Path) (tau tau' : Tau)
                     (q : Phi) (alpha : var) (k : Kappa),
                Value v  ->
                Value v' ->
                Value v1 ->
                set' (pack tau' v1 (etype q k tau))
                    (u_pe :: p) v (pack tau' v' (etype q k tau)).
Hint Constructors set'.


Require Import Heaps.
Require SepExpr Expr.
Require Import Provers.
Require Import EquivDec.
Require Import List.

Set Implicit Arguments.

Module SepExprTests (B : Heap).
  Module ST := SepTheoryX.SepTheoryX (B).
  Module Sep := SepExpr.SepExpr B ST.

  (** Just a test separation logic predicate **)
  Section Tests.
    Variable f : forall a b, nat -> ST.hprop a b nil.
    Variable h : forall a b, nat -> ST.hprop a b nil.
    Variable i : forall a b, nat -> ST.hprop a b nil.
    Variable g : bool -> nat -> nat -> nat.

    Ltac isConst e :=
      match e with
        | true => true
        | false => true
        | O => true
        | S ?e => isConst e
        | _ => false
      end.

    Definition nat_type : Expr.type :=
      {| Expr.Impl := nat
       ; Expr.Eq := fun x y => match equiv_dec x y with
                                 | left pf => Some pf
                                 | _ => None
                               end
       |}.

    Fixpoint star_all a b (f : nat -> ST.hprop a b nil) (n : nat) : ST.hprop a b nil :=
      match n with
        | 0 => f 0
        | S n => ST.star (f (S n)) (star_all f n)
      end.

    Fixpoint star_all_back a b (f : nat -> ST.hprop a b nil) (n m : nat) : ST.hprop a b nil :=
      match n with
        | 0 => f m
        | S n => ST.star (f (m - S n)) (star_all_back f n m)
      end.

    Opaque ST.himp ST.star ST.emp ST.inj ST.ex.

    Import SepExpr Sep Expr ExprUnify DepList.

    Ltac simplifier := cbv beta iota zeta delta [CancelSep sepCancel hash hash' liftSHeap sheapSubstU liftExpr
      SepExpr.FM.add SepExpr.FM.fold SepExpr.FM.map SepExpr.FM.find SepExpr.FM.remove
        SepExpr.FM.empty SepExpr.FM.insert_at_right
        other pures impures star_SHeap SHeap_empty
        unify_remove unify_remove_all exprUnifyArgs exprUnify empty_Subst Subst_lookup env_of_Subst
        Expr.Impl Expr.Eq
        List.map List.length List.app fold_left_2_opt List.fold_right List.nth_error
        starred sheapD exprD
        exprSubstU
        Compare_dec.lt_eq_lt_dec Compare_dec.lt_dec Peano_dec.eq_nat_dec
        nat_rec nat_rect forallEach env exists_subst multimap_join equiv_dec seq_dec
        Domain Range
        EqDec_tvar tvar_rec tvar_rect
        lookupAs sumbool_rec sumbool_rect
        fst snd
        eq_rec_r eq_rec eq_rect Logic.eq_sym f_equal get_Eq value
        nat_eq_eqdec
        eq_summary eq_summarize eq_prove
        sexprD Compare_dec.le_dec Compare_dec.le_gt_dec Compare_dec.le_lt_dec
        Subst_replace plus minus substV

        transitivityEqProverRec groupsOf transitivityEqProver addEquality expr_seq_dec
        applyD
        in_seq_dec eqD_seq groupWith Expr.typeof unifyArgs inSameGroup fold_right
        bool_eqdec Bool.bool_dec bool_rec bool_rect

        nat_type
    ]; fold plus; fold minus.

    Ltac sep := simpl; intros;
      Sep.sep isConst transitivityEqProverRec simplifier (nat_type :: nil); try reflexivity.

    Goal forall a b c x y, @ST.himp a b c (f _ _ (g y (x + x) 1)) (f _ _ 1).
      sep.
    Abort.

    Theorem t1 : forall a b c, @ST.himp a b c (f _ _ 0) (f _ _ 0).
      sep.
    Qed.

    Theorem t2 : forall a b c,
      @ST.himp a b c (ST.star (star_all_back (@h a b) 15 15) (star_all_back (@f a b) 15 15))
                     (ST.star (star_all (@f a b) 15) (star_all (@h a b) 15)).
      sep.
    Qed.

    Theorem t3 : forall a b c, @ST.himp a b c
      (ST.star (f _ _ 2) (f _ _ 1))
      (f _ _ 1).
      sep.
    Abort.

    Theorem t4 : forall a b c, @ST.himp a b c
      (ST.ex (fun y : nat => ST.ex (fun x : bool => ST.star (f _ _ (g x 1 2)) (f _ _ 1) )))
      (f _ _ 1).
      sep.
    Abort.

    Theorem t5 : forall a b c, @ST.himp a b c
      (ST.ex (fun y : nat => f _ _ y))
      (f _ _ 1).
      sep.
    Abort.

    Theorem t6 : forall a b c, @ST.himp a b c
      (f _ _ 1)
      (ST.ex (fun y : nat => f _ _ y)).
      sep.
    Qed.

    Theorem t7 : forall a b c, @ST.himp a b c
      (ST.star (f _ _ (g true 0 1)) (f _ _ (g true 1 2)))
      (ST.ex (fun y : nat => ST.star (f _ _ (g true 0 y)) (ST.ex (fun z : nat => f _ _ (g true 1 z))))).
      sep.
    Qed.

    Theorem t8 : forall a b c, @ST.himp a b c
      (ST.star (f _ _ (g true 0 1)) (f _ _ (g true 1 2)))
      (ST.ex (fun y : nat => ST.star (f _ _ (g true 1 y)) (ST.ex (fun z : nat => f _ _ (g true 0 z))))).
      sep.
    Qed.

    (** ** Test use of transitivity prover in cancellation *)

    Theorem t9 : forall a b c x y, x = y
      -> @ST.himp a b c  (f _ _ x) (f _ _ y).
      sep.
    Qed.

    Theorem t10 : forall a b c x y, x = y
      -> @ST.himp a b c  (f _ _ y) (f _ _ x).
      sep.
    Qed.

    Theorem t11 : forall a b c x y z, x = y
      -> x = z
      -> @ST.himp a b c  (f _ _ x) (f _ _ y).
      sep.
    Qed.

    Theorem t12 : forall a b c x y u v, x = y
      -> u = v
      -> @ST.himp a b c  (ST.star (f _ _ x) (f _ _ v)) (ST.star (f _ _ y) (f _ _ u)).
      sep.
    Qed.

    Theorem t13 : forall a b c x y z u v, x = y
      -> z = y
      -> u = v
      -> @ST.himp a b c  (ST.star (f _ _ x) (f _ _ v)) (ST.star (f _ _ z) (f _ _ u)).
      sep.
    Qed.

  End Tests.

End SepExprTests.


Require Import Category.Lib.
Require Import Category.Theory.Category.
Require Import Category.Theory.Isomorphism.
Require Import Category.Theory.Functor.
Require Import Category.Theory.Natural.Transformation.
Require Import Category.Theory.Adjunction.
Require Import Category.Theory.Kan.Extension.
Require Import Category.Structure.Cone.
Require Import Category.Structure.Limit.
Require Import Category.Functor.Diagonal.
Require Import Category.Instance.One.
Require Import Category.Instance.Sets.
Require Import Category.Structure.Cone.Const.

Generalizable All Variables.

Theorem Kan_Limit `(F : J ⟶ C) `{Lim : @Limit _ _ F} `{@RightKan _ _ (Erase J) C} :
  Lim ≅ Ran (Erase J) F ttt.
Proof.
  given (cone : Cone F). {
    pose (from ((@adj _ _ _ _ ran_adjoint
                   (Ran (Erase J) F) F)) nat_id) as adj_from.
    simpl carrier in adj_from.
    unshelve (refine {| vertex_obj := Ran (Erase J) F ttt |});
      [ assumption | unshelve econstructor ].
    - apply adj_from.
    - abstract (intros; rewrite (naturality[adj_from]); simpl; cat).
  }
  given (nat : Δ(Lim) ◯ Erase J ⟹ F). {
    transform; simpl.
    + exact vertex_map.
    + abstract(rewrite id_right; apply cone_coherence).
    + abstract(rewrite id_right; symmetry; apply cone_coherence).
  }
  pose (to (@adj _ _ _ _ ran_adjoint (Δ(Lim)) F) nat)
    as adj_to; simpl in adj_to.

  assert (to_from : adj_to ttt ∘ unique_obj (ump_limits cone) ≈ id). {
    simpl.
    spose (iso_to_from
             ((@adj _ _ _ _ ran_adjoint
                    (Ran (Erase J) F) F)) nat_id ttt) as X.
    rewrite fmap_id in X.
    rewrites.
    unfold adj_to; simpl.

    given (from_ran : Ran (Erase J) F ⟹ Δ(Lim)). {
      transform; simpl; intros.
      - destruct x.
        apply (unique_obj (ump_limits cone)).
      - destruct x, y, f; simpl.
        abstract cat.
      - destruct x, y, f; simpl.
        abstract cat.
    }

    spose (@to_adj_nat_l _ _ _ _ ran_adjoint
                         (Ran (Erase J) F) (Δ(Lim))
                         F nat from_ran ttt) as X0.
    rewrites.

    assert (∀ f g, f ≈ g
              → to (@adj _ _ _ _ ran_adjoint (Ran (Erase J) F) F) f
               ≈ to (@adj _ _ _ _ ran_adjoint (Ran (Erase J) F) F) g). {
      intros; rewrites; reflexivity.
    }
    simpl in X.
    rewrite <- X.
    - rewrite <- X0.
      apply X; simpl.
      apply (unique_property (ump_limits cone)).
    - simpl; reflexivity.
  }

  isomorphism; simpl.
  - apply adj_to.
  - apply (unique_obj (ump_limits cone)).
  - apply to_from.
  - simpl in *.
    (* Since half of the isomorphism has already been proven in [to_from], it
       is sufficient to show that either [(adj nat) ttt] is monic, or
       [unique_morphism (ump_limits cone)] is epic. I've chosen the latter due
       to Emily Riehl's statement in "Category Theory in Context" (p. 76):
       "... Proposition 3.1.7 implies that the only automorphism of [a limit
       object] l that commutes with the specified limit cone λ is the
       identity." *)

    assert (∀ (f g : Lim ~{ C }~> Lim),
              (∀ x, vertex_map[Lim] ∘ f ≈ @vertex_map _ _ _ _ (@coneFrom _ _ _ Lim) x) ->
              (∀ x, vertex_map[Lim] ∘ g ≈ @vertex_map _ _ _ _ (@coneFrom _ _ _ Lim) x) ->
              f ∘ unique_obj (ump_limits cone) ≈
              g ∘ unique_obj (ump_limits cone) → f ≈ g) as HA. {
      intros; clear adj_to to_from nat.
      rewrite <- (uniqueness (ump_limits Lim) _ X).
      rewrite <- (uniqueness (ump_limits Lim) _ X0).
      reflexivity.
    }

    (* Apply the consequence that [unique_morphism (ump_limits cone)] is epic
      on morphisms commuting with the limit cone, leaving us with the burden
      of showing that the two sides of our equivalence do in fact commute for
      [Lim F]. The right side is trivial (id), while the left side relies on
      the naturality of the Kan adjunction. *)
    apply HA; simpl; intros; cat; swap 1 2.
    + rewrite <- comp_assoc.
      rewrite to_from; cat.

    + rewrite comp_assoc.
      srewrite (unique_property (ump_limits cone)).
      srewrite_r (iso_from_to
                    ((@adj _ _ _ _ ran_adjoint (Δ(Lim)) F)) nat x).
      unfold adj_to.
      srewrite_r (@from_adj_nat_l _ _ _ _ ran_adjoint
                    (Δ(Lim)) (Ran (Erase J) F) F nat_id
                    (to (@adj _ _ _ _ ran_adjoint (Δ(Lim)) F) nat) x).
      sapply (@from_adj_respects
                _ _ _ _ (@ran_adjoint _ _ _ _ H) (Δ(Lim)) F).
      simpl; intros; cat.
Qed.