Inductive term : Set :=
    | Srt : sort -> term
    | Ref : nat -> term
    | Abs : term -> term -> term
    | App : term -> term -> term
    | Prod : term -> term -> term.Inductive term : Set :=
    | Srt : sort -> term
    | Ref : nat -> term
    | Abs : term -> term -> term
    | App : term -> term -> term
    | Prod : term -> term -> term.Inductive term : Set :=
    | Srt : sort -> term
    | Ref : nat -> term
    | Abs : term -> term -> term
    | App : term -> term -> term
    | Prod : term -> term -> term.Inductive term : Set :=
    | Srt : sort -> term
    | Ref : nat -> term
    | Abs : term -> term -> term
    | App : term -> term -> term
    | Prod : term -> term -> term.Inductive term : Set :=
    | Srt : sort -> term
    | Ref : nat -> term
    | Abs : term -> term -> term
    | App : term -> term -> term
    | Prod : term -> term -> term.Inductive term : Set :=
    | Srt : sort -> term
    | Ref : nat -> term
    | Abs : term -> term -> term
    | App : term -> term -> term
    | Prod : term -> term -> term.(* Variable bindings and free variables *)

  Inductive subt_bind (T M : term) : term -> Prop :=
    | Bsbt_abs : subt_bind T M (Abs T M)
    | Bsbt_prod : subt_bind T M (Prod T M).(* Variable bindings and free variables *)

  Inductive subt_bind (T M : term) : term -> Prop :=
    | Bsbt_abs : subt_bind T M (Abs T M)
    | Bsbt_prod : subt_bind T M (Prod T M).(* Variable bindings and free variables *)

  Inductive subt_bind (T M : term) : term -> Prop :=
    | Bsbt_abs : subt_bind T M (Abs T M)
    | Bsbt_prod : subt_bind T M (Prod T M).Inductive subt_nobind (m : term) : term -> Prop :=
    | Nbsbt_abs : forall n : term, subt_nobind m (Abs m n)
    | Nbsbt_app_l : forall v : term, subt_nobind m (App m v)
    | Nbsbt_app_r : forall u : term, subt_nobind m (App u m)
    | Nbsbt_prod : forall n : term, subt_nobind m (Prod m n).Inductive subt_nobind (m : term) : term -> Prop :=
    | Nbsbt_abs : forall n : term, subt_nobind m (Abs m n)
    | Nbsbt_app_l : forall v : term, subt_nobind m (App m v)
    | Nbsbt_app_r : forall u : term, subt_nobind m (App u m)
    | Nbsbt_prod : forall n : term, subt_nobind m (Prod m n).Inductive subt_nobind (m : term) : term -> Prop :=
    | Nbsbt_abs : forall n : term, subt_nobind m (Abs m n)
    | Nbsbt_app_l : forall v : term, subt_nobind m (App m v)
    | Nbsbt_app_r : forall u : term, subt_nobind m (App u m)
    | Nbsbt_prod : forall n : term, subt_nobind m (Prod m n).Inductive subt_nobind (m : term) : term -> Prop :=
    | Nbsbt_abs : forall n : term, subt_nobind m (Abs m n)
    | Nbsbt_app_l : forall v : term, subt_nobind m (App m v)
    | Nbsbt_app_r : forall u : term, subt_nobind m (App u m)
    | Nbsbt_prod : forall n : term, subt_nobind m (Prod m n).Inductive subt_nobind (m : term) : term -> Prop :=
    | Nbsbt_abs : forall n : term, subt_nobind m (Abs m n)
    | Nbsbt_app_l : forall v : term, subt_nobind m (App m v)
    | Nbsbt_app_r : forall u : term, subt_nobind m (App u m)
    | Nbsbt_prod : forall n : term, subt_nobind m (Prod m n).Inductive free_db : nat -> term -> Prop :=
    | Db_srt : forall (n : nat) (s : sort), free_db n (Srt s)
    | Db_ref : forall k n : nat, k > n -> free_db k (Ref n)
    | Db_abs :
        forall (k : nat) (A M : term),
        free_db k A -> free_db (S k) M -> free_db k (Abs A M)
    | Db_app :
        forall (k : nat) (u v : term),
        free_db k u -> free_db k v -> free_db k (App u v)
    | Db_prod :
        forall (k : nat) (A B : term),
        free_db k A -> free_db (S k) B -> free_db k (Prod A B).Inductive free_db : nat -> term -> Prop :=
    | Db_srt : forall (n : nat) (s : sort), free_db n (Srt s)
    | Db_ref : forall k n : nat, k > n -> free_db k (Ref n)
    | Db_abs :
        forall (k : nat) (A M : term),
        free_db k A -> free_db (S k) M -> free_db k (Abs A M)
    | Db_app :
        forall (k : nat) (u v : term),
        free_db k u -> free_db k v -> free_db k (App u v)
    | Db_prod :
        forall (k : nat) (A B : term),
        free_db k A -> free_db (S k) B -> free_db k (Prod A B).Inductive free_db : nat -> term -> Prop :=
    | Db_srt : forall (n : nat) (s : sort), free_db n (Srt s)
    | Db_ref : forall k n : nat, k > n -> free_db k (Ref n)
    | Db_abs :
        forall (k : nat) (A M : term),
        free_db k A -> free_db (S k) M -> free_db k (Abs A M)
    | Db_app :
        forall (k : nat) (u v : term),
        free_db k u -> free_db k v -> free_db k (App u v)
    | Db_prod :
        forall (k : nat) (A B : term),
        free_db k A -> free_db (S k) B -> free_db k (Prod A B).Inductive free_db : nat -> term -> Prop :=
    | Db_srt : forall (n : nat) (s : sort), free_db n (Srt s)
    | Db_ref : forall k n : nat, k > n -> free_db k (Ref n)
    | Db_abs :
        forall (k : nat) (A M : term),
        free_db k A -> free_db (S k) M -> free_db k (Abs A M)
    | Db_app :
        forall (k : nat) (u v : term),
        free_db k u -> free_db k v -> free_db k (App u v)
    | Db_prod :
        forall (k : nat) (A B : term),
        free_db k A -> free_db (S k) B -> free_db k (Prod A B).Inductive free_db : nat -> term -> Prop :=
    | Db_srt : forall (n : nat) (s : sort), free_db n (Srt s)
    | Db_ref : forall k n : nat, k > n -> free_db k (Ref n)
    | Db_abs :
        forall (k : nat) (A M : term),
        free_db k A -> free_db (S k) M -> free_db k (Abs A M)
    | Db_app :
        forall (k : nat) (u v : term),
        free_db k u -> free_db k v -> free_db k (App u v)
    | Db_prod :
        forall (k : nat) (A B : term),
        free_db k A -> free_db (S k) B -> free_db k (Prod A B).Inductive free_db : nat -> term -> Prop :=
    | Db_srt : forall (n : nat) (s : sort), free_db n (Srt s)
    | Db_ref : forall k n : nat, k > n -> free_db k (Ref n)
    | Db_abs :
        forall (k : nat) (A M : term),
        free_db k A -> free_db (S k) M -> free_db k (Abs A M)
    | Db_app :
        forall (k : nat) (u v : term),
        free_db k u -> free_db k v -> free_db k (App u v)
    | Db_prod :
        forall (k : nat) (A B : term),
        free_db k A -> free_db (S k) B -> free_db k (Prod A B).(* Relocation and substitution *)

  Fixpoint lift_rec (n : nat) (t : term) {struct t} : 
   nat -> term :=
    fun k =>
    match t with
    | Srt s => Srt s
    | Ref i =>
        Ref match le_gt_dec k i with
            | left _ => n + i
            | right _ => i
            end
    | Abs a m => Abs (lift_rec n a k) (lift_rec n m (S k))
    | App u v => App (lift_rec n u k) (lift_rec n v k)
    | Prod a b => Prod (lift_rec n a k) (lift_rec n b (S k))
    end.Definition lift (n : nat) (N : term) := lift_rec n N 0.Fixpoint subst_rec (ts t : term) {struct t} : nat -> term :=
    fun k =>
    match t with
    | Srt s => Srt s
    | Ref i =>
        match lt_eq_lt_dec k i with
        | inleft (left _) => Ref (pred i)
        | inleft (right _) => lift k ts
        | inright _ => Ref i
        end
    | Abs a m => Abs (subst_rec ts a k) (subst_rec ts m (S k))
    | App u v => App (subst_rec ts u k) (subst_rec ts v k)
    | Prod a b => Prod (subst_rec ts a k) (subst_rec ts b (S k))
    end.Definition subst (N M : term) := subst_rec N M 0.

(* Specifications: *)
Theorem lift_naif : forall (n : nat) (t : term), {u : term | u = lift n t}.
Theorem subst_naif : forall u v : term, {t : term | t = subst u v}.
Lemma lift_ref_ge : forall k n p : nat, p <= n -> lift_rec k (Ref n) p = Ref (k + n).
Lemma lift_ref_lt : forall k n p : nat, p > n -> lift_rec k (Ref n) p = Ref n.
Lemma subst_ref_lt : forall (u : term) (n k : nat), k > n -> subst_rec u (Ref n) k = Ref n.
Lemma subst_ref_gt : forall (u : term) (n k : nat), n > k -> subst_rec u (Ref n) k = Ref (pred n).
Lemma subst_ref_eq : forall (u : term) (n : nat), subst_rec u (Ref n) n = lift n u.
Lemma lift_rec0 : forall (M : term) (k : nat), lift_rec 0 M k = M.
Lemma lift0 : forall M : term, lift 0 M = M.
Lemma simpl_lift_rec : forall (M : term) (n k p i : nat), i <= k + n -> k <= i -> lift_rec p (lift_rec n M k) i = lift_rec (p + n) M k.
Lemma simpl_lift : forall (M : term) (n : nat), lift (S n) M = lift 1 (lift n M).
Lemma permute_lift_rec : forall (M : term) (n k p i : nat), i <= k -> lift_rec p (lift_rec n M k) i = lift_rec n (lift_rec p M i) (p + k).
Lemma permute_lift : forall (M : term) (k : nat), lift 1 (lift_rec 1 M k) = lift_rec 1 (lift 1 M) (S k).
Lemma simpl_subst_rec : forall (N M : term) (n p k : nat), p <= n + k -> k <= p -> subst_rec N (lift_rec (S n) M k) p = lift_rec n M k.
Lemma simpl_subst : forall (N M : term) (n p : nat), p <= n -> subst_rec N (lift (S n) M) p = lift n M.
Lemma commut_lift_subst_rec : forall (M N : term) (n p k : nat), k <= p -> lift_rec n (subst_rec N M p) k = subst_rec N (lift_rec n M k) (n + p).
Lemma commut_lift_subst : forall (M N : term) (k : nat), subst_rec N (lift 1 M) (S k) = lift 1 (subst_rec N M k).
Lemma distr_lift_subst_rec : forall (M N : term) (n p k : nat), lift_rec n (subst_rec N M p) (p + k) = subst_rec (lift_rec n N k) (lift_rec n M (S (p + k))) p.
Lemma distr_lift_subst : forall (M N : term) (n k : nat), lift_rec n (subst N M) k = subst (lift_rec n N k) (lift_rec n M (S k)).
Lemma distr_subst_rec : forall (M N P : term) (n p : nat), subst_rec P (subst_rec N M p) (p + n) = subst_rec (subst_rec P N n) (subst_rec P M (S (p + n))) p.
Lemma distr_subst : forall (P N M : term) (k : nat), subst_rec P (subst N M) k = subst (subst_rec P N k) (subst_rec P M (S k)).