Mentions légales du service

Skip to content
Snippets Groups Projects

System F normalization in coq

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by BLOT Valentin
    Edited
    Normalization.v 58.81 KiB
    (* Tested with coq 8.13.2 *)
    Require Import Arith.
    Require Import Lia.
    Require Import Bool.
    Require Import List.
    Reserved Notation "↑ d t" (at level 27, d at level 0, t at level 0).
    Reserved Notation "t [ n ↦ u ]" (at level 26, left associativity).
    Reserved Notation "⇑ d t" (at level 27, d at level 0, t at level 0).
    Reserved Notation "t [[ n ↦ s ]]" (at level 26, left associativity).
    Reserved Notation "⇧ d t" (at level 27, d at level 0, t at level 0).
    Reserved Notation "# n" (at level 25, n at level 0).
    Reserved Notation "'λ' t" (at level 28, t at level 0).
    Reserved Notation "t1 @ t2" (at level 29, left associativity).
    Reserved Notation "& n" (at level 25).
    Reserved Notation "∀ ty" (at level 28).
    Reserved Notation "ty1 ⇒ ty2" (at level 30, right associativity).
    Reserved Notation "t ≻ t'" (at level 70, no associativity).
    Reserved Notation "t ≻* t'" (at level 70, no associativity).
    Reserved Notation "t ≻' t'" (at level 70, no associativity).
    Reserved Notation "t ≻'* t'" (at level 70, no associativity).
    Reserved Notation "t ≻₀ t'" (at level 70, no associativity).
    Reserved Notation "t ≻₀* t'" (at level 70, no associativity).
    Reserved Notation "X <-> Y" (at level 95, no associativity).
    Reserved Notation "X <~> Y" (at level 95, no associativity).
    Reserved Notation "X <~~> Y" (at level 95, no associativity).
    Reserved Notation " t_ctx ⊩ term ∈ type " (at level 70, no associativity).
    Reserved Notation " ctx ⊢ term ¦ type " (at level 70, no associativity).
    Reserved Notation "p … n -1" (at level 30, no associativity).
    Set Implicit Arguments.
    Open Scope type_scope.
    (** * Equivalences
    
      Some definitions and lemmas about equivalences *)
    Lemma iff_forall : forall (A : Type) (P Q R : (A -> Prop) -> Prop),
      (forall X, R X -> (P X <-> Q X))
        -> ((forall X, R X -> P X) <-> (forall X, R X -> Q X)).
    Proof.
    intros A P Q R H.
    split; intros H' X HX; apply H; try apply H'; apply HX.
    Qed.
    Definition iffE (A : Type) (X Y : A -> Prop) : Prop :=
      forall (a : A), (X a) <-> (Y a).
    Infix "<~>" := iffE.
    Lemma iffE_trans : forall (A : Type) (X Y Z : A -> Prop),
      (X <~> Y) -> (Y <~> Z) -> (X <~> Z).
    Proof.
    simpl.
    intros A X Y Z Heq1 Heq2 t.
    split; intro H.
    apply Heq2; apply Heq1; assumption.
    apply Heq1; apply Heq2; assumption.
    Qed.
    Lemma iffE_forall : forall (A : Type) (P Q P' Q' : A -> Prop),
      (P <~> Q)
        -> (P' <~> Q')
        -> ((forall X, P X -> P' X) <-> (forall X, Q X -> Q' X)).
    Proof.
    intros A P Q P' Q' H H'.
    split; intros H0 X HX; apply H'; apply H0; apply H; apply HX.
    Qed.
    Definition iffE2 (A B : Type) (X Y : A -> B -> Prop) :=
      forall (a : A), (X a <~> Y a).
    Infix "<~~>" := iffE2.
    Lemma iffE2_trans : forall (A B : Type) (X Y Z : A -> B -> Prop),
      (X<~~> Y) -> (Y <~~> Z) -> (X <~~> Z).
    Proof.
    simpl.
    intros A B X Y Z Heq1 Heq2 n t.
    split; intro H.
    apply Heq2; apply Heq1; assumption.
    apply Heq1; apply Heq2; assumption.
    Qed.
    Section Substitutions.
    (** * Binary trees
    
      This section contains all the technical stuff about
      binary trees with binders (encoded using de Bruijn indices).
      These trees will be used for pure λ-terms as well as for
      System F types
      ----In this definition, the [A] parameter is only a trick allowing
      to have different notations for λ-terms and System F types *)
    Inductive MakeTree {A : Type} : Type :=
      | Leaf : nat -> @MakeTree A
      | Bind : @MakeTree A -> @MakeTree A
      | Node : @MakeTree A -> @MakeTree A -> @MakeTree A.
    Variable A : Type.
    Notation Tree := (@MakeTree A).
    Notation "# n" := (@Leaf A n).
    Notation "'λ' t" := (@Bind A t).
    Infix "@" := (@Node A).
    (** [shift d t] increases every free variable of [t] which has an
      index >= [d]. This will be used in the definition of substitution *)
    Fixpoint shift (depth : nat) (tree : Tree) : Tree :=
      match tree with
        | Leaf n => if depth <=? n then #(S n) else #n
        | Bind t => λ(shift (S depth) t)
        | Node t1 t2 => (shift depth t1)@(shift depth t2)
      end.
    Notation "↑ d t" := (shift d t).
    Lemma shift_leaf1 : forall (d n : nat),
      n >= d -> d(#n) = #(S n).
    Proof.
    intros d n Hge0.
    unfold shift.
    rewrite leb_correct; [ | lia ].
    reflexivity.
    Qed.
    Lemma shift_leaf2 : forall (d n : nat),
      n < d -> d(#n) = #n.
    Proof.
    intros d n Hlt.
    unfold shift.
    rewrite leb_correct_conv; [ | lia ].
    reflexivity.
    Qed.
    Lemma shift_bind : forall (d : nat) (t : Tree),
      d(λ t) = λ((S d) t).
    Proof.
    reflexivity.
    Qed.
    Lemma shift_node : forall (d : nat) (t t' : Tree),
      d(t@t') = (d t)@(d t').
    Proof.
    reflexivity.
    Qed.
    Lemma shift_shift : forall (d d' : nat) (t : Tree),
      d' <= d -> d'(d t) = (S d)(d' t).
    Proof.
    intros d d' t.
    revert d d'.
    induction t as [ n | t IH | t1 IH1 t2 IH2 ]; intros d d' Hle.
    *case (le_dec d n); intro Hdec.
     +rewrite 4 shift_leaf1 by lia.
      reflexivity.
     +case (le_dec d' n); intro Hdec'.
      -rewrite shift_leaf2, shift_leaf1, shift_leaf2 by lia.
       reflexivity.
      -rewrite 3 shift_leaf2 by lia.
       reflexivity.
    *rewrite 4 shift_bind.
     f_equal.
     rewrite IH by lia.
     reflexivity.
    *rewrite 4 shift_node.
     f_equal.
     +rewrite IH1 by lia.
      reflexivity.
     +rewrite IH2 by lia.
      reflexivity.
    Qed.
    (** [substitute n t t'] substitutes [t'] for the free variable of
      index [n] in [t], and decreases all the free variables of [t]
      which have an index > [n] *)
    Fixpoint substitute (n : nat) (tree tree' : Tree) : Tree :=
      match tree with
        | Leaf m => match m ?= n with Eq => tree' | Lt => #m | Gt => #(pred m) end
        | Bind t => λ(substitute (S n) t (0 tree'))
        | Node t1 t2 => (substitute n t1 tree')@(substitute n t2 tree')
      end.
    Notation "t [ n ↦ u ]" := (substitute n t u).
    Lemma substitute_leaf1 : forall (n : nat) (t : Tree),
      #n[nt] = t.
    Proof.
    intros n t.
    unfold substitute.
    rewrite (proj2 (Nat.compare_eq_iff _ _)) by reflexivity.
    reflexivity.
    Qed.
    Lemma substitute_leaf2 : forall (n m : nat) (t : Tree),
      m > n -> #m[nt] = #(pred m).
    Proof.
    intros n m t Hgt.
    unfold substitute.
    rewrite (proj1 (nat_compare_gt _ _)) by assumption.
    reflexivity.
    Qed.
    Lemma substitute_leaf3 : forall (n m : nat) (t : Tree),
      m < n -> #m[nt] = #m.
    Proof.
    intros n m t Hgt.
    unfold substitute.
    rewrite (proj1 (nat_compare_lt _ _)) by assumption.
    reflexivity.
    Qed.
    Lemma substitute_bind : forall (n : nat) (t t' : Tree),
      (λ t)[nt'] = λ(t[S n↦↑0 t']).
    Proof.
    reflexivity.
    Qed.
    Lemma substitute_node : forall (n : nat) (t1 t2 t' : Tree),
      (t1@t2)[nt'] = t1[nt']@t2[nt'].
    Proof.
    reflexivity.
    Qed.
    Lemma shift_substitute1 : forall (n d : nat) (t t' : Tree),
      d <= n -> (d t)[S n↦↑d t'] = d (t[nt']).
    Proof.
    intros n d t t'.
    revert n d t'.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros n d t' Hle.
    *case (Nat.compare_spec n m); intro Hdec.
     +rewrite Hdec.
      rewrite shift_leaf1, 2 substitute_leaf1 by lia.
      reflexivity.
     +rewrite shift_leaf1, 2 substitute_leaf2, shift_leaf1 by lia.
      f_equal; lia.
     +case (le_dec d m); intro Hdec'.
      -rewrite substitute_leaf3, shift_leaf1, substitute_leaf3 by lia.
       reflexivity.
      -rewrite substitute_leaf3, shift_leaf2, substitute_leaf3 by lia.
       reflexivity.
    *rewrite shift_bind, 2 substitute_bind, shift_bind.
     f_equal.
     rewrite shift_shift by lia.
     rewrite IH by lia.
     reflexivity.
    *rewrite shift_node, 2 substitute_node, shift_node.
     f_equal.
     +rewrite IH1 by lia.
      reflexivity.
     +rewrite IH2 by lia.
      reflexivity.
    Qed.
    Lemma shift_substitute2 : forall (n d : nat) (t t' : Tree),
      d >= n -> ((S d) t)[n↦↑d t'] = d(t[nt']).
    Proof.
    intros n d t t'.
    revert n d t'.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros n d t' Hle.
    *case (Nat.compare_spec n m); intro Hdec.
     +rewrite Hdec.
      rewrite shift_leaf2, 2 substitute_leaf1 by lia.
      reflexivity.
     +case (le_dec (S d) m); intro Hdec'.
      -rewrite substitute_leaf2, shift_leaf1, substitute_leaf2, shift_leaf1 by lia.
       f_equal; lia.
      -rewrite substitute_leaf2, shift_leaf2, substitute_leaf2, shift_leaf2 by lia.
       reflexivity.
     +rewrite shift_leaf2, 2 substitute_leaf3, shift_leaf2 by lia.
      reflexivity.
    *rewrite shift_bind, 2 substitute_bind, shift_bind.
     f_equal.
     rewrite shift_shift by lia.
     rewrite IH by lia.
     reflexivity.
    *rewrite shift_node, 2 substitute_node, shift_node.
     f_equal.
     +rewrite IH1 by lia.
      reflexivity.
     +rewrite IH2 by lia.
      reflexivity.
    Qed.
    Lemma shift_substitute3 : forall (d : nat) (t t' : Tree),
      (d t)[dt'] = t.
    Proof.
    intros d t.
    revert d.
    induction t as [ n | t IH | t1 IH1 t2 IH2 ]; intros d t'.
    *case (le_dec d n); intro Hdec.
     +rewrite shift_leaf1, substitute_leaf2 by lia.
      reflexivity.
     +rewrite shift_leaf2, substitute_leaf3 by lia.
      reflexivity.
    *rewrite shift_bind, substitute_bind.
     f_equal.
     apply IH.
    *rewrite shift_node, substitute_node.
     f_equal.
     +apply IH1.
     +apply IH2.
    Qed.
    Lemma substitute_substitute : forall (n n' : nat) (t t' t'' : Tree),
      n <= n' -> t[nt'][n'↦t''] = t[S n'↦↑n t''][nt'[n'↦t''] ].
    Proof.
    intros n n' t t' t'' Hle.
    revert n n' Hle t' t''.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros n n' Hle t' t''.
    *case (Nat.compare_spec n m); intro Hdec.
     +rewrite Hdec.
      rewrite substitute_leaf1, substitute_leaf3, substitute_leaf1 by lia.
      reflexivity.
     +case (Nat.compare_spec (S n') m); intro Hdec'.
      -rewrite <- Hdec'.
       rewrite substitute_leaf2, 2 substitute_leaf1, shift_substitute3 by lia.
       reflexivity.
      -rewrite 4 substitute_leaf2 by lia.
       reflexivity.
      -rewrite substitute_leaf2, 2 substitute_leaf3, substitute_leaf2 by lia.
       reflexivity.
     +rewrite 4 substitute_leaf3 by lia.
      reflexivity.
    *rewrite 4 substitute_bind.
     f_equal.
     rewrite IH by lia.
     rewrite shift_substitute1, <- shift_shift by lia.
     reflexivity.
    *rewrite 4 substitute_node.
     f_equal.
     +rewrite IH1 by lia.
      reflexivity.
     +rewrite IH2 by lia.
      reflexivity.
    Qed.
    (** [free_var t] picks the least index n such that every free variable
      appearing in [t] has index < n *)
    Fixpoint free_var (tree : Tree) :=
      match tree with
        | Leaf n => S n
        | Bind t => pred (free_var t)
        | Node t1 t2 => max (free_var t1) (free_var t2)
      end.
    Lemma free_var_leaf : forall (n : nat), free_var (#n) = S n.
    Proof.
    intro n.
    reflexivity.
    Qed.
    Lemma free_var_bind : forall (t : Tree),
      free_var (λ t) = pred (free_var t).
    Proof.
    intro t.
    reflexivity.
    Qed.
    Lemma free_var_node : forall (t1 t2 : Tree),
      free_var (t1@t2) = max (free_var t1) (free_var t2).
    Proof.
    intros t1 t2.
    reflexivity.
    Qed.
    Lemma shift_free_var : forall (t : Tree) (d : nat),
      d >= free_var t -> d t = t.
    Proof.
    intro t.
    induction t as [ n | t IH | t1 IH1 t2 IH2 ]; intro d.
    *rewrite free_var_leaf; intro Hge.
     rewrite shift_leaf2 by lia.
     reflexivity.
    *rewrite free_var_bind; intro Hge.
     rewrite shift_bind.
     f_equal.
     apply IH.
     lia.
    *rewrite free_var_node; intro Hge.
     rewrite shift_node.
     f_equal.
     +apply IH1.
      generalize (Max.le_max_l (free_var t1) (free_var t2)); lia.
     +apply IH2.
      generalize (Max.le_max_r (free_var t1) (free_var t2)); lia.
    Qed.
    Lemma substitute_free_var : forall (t : Tree) (n : nat) (t' : Tree),
      n >= free_var t -> t[nt'] = t.
    intro t.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros n t'.
    *rewrite free_var_leaf; intro Hge.
     rewrite substitute_leaf3 by lia.
     reflexivity.
    *rewrite free_var_bind; intro Hge.
     rewrite substitute_bind.
     f_equal.
     apply IH.
     lia.
    *rewrite free_var_node; intro Hge.
     rewrite substitute_node.
     f_equal.
     +apply IH1.
      generalize (Max.le_max_l (free_var t1) (free_var t2)); lia.
     +apply IH2.
      generalize (Max.le_max_r (free_var t1) (free_var t2)); lia.
    Qed.
    (** This lemma states that the substitution of a big enough variable
      for any given variable can be reverted *)
    Lemma id_substitute_shift_substitute : forall (t : Tree) (n p : nat),
      n >= free_var t -> (p (t[p#n]))[S n#p] = t.
    Proof.
    intro t; induction t as [ q | t IH | t1 IH1 t2 IH2 ]; intros n p.
    rewrite free_var_leaf; intro Hge.
    *case (Nat.compare_spec p q); intro Hdec.
     +rewrite Hdec.
      rewrite substitute_leaf1, shift_leaf1, substitute_leaf1 by lia.
      reflexivity.
     +rewrite substitute_leaf2, shift_leaf1, substitute_leaf3 by lia.
      f_equal.
      lia.
     +rewrite substitute_leaf3, shift_leaf2, substitute_leaf3 by lia.
      reflexivity.
    *rewrite free_var_bind; intro Hge.
     rewrite substitute_bind, shift_bind, substitute_bind.
     f_equal.
     rewrite 2 shift_leaf1 by lia.
     apply IH; lia.
    *rewrite free_var_node; intro Hge.
     rewrite substitute_node, shift_node, substitute_node.
     f_equal.
     +apply IH1.
      generalize (Max.le_max_l (free_var t1) (free_var t2)).
      lia.
     +apply IH2.
      generalize (Max.le_max_r (free_var t1) (free_var t2)).
      lia.
    Qed.
    (** [shift_list d s] performs [shift d t] for every [t] of [s].
      This will be used in the definition of parallel substitution *)
    Definition shift_list (depth : nat) (trees : list Tree) : list Tree :=
      map (fun tree : Tree => depth tree) trees.
    Notation "⇑ d t" := (shift_list d t).
    Lemma shift_list_nil : forall (d : nat), d nil = nil.
    Proof.
    intro d.
    unfold shift_list.
    reflexivity.
    Qed.
    Lemma shift_list_cons : forall (d : nat) (t : Tree) (trees : list Tree),
      d (t::trees) = d t :: d trees.
    Proof.
    intro d.
    unfold shift_list.
    reflexivity.
    Qed.
    Lemma shift_list_length : forall (d : nat) (trees : list Tree),
      length (d trees) = length trees.
    Proof.
    intros d trees.
    unfold shift_list.
    apply map_length.
    Qed.
    Lemma shift_list_nth : forall (d : nat) (trees : list Tree) (n : nat) (t : Tree),
      nth n (d trees) (d t) = d (nth n trees t).
    Proof.
    intros d trees n t.
    unfold shift_list.
    apply map_nth.
    Qed.
    Lemma shift_list_shift_list : forall (d d' : nat) (trees : list Tree),
      d' <= d -> d'(d trees) = (S d)(d' trees).
    Proof.
    intros d d' trees Hle.
    induction trees as [ | t trees IH ].
    *rewrite 2 shift_list_nil.
     reflexivity.
    *rewrite 4 shift_list_cons, shift_shift by lia.
     rewrite IH.
     reflexivity.
    Qed.
    (** [substitute_list n t s] performs parallel substitution of the
      [m]th element of [s] for the free variable of index [n + m] in [t],
      and subtracts [length s] to all the free variables of [t]
      which have an index >= [n + length s] *)
    Fixpoint substitute_list (n : nat) (tree : Tree) (subst : list Tree) : Tree :=
      match tree with
        | Leaf m => if n <=? m then nth (m - n) subst (#(m - (length subst))) else #m
        | Bind t => λ (substitute_list (S n) t (0 subst))
        | Node t1 t2 => (substitute_list n t1 subst)@(substitute_list n t2 subst)
      end.
    Notation " t [[ n ↦ s ]]" := (substitute_list n t s).
    Lemma substitute_list_leaf1 : forall (n m : nat) (s : list Tree),
      m >= n -> #m[[ns]] = nth (m - n) s (#(m - length s)).
    Proof.
    intros n m s Hge.
    unfold substitute_list.
    rewrite leb_correct by lia.
    reflexivity.
    Qed.
    Lemma substitute_list_leaf2 : forall (n m : nat) (s : list Tree),
      m < n -> #m[[ns]] = #m.
    Proof.
    intros n m s Hge.
    unfold substitute_list.
    rewrite leb_correct_conv by lia.
    reflexivity.
    Qed.
    Lemma substitute_list_bind : forall (n : nat) (t : Tree) (s : list Tree),
      (λ t)[[ns]] = λ (t[[S n↦⇑0 s]]).
    Proof.
    intros n t s.
    reflexivity.
    Qed.
    Lemma substitute_list_node : forall (n : nat) (t1 t2 : Tree) (s : list Tree),
      (t1@t2)[[ns]] = t1[[ns]]@t2[[ns]].
    Proof.
    intros n t1 t2 s.
    reflexivity.
    Qed.
    Lemma substitute_list_nil : forall (n : nat) (t : Tree), t[[nnil]] = t.
    Proof.
    intros n t; revert n.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intro n.
    *case (le_dec n m); intro Hdec.
     +rewrite substitute_list_leaf1 by lia.
      rewrite nth_overflow; [ | simpl; lia ].
      f_equal; simpl; lia.
     +rewrite substitute_list_leaf2 by lia.
      reflexivity.
    *rewrite substitute_list_bind.
     f_equal.
     rewrite IH; reflexivity.
    *rewrite substitute_list_node.
     f_equal.
     +rewrite IH1; reflexivity.
     +rewrite IH2; reflexivity.
    Qed.
    Lemma substitute_list_cons : forall (n : nat) (t : Tree) (s : list Tree) (t' : Tree),
      t[[nt'::s]] = t[[S n↦⇑n s]][nt'].
    Proof.
    intros n t; revert n.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros n s t'.
    *case (Nat.compare_spec n m); intro Hdec.
     +rewrite Hdec.
      rewrite substitute_list_leaf1, substitute_list_leaf2, substitute_leaf1 by lia.
      replace (m - m) with 0 by lia; simpl nth.
      reflexivity.
     +rewrite 2 substitute_list_leaf1, shift_list_length by lia.
      destruct m as [ | m ]; [ lia | ].
      replace (S m - n) with (S (m - n)) by lia; simpl nth at 1.
      replace (S m - S n) with (m - n) by lia.
      rewrite <- (shift_substitute3 n _ t'); f_equal.
      rewrite <- shift_list_nth.
      case (lt_dec (m - n) (length s)); intro Hdec'.
      -apply nth_indep.
       rewrite shift_list_length; lia.
      -f_equal.
       rewrite shift_leaf1 by lia.
       f_equal; lia.
     +rewrite 2 substitute_list_leaf2, substitute_leaf3 by lia.
      reflexivity.
    *rewrite 2 substitute_list_bind, substitute_bind, shift_list_cons, shift_list_shift_list by lia.
     f_equal; apply IH.
    *rewrite 2 substitute_list_node, substitute_node.
     f_equal.
     +apply IH1.
     +apply IH2.
    Qed.
    (** [var_seq p n] is the list of variables of index  [p] to [n-1] *)
    Definition var_seq (p n : nat) : list Tree :=
      map (fun n => #n) (seq p (n - p)).
    Notation "p … n -1" := (var_seq p n).
    Lemma var_nil : forall (p n : nat), p >= n -> (pn-1) = nil.
    Proof.
    intros n p Hle.
    unfold var_seq.
    replace (p - n) with 0 by lia.
    reflexivity.
    Qed.
    Lemma var_cons : forall (p n : nat),
      p < n -> (pn-1) = #p::((S p)n-1).
    Proof.
    intros n p Hle.
    unfold var_seq.
    destruct p as [ | p ]; [ lia | ].
    replace (S p - n) with (S (p - n)) by lia.
    reflexivity.
    Qed.
    Lemma var_nth : forall (p n m : nat) (t : Tree),
      m < n - p -> nth m (pn-1) t = #(p + m).
    Proof.
    intros p n m t Hineq.
    unfold var_seq.
    rewrite (nth_indep _ _ (#0)).
    *rewrite map_nth, seq_nth by lia.
     f_equal; lia.
    *rewrite map_length, seq_length.
     assumption.
    Qed.
    Lemma var_shift : forall (p n d : nat),
      d <= p -> d(pn-1) = (S p)(S n)-1.
    Proof.
    intros p n d Hle.
    unfold var_seq.
    unfold shift_list.
    rewrite map_map, <- seq_shift, map_map.
    replace (S n - S p) with (n - p) by lia.
    generalize (n - p); clear n; intro n.
    revert p d Hle.
    induction n as [ | n IH ]; intros p d Hle.
    *reflexivity.
    *simpl seq.
     simpl map.
     rewrite leb_correct by lia.
     f_equal.
     rewrite <- (IH _ d) by lia.
     apply map_ext.
     reflexivity.
    Qed.
    Lemma id_subst : forall (t : Tree) (n p : nat),
      n >= free_var t -> t[[ppn-1]] = t.
    Proof.
    intro t.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros n p.
    *rewrite free_var_leaf.
     case (le_dec p m); intros Hdec Hle.
     +rewrite substitute_list_leaf1 by lia.
      rewrite var_nth by lia.
      f_equal; lia.
     +rewrite substitute_list_leaf2 by lia.
      reflexivity.
    *rewrite free_var_bind; intro Hge.
     rewrite substitute_list_bind, var_shift by lia.
     rewrite IH by lia.
     reflexivity.
    *rewrite free_var_node; intro Hge.
     rewrite substitute_list_node.
     rewrite IH1 by (generalize (Max.le_max_l (free_var t1) (free_var t2)); lia).
     rewrite IH2 by (generalize (Max.le_max_r (free_var t1) (free_var t2)); lia).
     reflexivity.
    Qed.
    End Substitutions.
    (** * Confluence of pure λ-calculus
    
      In the following, we prove confluence of pure λ-calculus, but
      this is completely independent from the strong normalization proof.
      We follow the proof of Krivine's book "Lambda-Calculus Types and
      Models". *)
    Section ReflTransClosure.
    Variable A : Type.
    (** Here are the definitions of confluence and reflexive
      transitive closure for arbitrary relations *)
    Definition confluent (R : A -> A -> Prop) : Prop :=
      forall (t t' t'' : A),
        R t t' ->  R t t'' -> exists t''' : A, R t' t''' /\ R t'' t'''.
    Inductive refl_trans_closure (R : A -> A -> Prop) : A -> A -> Prop :=
      | Refl : forall (t : A), refl_trans_closure R t t
      | Trans : forall (t t' t'' : A), (R t t') -> refl_trans_closure R t' t'' -> refl_trans_closure R t t''.
    Section Confluent.
    Variable R : A -> A -> Prop.
    Infix "≻" := R.
    Infix "≻*" := (refl_trans_closure R).
    Lemma refl_trans_refl : forall (t : A), t * t.
    Proof.
    intro t.
    apply Refl.
    Qed.
    Lemma refl_trans_trans : forall (t t' t'' : A),
      t * t' -> t' * t'' -> t * t''.
    Proof.
    intros t t' t'' Hred Hred'.
    induction Hred as [ t | t0 t t' Hred0 Hred IH ].
    *assumption.
    *eapply Trans.
     +apply Hred0.
     +apply IH.
      assumption.
    Qed.
    Lemma refl_trans_confluent : confluent R -> confluent (refl_trans_closure R).
    Proof.
    intro Hconfl.
    assert (forall (t : A) (t' : A) (t'' : A),
      t * t' -> t  t'' -> exists t''' : A, t'  t''' /\ t'' * t''') as H.
    *intros t t' t'' Hred'.
     revert t''.
     induction Hred' as [ t | t0 t0' t' Hred0' Hred' IH ]; intros t0'' Hred0''.
     +exists t0''.
      split.
      -assumption.
      -apply Refl.
     +edestruct Hconfl as [ t0''' [ Hred00' Hred00'' ] ]; [ exact Hred0' | exact Hred0'' | ]; clear Hred0' Hred0''.
      edestruct IH as [ t''' [ Hred000' Hred000'' ] ]; [ exact Hred00' | ]; clear IH.
      exists t'''.
      split.
      -assumption.
      -eapply Trans; [ exact Hred00'' | exact Hred000'' ].
    *intros t t' t'' Hred' Hred''.
     revert t' Hred'.
     induction Hred'' as [ t | t0 t0'' t'' Hred0'' Hred'' IH ]; intros t' Hred'.
     +exists t'.
      split.
      -apply Refl.
      -assumption.
     +edestruct H as [ t1' [ Hred1' Hred1'' ] ]; [ exact Hred' | exact Hred0'' | ]; clear H.
      edestruct IH as [ t''' [ Hred2' Hred2'' ] ]; [ exact Hred1'' | ]; clear IH.
      exists t'''.
      split.
      -eapply Trans; [ exact Hred1' | exact Hred2' ].
      -assumption.
    Qed.
    End Confluent.
    Variable R : A -> A -> Prop.
    Infix "≻" := R.
    Infix "≻*" := (refl_trans_closure R).
    Variable R' : A -> A -> Prop.
    Infix "≻'" := R'.
    Infix "≻'*" := (refl_trans_closure R').
    Lemma refl_trans_incl :
      (forall (t : A) (t' : A), t  t' -> t ≻'* t')
       -> (forall (t : A) (t' : A), t * t' -> t ≻'* t').
    Proof.
    intros Hinc t t' Hred.
    induction Hred as [ t | t t' t'' Hred Hred' IH ].
    *apply Refl.
    *eapply refl_trans_trans.
     +apply Hinc.
      exact Hred.
     +assumption.
    Qed.
    End ReflTransClosure.
    Notation "↑ d t" := (shift d t).
    Notation " t [ n ↦ u ]" := (substitute n t u).
    Notation "⇑ d t" := (shift_list d t).
    Notation " t [[ n ↦ s ]]" := (substitute_list n t s).
    Notation "p … n -1" := (var_seq _ p n).
    (** TermParam is a custom type allowing notations which are specific
      to λ-terms *)
    Inductive TermParam :=.
    Notation Term := (@MakeTree TermParam).
    Notation "# n" := (@Leaf TermParam n).
    Notation "'λ' t" := (@Bind TermParam t).
    Infix "@" := (@Node TermParam).
    (** Our [red0] is Krivine's ρ *)
    Inductive red0 : Term -> Term -> Prop :=
      | Ctx_Var0 : forall (n : nat), red0 (#n) (#n)
      | Beta0 : forall (t1 t1' t2 t2' : Term), red0 t1 t1' -> red0 t2 t2' -> red0 (λ t1@t2) (t1'[0t2'])
      | Ctx_Abs0 : forall (t t' : Term), red0 t t' -> red0 (λ t) (λ t')
      | Ctx_App0 : forall (t1 t1' t2 t2' : Term), red0 t1 t1' -> red0 t2 t2' -> red0 (t1@t2) (t1'@t2').
    Infix "≻₀" := red0.
    Infix "≻₀*" := (refl_trans_closure red0).
    Lemma red0_refl : forall (t : Term), t ≻₀ t.
    Proof.
    intro t.
    induction t as [ n | t IH | t1 IH1 t2 IH2 ].
    *apply Ctx_Var0.
    *apply Ctx_Abs0; assumption.
    *apply Ctx_App0; assumption.
    Qed.
    Lemma red0_shift : forall (t t' : Term) (d : nat),
      t ≻₀ t' -> d t ≻₀ d t'.
    Proof.
    intros t t' d Hred.
    revert d.
    induction Hred as [ n | t1 t1' t2 t2' Hred1 IH1 Hred2 IH2 | t t' Hred IH | t1 t1' t2 t2' Hred1 IH1 Hred2 IH2 ]; intro d.
    *apply red0_refl.
    *rewrite shift_node, shift_bind, <- shift_substitute2 by lia.
     apply Beta0.
     +apply IH1.
     +apply IH2.
    *rewrite 2 shift_bind.
     apply Ctx_Abs0.
     apply IH.
    *rewrite 2 shift_node.
     apply Ctx_App0.
     +apply IH1.
     +apply IH2.
    Qed.
    Lemma red0_subst : forall (t1 t1' t2 t2' : Term) (n : nat),
      t1 ≻₀ t1' -> t2 ≻₀ t2' ->  t1[nt2] ≻₀ t1'[nt2'].
    Proof.
    intros t1 t1' t2 t2' n Hred1 Hred2.
    revert t2 t2' Hred2 n.
    induction Hred1 as [ m | t11 t11' t12 t12' Hred11 IH1 Hred12 IH2 | t1 t1' Hred1' IH | t11 t11' t12 t12' Hred11 IH1 Hred12 IH2 ]; intros t2 t2' Hred2 n.
    *case (CompSpec2Type (Nat.compare_spec n m)); intro Hdec.
     +rewrite Hdec.
      rewrite 2 substitute_leaf1.
      assumption.
     +rewrite 2 substitute_leaf2 by lia.
      apply Ctx_Var0.
     +rewrite 2 substitute_leaf3 by lia.
      apply Ctx_Var0.
    *rewrite substitute_node, substitute_bind, substitute_substitute by lia.
     apply Beta0.
     +apply IH1.
      apply red0_shift.
      assumption.
     +apply IH2.
      assumption.
    *rewrite 2 substitute_bind.
     apply Ctx_Abs0.
     apply IH.
     apply red0_shift.
     assumption.
    *rewrite 2 substitute_node.
     apply Ctx_App0.
     +apply IH1.
      assumption.
     +apply IH2.
      assumption.
    Qed.
    Lemma red0_confluent : confluent red0.
    Proof.
    intro t; induction t as [ n | t IH | t1 IH1 t2 IH2 ]; intros t' t'' Hred' Hred''.
    *inversion_clear Hred' as [ n1 | | | ].
     inversion_clear Hred'' as [ n2 | | | ].
     exists (#n).
     split; apply Ctx_Var0.
    *revert IH.
     inversion_clear Hred' as [ | | t0 t0' Hred0' | ].
     inversion_clear Hred'' as [ | | t0 t0'' Hred0'' | ]; intro IH.
     clear t' t''.
     edestruct IH as [ t''' [ Hred' Hred'' ] ]; [ exact Hred0' | exact Hred0'' | ]; clear IH.
     exists (λ t''').
     split; apply Ctx_Abs0; assumption.
    *revert Hred'' IH1 IH2.
     inversion_clear Hred' as [ | t01 t01' t02 t02' Hred01' Hred02' | | t01 t01' t02 t02' Hred01' Hred02' ]; intro Hred''.
     +rename t2 into t02; clear t1 t'.
      inversion_clear Hred'' as [ | t001 t01'' t002 t02'' Hred01'' Hred02'' | | t001 t01'' t002 t02'' Hred01'' Hred02'' ]; intros IH1 IH2; clear t''.
      -edestruct IH1 as [ t01''' [ Hred1' Hred1'' ] ]; [ apply Ctx_Abs0; exact Hred01' | apply Ctx_Abs0; exact Hred01'' | ]; clear IH1.
       revert Hred1''.
       inversion_clear Hred1' as [ | | t001' t001''' Hred001' | ]; intro Hred1''.
       clear t01'''; rename t001''' into t01'''; rename Hred001' into Hred1'.
       inversion_clear Hred1'' as [ | | t001'' t001''' Hred001'' | ].
       rename Hred001'' into Hred1''.
       edestruct IH2 as [ t02''' [ Hred2' Hred2'' ] ]; [ exact Hred02' | exact Hred02'' | ]; clear IH2.
       exists (t01'''[0t02''']).
       split; apply red0_subst; assumption.
      -inversion_clear Hred01'' as [ | | t001 t001'' Hred001'' | ].
       clear t01''; rename t001'' into t01''; rename Hred001'' into Hred01''.
       edestruct IH1 as [ t01''' [ Hred1' Hred1'' ] ]; [ apply Ctx_Abs0; exact Hred01' | apply Ctx_Abs0; exact Hred01'' | ]; clear IH1.
       revert Hred1''.
       inversion_clear Hred1' as [ | | t001' t001''' Hred001' | ]; intro Hred1''.
       clear t01'''; rename t001''' into t01'''; rename Hred001' into Hred1'.
       inversion_clear Hred1'' as [ | | t001'' t001''' Hred001'' | ].
       rename Hred001'' into Hred1''.
       edestruct IH2 as [ t02''' [ Hred2' Hred2'' ] ]; [ exact Hred02' | exact Hred02'' | ]; clear IH2.
       exists (t01'''[0t02''']).
       split; [ apply red0_subst; assumption | apply Beta0; assumption ].
     +rename t01' into t1'; rename t02' into t2'; rename Hred01' into Hred1'; rename Hred02' into Hred2'; clear t'.
      revert Hred1'.
      inversion_clear Hred'' as [ | t01 t1'' t02 t2'' Hred1'' Hred2'' | | t01 t1'' t02 t2'' Hred1'' Hred2'' ]; intros Hred1' IH1 IH2; clear t''.
      -clear t1; rename t01 into t1.
       edestruct IH2 as [ t2''' [ Hred02' Hred02'' ] ]; [ exact Hred2' | exact Hred2'' | ]; clear IH2.
       inversion_clear Hred1' as [ | | t01 t01' Hred01' | ].
       clear t1'; rename t01' into t1'; rename Hred01' into Hred1'.
       edestruct IH1 as [ t1''' [ Hred01' Hred01'' ] ]; [ apply Ctx_Abs0; exact Hred1' | apply Ctx_Abs0; exact Hred1'' | ]; clear IH1.
       revert Hred01''.
       inversion_clear Hred01' as [ | | t01' t01''' Hred001' | ]; intro Hred01''.
       clear t1'''; rename t01''' into t1'''; rename Hred001' into Hred01'.
       inversion_clear Hred01'' as [ | | t01'' t01''' Hred001'' | ].
       rename Hred001'' into Hred01''.
       exists (t1'''[0t2''']).
       split; [ apply Beta0; assumption | apply red0_subst; assumption ].
      -edestruct IH1 as [ t1''' [ Hred01' Hred01'' ] ]; [ exact Hred1' | exact Hred1'' | ].
       edestruct IH2 as [ t2''' [ Hred02' Hred02'' ] ]; [ exact Hred2' | exact Hred2'' | ].
       exists (t1'''@t2''').
       split; apply Ctx_App0; assumption.
    Qed.
     (** Our [beta] is Krivine's β₀ *)
    Inductive beta : Term -> Term -> Prop :=
      | Beta : forall (t t' : Term), beta ((λ t)@t') (t[0t'])
      | Ctx_Abs : forall (t t' : Term), beta t t' -> beta (λ t) (λ t')
      | Ctx_App_l : forall (t1 t1' : Term) (t2 : Term), beta t1 t1' -> beta (t1@t2) (t1'@t2)
      | Ctx_App_r : forall (t1 t2 : Term) (t2' : Term), beta t2 t2' -> beta (t1@t2) (t1@t2').
    Infix "≻" := beta.
    Infix "≻*" := (refl_trans_closure beta).
    Lemma beta_red0_incl : forall (t t' : Term), t  t' -> t ≻₀ t'.
    Proof.
    intros t t' Hred.
    induction Hred as [ t t' | t t' IH | t1 t1' t2 Hred IH | t1 t2 t2' Hred IH ].
    *apply Beta0.
     +apply red0_refl.
     +apply red0_refl.
    *apply Ctx_Abs0; assumption.
    *apply Ctx_App0.
     +assumption.
     +apply red0_refl.
    *apply Ctx_App0.
     +apply red0_refl.
     +assumption.
    Qed.
    Lemma beta_substitute : forall (t t' t'' : Term) (n : nat),
      t  t' -> t[nt'']  t'[nt''].
    Proof.
    intro t.
    induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros t' t'' n Hred.
    *inversion Hred.
    *inversion_clear Hred as [ | t''' t'''' Hred' | | ].
     rewrite 2 substitute_bind.
     apply Ctx_Abs.
     apply IH.
     assumption.
    *inversion_clear Hred as [ t1' t2' | | t1' t1'' t2' Hred1 | t1' t2' t2'' Hred2 ].
     +rewrite substitute_node, substitute_bind, substitute_substitute by lia.
      apply Beta.
     +rewrite 2 substitute_node.
      apply Ctx_App_l.
      apply IH1.
      assumption.
     +rewrite 2 substitute_node.
      apply Ctx_App_r.
      apply IH2.
      assumption.
    Qed.
    Lemma shift_beta : forall (t t' : Term) (d : nat),
      d t  t' -> exists t'' : Term, t  t'' /\ t' = d t''.
    Proof.
    intro t; induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros t' d.
    *case (le_dec d m); intro Hdec.
     +rewrite shift_leaf1 by lia.
      intro Hred; inversion Hred.
     +rewrite shift_leaf2 by lia.
      intro Hred; inversion Hred.
    *rewrite shift_bind.
     intro Hred; inversion_clear Hred as [ | t'' t''' Hred' | | ].
     clear t'.
     edestruct IH as [ t' [ Hredt' Heqt' ] ]; [ exact Hred' | ].
     exists (λ t').
     split.
     +apply Ctx_Abs.
      assumption.
     +rewrite shift_bind.
      rewrite Heqt'.
      reflexivity.
    *rewrite shift_node.
     remember (d t1) as t001 eqn:Heqt1; intro Hred; revert Heqt1.
     inversion_clear Hred as [ t01 t02 | | t01 t01' t02 Hred01 | t01 t02 t02' Hred02 ].
     +clear t001 IH1 IH2.
      destruct t1 as [ m | t1 | t11 t12 ].
      -case (le_dec d m); intro Hdec; [ rewrite shift_leaf1 by lia | rewrite shift_leaf2 by lia ]; discriminate.
      -rewrite shift_bind.
       intro Heqt1; injection Heqt1; clear Heqt1.
       intro Heqt1; rewrite Heqt1; clear t01 Heqt1.
       exists (t1[0t2]).
       split; [ apply Beta | ].
       apply shift_substitute2; lia.
      -rewrite shift_node; discriminate.
     +intro Heq; rewrite Heq in Hred01; clear t001 Heq IH2.
      edestruct IH1 as [ t1' [ Hred1 Heq1' ] ]; [ exact Hred01 | ]; clear IH1 Hred01.
      rewrite Heq1'; clear t01' Heq1'.
      exists (t1'@t2).
      split.
      -apply Ctx_App_l.
       assumption.
      -rewrite shift_node.
       reflexivity.
     +intro Heq; rewrite Heq; clear t001 Heq IH1.
      edestruct IH2 as [ t2' [ Hred2 Heq2' ] ]; [ exact Hred02 | ]; clear IH2 Hred02.
      rewrite Heq2'; clear t02' Heq2'.
      exists (t1@t2').
      split.
      -apply Ctx_App_r.
       assumption.
      -rewrite shift_node.
       reflexivity.
    Qed.
    Lemma substitute_leaf_beta : forall (t t' : Term) (p q : nat),
      t[p#q]  t' -> exists t'' : Term,  t  t'' /\ t' = t''[p#q].
    Proof.
    intro t; induction t as [ m | t IH | t1 IH1 t2 IH2 ]; intros t' p q.
    *case (CompSpec2Type (Nat.compare_spec p m)); intro Hdec.
     +rewrite Hdec.
      rewrite substitute_leaf1.
      intro Hred; inversion Hred.
     +rewrite substitute_leaf2 by lia.
      intro Hred; inversion Hred.
     +rewrite substitute_leaf3 by lia.
      intro Hred; inversion Hred.
    *rewrite substitute_bind.
     intro Hred; inversion_clear Hred as [ | t'' t''' Hred' | | ].
     clear t'.
     edestruct IH as [ t' [ Hredt' Heqt' ] ]; [ exact Hred' | ].
     exists (λ t').
     split; [ apply Ctx_Abs; exact Hredt' | ].
     rewrite substitute_bind.
     rewrite Heqt'.
     rewrite shift_leaf1 by lia.
     reflexivity.
    *rewrite substitute_node.
     remember (t1[p#q]) as t001 eqn:Heqt1; intro Hred; revert Heqt1.
     inversion_clear Hred as [ t01 t02 | | t01 t01' t02 Hred01 | t01 t02 t02' Hred02 ].
     +clear t001 IH1 IH2.
      destruct t1 as [ m | t1 | t11 t12 ].
      -case (CompSpec2Type (Nat.compare_spec p m)); intro Hdec; [ rewrite Hdec; rewrite substitute_leaf1 by lia | rewrite substitute_leaf2 by lia | rewrite substitute_leaf3 by lia ]; discriminate.
      -rewrite substitute_bind.
       intro Heqt1; injection Heqt1; clear Heqt1.
       intro Heqt1; rewrite Heqt1; clear t01 Heqt1.
       exists (t1[0t2]).
       split; [ apply Beta | ].
       symmetry; apply substitute_substitute; lia.
      -rewrite substitute_node; discriminate.
     +intro Heq; rewrite Heq in Hred01; clear t001 Heq IH2.
      edestruct IH1 as [ t1' [ Hred1 Heq1' ] ]; [ exact Hred01 | ]; clear IH1 Hred01.
      rewrite Heq1'; clear t01' Heq1'.
      exists (t1'@t2).
      split.
      -apply Ctx_App_l.
       assumption.
      -rewrite substitute_node.
       reflexivity.
     +intro Heq; rewrite Heq; clear t001 Heq IH1.
      edestruct IH2 as [ t2' [ Hred2 Heq2' ] ]; [ exact Hred02 | ]; clear IH2 Hred02.
      rewrite Heq2'; clear t02' Heq2'.
      exists (t1@t2').
      split.
      -apply Ctx_App_r.
       assumption.
      -rewrite substitute_node.
       reflexivity.
    Qed.
    Lemma beta_cl_leaf : forall (n : nat), #n * #n.
    Proof.
    intro n.
    apply Refl.
    Qed.
    Lemma beta_cl_bind : forall (t t' : Term), t * t' -> λ t * λ t'.
    Proof.
    intros t t' Hred.
    induction Hred as [ t | t t' t'' Hred Hred' IH ].
    *apply Refl.
    *eapply Trans.
     +apply Ctx_Abs.
      exact Hred.
     +assumption.
    Qed.
    Lemma beta_cl_node : forall (t1 t1' t2 t2' : Term),
      t1 * t1' -> t2 * t2' -> t1@t2 * t1'@t2'.
    Proof.
    intros t1 t1' t2 t2' Hred1.
    revert t2 t2'.
    induction Hred1 as [ t1 | t1 t1' t1'' Hred1 Hred1' IH1 ]; intros t2 t2' Hred2.
    *induction Hred2 as [ t2 | t2 t2' t2'' Hred2 Hred2' IH2 ].
     +apply Refl.
     +eapply Trans.
      -apply Ctx_App_r.
       exact Hred2.
      -apply IH2.
    *induction Hred2 as [ t2 | t2 t2' t2'' Hred2 Hred2' IH2 ].
     +eapply Trans.
      -apply Ctx_App_l.
       exact Hred1.
      -apply IH1.
       apply Refl.
     +eapply Trans.
      -apply Ctx_App_r.
       exact Hred2.
      -apply IH2.
    Qed.
    Lemma red0_beta_cl_incl : forall (t t' : Term), t ≻₀ t' -> t * t'.
    Proof.
    intros t t' Hred.
    induction Hred as [ n | t1 t1' t2 t2' Hred1 IH1 Hred2 IH2 | t t' Hred IH | t1 t1' t2 t2' Hred1 IH1 Hred2 IH2 ].
    *apply Refl.
    *eapply refl_trans_trans.
     +apply beta_cl_node.
      -apply beta_cl_bind.
       exact IH1.
      -exact IH2.
     +eapply Trans.
      -apply Beta.
      -apply Refl.
    *apply beta_cl_bind; assumption.
    *apply beta_cl_node; assumption.
    Qed.
    Lemma red0_beta_equiv : forall (t t' : Term), t ≻₀* t' <-> t * t'.
    Proof.
    intros t t'.
    split.
    *apply refl_trans_incl.
     apply red0_beta_cl_incl.
    *apply refl_trans_incl.
     clear t t'; intros t t' Hred.
     eapply Trans; [ | apply Refl ].
     apply beta_red0_incl; assumption.
    Qed.
    Lemma beta_cl_confluent : confluent (refl_trans_closure beta).
    Proof.
    assert (confluent (refl_trans_closure red0)) as red0_conf.
    *apply refl_trans_confluent.
     apply red0_confluent.
    *intros t t1 t2.
     intro Hred1; generalize (proj2 (red0_beta_equiv _ _) Hred1); clear Hred1; intro Hred1.
     intro Hred2; generalize (proj2 (red0_beta_equiv _ _) Hred2); clear Hred2; intro Hred2.
     edestruct red0_conf as  [ t' Hred ]; [ exact Hred1 | exact Hred2 | ]; clear red0_conf Hred1 Hred2.
     destruct Hred as [ Hred1 Hred2 ].
     exists t'.
     split; apply red0_beta_equiv; assumption.
    Qed.
    (** * Strong normalization of system F
    
      We now move to the strong normalization proof. We follow the
      proof of Girard-Taylor-Lafont's "Proofs and Types"
      ----
      The set of normalizing terms is inductively defined *)
    Inductive Normalizing (t : Term) : Prop :=
      | NormalizingInd : (forall (t' : Term), t  t' -> Normalizing t') -> Normalizing t.
    Lemma normalizing_abs : forall (t : Term), Normalizing (λ t) -> Normalizing t.
    Proof.
    intros t Hnorm.
    remember (λ t) as t' eqn:Heq.
    revert t Heq.
    induction Hnorm as [ t _ IH ]; intros t' Heq.
    apply NormalizingInd.
    intros t'' Hred.
    eapply IH.
    *rewrite Heq.
     apply Ctx_Abs.
     exact Hred.
    *reflexivity.
    Qed.
    Lemma normalizing_app : forall (t1 t2 : Term),
      Normalizing (t1@t2) -> Normalizing t1 /\ Normalizing t2.
    Proof.
    intros t1 t2 Hnorm.
    remember (t1@t2) as t' eqn:Heq.
    revert t1 t2 Heq.
    induction Hnorm as [ t _ IH ]; intros t1 t2 Heq.
    split; apply NormalizingInd.
    *intros t1' Hred.
     edestruct IH as [ Hnorm _ ].
     +rewrite Heq.
      apply Ctx_App_l.
      exact Hred.
     +reflexivity.
     +assumption.
    *intros t2' Hred.
     edestruct IH as [ _ Hnorm ].
     +rewrite Heq.
      apply Ctx_App_r.
      exact Hred.
     +reflexivity.
     +assumption.
    Qed.
    Lemma var_normalizing : forall (n : nat), Normalizing (#n).
    Proof.
    intro n; apply NormalizingInd; intros t Hred; inversion Hred.
    Qed.
    Lemma abs_normalizing : forall (t : Term),
      Normalizing t -> Normalizing (λ t).
    Proof.
    intros t Hnorm.
    induction Hnorm as [ t _ IH ].
    apply NormalizingInd; intros t' Hred.
    revert IH.
    inversion_clear Hred as [ | t'' t''' Hred' | | ]; intro IH.
    apply IH.
    assumption.
    Qed.
    Lemma shift_normalizing : forall (t : Term),
      Normalizing t -> Normalizing (0 t).
    Proof.
    intros t Hnorm.
    induction Hnorm as [ t _ IH ].
    apply NormalizingInd.
    intros t' Hred.
    destruct (@shift_beta _ _ _ Hred) as [ t'' Ht'' ].
    destruct Ht'' as [ Hred' Heq ].
    rewrite Heq.
    apply IH.
    assumption.
    Qed.
    Lemma substitute_leaf_normalizing : forall (t : Term) (p q : nat),
      Normalizing t -> Normalizing (t[p#q]).
    Proof.
    intros t p q Hnorm.
    induction Hnorm as [ t _ IH ].
    apply NormalizingInd.
    intros t' Hred.
    destruct (@substitute_leaf_beta _ _ _ _ Hred) as [ t'' Ht'' ].
    destruct Ht'' as [ Hred' Heq ].
    rewrite Heq.
    apply IH.
    assumption.
    Qed.
    (** This innocent-looking lemma is essential in the λ-introduction case
      of the normalization proof *)
    Lemma normalizing_substitute_leaf : forall (t : Term),
      (forall (n : nat), Normalizing (t[0#n])) -> Normalizing t.
    Proof.
    intros t Ht.
    rewrite <- (@id_substitute_shift_substitute _ _ (free_var t) 0) by lia.
    apply substitute_leaf_normalizing.
    apply shift_normalizing.
    apply Ht.
    Qed.
    (** Definition of closure under β-reduction *)
    Definition RedClosed (X : Term -> Prop) : Prop :=
      forall (t t' : Term), X t -> t  t' -> X t'.
    (** A term is neutral if it is not an abstraction *)
    Inductive Neutral : Term -> Prop :=
      | Var_Neutral : forall (n : nat), Neutral (#n)
      | App_Neutral : forall (t1 t2 : Term), Neutral (t1@t2).
    (** A set of terms is saturated if a neutral term is in the set
      whenever any one-step reduction of the term is in the set *)
    Definition Saturated (X : Term -> Prop) :=
      forall (t : Term), Neutral t -> (forall (t' : Term), t  t' -> X t') -> X t.
    (** A reducibility candidate is a reduction-closed saturated set
      of normalizing terms *)
    Definition RedCand (X : Term -> Prop) :=
      (forall (t : Term), X t -> Normalizing t) /\ RedClosed X /\ Saturated X.
    Lemma RedCand_Normalizing : forall (X : Term -> Prop),
      RedCand X -> forall (t : Term), X t -> Normalizing t.
    Proof.
    intros X Hrc.
    destruct Hrc as [ Hrc [ Hnorm Hprogress ] ]; tauto.
    Qed.
    Lemma RedCand_RedClosed : forall (X : Term -> Prop), RedCand X -> RedClosed X.
    Proof.
    intros X Hrc.
    destruct Hrc as [ Hrc [ Hnorm Hprogress ] ]; tauto.
    Qed.
    Lemma RedCand_Saturated : forall (X : Term -> Prop),
      RedCand X -> Saturated X.
    Proof.
    intros X Hrc.
    destruct Hrc as [ Hrc [ Hnorm Hprogress ] ]; tauto.
    Qed.
    (** A reducibility candidate contains all the variables *)
    Lemma RedCandVar : forall (X : Term -> Prop) (n : nat), RedCand X -> X (#n).
    Proof.
    intros X n Hrc.
    apply RedCand_Saturated.
    *assumption.
    *apply Var_Neutral.
    *intros t' Hred.
     inversion Hred.
    Qed.
    (** The set of all normalizing terms is a reducibility candidate *)
    Lemma SNRedCand : RedCand Normalizing.
    Proof.
    split; [ | split ].
    *tauto.
    *intros t t' Hnorm.
     destruct Hnorm as [ IH ]; intro Hred.
     eapply IH; exact Hred.
    *intros term Hneutral IH.
     apply NormalizingInd; assumption.
    Qed.
    (** We build the arrow of any two sets of terms *)
    Definition RedCandArrow (X Y : Term -> Prop) (t1 : Term) : Prop :=
      forall (t2 : Term), X t2 -> Y (t1@t2).
    (** The arrow of two reducibility candidates is a reducibility candidate *)
    Lemma red_cand_arrow_RedCand : forall (X Y : Term -> Prop),
      RedCand X -> RedCand Y -> RedCand (RedCandArrow X Y).
    Proof.
    intros X Y Hrc1 Hrc2.
    split; [ | split ].
    *intros t1 Ht1.
     eapply proj1; apply (@normalizing_app t1 (#0)).
     eapply RedCand_Normalizing; [ exact Hrc2 | ].
     apply Ht1.
     apply RedCand_Saturated.
     +assumption.
     +apply Var_Neutral.
     +intros t' Hred; inversion Hred.
    *intros t1 t1' Ht Hred t2 Ht2.
     eapply RedCand_RedClosed; [ assumption | | apply Ctx_App_l; exact Hred ].
     apply Ht; assumption.
    *intros t1 Hneutral Ht1 t2 Ht2.
     pose proof (@RedCand_Normalizing _ Hrc1 _ Ht2) as Hnorm.
     induction Hnorm as [ t2 _ IH ].
     apply RedCand_Saturated.
     +assumption.
     +apply App_Neutral.
     +intros t' Hred.
      revert Hneutral.
      inversion_clear Hred as [ t1' t2' | | t1' t1'' t2' Hred1 | t1' t2' t2'' Hred2 ]; intro Hneutral.
      -inversion Hneutral.
      -apply Ht1; assumption.
      -apply IH; [ assumption | ].
       eapply RedCand_RedClosed; [ assumption | exact Ht2 | assumption ].
    Qed.
    (** If [X] and [Y] are two reducibility candidates and if [t1] is a term
      such that for any [t1]X, [t1[0t2]]Y, then λt1X *)
    Lemma red_cand_arrow_substitute : forall (X Y : Term -> Prop) (t1 : Term),
      RedCand X -> RedCand Y -> (forall (t2 : Term), X t2 -> Y (t1[0t2])) -> RedCandArrow X Y (λ t1).
    Proof.
    intros X Y t1 HrcX HrcY Ht1.
    assert (Normalizing t1) as Hnorm1.
    *apply normalizing_substitute_leaf.
     intro n.
     eapply RedCand_Normalizing.
     +exact HrcY.
     +apply Ht1.
      apply RedCandVar.
      exact HrcX.
    *revert Ht1.
     induction Hnorm1 as [ t1 _ IH1 ].
     intros Ht1 t2 Ht2.
     generalize (@RedCand_Normalizing _ HrcX _ Ht2); intro Hnorm2.
     induction Hnorm2 as [ t2 _ IH2 ].
     apply RedCand_Saturated; [ assumption | apply App_Neutral | ].
     intros t' Hred.
     inversion_clear Hred as [ t1' t2' | | t1' t1'' t2' Hred1 | t1' t2' t2'' Hred2 ].
     +apply Ht1.
      assumption.
     +inversion_clear Hred1 as [ | t1''' t1'''' Hred1' | | ].
      apply IH1; [ assumption | | assumption ].
      intros t2' Ht2'.
      eapply RedCand_RedClosed.
      -assumption.
      -apply Ht1; exact Ht2'.
      -apply beta_substitute; assumption.
     +apply IH2; [ assumption | ].
      eapply RedCand_RedClosed; [ assumption | exact Ht2 | assumption ].
    Qed.
    (** A type context is the assignment of a set of terms for each
      type variable. If [t_ctx] is such a type context and ix [X] is
      a set of terms, then [shift_t_ctx d t_ctx X] is the type context
      obtained by assigning to n + 1 the set that was assigned to n in
      t_ctx, and assigning [X] to 0 *)
    Definition shift_t_ctx (d : nat) (t_ctx : nat -> Term -> Prop) (X : Term -> Prop) : nat -> Term -> Prop :=
      fun n => match Nat.compare n d with Eq => X | Lt => t_ctx n | Gt => t_ctx (pred n) end.
    Notation "⇧ d t" := (shift_t_ctx d t).
    Lemma shift_t_ctx1 : forall (d : nat) (t_ctx : nat -> Term -> Prop) (X : Term -> Prop),
      d t_ctx X d = X.
    Proof.
    intros d t_ctx X.
    unfold shift_t_ctx.
    rewrite (proj2 (Nat.compare_eq_iff _ _)) by reflexivity.
    reflexivity.
    Qed.
    Lemma shift_t_ctx2 : forall (d : nat) (t_ctx : nat -> Term -> Prop) (X : Term -> Prop) (n : nat),
      n >= d -> d t_ctx X (S n) = t_ctx n.
    Proof.
    intros d t_ctx X n Hge.
    unfold shift_t_ctx.
    rewrite (proj1 (nat_compare_gt _ _)) by lia.
    reflexivity.
    Qed.
    Lemma shift_t_ctx3 : forall (d : nat) (t_ctx : nat -> Term -> Prop) (X : Term -> Prop) (n : nat),
      n < d -> d t_ctx X n = t_ctx n.
    Proof.
    intros d t_ctx X n Hlt.
    unfold shift_t_ctx.
    rewrite (proj1 (nat_compare_lt _ _)) by lia.
    reflexivity.
    Qed.
    Lemma shift_t_ctx_ext1 : forall (t_ctx : nat -> Term -> Prop) (X Y : Term -> Prop),
      (X <~> Y) -> forall (d : nat), d t_ctx X <~~> d t_ctx Y.
    Proof.
    simpl.
    intros t_ctx X Y Heq d n term.
    case (CompSpec2Type (Nat.compare_spec n d)); intro Hdec.
    *rewrite Hdec.
     rewrite 2 shift_t_ctx1.
     apply Heq.
    *rewrite 2 shift_t_ctx3 by lia.
     unfold iffE; tauto.
    *destruct n; [ lia | ].
     rewrite 2 shift_t_ctx2 by lia.
     unfold iffE; tauto.
    Qed.
    Lemma shift_t_ctx_ext2 : forall (t_ctx t_ctx' : nat -> Term -> Prop) (X : Term -> Prop),
      (t_ctx <~~> t_ctx') -> forall (d : nat), d t_ctx X <~~> d t_ctx' X.
    Proof.
    simpl.
    intros t_ctx t_ctx' X Heq d n t.
    case (CompSpec2Type (Nat.compare_spec n d)); intro Hdec.
    *rewrite Hdec.
     rewrite 2 shift_t_ctx1.
     unfold iffE; tauto.
    *rewrite 2 shift_t_ctx3 by lia.
     apply Heq.
    *destruct n as [ | n ]; [ lia | ].
     rewrite 2 shift_t_ctx2 by lia.
     apply Heq.
    Qed.
    Lemma shift_shift_t_ctx : forall (t_ctx : nat -> Term -> Prop) (X Y : Term -> Prop) (d : nat),
      0 (d t_ctx X) Y <~~> (S d) (0 t_ctx Y) X.
    Proof.
    simpl.
    intros t_ctx X Y d n term.
    case (CompSpec2Type (Nat.compare_spec n (S d))); intro Hdec.
    *rewrite Hdec.
     rewrite shift_t_ctx1, shift_t_ctx2, shift_t_ctx1 by lia.
     unfold iffE; tauto.
    *rewrite (@shift_t_ctx3 (S d)) by lia.
     destruct n as [ | n ].
     +rewrite 2 shift_t_ctx1.
      unfold iffE; tauto.
     +rewrite 2 shift_t_ctx2, shift_t_ctx3 by lia.
      unfold iffE; tauto.
    *destruct n as [ | n ]; [ lia | ].
     rewrite 2 shift_t_ctx2 by lia.
     destruct n as [ | n ]; [ lia | ].
     rewrite 2 shift_t_ctx2 by lia.
     unfold iffE; tauto.
    Qed.
    (** TypeParam is a custom type allowing notations which are specific
      to System F types *)
    Inductive TypeFParam :=.
    Notation TypeF := (@MakeTree TypeFParam).
    Notation "& n" := (@Leaf TypeFParam n).
    Notation "∀ ty" := (@Bind TypeFParam ty).
    Infix "⇒" := (@Node TypeFParam).
    (** In a given type context, we associate to every type
      a set of terms *)
    Fixpoint red_cand (t_ctx : nat -> Term -> Prop) (ty : TypeF) (t : Term) : Prop :=
      match ty with
        | Leaf n => t_ctx n t
        | Bind ty => forall (X : Term -> Prop), RedCand X -> red_cand (0 t_ctx X) ty t
        | Node ty ty' => RedCandArrow (red_cand t_ctx ty) (red_cand t_ctx ty') t
      end.
    Notation " t_ctx ⊩ t ∈ ty " := (red_cand t_ctx ty t).
    Lemma red_cand_atom : forall (t_ctx : nat -> Term -> Prop) (n : nat), red_cand t_ctx (&n) = t_ctx n.
    Proof.
    reflexivity.
    Qed.
    Lemma red_cand_forall : forall (t_ctx : nat -> Term -> Prop) (ty : TypeF),
      red_cand t_ctx (ty) = fun (t : Term) => forall (X : Term -> Prop), RedCand X -> (0 t_ctx X)tty.
    Proof.
    reflexivity.
    Qed.
    Lemma red_cand_arrow : forall (t_ctx : nat -> Term -> Prop) (ty : TypeF) (ty' : TypeF),
      red_cand t_ctx (tyty') = RedCandArrow (red_cand t_ctx ty) (red_cand t_ctx ty').
    Proof.
    reflexivity.
    Qed.
    Lemma red_cand_ext : forall (t_ctx t_ctx' : nat -> Term -> Prop),
      (t_ctx <~~> t_ctx') -> (forall (ty : TypeF), red_cand t_ctx ty <~> red_cand t_ctx' ty).
    Proof.
    simpl.
    intros t_ctx t_ctx' Heq ty t.
    revert t_ctx t_ctx' Heq t.
    induction ty as [ m | ty IH | ty IH ty' IH' ]; intros t_ctx t_ctx' Heq t.
    *rewrite 2 red_cand_atom.
     split; apply Heq.
    *rewrite 2 red_cand_forall.
     split.
     +intros Ht_ctx X HrcX.
      eapply IH; [ clear IH Ht_ctx HrcX | apply Ht_ctx; exact HrcX ].
      intros n t'.
      destruct n as [ | n ].
      -rewrite 2 shift_t_ctx1.
       unfold iffE; tauto.
      -rewrite 2 shift_t_ctx2 by lia.
       split; apply Heq.
     +intros Ht_ctx' X HrcX.
      eapply IH; [ clear IH Ht_ctx' HrcX | apply Ht_ctx'; exact HrcX ].
      intros n t'.
      destruct n as [ | n ].
      -rewrite 2 shift_t_ctx1.
       unfold iffE; tauto.
      -rewrite 2 shift_t_ctx2 by lia.
       split; apply Heq.
    *rename t into t1.
     rewrite 2 red_cand_arrow.
     split.
     +intros Ht1 t2 Ht2.
      apply (IH' t_ctx); [ split; apply Heq | ].
      apply Ht1.
      apply (IH t_ctx'); [ split; apply Heq | ].
      assumption.
     +intros Ht1 t2 Ht2.
      apply (IH' t_ctx'); [ split; apply Heq | ].
      apply Ht1.
      apply (IH t_ctx); [ split; apply Heq | ].
      assumption.
    Qed.
    (** If every set of terms in the type context is a reducibility
      candidate, then the set of terms associated to any type is a
      reducibility candidate *)
    Lemma red_cand_RedCand : forall (t_ctx : nat -> Term -> Prop) (ty : TypeF),
      (forall (n : nat), RedCand (t_ctx n)) -> RedCand (red_cand t_ctx ty).
    Proof.
    intros t_ctx ty.
    revert t_ctx.
    induction ty as [ n | ty IH | ty1 IH1 ty2 IH2 ]; intros t_ctx Ht_ctx.
    *rewrite red_cand_atom.
     apply Ht_ctx.
    *assert (forall (X : Term -> Prop), RedCand X -> RedCand (red_cand (0 t_ctx X) ty)) as Hrc; [ | clear IH; rename Hrc into IH ].
     +intros X Hrc; apply IH.
      intro n; destruct n; [ rewrite shift_t_ctx1; assumption | rewrite shift_t_ctx2 by lia; apply Ht_ctx ].
     +clear Ht_ctx.
      rewrite red_cand_forall.
      split; [ | split ].
      -intros t Ht.
       eapply RedCand_Normalizing; [ apply IH; exact SNRedCand | apply Ht; exact SNRedCand ].
      -intros t t' Ht Hred X HrcX.
       eapply RedCand_RedClosed; [ apply IH; exact HrcX | apply Ht; exact HrcX | exact Hred ].
      -intros t Hneutral Ht X HrcX.
       eapply RedCand_Saturated; [ apply IH; exact HrcX | exact Hneutral | ].
       intros t' Hred.
       apply Ht; assumption.
    *rewrite red_cand_arrow.
     apply red_cand_arrow_RedCand.
     +apply IH1; assumption.
     +apply IH2; assumption.
    Qed.
    Lemma red_cand_shift : forall (t_ctx : nat -> Term -> Prop) (X : Term -> Prop) (ty : TypeF) (d : nat),
      red_cand t_ctx ty <~> red_cand (d t_ctx X) (d ty).
    Proof.
    intros t_ctx X ty.
    revert t_ctx X.
    induction ty as [ n | ty IH | ty1 IH1 ty2 IH2 ]; intros t_ctx X d t.
    *case (CompSpec2Type (Nat.compare_spec n d)); intro Hdec.
     +rewrite Hdec.
      rewrite shift_leaf1, 2 red_cand_atom, shift_t_ctx2 by lia.
      unfold iffE; tauto.
     +rewrite shift_leaf2, 2 red_cand_atom, shift_t_ctx3 by lia.
      unfold iffE; tauto.
     +rewrite shift_leaf1, 2 red_cand_atom, shift_t_ctx2 by lia.
      unfold iffE; tauto.
    *rewrite shift_bind, 2 red_cand_forall.
     split.
     +intros Ht Y HrcY.
      apply (proj2 (@red_cand_ext _ _ (shift_shift_t_ctx _ _ _ _) _ _)).
      apply (proj1 (IH _ _ _ _)).
      apply Ht; assumption.
     +intros Ht Y HrcY.
      apply (proj2 (IH _ X (S d) _)).
      apply (proj1 (@red_cand_ext _ _ (shift_shift_t_ctx _ _ _ _) _ _)).
      apply Ht; assumption.
    *rename t into t1.
     rewrite shift_node, 2 red_cand_arrow.
     split.
     +intros Ht1 t2 Ht2.
      apply IH2.
      apply Ht1.
      eapply IH1; exact Ht2.
     +intros Ht1 t2 Ht2.
      eapply IH2.
      apply Ht1.
      eapply IH1; exact Ht2.
    Qed.
    Lemma red_cand_shift_substitute : forall (t_ctx : nat -> Term -> Prop) (ty ty' : TypeF) (n : nat),
      red_cand (n t_ctx (red_cand t_ctx ty')) ty <~> red_cand t_ctx (ty[nty']).
    Proof.
    intros t_ctx ty; revert t_ctx.
    induction ty as [ m | ty IH | ty1 IH1 ty2 IH2 ]; intros t_ctx ty' n t.
    *rewrite red_cand_atom.
     case (CompSpec2Type (Nat.compare_spec m n)); intro Hdec.
     +rewrite Hdec.
      rewrite shift_t_ctx1, substitute_leaf1.
      unfold iff; tauto.
     +rewrite shift_t_ctx3, substitute_leaf3, red_cand_atom by lia.
      unfold iff; tauto.
     +destruct m as [ | m ]; [ lia | ].
      rewrite shift_t_ctx2, substitute_leaf2, red_cand_atom by lia.
      unfold iff; tauto.
    *rewrite red_cand_forall, substitute_bind, red_cand_forall.
     apply iff_forall.
     intros X HrcX.
     eapply iffE_trans; [ | apply IH ]; intro t'.
     apply red_cand_ext; clear t'.
     eapply iffE2_trans; intros n' t'; [ | apply shift_shift_t_ctx ].
     apply shift_t_ctx_ext2.
     apply shift_t_ctx_ext1.
     apply red_cand_shift.
    *rewrite red_cand_arrow.
     rewrite substitute_node.
     rewrite red_cand_arrow.
     unfold RedCandArrow.
     apply iffE_forall; intro X.
     +apply IH1.
     +apply IH2.
    Qed.
    (** We define inductively the typing derivations of System F. Since we
      work with de Bruijn indices, a context is just a list of types, where
      the nth element is the type of the variable of index n *)
    Inductive TermF : list TypeF -> Term -> TypeF -> Type :=
      | VarF0 : forall (ctx : list TypeF) (ty : TypeF), TermF (ty :: ctx) (#0) ty
      | VarFS : forall (ctx : list TypeF) (ty : TypeF) (n : nat) (ty' : TypeF), TermF ctx (#n) ty' -> TermF (ty :: ctx) (#(S n)) ty'
      | AbsF : forall (ctx : list TypeF) (ty : TypeF) (t : Term) (ty' : TypeF), TermF (ty :: ctx) t ty' -> TermF ctx (λ t) (tyty')
      | AppF : forall (ctx : list TypeF) (t : Term) (ty : TypeF) (ty' : TypeF) (t' : Term), TermF ctx t (tyty') -> TermF ctx t' ty -> TermF ctx (t@t') ty'
      | TAbsF : forall (ctx : list TypeF) (t : Term) (ty : TypeF), TermF (map (fun t => 0 t) ctx) t ty -> TermF ctx t (ty)
      | TAppF : forall (ctx : list TypeF) (t : Term) (ty : TypeF) (ty' : TypeF), TermF ctx t (ty) -> TermF ctx t (ty[0ty']).
    Notation " ctx ⊢ t ¦ ty " := (TermF ctx t ty).
    Lemma omegaTypeF : nil⊢λ(#0@#0)¦∀(&0&0)⇒∀(&0&0).
    Proof.
    apply AbsF.
    eapply AppF; [ | apply VarF0 ].
    replace ((&0&0)⇒∀(&0&0)) with ((&0&0)[0↦∀(&0&0)]); [ | reflexivity ].
    apply TAppF.
    apply VarF0.
    Qed.
    Lemma context_length : forall (ctx : list TypeF) (ty : TypeF) (n : nat),
      ctx#n¦ty -> length ctx > n.
    Proof.
    intros ctx ty n Hderiv.
    remember (#n) as t eqn:Heq.
    revert n Heq.
    induction Hderiv as [ ctx ty | ctx ty m ty' Hderiv IH | ctx ty t ty' Hderiv IH | ctx t ty ty' t' Hderiv1 IH1 Hderiv2 IH2 | ctx  t ty Hderiv IH | ctx t ty ty' Hderiv IH ]; intros n Heq.
    *injection Heq as Heq'.
     simpl; lia.
    *injection Heq as Heq'.
     generalize (IH _ eq_refl).
     simpl; lia.
    *discriminate.
    *discriminate.
    *rewrite map_length in IH.
     apply IH; tauto.
    *apply IH; tauto.
    Qed.
    Lemma free_var_context_length : forall (ctx : list TypeF) (ty : TypeF) (t : Term),
      ctxt¦ty -> free_var t <= length ctx.
    Proof.
    intros ctx ty t Hderiv.
    induction Hderiv as [ ctx ty | ctx ty n ty' Hderiv IH | ctx ty t1 ty' Hderiv IH | ctx t ty ty' t' Hderiv1 IH1 Hderiv2 IH2 | ctx t ty Hderiv IH | ctx t ty ty' Hderiv IH ].
    *rewrite free_var_leaf.
     simpl length; lia.
    *rewrite free_var_leaf in IH; rewrite free_var_leaf.
     simpl length; lia.
    *rewrite free_var_bind.
     simpl length in IH; lia.
    *rewrite free_var_node.
     apply Max.max_lub; assumption.
    *rewrite map_length in IH; assumption.
    *assumption.
    Qed.
    (** In a given type context, a substitution is compatible with a
      context if any element of the subtitution is in the reducibility
      candidate associated to the corresponding type in the context *)
    Inductive CompatSubst : (nat -> Term -> Prop) -> list TypeF -> list Term -> Prop :=
      | CompatNil : forall (t_ctx : nat -> Term -> Prop), CompatSubst t_ctx nil nil
      | CompatCons : forall (t_ctx : nat -> Term -> Prop) (ctx : list TypeF) (s : list Term) (ty : TypeF) (t : Term), CompatSubst t_ctx ctx s -> red_cand t_ctx ty t -> CompatSubst t_ctx (ty :: ctx) (t :: s).
    Lemma compat_length : forall (t_ctx : nat -> Term -> Prop) (ctx : list TypeF) (s : list Term), CompatSubst t_ctx ctx s -> length ctx = length s.
    Proof.
    intros t_ctx ctx s Hcompat.
    induction Hcompat as [ | t_ctx ctx s ty t _ IH ].
    *reflexivity.
    *simpl; rewrite IH; reflexivity.
    Qed.
    Lemma compat_id : forall (t_ctx : nat -> Term -> Prop) (ctx : list TypeF),
      (forall (n : nat), RedCand (t_ctx n)) -> CompatSubst t_ctx ctx (0(length ctx)-1).
    Proof.
    intros t_ctx ctx Ht_ctx.
    replace (length ctx) with (0 + length ctx)%nat by lia.
    generalize 0.
    induction ctx as [ | ty ctx IH ]; intro n.
    *rewrite var_nil by (simpl; lia).
     apply CompatNil.
    *rewrite var_cons by (simpl; lia).
     apply CompatCons.
     +simpl length.
      replace (n + S (length ctx))%nat with (S n + length ctx)%nat by lia.
      apply IH.
     +apply RedCandVar.
      apply red_cand_RedCand.
      assumption.
    Qed.
    (** If [t] has type [ty] in context [ctx], then for any
      type context [t_ctx] consisting of reducibility candidates,
      and for any substitution [s] compatible with [ctx] in [t_ctx],
      [t] is in the reducibility candidate associated to [ty] *)  
    Lemma termf_red_cand : forall (t_ctx : nat -> Term -> Prop) (ctx : list TypeF) (s : list Term) (t : Term) (ty : TypeF),
      (forall (n : nat), RedCand (t_ctx n)) -> CompatSubst t_ctx ctx s -> ctxt¦ty -> red_cand t_ctx ty (t[[0s]]).
    Proof.
    intros t_ctx ctx s t ty Ht_ctx Hcompat Hderiv.
    revert t_ctx Ht_ctx s Hcompat.
    induction Hderiv as [ ctx ty | ctx ty n ty' Hderiv IH | ctx ty t1 ty' Hderiv IH | ctx t ty ty' t' Hderiv1 IH1 Hderiv2 IH2 | ctx t ty Hderiv IH | ctx t ty ty' Hderiv IH ]; intros t_ctx Ht_ctx s Hcompat.
    *destruct s.
     +inversion Hcompat.
     +inversion_clear Hcompat.
      simpl.
      assumption.
    *destruct s.
     +inversion Hcompat.
     +inversion_clear Hcompat as [ | t_ctx' ctx' s' ty'' t' Hcompat' _ ].
      specialize (IH _ Ht_ctx _ Hcompat').
      rewrite substitute_list_leaf1 in IH by lia.
      replace (n - 0) with n in IH by lia.
      rewrite substitute_list_cons.
      rewrite substitute_list_leaf1 by lia.
      replace ((S n) - 1) with n by lia.
      erewrite nth_indep.
      -rewrite shift_list_nth.
       rewrite shift_substitute3.
       exact IH.
      -rewrite shift_list_length.
       erewrite <- compat_length; [ | exact Hcompat' ].
       eapply context_length.
       exact Hderiv.
    *rewrite red_cand_arrow.
     rewrite substitute_list_bind.
     apply red_cand_arrow_substitute.
     +apply red_cand_RedCand.
      assumption.
     +apply red_cand_RedCand.
      assumption.
     +intros t2 Ht2.
      generalize (IH _ Ht_ctx (t2 :: s)); clear IH; intro IH.
      rewrite substitute_list_cons in IH.
      apply IH.
      apply CompatCons; assumption.
    *specialize (IH1 _ Ht_ctx s Hcompat).
     rewrite substitute_list_node.
     rewrite red_cand_arrow in IH1.
     apply IH1.
     apply IH2; assumption.
    *rewrite red_cand_forall.
     intros X HrcX.
     apply IH; clear IH t Hderiv ty.
     +intro n.
      destruct n.
      -simpl.
       assumption.
      -apply Ht_ctx.
     +revert s Hcompat.
      induction ctx as [ | ty ctx IH ]; intros s Hcompat.
      -destruct s; [ | inversion Hcompat ].
       apply CompatNil.
      -destruct s as [ | t s ]; [ inversion Hcompat | ].
       simpl.
       inversion_clear Hcompat as [ | t_ctx' ctx' s' ty' t' Hcompat' Ht' ].
       apply CompatCons; [ apply IH; assumption | clear IH Hcompat' ].
       unfold shift_t_ctx.
       unfold shift.
       apply red_cand_shift; assumption.
    *specialize (IH t_ctx Ht_ctx s Hcompat).
     rewrite red_cand_forall in IH.
     specialize (IH (red_cand t_ctx ty')).
     assert (RedCand (red_cand t_ctx ty')) as Hty'.
     apply red_cand_RedCand; assumption.
     generalize (IH Hty'); clear IH Hty'.
     apply red_cand_shift_substitute.
    Qed.
    (** In particular, any well-typed term is normalizing *)
    Lemma termf_norm : forall (ctx : list TypeF) (t : Term) (ty : TypeF),
      ctxt¦ty -> Normalizing t.
    Proof.
    intros ctx t ty Hderiv.
    apply (@RedCand_Normalizing (red_cand (fun n => Normalizing) ty)).
    *apply red_cand_RedCand.
     intro n; apply SNRedCand.
    *rewrite <- (id_subst _ 0 (free_var_context_length Hderiv)).
     eapply termf_red_cand; [ | | exact Hderiv ].
     +intro n'; apply SNRedCand.
     +apply compat_id.
      intro n; apply SNRedCand.
    Qed.
    (** Moreover, being a normal form is a decidable property *)
    Lemma normal_dec : forall (t : Term), (forall (t' : Term), ~ t  t') + {t' : Term | t  t'}.
    Proof.
    intro t; induction t as [ n | t IH | t1 IH1 t2 IH2 ].
    *left.
     intros t' Hred; inversion Hred.
    *destruct IH as [ IH | IH ].
     +left.
      intros t' Hred.
      inversion_clear Hred as [ | t'' t''' Hred' | | ].
      eapply IH; exact Hred'.
     +right.
      destruct IH as [ t' IH ].
      exists (λ t').
      apply Ctx_Abs; assumption.
    *destruct IH1 as [ IH1 | [ t1' IH1] ]; [ | right; exists (t1'@t2); apply Ctx_App_l; assumption ].
     destruct IH2 as [ IH2 | [ t2' IH2 ] ]; [ | right; exists (t1@t2'); apply Ctx_App_r; assumption ].
     destruct t1 as [ n | t1 | t11 t12 ].
     +left.
      intros t' Hred.
      inversion_clear Hred as [ | | t1 t1' t2' Hred1 | t1 t2' t2'' Hred2 ]; [ inversion Hred1 | ].
      eapply IH2; exact Hred2.
     +right.
      exists (t1[0t2]).
      apply Beta.
     +left.
      intros t' Hred.
      inversion_clear Hred as [ | | t1' t1'' t2' Hred1 | t1' t2' t2'' Hred2 ].
      -eapply IH1; exact Hred1.
      -eapply IH2; exact Hred2.
    Qed.
    (** Therefore we can compute the normal form of any normalizing term *)
    Lemma normal_form : forall (t : Term), Normalizing t -> {t' : Term | t * t' /\ forall t'', ~ t'  t''}.
    Proof.
    intros t Hnorm.
    induction Hnorm as [ t _ IH ].
    case (normal_dec t); intro Hdec.
    *exists t.
     split.
     +apply Refl.
     +assumption.
    *destruct Hdec as [t' Hred ].
     destruct (IH _ Hred) as [ t'' IH' ]; clear IH.
     destruct IH' as [ Hred' Hnorm ].
     exists t''.
     split.
     +eapply Trans.
      -exact Hred.
      -assumption.
     +assumption.
    Qed.
    (** So in particular we can compute the normal form of any term
      which is well-typed in System F *)
    Lemma termf_normal_form : forall (ctx : list TypeF) (t : Term) (ty : TypeF),
      TermF ctx t ty -> {t' : Term | t * t' /\ forall t'', ~ t'  t''}.
    Proof.
    intros ctx t ty Hderiv.
    apply normal_form.
    eapply termf_norm; exact Hderiv.
    Qed.
    Require Extraction.
    Extract Inductive bool => "bool" [ "true" "false" ].
    Extract Inductive list => "list" [ "[]" "(::)" ].
    Extract Inlined Constant length => "List.length".
    Extract Inlined Constant map => "List.map".
    Extract Inductive nat => int [ "0" "succ" ] "(fun fO fS n -> if n=0 then fO () else fS (n-1))".
    Extract Inlined Constant plus => "(+)".
    Extract Inlined Constant max => "max".
    Extraction "Normalization.ml" termf_normal_form.
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment