DecSyn.fp_red

Definitions and properties of untyped relations

From Require Ltac2.
Import Ltac2.Notations.

Import Ltac2.Control.
Require Import ssreflect ssrbool.
Require Import FunInd.
Require Import Arith.Wf_nat (well_founded_lt_compat).
Require Import Psatz.
From stdpp Require Import relations (rtc (..), rtc_once, rtc_r, sn).
From Hammer Require Import Tactics.
Require Import Autosubst2.core Autosubst2.unscoped Autosubst2.syntax common.
Require Import Btauto.

Ltac2 spec_refl () :=
  List.iter
    (fun a match a with
           | (i, _, _)
               let h := Control.hyp i in
               try (specialize $h with (1 := eq_refl))
           end) (Control.hyps ()).

Ltac spec_refl := :(Control.enter spec_refl).

Parallel eta-reduction

Module EPar.
  Inductive R : PTm PTm Prop :=
  (****************** Eta ***********************)
  | AppEta a0 a1 :
    R
    R (PAbs (PApp (ren_PTm shift ) (VarPTm var_zero)))
  | PairEta a0 a1 :
    R
    R (PPair (PProj PL ) (PProj PR ))
  (*************** Congruence ********************)
  | AbsCong a0 a1 :
    R
    R (PAbs ) (PAbs )
  | AppCong a0 a1 b0 b1 :
    R
    R
    R (PApp ) (PApp )
  | PairCong a0 a1 b0 b1 :
    R
    R
    R (PPair ) (PPair )
  | ProjCong p a0 a1 :
    R
    R (PProj p ) (PProj p )
  | VarTm i :
    R (VarPTm i) (VarPTm i)
  | Univ i :
    R (PUniv i) (PUniv i)
  | BindCong p A0 A1 B0 B1 :
    R
    R
    R (PBind p ) (PBind p )
  | NatCong :
    R PNat PNat
  | IndCong P0 P1 a0 a1 b0 b1 c0 c1 :
    R
    R
    R
    R
    (* ----------------------- *)
    R (PInd ) (PInd )
  | ZeroCong :
    R PZero PZero
  | SucCong a0 a1 :
    R
    (* ------------ *)
    R (PSuc ) (PSuc ).

  Lemma refl (a : PTm) : R a a.
  Proof.
    elim : a; hauto lq:on ctrs:R.
  Qed.


  Derive Dependent Inversion inv with ( (a b : PTm), R a b) Sort Prop.

  Lemma AppEta' a0 a1 (u : PTm) :
    u = (PAbs (PApp (ren_PTm shift ) (VarPTm var_zero)))
    R
    R u .
  Proof. move . apply AppEta. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    R a b R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ.
    elim : a b /h.

    all : try qauto ctrs:R.

    move ha iha ξ /=.
    eapply AppEta'; eauto. by asimpl.
  Qed.


  Lemma morphing_ren (ρ0 ρ1 : PTm) (ξ : ) :
    ( i, R (ρ0 i) (ρ1 i))
    ( i, R (funcomp (ren_PTm ξ) ρ0 i) ((funcomp (ren_PTm ξ) ρ1) i)).
  Proof. eauto using renaming. Qed.

  Lemma morphing_ext (ρ0 ρ1 : PTm) a b :
    R a b
    ( i, R (ρ0 i) (ρ1 i))
    ( i, R ((scons a ρ0) i) ((scons b ρ1) i)).
  Proof. hauto q:on inv:. Qed.

  Lemma morphing_up (ρ0 ρ1 : PTm) :
    ( i, R (ρ0 i) (ρ1 i))
    ( i, R (up_PTm_PTm ρ0 i) (up_PTm_PTm ρ1 i)).
  Proof. hauto l:on ctrs:R use:morphing_ext, morphing_ren unfold:up_PTm_PTm. Qed.

  Lemma morphing (a b : PTm) (ρ0 ρ1 : PTm) :
    ( i, R (ρ0 i) (ρ1 i))
    R a b R (subst_PTm ρ0 a) (subst_PTm ρ1 b).
  Proof.
    move + h. move : ρ0 ρ1. elim : a b / h.
    move ha iha ρ0 ρ1 hρ /=.
    eapply AppEta'; eauto. by asimpl.
    all : hauto lq:on ctrs:R use:morphing_up.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    R a b R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof. hauto l:on use:morphing, refl. Qed.

End EPar.

The inductive characterization of strong normalization

Inductive SNe : PTm Prop :=
| N_Var i :
  SNe (VarPTm i)
| N_App a b :
  SNe a
  SN b
  SNe (PApp a b)
| N_Proj p a :
  SNe a
  SNe (PProj p a)
| N_Ind P a b c :
  SN P
  SNe a
  SN b
  SN c
  SNe (PInd P a b c)
with SN : PTm Prop :=
| N_Pair a b :
  SN a
  SN b
  SN (PPair a b)
| N_Abs a :
  SN a
  SN (PAbs a)
| N_SNe a :
  SNe a
  SN a
| N_Exp a b :
  TRedSN a b
  SN b
  SN a
| N_Bind p A B :
  SN A
  SN B
  SN (PBind p A B)
| N_Univ i :
  SN (PUniv i)
| N_Nat :
  SN PNat
| N_Zero :
  SN PZero
| N_Suc a :
  SN a
  SN (PSuc a)
with TRedSN : PTm PTm Prop :=
| N_β a b :
  SN b
  TRedSN (PApp (PAbs a) b) (subst_PTm (scons b VarPTm) a)
| N_AppL a0 a1 b :
  SN b
  TRedSN
  TRedSN (PApp b) (PApp b)
| N_ProjPairL a b :
  SN b
  TRedSN (PProj PL (PPair a b)) a
| N_ProjPairR a b :
  SN a
  TRedSN (PProj PR (PPair a b)) b
| N_ProjCong p a b :
  TRedSN a b
  TRedSN (PProj p a) (PProj p b)
| N_IndZero P b c :
  SN P
  SN b
  SN c
  TRedSN (PInd P PZero b c) b
| N_IndSuc P a b c :
  SN P
  SN a
  SN b
  SN c
  TRedSN (PInd P (PSuc a) b c) (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)
| N_IndCong P a0 a1 b c :
  SN P
  SN b
  SN c
  TRedSN
  TRedSN (PInd P b c) (PInd P b c).

Derive Inversion tred_inv with ( (a b : PTm), TRedSN a b) Sort Prop.

Inductive SNat : PTm Prop :=
| S_Zero : SNat PZero
| S_Neu a : SNe a SNat a
| S_Suc a : SNat a SNat (PSuc a)
| S_Red a b : TRedSN a b SNat b SNat a.

Lemma PProj_imp p a :
  ishf a
  ~~ ispair a
   SN (PProj p a).
Proof.
  move + + h. move E : (PProj p a) h u h.
  move : p a E.
  elim : u / h //=.
  hauto lq:on inv:SNe,PTm.
  hauto lq:on inv:TRedSN.
Qed.


Lemma PApp_imp a b :
  ishf a
  ~~ isabs a
   SN (PApp a b).
Proof.
  move + + h. move E : (PApp a b) h u h.
  move : a b E. elim : u /h//=.
  hauto lq:on inv:SNe,PTm.
  hauto lq:on inv:TRedSN.
Qed.


Lemma PInd_imp P (a : PTm) b c :
  ishf a
  ~~ iszero a
  ~~ issuc a
   SN (PInd P a b c).
Proof.
  move + + + h. move E : (PInd P a b c) h u h.
  move : P a b c E.
  elim : u /h //=.
  hauto lq:on inv:SNe,PTm.
  hauto lq:on inv:TRedSN.
Qed.


Lemma PProjAbs_imp p (a : PTm) :
   SN (PProj p (PAbs a)).
Proof.
  sfirstorder use:PProj_imp.
Qed.


Lemma PAppPair_imp (a b0 b1 : PTm ) :
   SN (PApp (PPair ) a).
Proof.
  sfirstorder use:PApp_imp.
Qed.


Lemma PAppBind_imp p (A : PTm) B b :
   SN (PApp (PBind p A B) b).
Proof.
  sfirstorder use:PApp_imp.
Qed.


Lemma PProjBind_imp p p' (A : PTm) B :
   SN (PProj p (PBind p' A B)).
Proof.
  move E :(PProj p (PBind p' A B)) u hu.
  move : p p' A B E.
  elim : u /hu//=.
  hauto lq:on inv:SNe.
  hauto lq:on inv:TRedSN.
Qed.


Scheme sne_ind := Induction for SNe Sort Prop
  with sn_ind := Induction for SN Sort Prop
  with sred_ind := Induction for TRedSN Sort Prop.

Combined Scheme sn_mutual from sne_ind, sn_ind, sred_ind.

Fixpoint ne (a : PTm) :=
  match a with
  | VarPTm i true
  | PApp a b ne a && nf b
  | PAbs a false
  | PPair _ _ false
  | PProj _ a ne a
  | PUniv _ false
  | PBind _ _ _ false
  | PInd P a b c nf P && ne a && nf b && nf c
  | PNat false
  | PSuc a false
  | PZero false
  end
with nf (a : PTm) :=
  match a with
  | VarPTm i true
  | PApp a b ne a && nf b
  | PAbs a nf a
  | PPair a b nf a && nf b
  | PProj _ a ne a
  | PUniv _ true
  | PBind _ A B nf A && nf B
  | PInd P a b c nf P && ne a && nf b && nf c
  | PNat true
  | PSuc a nf a
  | PZero true
end.

Lemma ne_nf a : ne a nf a.
Proof. elim : a //=. Qed.

Lemma ne_nf_ren (a : PTm) (ξ : ) :
  (ne a ne (ren_PTm ξ a)) (nf a nf (ren_PTm ξ a)).
Proof.
  move : ξ. elim : a //=; solve [hauto b:on].
Qed.


Inductive TRedSN' (a : PTm) : PTm Prop :=
| T_Refl :
  TRedSN' a a
| T_Once b :
  TRedSN a b
  TRedSN' a b.

Lemma SN_Proj p (a : PTm) :
  SN (PProj p a) SN a.
Proof.
  move E : (PProj p a) u h.
  move : a E.
  elim : u / h n //=; sauto.
Qed.


Lemma N_β' a (b : PTm) u :
  u = (subst_PTm (scons b VarPTm) a)
  SN b
  TRedSN (PApp (PAbs a) b) u.
Proof. move . apply N_β. Qed.

Lemma N_IndSuc' P a b c u :
  u = (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)
  SN P
  SN a
  SN b
  SN c
  TRedSN (PInd P (PSuc a) b c) u.
Proof. move . apply N_IndSuc. Qed.

Lemma sn_renaming :
  ( (a : PTm) (s : SNe a), (ξ : ), SNe (ren_PTm ξ a))
  ( (a : PTm) (s : SN a), (ξ : ), SN (ren_PTm ξ a))
  ( (a b : PTm) (_ : TRedSN a b), (ξ : ), TRedSN (ren_PTm ξ a) (ren_PTm ξ b)).
Proof.
  apply sn_mutual n; try qauto ctrs:SN, SNe, TRedSN depth:1.
  move */=. apply N_β';eauto. by asimpl.

  move */=. apply N_IndSuc'; eauto. by asimpl.
Qed.


Lemma ne_nf_embed (a : PTm) :
  (ne a SNe a) (nf a SN a).
Proof.
  elim : a //=; hauto qb:on ctrs:SNe, SN.
Qed.


#[export]Hint Constructors SN SNe TRedSN : sn.

Ltac2 rec solve_anti_ren () :=
  let x := Fresh.in_goal (Option.get (Ident.of_string "x")) in
  intro $x;
  lazy_match! Constr.type (Control.hyp x) with
  | (:(case;qauto depth:2 db:sn))
  | PTm (:(case;qauto depth:2 db:sn))
  | _ solve_anti_ren ()
  end.

Ltac solve_anti_ren := :(Control.enter solve_anti_ren).

Lemma sn_antirenaming :
  ( (a : PTm) (s : SNe a), (ξ : ) b, a = ren_PTm ξ b SNe b)
  ( (a : PTm) (s : SN a), (ξ : ) b, a = ren_PTm ξ b SN b)
  ( (a b : PTm) (_ : TRedSN a b), (ξ : ) a0,
      a = ren_PTm ξ b0, TRedSN b = ren_PTm ξ ).
Proof.
  apply sn_mutual; try solve_anti_ren.
  move *. subst. spec_refl. hauto lq:on ctrs:TRedSN, SN.

  move a b ha iha ξ []//= u [+ ?]. subst.
  case : u //= u [*]. subst.
  spec_refl. eexists. split. apply N_β//. by asimpl.

  move a b hb ihb ξ[]//= p [? +]. subst.
  case : //= p [*]. subst.
  spec_refl. by eauto with sn.

  move a b ha iha ξ[]//= u [? ]. subst.
  case : //=. move p [*]. subst.
  spec_refl. by eauto with sn.

  move P b c ha iha hb ihb hc ihc ξ []//= [?]. subst.
  case : //= _ *. subst.
  spec_refl. by eauto with sn.

  move P a b c hP ihP ha iha hb ihb hc ihc ξ []//= [?]. subst.
  case : //= [*]. subst.
  spec_refl. eexists; repeat split; eauto with sn.
  by asimpl.
Qed.


Lemma sn_unmorphing :
  ( (a : PTm) (s : SNe a), (ρ : PTm) b, a = subst_PTm ρ b SNe b)
  ( (a : PTm) (s : SN a), (ρ : PTm) b, a = subst_PTm ρ b SN b)
  ( (a b : PTm) (_ : TRedSN a b), (ρ : PTm) a0,
      a = subst_PTm ρ ( b0, b = subst_PTm ρ TRedSN ) SNe ).
Proof.
  apply sn_mutual; try solve_anti_ren.
  - hauto q:on db:sn.
  - move a b ha iha ξ .
    case : //=.
    + hauto lq:on rew:off db:sn.
    + move p [+ ?]. subst.
      case : p //=.
      hauto lq:on db:sn.
      move p [?]. subst.
      asimpl. left.
      spec_refl.
      eexists. split; last by eauto using N_β.
      by asimpl.
  - move b hb ihb ha iha ρ []//=.
    + hauto lq:on rew:off db:sn.
    + move [*]. subst.
      spec_refl.
      case : iha.
      * move [u [? hu]]. subst.
        left. eexists. split; eauto using N_AppL.
        reflexivity.
      * move h.
        right.
        apply N_App //.
  - move a b hb ihb ρ []//=.
    + hauto l:on ctrs:TRedSN.
    + move p [?]. subst.
      case : //=.
      * hauto lq:on rew:off db:sn.
      * move p [*]. subst.
        hauto lq:on db:sn.
  - move a b ha iha ρ []//=; first by hauto l:on db:sn.
    case //=. move []//=.
    + hauto lq:on db:sn.
    + hauto lq:on db:sn.
  - move p a b ha iha ρ []//=; first by hauto l:on db:sn.
    move [*]. subst.
    spec_refl.
    case : iha.
    + move [ [? h]]. subst.
      left. eexists. split; last by eauto with sn.
      reflexivity.
    + hauto lq:on db:sn.
  - move P b c hP ihP hb ihb hc ihc ρ []//=.
    + hauto lq:on db:sn.
    + move p []//=.
      * hauto lq:on db:sn.
      * hauto q:on db:sn.
  - move P a b c hP ihP ha iha hb ihb hc ihc ρ []//=.
    + hauto lq:on db:sn.
    + move [?]. subst.
      case : //=.
      * hauto lq:on db:sn.
      * move [*]. subst.
        spec_refl.
        left. eexists. split; last by eauto with sn.
        by asimpl.
  - move P b c hP ihP hb ihb hc ihc ha iha ρ[]//=.
    + hauto lq:on db:sn.
    + move [*]. subst.
      spec_refl.
      case : iha.
      * move [][?]h. subst.
        left. eexists; split; last by eauto with sn.
        asimpl. eauto with sn.
      * hauto lq:on db:sn.
Qed.


Lemma SN_AppInv : (a b : PTm), SN (PApp a b) SN a SN b.
Proof.
  move a b. move E : (PApp a b) u hu. move : a b E.
  elim : u /hu//=.
  hauto lq:on rew:off inv:SNe db:sn.
  move a b ha hb ihb ?. subst.
  inversion ha; subst.
  move {ihb}.
  hecrush use:sn_unmorphing.
  hauto lq:on db:sn.
Qed.


Lemma SN_ProjInv : p (a : PTm), SN (PProj p a) SN a.
Proof.
  move p a. move E : (PProj p a) u hu.
  move : p a E.
  elim : u / hu //=.
  hauto lq:on rew:off inv:SNe db:sn.
  hauto lq:on rew:off inv:TRedSN db:sn.
Qed.


Lemma SN_IndInv : P (a : PTm) b c, SN (PInd P a b c) SN P SN a SN b SN c.
Proof.
  move P a b c. move E : (PInd P a b c) u hu. move : P a b c E.
  elim : u / hu //=.
  hauto lq:on rew:off inv:SNe db:sn.
  hauto lq:on rew:off inv:TRedSN db:sn.
Qed.


Lemma epar_sn_preservation :
  ( (a : PTm) (s : SNe a), b, EPar.R a b SNe b)
  ( (a : PTm) (s : SN a), b, EPar.R a b SN b)
  ( (a b : PTm) (_ : TRedSN a b), c, EPar.R a c d, TRedSN' c d EPar.R b d).
Proof.
  apply sn_mutual.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - move a b ha iha hb ihb .
    inversion 1; subst.
    + have /iha : (EPar.R (PProj PL ) (PProj PL )) by sauto lq:on.
      sfirstorder use:SN_Proj.
    + sauto lq:on.
  - move a ha iha b.
    inversion 1; subst.
    + have : EPar.R (PApp (ren_PTm shift ) (VarPTm var_zero)) (PApp (ren_PTm shift b) (VarPTm var_zero)).
      apply EPar.AppCong; eauto using EPar.refl.
      sfirstorder use:EPar.renaming.
      move /iha.
      move /SN_AppInv [+ _].
      hauto l:on use:sn_antirenaming.
    + sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - sauto lq:on.
  - move a b ha iha c .
    inversion ; subst.
    inversion ; subst.
    + exists (PApp ). split. sfirstorder.
      asimpl.
      sauto lq:on.
    + have {}/iha := iha.
      exists (subst_PTm (scons VarPTm) ).
      split.
      sauto lq:on.
      hauto lq:on use:EPar.morphing, EPar.refl inv:.
  - sauto.
  - move a b hb ihb c.
    elim /EPar.inv //= _.
    move p ha [*]. subst.
    elim /EPar.inv : ha //= _.
    + move ha' [*]. subst.
      exists (PProj PL ).
      split. sauto.
      sauto lq:on.
    + sauto lq:on rew:off.
  - move a b ha iha c.
    elim /EPar.inv //=_.
    move => p + [*]. subst.
    elim /EPar.inv => //=_.
    + move => [*]. subst.
      exists (PProj PR ).
      split. sauto.
      sauto lq:on.
    + sauto lq:on.
  - sauto.
  - sauto q:on.
  - move => P a b c hP ihP ha iha hb ihb hc ihc u.
    elim /EPar.inv => //=_.
    move => [*]. subst.
    elim /EPar.inv : => //=_.
    move => [*]. subst.
    eexists. split. apply T_Once. apply N_IndSuc; eauto.
    hauto q:on ctrs:EPar.R use:EPar.morphing, EPar.refl inv:.
  - sauto q:on.
Qed.


beta-reduction

Module RRed.
  Inductive R : PTm PTm Prop :=
  (****************** Beta ***********************)
  | AppAbs a b :
    R (PApp (PAbs a) b) (subst_PTm (scons b VarPTm) a)

  | ProjPair p a b :
    R (PProj p (PPair a b)) (if p is PL then a else b)

  | IndZero P b c :
    R (PInd P PZero b c) b

  | IndSuc P a b c :
    R (PInd P (PSuc a) b c) (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)
  (*************** Congruence ********************)
  | AbsCong a0 a1 :
    R
    R (PAbs ) (PAbs )
  | AppCong0 a0 a1 b :
    R
    R (PApp b) (PApp b)
  | AppCong1 a b0 b1 :
    R
    R (PApp a ) (PApp a )
  | PairCong0 a0 a1 b :
    R
    R (PPair b) (PPair b)
  | PairCong1 a b0 b1 :
    R
    R (PPair a ) (PPair a )
  | ProjCong p a0 a1 :
    R
    R (PProj p ) (PProj p )
  | BindCong0 p A0 A1 B :
    R
    R (PBind p B) (PBind p B)
  | BindCong1 p A B0 B1 :
    R
    R (PBind p A ) (PBind p A )
  | IndCong0 P0 P1 a b c :
    R
    R (PInd a b c) (PInd a b c)
  | IndCong1 P a0 a1 b c :
    R
    R (PInd P b c) (PInd P b c)
  | IndCong2 P a b0 b1 c :
    R
    R (PInd P a c) (PInd P a c)
  | IndCong3 P a b c0 c1 :
    R
    R (PInd P a b ) (PInd P a b )
  | SucCong a0 a1 :
    R
    R (PSuc ) (PSuc ).

  Derive Inversion inv with ( (a b : PTm), R a b) Sort Prop.

  Lemma AppAbs' a (b : PTm) u :
    u = (subst_PTm (scons b VarPTm) a)
    R (PApp (PAbs a) b) u.
  Proof.
    move . by apply AppAbs. Qed.


  Lemma IndSuc' P a b c u :
    u = (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)
    R (PInd P (PSuc a) b c) u.
  Proof. move . apply IndSuc. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    R a b R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ.
    elim : a b /h.

    all : try qauto ctrs:R.
    move a b ξ /=.
    apply AppAbs'. by asimpl.
    move */=; apply IndSuc'; eauto. by asimpl.
  Qed.


  Ltac2 rec solve_anti_ren () :=
    let x := Fresh.in_goal (Option.get (Ident.of_string "x")) in
    intro $x;
    lazy_match! Constr.type (Control.hyp x) with
    | (:(case;hauto q:on depth:2 ctrs:RRed.R))
    | PTm (:(case;hauto q:on depth:2 ctrs:RRed.R))
    | _ solve_anti_ren ()
    end.

  Ltac solve_anti_ren := :(Control.enter solve_anti_ren).

  Lemma antirenaming (a : PTm) (b : PTm) (ξ : ) :
    R (ren_PTm ξ a) b b0, R a ren_PTm ξ = b.
  Proof.
    move E : (ren_PTm ξ a) u h.
    move : ξ a E. elim : u b/h; try solve_anti_ren.
    - move a b ξ []//=. move []//= t [*]. subst.
      eexists. split. apply AppAbs. by asimpl.
    - move p a b ξ []//=.
      move []//=. hauto q:on ctrs:R.
    - move p b c ξ []//= P [*]. subst.
      destruct //=.
      hauto lq:on ctrs:R.
    - move P a b c ξ []//=.
      move p [?]. subst.
      case : //=.
      move [?] *. subst. eexists. split; eauto using IndSuc.
      by asimpl.
  Qed.


  Lemma nf_imp (a b : PTm) :
    nf a
    R a b False.
  Proof. move/[swap]. induction 1; hauto qb:on inv:PTm. Qed.

  Lemma FromRedSN (a b : PTm) :
    TRedSN a b
    RRed.R a b.
  Proof. induction 1; hauto lq:on ctrs:RRed.R. Qed.

  Lemma substing (a b : PTm) (ρ : PTm) :
    R a b R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    move h. move : ρ. elim : a b / h n.

    all : try hauto lq:on ctrs:R.
    move */=. eapply AppAbs'; eauto; cycle 1. by asimpl.
    move */=; apply : IndSuc'; eauto. by asimpl.
  Qed.


  Lemma abs_preservation a b : isabs a R a b isabs b. hauto q:on inv:R. Qed.

End RRed.

Parallel beta-reduction

Module RPar.
  Inductive R : PTm PTm Prop :=
  (****************** Beta ***********************)
  | AppAbs a0 a1 b0 b1 :
    R
    R
    R (PApp (PAbs ) ) (subst_PTm (scons VarPTm) )

  | ProjPair p a0 a1 b0 b1 :
    R
    R
    R (PProj p (PPair )) (if p is PL then else )

  | IndZero P b0 b1 c :
    R
    R (PInd P PZero c)

  | IndSuc P0 P1 a0 a1 b0 b1 c0 c1 :
    R
    R
    R
    R
    (* ----------------------------- *)
    R (PInd (PSuc ) ) (subst_PTm (scons (PInd ) (scons VarPTm)) )

  (*************** Congruence ********************)
  | AbsCong a0 a1 :
    R
    R (PAbs ) (PAbs )
  | AppCong a0 a1 b0 b1 :
    R
    R
    R (PApp ) (PApp )
  | PairCong a0 a1 b0 b1 :
    R
    R
    R (PPair ) (PPair )
  | ProjCong p a0 a1 :
    R
    R (PProj p ) (PProj p )
  | VarTm i :
    R (VarPTm i) (VarPTm i)
  | Univ i :
    R (PUniv i) (PUniv i)
  | BindCong p A0 A1 B0 B1 :
    R
    R
    R (PBind p ) (PBind p )
  | NatCong :
    R PNat PNat
  | IndCong P0 P1 a0 a1 b0 b1 c0 c1 :
    R
    R
    R
    R
    (* ----------------------- *)
    R (PInd ) (PInd )
  | ZeroCong :
    R PZero PZero
  | SucCong a0 a1 :
    R
    (* ------------ *)
    R (PSuc ) (PSuc ).

  Lemma refl (a : PTm) : R a a.
  Proof.
    elim : a; hauto lq:on ctrs:R.
  Qed.


  Derive Dependent Inversion inv with ( (a b : PTm), R a b) Sort Prop.

  Lemma AppAbs' a0 a1 (b0 b1 : PTm) u :
    u = (subst_PTm (scons VarPTm) )
    R
    R
    R (PApp (PAbs ) ) u.
  Proof. move . apply AppAbs. Qed.

  Lemma ProjPair' p u (a0 a1 b0 b1 : PTm) :
    u = (if p is PL then else )
    R
    R
    R (PProj p (PPair )) u.
  Proof. move . apply ProjPair. Qed.

  Lemma IndSuc' P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 u :
    u = (subst_PTm (scons (PInd ) (scons VarPTm)) )
    R
    R
    R
    R
    (* ----------------------------- *)
    R (PInd (PSuc ) ) u.
  Proof. move . apply IndSuc. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    R a b R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ.
    elim : a b /h.

    all : try qauto ctrs:R use:ProjPair'.

    move ha iha hb ihb ξ /=.
    eapply AppAbs'; eauto. by asimpl.

    move * /=. apply : IndSuc'; eauto. by asimpl.
  Qed.


  Lemma morphing_ren (ρ0 ρ1 : PTm) (ξ : ) :
    ( i, R (ρ0 i) (ρ1 i))
    ( i, R ((funcomp (ren_PTm ξ) ρ0) i) ((funcomp (ren_PTm ξ) ρ1) i)).
  Proof. eauto using renaming. Qed.

  Lemma morphing_ext (ρ0 ρ1 : PTm) a b :
    R a b
    ( i, R (ρ0 i) (ρ1 i))
    ( i, R ((scons a ρ0) i) ((scons b ρ1) i)).
  Proof. hauto q:on inv:. Qed.

  Lemma morphing_up (ρ0 ρ1 : PTm) :
    ( i, R (ρ0 i) (ρ1 i))
    ( i, R (up_PTm_PTm ρ0 i) (up_PTm_PTm ρ1 i)).
  Proof. hauto l:on ctrs:R use:morphing_ext, morphing_ren unfold:up_PTm_PTm. Qed.

  Lemma morphing (a b : PTm) (ρ0 ρ1 : PTm) :
    ( i, R (ρ0 i) (ρ1 i))
    R a b R (subst_PTm ρ0 a) (subst_PTm ρ1 b).
  Proof.
    move + h. move : ρ0 ρ1. elim : a b / h.
    all : try hauto lq:on ctrs:R use:morphing_up, ProjPair'.
    move ha iha hb ihb ρ0 ρ1 hρ /=.
    eapply AppAbs'; eauto; cycle 1. sfirstorder use:morphing_up. by asimpl.
    move */=; eapply IndSuc'; eauto; cycle 1.
    sfirstorder use:morphing_up.
    sfirstorder use:morphing_up.
    by asimpl.
  Qed.


  Lemma substing (a : PTm) b (ρ : PTm) :
    R a b
    R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    hauto l:on use:morphing, refl.
  Qed.


  Lemma cong (a0 a1 : PTm) b0 b1 :
    R
    R
    R (subst_PTm (scons VarPTm) ) (subst_PTm (scons VarPTm) ).
  Proof.
    move . apply morphing//.
    hauto q:on inv: ctrs:R.
  Qed.


  Lemma FromRRed (a b : PTm) :
    RRed.R a b RPar.R a b.
  Proof.
    induction 1; qauto l:on use:RPar.refl ctrs:RPar.R.
  Qed.


  Function tstar (a : PTm) :=
    match a with
    | VarPTm i a
    | PAbs a PAbs (tstar a)
    | PApp (PAbs a) b subst_PTm (scons (tstar b) VarPTm) (tstar a)
    | PApp a b PApp (tstar a) (tstar b)
    | PPair a b PPair (tstar a) (tstar b)
    | PProj p (PPair a b) if p is PL then (tstar a) else (tstar b)
    | PProj p a PProj p (tstar a)
    | PUniv i PUniv i
    | PBind p A B PBind p (tstar A) (tstar B)
    | PNat PNat
    | PZero PZero
    | PSuc a PSuc (tstar a)
    | PInd P PZero b c tstar b
    | PInd P (PSuc a) b c
        (subst_PTm (scons (PInd (tstar P) (tstar a) (tstar b) (tstar c)) (scons (tstar a) VarPTm)) (tstar c))
    | PInd P a b c PInd (tstar P) (tstar a) (tstar b) (tstar c)
    end.

  Lemma triangle (a b : PTm) :
    RPar.R a b RPar.R b (tstar a).
  Proof.
    move : b.
    apply tstar_ind {}a.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on rew:off inv:R use:cong ctrs:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - move p b ? ? ih . subst.
      elim /inv //=_.
      + move p [*]. subst.
        by apply ih.
      + move p ha [*]. subst.
        elim /inv : ha //=_.
        move [*]. subst.
        apply : ProjPair'; eauto using refl.
    - move p b ? ?. subst.
      case : //= _.
      move ih . elim /inv //=_.
      + hauto l:on.
      + move p ha [*]. subst.
        elim /inv : ha //=_ > ? ? [*]. subst.
        apply : ProjPair'; eauto using refl.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - hauto lq:on ctrs:R inv:R.
    - move P b c ? hP ihP ha iha hb ihb u. subst.
      elim /inv //= _.
      + move hP' ha' hb' hc' [*]. subst.
        apply morphing. hauto lq:on ctrs:R inv:.
        eauto.
      + sauto q:on ctrs:R.
    - sauto lq:on.
  Qed.


  Lemma diamond (a b c : PTm) :
    R a b R a c d, R b d R c d.
  Proof. eauto using triangle. Qed.
End RPar.

Lemma red_sn_preservation :
  ( (a : PTm) (s : SNe a), b, RPar.R a b SNe b)
  ( (a : PTm) (s : SN a), b, RPar.R a b SN b)
  ( (a b : PTm) (_ : TRedSN a b), c, RPar.R a c d, TRedSN' c d RPar.R b d).
Proof.
  apply sn_mutual.
  - hauto l:on inv:RPar.R.
  - qauto l:on inv:RPar.R,SNe,SN ctrs:SNe.
  - hauto lq:on inv:RPar.R, SNe ctrs:SNe.
  - hauto lq:on inv:RPar.R, SNe ctrs:SNe.
  (* - qauto l:on inv:RPar.R, SN,SNe ctrs:SNe. *)
  - qauto l:on ctrs:SN inv:RPar.R.
  - hauto lq:on ctrs:SN inv:RPar.R.
  - hauto lq:on ctrs:SN.
  - hauto q:on ctrs:SN inv:SN, TRedSN'.
  - hauto lq:on ctrs:SN inv:RPar.R.
  - hauto lq:on ctrs:SN inv:RPar.R.
  - hauto l:on inv:RPar.R.
  - hauto l:on inv:RPar.R.
  - hauto lq:on ctrs:SN inv:RPar.R.
  - move a b ha iha hb ihb.
    elim /RPar.inv : ihb //=_.
    + move [*]. subst.
      eauto using RPar.cong, T_Refl.
    + move [*]. subst.
      elim /RPar.inv : //=_.
      move h [*]. subst.
      eexists. split. apply T_Once. hauto lq:on ctrs:TRedSN.
      eauto using RPar.cong.
  - move b hb ihb ha iha c.
    elim /RPar.inv //=_.
    + qauto l:on inv:TRedSN.
    + move [*]. subst.
      have {}/iha := .
      move [d [ ]].
      hauto lq:on rew:off inv:TRedSN' ctrs:TRedSN, RPar.R, TRedSN'.
  - hauto lq:on inv:RPar.R ctrs:RPar.R, TRedSN', TRedSN.
  - hauto lq:on inv:RPar.R ctrs:RPar.R, TRedSN', TRedSN.
  - sauto.
  - sauto.
  - move P a b c hP ihP ha iha hb ihb hc ihc u.
    elim /RPar.inv //=_.
    + move hP' ha' hb' hc' [*]. subst.
      eexists. split; first by apply T_Refl.
      apply RPar.morphing//. hauto lq:on ctrs:RPar.R inv:.
    + move hP' ha' hb' hc' [*]. subst.
      elim /RPar.inv : ha' //=_.
      move ha' [*]. subst.
      eexists. split. apply T_Once.
      apply N_IndSuc; eauto.
      hauto q:on use:RPar.morphing ctrs:RPar.R inv:.
  - sauto q:on.
Qed.


Module RReds.

  Lemma abs_preservation a b : isabs a rtc RRed.R a b isabs b.
    induction 2; hauto lq:on use:RRed.abs_preservation. Qed.

  #[local]Ltac solve_s_rec :=
  move *; eapply rtc_l; eauto;
         hauto lq:on ctrs:RRed.R.

  #[local]Ltac solve_s :=
    repeat (induction 1; last by solve_s_rec); apply rtc_refl.

  Lemma AbsCong (a b : PTm) :
    rtc RRed.R a b
    rtc RRed.R (PAbs a) (PAbs b).
  Proof. solve_s. Qed.

  Lemma AppCong (a0 a1 b0 b1 : PTm) :
    rtc RRed.R
    rtc RRed.R
    rtc RRed.R (PApp ) (PApp ).
  Proof. solve_s. Qed.

  Lemma PairCong (a0 a1 b0 b1 : PTm) :
    rtc RRed.R
    rtc RRed.R
    rtc RRed.R (PPair ) (PPair ).
  Proof. solve_s. Qed.

  Lemma ProjCong p (a0 a1 : PTm) :
    rtc RRed.R
    rtc RRed.R (PProj p ) (PProj p ).
  Proof. solve_s. Qed.

  Lemma SucCong (a0 a1 : PTm) :
    rtc RRed.R
    rtc RRed.R (PSuc ) (PSuc ).
  Proof. solve_s. Qed.

  Lemma IndCong P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 :
    rtc RRed.R
    rtc RRed.R
    rtc RRed.R
    rtc RRed.R
    rtc RRed.R (PInd ) (PInd ).
  Proof. solve_s. Qed.

  Lemma BindCong p (A0 A1 : PTm) B0 B1 :
    rtc RRed.R
    rtc RRed.R
    rtc RRed.R (PBind p ) (PBind p ).
  Proof. solve_s. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    rtc RRed.R a b rtc RRed.R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ. elim : a b /h; hauto lq:on ctrs:rtc use:RRed.renaming.
  Qed.


  Lemma FromRPar (a b : PTm) (h : RPar.R a b) :
    rtc RRed.R a b.
  Proof.
    elim : a b /h; eauto using AbsCong, AppCong, PairCong, ProjCong, rtc_refl, BindCong, IndCong, SucCong.
    move ha iha hb ihb.
    apply : rtc_r; last by apply RRed.AppAbs.
    by eauto using AppCong, AbsCong.

    move p ha iha hb ihb.
    apply : rtc_r; last by apply RRed.ProjPair.
    by eauto using PairCong, ProjCong.

    hauto lq:on ctrs:RRed.R, rtc.

    move *.
    apply : rtc_r; last by apply RRed.IndSuc.
    by eauto using SucCong, IndCong.
  Qed.


  Lemma RParIff (a b : PTm) :
    rtc RRed.R a b rtc RPar.R a b.
  Proof.
    split.
    induction 1; hauto l:on ctrs:rtc use:RPar.FromRRed, @relations.rtc_transitive.
    induction 1; hauto l:on ctrs:rtc use:FromRPar, @relations.rtc_transitive.
  Qed.


  Lemma nf_refl (a b : PTm) :
    rtc RRed.R a b nf a a = b.
  Proof. induction 1; sfirstorder use:RRed.nf_imp. Qed.

  Lemma FromRedSNs (a b : PTm) :
    rtc TRedSN a b
    rtc RRed.R a b.
  Proof. induction 1; hauto lq:on ctrs:rtc use:RRed.FromRedSN. Qed.

End RReds.

Restrictive parallel eta-reduction

Module NeEPar.
  Inductive R_nonelim : PTm PTm Prop :=
  (****************** Eta ***********************)
  | AppEta a0 a1 :
    ~~ ishf
    R_elim
    R_nonelim (PAbs (PApp (ren_PTm shift ) (VarPTm var_zero)))
  | PairEta a0 a1 :
    ~~ ishf
    R_elim
    R_nonelim (PPair (PProj PL ) (PProj PR ))
  (*************** Congruence ********************)
  | R_inj a b :
    R_elim a b
    R_nonelim a b
  with R_elim : PTm PTm Prop :=
  | NAbsCong a0 a1 :
    R_nonelim
    R_elim (PAbs ) (PAbs )
  | NAppCong a0 a1 b0 b1 :
    R_elim
    R_nonelim
    R_elim (PApp ) (PApp )
  | NPairCong a0 a1 b0 b1 :
    R_nonelim
    R_nonelim
    R_elim (PPair ) (PPair )
  | NProjCong p a0 a1 :
    R_elim
    R_elim (PProj p ) (PProj p )
  | NVarTm i :
    R_elim (VarPTm i) (VarPTm i)
  | NUniv i :
    R_elim (PUniv i) (PUniv i)
  | NBindCong p A0 A1 B0 B1 :
    R_nonelim
    R_nonelim
    R_elim (PBind p ) (PBind p )
  | NNatCong :
    R_elim PNat PNat
  | NIndCong P0 P1 a0 a1 b0 b1 c0 c1 :
    R_nonelim
    R_elim
    R_nonelim
    R_nonelim
    (* ----------------------- *)
    R_elim (PInd ) (PInd )
  | NZeroCong :
    R_elim PZero PZero
  | NSucCong a0 a1 :
    R_nonelim
    (* ------------ *)
    R_elim (PSuc ) (PSuc ).

  Scheme epar_elim_ind := Induction for R_elim Sort Prop
      with epar_nonelim_ind := Induction for R_nonelim Sort Prop.

  Combined Scheme epar_mutual from epar_elim_ind, epar_nonelim_ind.

  Lemma R_elim_nf :
    ( (a b : PTm), R_elim a b nf b nf a)
      ( (a b : PTm), R_nonelim a b nf b nf a).
  Proof.
    apply epar_mutual //=.
    - move h ih h' ih' /andP [ ].
      have : nf by eauto.
      suff : ne by qauto b:on.
      hauto q:on inv:R_elim.
    - hauto lb:on.
    - hauto lq:on inv:R_elim.
    - hauto b:on.
    - hauto lqb:on inv:R_elim.
    - move /negP ha' ha ih .
      have {ih} := ih .
      move .
      suff : ne by hauto lb:on drew:off use:ne_nf_ren.
      inversion ha; subst //=.
    - move /negP ha' ha ih .
      have {}ih := ih .
      have : ne by hauto lq:on inv:PTm.
      qauto lb:on.
  Qed.


  Lemma R_nonelim_nothf (a b : PTm) :
    R_nonelim a b
    ~~ ishf a
    R_elim a b.
  Proof.
    move h. elim : a b /h//=; hauto lq:on ctrs:R_elim.
  Qed.


  Lemma R_elim_nonelim (a b : PTm) :
    R_elim a b
    R_nonelim a b.
    hauto lq:on ctrs:R_nonelim.
  Qed.

  Lemma ToEPar : ( (a b : PTm), R_elim a b EPar.R a b)
                 ( (a b : PTm), R_nonelim a b EPar.R a b).
  Proof.
    apply epar_mutual; qauto l:on ctrs:EPar.R.
  Qed.


End NeEPar.

The abstract signature that is sufficient for eta-postponement to hold

Module Type NoForbid.
  Parameter P : PTm Prop.

  Axiom P_EPar : (a b : PTm), EPar.R a b P a P b.
  Axiom P_RRed : (a b : PTm), RRed.R a b P a P b.
  Axiom PApp_imp : a b, ishf a ~~ isabs a P (PApp a b).
  Axiom PProj_imp : p a, ishf a ~~ ispair a P (PProj p a).
  Axiom PInd_imp : Q (a : PTm) b c,
  ishf a
  ~~ iszero a
  ~~ issuc a P (PInd Q a b c).

  Axiom P_AppInv : (a b : PTm), P (PApp a b) P a P b.
  Axiom P_AbsInv : (a : PTm), P (PAbs a) P a.
  Axiom P_SucInv : (a : PTm), P (PSuc a) P a.
  Axiom P_BindInv : p (A : PTm) B, P (PBind p A B) P A P B.
  Axiom P_IndInv : Q (a : PTm) b c, P (PInd Q a b c) P Q P a P b P c.

  Axiom P_PairInv : (a b : PTm), P (PPair a b) P a P b.
  Axiom P_ProjInv : p (a : PTm), P (PProj p a) P a.
  Axiom P_renaming : (ξ : ) a , P (ren_PTm ξ a) P a.
End NoForbid.

Module Type NoForbid_FactSig (M : NoForbid).

  Axiom P_EPars : (a b : PTm), rtc EPar.R a b M.P a M.P b.

  Axiom P_RReds : (a b : PTm), rtc RRed.R a b M.P a M.P b.

End NoForbid_FactSig.

Module NoForbid_Fact (M : NoForbid) : NoForbid_FactSig M.
  Import M.

  Lemma P_EPars : (a b : PTm), rtc EPar.R a b P a P b.
  Proof.
    induction 1; eauto using P_EPar, rtc_l, rtc_refl.
  Qed.


  Lemma P_RReds : (a b : PTm), rtc RRed.R a b P a P b.
  Proof.
    induction 1; eauto using P_RRed, rtc_l, rtc_refl.
  Qed.

End NoForbid_Fact.

Module SN_NoForbid <: NoForbid.
  Definition P := @SN.

  Lemma P_EPar : (a b : PTm), EPar.R a b P a P b.
  Proof. sfirstorder use:epar_sn_preservation. Qed.

  Lemma P_RRed : (a b : PTm), RRed.R a b P a P b.
  Proof. hauto q:on use:red_sn_preservation, RPar.FromRRed. Qed.

  Lemma PApp_imp : a b, ishf a ~~ isabs a P (PApp a b).
    sfirstorder use:fp_red.PApp_imp. Qed.
  Lemma PProj_imp : p a, ishf a ~~ ispair a P (PProj p a).
    sfirstorder use:fp_red.PProj_imp. Qed.

  Lemma PInd_imp : Q (a : PTm) b c,
      ishf a
      ~~ iszero a
      ~~ issuc a P (PInd Q a b c).
  Proof. sfirstorder use: fp_red.PInd_imp. Qed.

  Lemma P_AppInv : (a b : PTm), P (PApp a b) P a P b.
  Proof. sfirstorder use:SN_AppInv. Qed.

  Lemma P_PairInv : (a b : PTm), P (PPair a b) P a P b.
    move a b. move E : (PPair a b) u h.
    move : a b E. elim : u / h; sauto lq:on rew:off. Qed.

  Lemma P_ProjInv : p (a : PTm), P (PProj p a) P a.
  Proof. sfirstorder use:SN_ProjInv. Qed.

  Lemma P_BindInv : p (A : PTm) B, P (PBind p A B) P A P B.
  Proof.
    move p A B.
    move E : (PBind p A B) u hu.
    move : p A B E. elim : u /hu//=;sauto lq:on rew:off.
  Qed.


  Lemma P_SucInv : (a : PTm), P (PSuc a) P a.
  Proof. sauto lq:on. Qed.

  Lemma P_AbsInv : (a : PTm), P (PAbs a) P a.
  Proof.
    move a. move E : (PAbs a) u h.
    move : E. move : a.
    induction h; sauto lq:on rew:off.
  Qed.


  Lemma P_renaming : (ξ : ) a , P (ren_PTm ξ a) P a.
  Proof. hauto lq:on use:sn_antirenaming, sn_renaming. Qed.

  Lemma P_IndInv : Q (a : PTm) b c, P (PInd Q a b c) P Q P a P b P c.
  Proof. sfirstorder use:SN_IndInv. Qed.

End SN_NoForbid.

Module NoForbid_FactSN := NoForbid_Fact SN_NoForbid.

Proof of eta-postponement parameterized by an abstract predicate P

Module UniqueNF (M : NoForbid) (MFacts : NoForbid_FactSig M).
  Import M MFacts.
  #[local]Hint Resolve P_EPar P_RRed PApp_imp PProj_imp : forbid.

  Lemma η_split (a0 a1 : PTm) :
    EPar.R
    P
     b, rtc RRed.R b NeEPar.R_nonelim b .
  Proof.
    move h. elim : /h .
    - move ha ih /[dup] hP.
      move /P_AbsInv /P_AppInv [/P_renaming _].
      have {ih} := ih .
      move [b [ ]].
      case /orP : (orNb (ishf b)).
      exists (PAbs (PApp (ren_PTm shift b) (VarPTm var_zero))).
      split. apply RReds.AbsCong. apply RReds.AppCong; auto using rtc_refl.
      by eauto using RReds.renaming.
      apply NeEPar.AppEta//.
      sfirstorder use:NeEPar.R_nonelim_nothf.
      case /orP : (orbN (isabs b)).
      + case : b //= p _ _.
        set q := PAbs _.
        suff : rtc RRed.R q (PAbs p) by sfirstorder.
        subst q.
        apply : rtc_r.
        apply RReds.AbsCong. apply RReds.AppCong.
        by eauto using RReds.renaming.
        apply rtc_refl.
        apply : RRed.AbsCong /=.
        apply RRed.AppAbs'. asimpl.
        set y := subst_PTm _ _.
        suff : ren_PTm id p = y. by asimpl.
        subst y.
        substify.
        apply ext_PTm.
        case //=.
      (* violates SN *)
      + move /P_AbsInv in hP.
        have {}hP : P (PApp (ren_PTm shift b) (VarPTm var_zero))
          by sfirstorder use:P_RReds, RReds.AppCong, @rtc_refl, RReds.renaming.
        move ? ?.
        have ? : ~~ isabs (ren_PTm shift b) by scongruence use:isabs_ren.
        have ? : ishf (ren_PTm shift b) by scongruence use:ishf_ren.
        exfalso.
        sfirstorder use:PApp_imp.
    - move h ih /[dup] hP.
      move /P_PairInv [/P_ProjInv + _].
      move : ih /[apply].
      move [b [ ]].
      case /orP : (orNb (ishf b)).
      exists (PPair (PProj PL b) (PProj PR b)).
      split. sfirstorder use:RReds.PairCong,RReds.ProjCong.
      hauto lq:on ctrs:NeEPar.R_nonelim use:NeEPar.R_nonelim_nothf.
      case /orP : (orbN (ispair b)).
      + case : b //=.
        move _ _.
        exists (PPair ).
        split //=.
        apply RReds.PairCong.
        apply : rtc_r; eauto using RReds.ProjCong.
        apply RRed.ProjPair.
        apply : rtc_r; eauto using RReds.ProjCong.
        apply RRed.ProjPair.
      + move ? ?. exfalso.
        move/P_PairInv : hP[hP _].
        have : rtc RRed.R (PProj PL ) (PProj PL b)
          by eauto using RReds.ProjCong.
        move : P_RReds hP. repeat move/[apply] /=.
        sfirstorder use:PProj_imp.
    - hauto lq:on ctrs:NeEPar.R_elim, NeEPar.R_nonelim use:RReds.AbsCong, P_AbsInv.
    - move ha iha hb ihb.
      move /[dup] hP /P_AppInv [ ].
      have {iha} [ [ ]] := iha .
      have {ihb} [ [ ]] := ihb .
      case /orP : (orNb (ishf )) [h|].
      + exists (PApp ). split; first by eauto using RReds.AppCong.
        hauto lq:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim use:NeEPar.R_nonelim_nothf.
      + case /orP : (orbN (isabs )).
        (* case : a2 iha0 iha1 => //=. *)
        * case : //= p _ _.
          inversion ; subst.
          ** exists (PApp ).
             split.
             apply : rtc_r.
             apply RReds.AppCong; eauto.
             apply RRed.AppAbs'. by asimpl.
             hauto lq:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim.
          ** hauto lq:on ctrs:NeEPar.R_nonelim,NeEPar.R_elim use:RReds.AppCong.
        (* Impossible *)
        * move *. exfalso.
          have : P (PApp ) by sfirstorder use:RReds.AppCong, @rtc_refl, P_RReds.
          sfirstorder use:PApp_imp.
    - hauto lq:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim use:RReds.PairCong, P_PairInv.
    - move p ha ih /[dup] hP /P_ProjInv.
      move : ih /[apply]. move [ [ ]].
      case /orP : (orNb (ishf )) [h|].
      exists (PProj p ).
      split. eauto using RReds.ProjCong.
      qauto l:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim use:NeEPar.R_nonelim_nothf.

      case /orP : (orNb (ispair )).
      + move *. exfalso.
        have : rtc RRed.R (PProj p ) (PProj p )
          by sfirstorder use:RReds.ProjCong ctrs:rtc.
        move : P_RReds hP. repeat move/[apply].
        sfirstorder use:PProj_imp.
      + case : //= _ _.
        inversion ; subst.
        * exists (PProj p ). split.
          apply : rtc_r.
          apply RReds.ProjCong; eauto.
          clear. hauto l:on inv:PTag.
          hauto lq:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim.
        * hauto lq:on ctrs:NeEPar.R_nonelim,NeEPar.R_elim use:RReds.ProjCong.
    - hauto lq:on ctrs:rtc, NeEPar.R_nonelim,NeEPar.R_elim.
    - hauto lq:on ctrs:NeEPar.R_nonelim,NeEPar.R_elim,rtc.
    - hauto lq:on ctrs:NeEPar.R_nonelim, rtc,NeEPar.R_elim use:RReds.BindCong, P_BindInv.
    - hauto lq:on ctrs:NeEPar.R_nonelim, rtc ,NeEPar.R_elim use:RReds.BindCong, P_BindInv.
    - move hP ihP ha iha hb ihb hc ihc /[dup] hInd /P_IndInv.
      move []. move : ihP /[apply].
      move [][].
      move []. move : iha /[apply].
      move [][].
      move []. move : ihb /[apply].
      move [][].
      move : ihc /[apply].
      move [][].
      case /orP : (orNb (ishf )) [h|].
      + eexists. split. by eauto using RReds.IndCong.
        hauto q:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim use:NeEPar.R_nonelim_nothf.
      + move h.
        case /orP : (orNb (issuc || iszero )).
        * move /norP.
          have : P (PInd ) by eauto using P_RReds, RReds.IndCong.
          hauto lq:on use:PInd_imp.
        * move .
          eexists. split. eauto using RReds.IndCong.
          apply NeEPar.R_inj.
          apply NeEPar.NIndCong; eauto.
          move : . clear.
          inversion 1; subst //=; hauto lq:on ctrs:NeEPar.R_elim.
    - hauto lq:on ctrs:NeEPar.R_nonelim,NeEPar.R_elim,rtc.
    - hauto lq:on ctrs:NeEPar.R_nonelim, NeEPar.R_elim use:RReds.SucCong, P_SucInv.
  Qed.


  Lemma η_postponement a b c :
    P a
    EPar.R a b
    RRed.R b c
     d, rtc RRed.R a d EPar.R d c.
  Proof.
    move + h.
    move : c.
    elim : a b /h //=.
    - move ha iha c /[dup] hP /P_AbsInv /P_AppInv [/P_renaming hP' _] hc.
      move : iha (hP') (hc); repeat move/[apply].
      move [d [ ]].
      exists (PAbs (PApp (ren_PTm shift d) (VarPTm var_zero))).
      split. hauto lq:on rew:off ctrs:rtc use:RReds.AbsCong, RReds.AppCong, RReds.renaming.
      hauto lq:on ctrs:EPar.R.
    - move ha iha c /P_PairInv [/P_ProjInv + _].
      move /iha /[apply].
      move [d [ ]].
      exists (PPair (PProj PL d) (PProj PR d)).
      hauto lq:on ctrs:EPar.R use:RReds.PairCong, RReds.ProjCong.
    - move ha iha c /P_AbsInv /[swap].
      elim /RRed.inv //=_.
      move + [? ?]. subst.
      move : iha; repeat move/[apply].
      hauto lq:on use:RReds.AbsCong ctrs:EPar.R.
    - move ha iha hb ihb c hP.
      elim /RRed.inv //= _.
      + move [*]. subst.
        have [hP' hP''] : P P by sfirstorder use:P_AppInv.
        move {iha ihb}.
        move /η_split /(_ hP') : ha.
        move [b [ ]].
        inversion ; subst.
        * inversion ; subst.
          exists (subst_PTm (scons VarPTm) ).
          split; last by scongruence use:EPar.morphing.
          apply : relations.rtc_transitive.
          apply RReds.AppCong.
          eassumption.
          apply rtc_refl.
          apply : rtc_l.
          apply RRed.AppCong0. apply RRed.AbsCong. simpl. apply RRed.AppAbs.
          asimpl.
          apply rtc_once.
          apply RRed.AppAbs'. by asimpl.
        * exfalso.
          move : hP . clear hP .
          have : rtc RRed.R (PApp ) (PApp (PPair (PProj PL ) (PProj PR )) )
            by qauto l:on ctrs:rtc use:RReds.AppCong.
          move : P_RReds hP. repeat move/[apply].
          sfirstorder use:PApp_imp.
        * inversion H; subst.
          exists (subst_PTm (scons VarPTm) ).
          split.
          apply : rtc_r; last by apply RRed.AppAbs.
          hauto lq:on ctrs:rtc use:RReds.AppCong.
          hauto l:on inv: use:EPar.morphing,NeEPar.ToEPar.
      + move [*]. subst.
        move : iha () {ihb} /[apply].
        have : P by sfirstorder use:P_AppInv.
        move /[swap]/[apply].
        move [d [ ]].
        exists (PApp d ).
        hauto lq:on ctrs:EPar.R, rtc use:RReds.AppCong.
      + move [*]. subst.
        move {iha}.
        have : P by sfirstorder use:P_AppInv.
        move : ihb ; repeat move /[apply].
        hauto lq:on rew:off ctrs:EPar.R, rtc use:RReds.AppCong.
    - move ha iha hb ihb c /P_PairInv [hP hP'].
      elim /RRed.inv //=_;
        hauto lq:on rew:off ctrs:EPar.R, rtc use:RReds.PairCong.
    - move p ha iha c /[dup] hP /P_ProjInv hP'.
      elim / RRed.inv //= _.
      + move [*]. subst.
        move : η_split hP' ha; repeat move/[apply].
        move [ [ ]].
        inversion ; subst.
        * sauto q:on ctrs:rtc use:RReds.ProjCong, PProj_imp, P_RReds.
        * inversion ; subst.
          exists (if p is PL then else ).
          split; last by scongruence use:NeEPar.ToEPar.
          apply : relations.rtc_transitive.
          apply RReds.ProjCong; eauto.
          apply : rtc_l.
          apply RRed.ProjCong.
          apply RRed.PairCong0.
          apply RRed.ProjPair.
          apply : rtc_l.
          apply RRed.ProjCong.
          apply RRed.PairCong1.
          apply RRed.ProjPair.
          apply rtc_once. apply RRed.ProjPair.
        * inversion H; subst.
          exists (if p is PL then else ).
          split; last by hauto lq:on use:NeEPar.ToEPar.
          apply : relations.rtc_transitive.
          eauto using RReds.ProjCong.
          apply rtc_once.
          apply RRed.ProjPair.
      + move [*]. subst.
        move : iha hP' ;repeat move/[apply].
        hauto lq:on ctrs:rtc, EPar.R use:RReds.ProjCong.
    - hauto lq:on inv:RRed.R.
    - hauto lq:on inv:RRed.R ctrs:rtc.
    - sauto lq:on ctrs:EPar.R, rtc use:RReds.BindCong, P_BindInv, @relations.rtc_transitive.
    - hauto lq:on inv:RRed.R ctrs:rtc.
    - move hP ihP ha iha hb ihb hc ihc u.
      move /[dup] hInd.
      move /P_IndInv.
      move [][][].
      elim /RRed.inv //= _.
      + move [*]. subst.
        move /η_split : ha; repeat move/[apply].
        move [][] {iha}.
        inversion ; subst.
        * exfalso.
          have :P (PInd (PAbs (PApp (ren_PTm shift ) (VarPTm var_zero))) ) by eauto using RReds.IndCong, rtc_refl, P_RReds.
          clear. hauto lq:on use:PInd_imp.
        * have :P (PInd (PPair (PProj PL ) (PProj PR )) ) by eauto using RReds.IndCong, rtc_refl, P_RReds.
          clear. hauto lq:on use:PInd_imp.
        * eexists. split; eauto.
          apply : rtc_r.
          apply : RReds.IndCong; eauto; eauto using rtc_refl.
          inversion H; subst.
          apply RRed.IndZero.
      + move c [*]. subst.
        move /η_split /(_ ) : ha.
        move [][].
        inversion ; subst.
        * have :P (PInd (PAbs (PApp (ren_PTm shift ) (VarPTm var_zero))) ) by eauto using RReds.IndCong, rtc_refl, P_RReds.
          clear. hauto q:on use:PInd_imp.
        * have :P (PInd (PPair (PProj PL ) (PProj PR )) ) by eauto using RReds.IndCong, rtc_refl, P_RReds.
          clear. hauto q:on use:PInd_imp.
        * inversion H; subst.
          eexists. split.
          apply : rtc_r.
          apply RReds.IndCong; eauto; eauto using rtc_refl.
          apply RRed.IndSuc.
          apply EPar.morphing;eauto.
          case [|].
          hauto lq:on rew:off ctrs:EPar.R use:NeEPar.ToEPar.
          case [|i].
          hauto lq:on rew:off ctrs:EPar.R use:NeEPar.ToEPar.
          asimpl. apply EPar.VarTm.
      + move c [*]. subst.
        move : ihP . repeat move/[apply].
        move [][].
        exists (PInd ).
        sfirstorder use:RReds.IndCong, @rtc_refl, EPar.IndCong.
      + move c + [*]. subst.
        move : iha ; repeat move/[apply].
        move [][*].
        exists (PInd ).
        sfirstorder use:RReds.IndCong, @rtc_refl, EPar.IndCong.
      + move c + [*]. subst.
        move : ihb ; repeat move/[apply].
        move [][*].
        exists (PInd ).
        sfirstorder use:RReds.IndCong, @rtc_refl, EPar.IndCong.
      + move c + [*]. subst.
        move : ihc ; repeat move/[apply].
        move [][*].
        exists (PInd ).
        sfirstorder use:RReds.IndCong, @rtc_refl, EPar.IndCong.
    - hauto lq:on inv:RRed.R ctrs:rtc, EPar.R.
    - move ha iha u /P_SucInv .
      elim /RRed.inv //= _ h [*]. subst.
      move : iha () (h); repeat move/[apply].
      move [ [ ]].
      exists (PSuc ).
      split. by apply RReds.SucCong.
      by apply EPar.SucCong.
  Qed.


  Lemma η_postponement_strengthened a b c :
    P a
    EPar.R a b
    RRed.R b c
     d, rtc RRed.R a d NeEPar.R_nonelim d c.
  Proof.
    move .
    have : d, rtc RRed.R a d EPar.R d c by eauto using η_postponement.
    move [d [ /η_split]].
    move /(_ ltac:(eauto using P_RReds)).
    sfirstorder use:@relations.rtc_transitive.
  Qed.


  Lemma η_postponement_star a b c :
    P a
    EPar.R a b
    rtc RRed.R b c
     d, rtc RRed.R a d NeEPar.R_nonelim d c.
  Proof.
    move => + + h. move : a.
    elim : b c / h.
    - sfirstorder use:η_split.
    - move => ha ha' iha u hu hu'.
      move : η_postponement_strengthened (hu) ha hu'; repeat move/[apply].
      move => [d [ ]].
      have : P d by sfirstorder use:P_RReds.
      have : EPar.R d by sfirstorder use:NeEPar.ToEPar.
      move : iha ; repeat move/[apply].
      sfirstorder use:@relations.rtc_transitive.
  Qed.


End UniqueNF.

Module SN_UniqueNF := UniqueNF SN_NoForbid NoForbid_FactSN.

eta-reduction

Module ERed.
  Inductive R : PTm PTm Prop :=

  (****************** Eta ***********************)
  | AppEta a :
    R (PAbs (PApp (ren_PTm shift a) (VarPTm var_zero))) a
  | PairEta a :
    R (PPair (PProj PL a) (PProj PR a)) a

  (*************** Congruence ********************)
  | AbsCong a0 a1 :
    R
    R (PAbs ) (PAbs )
  | AppCong0 a0 a1 b :
    R
    R (PApp b) (PApp b)
  | AppCong1 a b0 b1 :
    R
    R (PApp a ) (PApp a )
  | PairCong0 a0 a1 b :
    R
    R (PPair b) (PPair b)
  | PairCong1 a b0 b1 :
    R
    R (PPair a ) (PPair a )
  | ProjCong p a0 a1 :
    R
    R (PProj p ) (PProj p )
  | BindCong0 p A0 A1 B :
    R
    R (PBind p B) (PBind p B)
  | BindCong1 p A B0 B1 :
    R
    R (PBind p A ) (PBind p A )
  | IndCong0 P0 P1 a b c :
    R
    R (PInd a b c) (PInd a b c)
  | IndCong1 P a0 a1 b c :
    R
    R (PInd P b c) (PInd P b c)
  | IndCong2 P a b0 b1 c :
    R
    R (PInd P a c) (PInd P a c)
  | IndCong3 P a b c0 c1 :
    R
    R (PInd P a b ) (PInd P a b )
  | SucCong a0 a1 :
    R
    R (PSuc ) (PSuc ).

  Derive Inversion inv with ( (a b : PTm), R a b) Sort Prop.

  Lemma abs_back_preservation (a b : PTm) :
    SN a R a b isabs b isabs a.
  Proof.
    move + h.
    elim : a b /h //=.
    case //=. move p. move /SN_NoForbid.P_PairInv.
    sfirstorder use:SN_NoForbid.PProj_imp.
  Qed.


  Lemma ToEPar (a b : PTm) :
    ERed.R a b EPar.R a b.
  Proof.
    induction 1; hauto lq:on use:EPar.refl ctrs:EPar.R.
  Qed.


  Ltac2 rec solve_anti_ren () :=
    let x := Fresh.in_goal (Option.get (Ident.of_string "x")) in
    intro $x;
    lazy_match! Constr.type (Control.hyp x) with
    | _ (:(case;hauto q:on depth:2 ctrs:ERed.R))
    | _ solve_anti_ren ()
    end.

  Ltac solve_anti_ren := :(Control.enter solve_anti_ren).

  Lemma AppEta' a u :
    u = (PApp (ren_PTm shift a) (VarPTm var_zero))
    R (PAbs u) a.
  Proof. move . apply AppEta. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    R a b R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ.
    elim : a b /h.

    move a ξ /=.
    apply AppEta'; eauto. by asimpl.
    all : qauto ctrs:R.
  Qed.


  (* Characterization of variable freeness conditions *)
  Definition tm_i_free a (i : ) := (ξ ξ0 : ), ξ i ξ0 i ren_PTm ξ a = ren_PTm ξ0 a.

  Lemma subst_differ_one_ren_up i (ξ0 ξ1 : ) :
    ( j, i j ξ0 j = ξ1 j)
    ( j, shift i j upRen_PTm_PTm ξ0 j = upRen_PTm_PTm ξ1 j).
  Proof.
    move .
    case // j.
    asimpl.
    move h. have : i j by hauto lq:on rew:off.
    move /. by rewrite /funcomp .
  Qed.


  Lemma tm_free_ren_any a i :
    tm_i_free a i
     (ξ0 ξ1 : ), ( j, i j ξ0 j = ξ1 j)
             ren_PTm ξ0 a = ren_PTm ξ1 a.
  Proof.
    rewrite /tm_i_free.
    move [+ [+ [+ +]]].
    move : i.
    elim : a.
    - hauto q:on.
    - move a iha i ρ0 ρ1 h [] h' ξ0 ξ1 /=.
      f_equal. move /subst_differ_one_ren_up in .
      move /(_ (shift i)) in iha.
      move : iha ; move/[apply].
      apply; cycle 1; eauto.
    - hauto lq:on rew:off.
    - hauto lq:on rew:off.
    - hauto lq:on rew:off.
    - move p A ihA a iha i ρ0 ρ1 hρ [] ? h ξ0 ξ1 /=.
      f_equal. hauto lq:on rew:off.
      move /subst_differ_one_ren_up in .
      move /(_ (shift i)) in iha.
      move : iha (). repeat move/[apply].
      move /(_ (upRen_PTm_PTm ρ0) (upRen_PTm_PTm ρ1)).
      apply. simpl. rewrite /funcomp. scongruence.
      sfirstorder.
    - hauto lq:on rew:off.
    - hauto lq:on rew:off.
    - hauto lq:on rew:off.
    - hauto lq:on rew:off.
    - move P ihP a iha b ihb c ihc i ρ0 ρ1 hρ /= []hP
               ha hb hc ξ0 ξ1 .
      f_equal;eauto.
      apply : ihP; cycle 1; eauto using subst_differ_one_ren_up.
      apply : ihc; cycle 1; eauto using subst_differ_one_ren_up.
      hauto l:on.
  Qed.


  Lemma antirenaming (a : PTm) (b : PTm) (ξ : ) :
    ( i j, ξ i = ξ j i = j)
    R (ren_PTm ξ a) b b0, R a ren_PTm ξ = b.
  Proof.
    move .
    move E : (ren_PTm ξ a) u hu.
    move : ξ a E.
    elim : u b / hu; try solve_anti_ren.
    - move a ξ []//=.
      move u [].
      case : u //=.
      move [].
      case : //=.
      move i /[swap] [].
      case : i //= _ h.
      suff : p, ren_PTm shift p = .
      move [p ?]. subst.
      move : h. asimpl.
      replace (ren_PTm (funcomp S ξ) p) with
        (ren_PTm shift (ren_PTm ξ p)); last by asimpl.
      move /ren_injective.
      move /(_ ltac:(hauto l:on unfold:ren_inj)).
      move ?. subst.
      exists p. split//. apply AppEta.
      set u := ren_PTm (scons 0 id) .
      suff : ren_PTm shift u = by eauto.
      subst u.
      asimpl.
      have hE : = ren_PTm id by asimpl.
      rewrite {2}hE. move{hE}.
      apply (tm_free_ren_any _ 0); last by qauto l:on inv:.
      rewrite /tm_i_free.
      have h' := h.
      apply f_equal with (f := ren_PTm (scons 0 id)) in h.
      apply f_equal with (f := ren_PTm (scons 1 id)) in h'.
      move : h'. asimpl *. subst.
      move : h. asimpl. move *. do 2 eexists. split; eauto.
      scongruence.
    - move a ξ [] //=.
      move u [].
      case : u //=.
      case : //=.
      move p [? ?] [? h]. subst.
      have ? : = by eauto using ren_injective. subst.
      hauto l:on.
    - move ha iha ξ []//= p [?]. subst.
      fcrush use:up_injective.
    - move p A hB ihB ξ + .
      case //= p' [*]. subst.
      have : ( i j, (upRen_PTm_PTm ξ) i = (upRen_PTm_PTm ξ) j i = j) by sfirstorder use:up_injective.
      move {}/ihB ihB.
      spec_refl.
      sauto lq:on.
    - move a b c hP ihP ξ []//=.
      move > /up_injective.
      hauto q:on ctrs:R.
    - move P a b hc ihc ξ []//=.
      move > /up_injective /up_injective.
      hauto q:on ctrs:R.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    R a b R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    move h. move : ρ. elim : a b /h.
    move a ρ /=.
    eapply AppEta'; eauto. by asimpl.
    all : hauto lq:on ctrs:R.
  Qed.


  Lemma nf_preservation (a b : PTm) :
    ERed.R a b (nf a nf b) (ne a ne b).
  Proof.
    move h.
    elim : a b /h //=;
      hauto lqb:on use:ne_nf_ren,ne_nf.
  Qed.


End ERed.

Module EReds.

  #[local]Ltac solve_s_rec :=
  move *; eapply rtc_l; eauto;
         hauto lq:on ctrs:ERed.R.

  #[local]Ltac solve_s :=
    repeat (induction 1; last by solve_s_rec); apply rtc_refl.

  Lemma AbsCong (a b : PTm) :
    rtc ERed.R a b
    rtc ERed.R (PAbs a) (PAbs b).
  Proof. solve_s. Qed.

  Lemma AppCong (a0 a1 b0 b1 : PTm) :
    rtc ERed.R
    rtc ERed.R
    rtc ERed.R (PApp ) (PApp ).
  Proof. solve_s. Qed.

  Lemma PairCong (a0 a1 b0 b1 : PTm) :
    rtc ERed.R
    rtc ERed.R
    rtc ERed.R (PPair ) (PPair ).
  Proof. solve_s. Qed.

  Lemma ProjCong p (a0 a1 : PTm) :
    rtc ERed.R
    rtc ERed.R (PProj p ) (PProj p ).
  Proof. solve_s. Qed.

  Lemma BindCong p (A0 A1 : PTm) B0 B1 :
    rtc ERed.R
    rtc ERed.R
    rtc ERed.R (PBind p ) (PBind p ).
  Proof. solve_s. Qed.

  Lemma SucCong (a0 a1 : PTm) :
    rtc ERed.R
    rtc ERed.R (PSuc ) (PSuc ).
  Proof. solve_s. Qed.

  Lemma IndCong P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 :
    rtc ERed.R
    rtc ERed.R
    rtc ERed.R
    rtc ERed.R
    rtc ERed.R (PInd ) (PInd ).
  Proof. solve_s. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    rtc ERed.R a b rtc ERed.R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof. induction 1; hauto l:on use:ERed.renaming ctrs:rtc. Qed.

  Lemma FromEPar (a b : PTm) :
    EPar.R a b
    rtc ERed.R a b.
  Proof.
    move h. elim : a b /h; eauto using AbsCong, AppCong, PairCong, ProjCong, rtc_refl, BindCong, IndCong, SucCong.
    - move _ h.
      have {}h : rtc ERed.R (ren_PTm shift ) (ren_PTm shift ) by apply renaming.
      apply : rtc_r. apply AbsCong. apply AppCong; eauto. apply rtc_refl.
      apply ERed.AppEta.
    - move _ h.
      apply : rtc_r.
      apply PairCong; eauto using ProjCong.
      apply ERed.PairEta.
  Qed.


  Lemma FromEPars (a b : PTm) :
    rtc EPar.R a b
    rtc ERed.R a b.
  Proof. induction 1; hauto l:on use:FromEPar, @relations.rtc_transitive. Qed.

  Lemma substing (a b : PTm) (ρ : PTm) :
    rtc ERed.R a b rtc ERed.R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    induction 1; hauto lq:on ctrs:rtc use:ERed.substing.
  Qed.


  Lemma app_inv (a0 b0 C : PTm) :
    rtc ERed.R (PApp ) C
     a1 b1, C = PApp
               rtc ERed.R
               rtc ERed.R .
  Proof.
    move E : (PApp ) u hu.
    move : E.
    elim : u C / hu.
    - hauto lq:on ctrs:rtc.
    - move ha iha ?. subst.
      inversion ha; subst; spec_refl;
      hauto lq:on rew:off ctrs:rtc, ERed.R inv:ERed.R.
  Qed.


  Lemma proj_inv p (a C : PTm) :
    rtc ERed.R (PProj p a) C
     c, C = PProj p c rtc ERed.R a c.
  Proof.
    move E : (PProj p a) u hu.
    move : p a E.
    elim : u C /hu;
      scrush ctrs:rtc,ERed.R inv:ERed.R.
  Qed.


  Lemma bind_inv p (A : PTm) B C :
    rtc ERed.R (PBind p A B) C
     A0 B0, C = PBind p rtc ERed.R A rtc ERed.R B .
  Proof.
    move E : (PBind p A B) u hu.
    move : p A B E.
    elim : u C / hu.
    hauto lq:on ctrs:rtc.
    hauto lq:on rew:off ctrs:rtc, ERed.R inv:ERed.R, rtc.
  Qed.


  Lemma suc_inv (a : PTm) C :
    rtc ERed.R (PSuc a) C
     b, rtc ERed.R a b C = PSuc b.
  Proof.
    move E : (PSuc a) u hu.
    move : a E.
    elim : u C / hu//=.
    - hauto l:on.
    - hauto lq:on rew:off ctrs:rtc, ERed.R inv:ERed.R, rtc.
  Qed.


  Lemma zero_inv (C : PTm) :
    rtc ERed.R PZero C C = PZero.
    move E : PZero u hu.
    move : E. elim : u C /hu//=.
    - hauto lq:on rew:off ctrs:rtc, ERed.R inv:ERed.R, rtc.
  Qed.

  Lemma ind_inv P (a : PTm) b c C :
    rtc ERed.R (PInd P a b c) C
     P0 a0 b0 c0, rtc ERed.R P rtc ERed.R a
                      rtc ERed.R b rtc ERed.R c
                      C = PInd .
  Proof.
    move E : (PInd P a b c) u hu.
    move : P a b c E.
    elim : u C / hu.
    - hauto lq:on ctrs:rtc.
    - scrush ctrs:rtc, ERed.R inv:ERed.R, rtc.
  Qed.


End EReds.

#[export]Hint Constructors ERed.R RRed.R EPar.R : red.

Module EJoin.
  Definition R (a b : PTm) := c, rtc ERed.R a c rtc ERed.R b c.

  Lemma hne_app_inj (a0 b0 a1 b1 : PTm) :
    R (PApp ) (PApp )
    R R .
  Proof.
    hauto lq:on use:EReds.app_inv.
  Qed.


  Lemma hne_proj_inj p0 p1 (a0 a1 : PTm) :
    R (PProj ) (PProj )
     = R .
  Proof.
    hauto lq:on rew:off use:EReds.proj_inv.
  Qed.


  Lemma bind_inj p0 p1 (A0 A1 : PTm) B0 B1 :
    R (PBind ) (PBind )
     = R R .
  Proof.
    hauto lq:on rew:off use:EReds.bind_inv.
  Qed.


  Lemma suc_inj (A0 A1 : PTm) :
    R (PSuc ) (PSuc )
    R .
  Proof.
    hauto lq:on rew:off use:EReds.suc_inv.
  Qed.


  Lemma ind_inj P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 :
    R (PInd ) (PInd )
    R R R R .
  Proof. hauto q:on use:EReds.ind_inv. Qed.

End EJoin.

betaeta-reduction

Module RERed.
  Inductive R : PTm PTm Prop :=
  (****************** Beta ***********************)
  | AppAbs a b :
    R (PApp (PAbs a) b) (subst_PTm (scons b VarPTm) a)

  | ProjPair p a b :
    R (PProj p (PPair a b)) (if p is PL then a else b)

  | IndZero P b c :
    R (PInd P PZero b c) b

  | IndSuc P a b c :
    R (PInd P (PSuc a) b c) (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)

  (****************** Eta ***********************)
  | AppEta a :
    R (PAbs (PApp (ren_PTm shift a) (VarPTm var_zero))) a
  | PairEta a :
    R (PPair (PProj PL a) (PProj PR a)) a

  (*************** Congruence ********************)
  | AbsCong a0 a1 :
    R
    R (PAbs ) (PAbs )
  | AppCong0 a0 a1 b :
    R
    R (PApp b) (PApp b)
  | AppCong1 a b0 b1 :
    R
    R (PApp a ) (PApp a )
  | PairCong0 a0 a1 b :
    R
    R (PPair b) (PPair b)
  | PairCong1 a b0 b1 :
    R
    R (PPair a ) (PPair a )
  | ProjCong p a0 a1 :
    R
    R (PProj p ) (PProj p )
  | BindCong0 p A0 A1 B :
    R
    R (PBind p B) (PBind p B)
  | BindCong1 p A B0 B1 :
    R
    R (PBind p A ) (PBind p A )
  | IndCong0 P0 P1 a b c :
    R
    R (PInd a b c) (PInd a b c)
  | IndCong1 P a0 a1 b c :
    R
    R (PInd P b c) (PInd P b c)
  | IndCong2 P a b0 b1 c :
    R
    R (PInd P a c) (PInd P a c)
  | IndCong3 P a b c0 c1 :
    R
    R (PInd P a b ) (PInd P a b )
  | SucCong a0 a1 :
    R
    R (PSuc ) (PSuc ).

  Lemma ToBetaEta (a b : PTm) :
    R a b
    ERed.R a b RRed.R a b.
  Proof. induction 1; hauto lq:on db:red. Qed.

  Lemma FromBeta (a b : PTm) :
    RRed.R a b RERed.R a b.
  Proof. induction 1; qauto l:on ctrs:R. Qed.

  Lemma FromEta (a b : PTm) :
    ERed.R a b RERed.R a b.
  Proof. induction 1; qauto l:on ctrs:R. Qed.

  Lemma ToBetaEtaPar (a b : PTm) :
    R a b
    EPar.R a b RRed.R a b.
  Proof.
    hauto q:on use:ERed.ToEPar, ToBetaEta.
  Qed.


  Lemma sn_preservation (a b : PTm) :
    R a b
    SN a
    SN b.
  Proof. hauto q:on use:ToBetaEtaPar, epar_sn_preservation, red_sn_preservation, RPar.FromRRed. Qed.

  Lemma bind_preservation (a b : PTm) :
    R a b isbind a isbind b.
  Proof. hauto q:on inv:R. Qed.

  Lemma univ_preservation (a b : PTm) :
    R a b isuniv a isuniv b.
  Proof. hauto q:on inv:R. Qed.

  Lemma nat_preservation (a b : PTm) :
    R a b isnat a isnat b.
  Proof. hauto q:on inv:R. Qed.

  Lemma sne_preservation (a b : PTm) :
    R a b SNe a SNe b.
  Proof.
    hauto q:on use:ToBetaEtaPar, RPar.FromRRed use:red_sn_preservation, epar_sn_preservation.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    RERed.R a b RERed.R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    hauto q:on use:ToBetaEta, FromBeta, FromEta, RRed.substing, ERed.substing.
  Qed.


  Lemma hne_preservation (a b : PTm) :
    RERed.R a b ishne a ishne b.
  Proof. induction 1; sfirstorder. Qed.

End RERed.

Module REReds.
  Lemma hne_preservation (a b : PTm) :
    rtc RERed.R a b ishne a ishne b.
  Proof. induction 1; eauto using RERed.hne_preservation, rtc_refl, rtc. Qed.

  Lemma sn_preservation (a b : PTm) :
    rtc RERed.R a b
    SN a
    SN b.
  Proof. induction 1; eauto using RERed.sn_preservation. Qed.

  Lemma FromRReds (a b : PTm) :
    rtc RRed.R a b
    rtc RERed.R a b.
  Proof. induction 1; hauto lq:on ctrs:rtc use:RERed.FromBeta. Qed.

  Lemma FromEReds (a b : PTm) :
    rtc ERed.R a b
    rtc RERed.R a b.
  Proof. induction 1; hauto lq:on ctrs:rtc use:RERed.FromEta. Qed.

  #[local]Ltac solve_s_rec :=
    move *; eapply rtc_l; eauto;
           hauto lq:on ctrs:RERed.R.

  #[local]Ltac solve_s :=
    repeat (induction 1; last by solve_s_rec); apply rtc_refl.

  Lemma AbsCong (a b : PTm) :
    rtc RERed.R a b
    rtc RERed.R (PAbs a) (PAbs b).
  Proof. solve_s. Qed.

  Lemma AppCong (a0 a1 b0 b1 : PTm) :
    rtc RERed.R
    rtc RERed.R
    rtc RERed.R (PApp ) (PApp ).
  Proof. solve_s. Qed.

  Lemma PairCong (a0 a1 b0 b1 : PTm) :
    rtc RERed.R
    rtc RERed.R
    rtc RERed.R (PPair ) (PPair ).
  Proof. solve_s. Qed.

  Lemma ProjCong p (a0 a1 : PTm) :
    rtc RERed.R
    rtc RERed.R (PProj p ) (PProj p ).
  Proof. solve_s. Qed.

  Lemma SucCong (a0 a1 : PTm) :
    rtc RERed.R
    rtc RERed.R (PSuc ) (PSuc ).
  Proof. solve_s. Qed.

  Lemma IndCong P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 :
    rtc RERed.R
    rtc RERed.R
    rtc RERed.R
    rtc RERed.R
    rtc RERed.R (PInd ) (PInd ).
  Proof. solve_s. Qed.

  Lemma BindCong p (A0 A1 : PTm) B0 B1 :
    rtc RERed.R
    rtc RERed.R
    rtc RERed.R (PBind p ) (PBind p ).
  Proof. solve_s. Qed.

  Lemma bind_preservation (a b : PTm) :
    rtc RERed.R a b isbind a isbind b.
  Proof. induction 1; qauto l:on ctrs:rtc use:RERed.bind_preservation. Qed.

  Lemma univ_preservation (a b : PTm) :
    rtc RERed.R a b isuniv a isuniv b.
  Proof. induction 1; qauto l:on ctrs:rtc use:RERed.univ_preservation. Qed.

  Lemma nat_preservation (a b : PTm) :
    rtc RERed.R a b isnat a isnat b.
  Proof. induction 1; qauto l:on ctrs:rtc use:RERed.nat_preservation. Qed.

  Lemma sne_preservation (a b : PTm) :
    rtc RERed.R a b SNe a SNe b.
  Proof. induction 1; qauto l:on ctrs:rtc use:RERed.sne_preservation. Qed.

  Lemma bind_inv p A B C :
    rtc RERed.R(PBind p A B) C
     A0 B0, C = PBind p rtc RERed.R A rtc RERed.R B .
  Proof.
    move E : (PBind p A B) u hu.
    move : p A B E.
    elim : u C / hu; sauto lq:on rew:off.
  Qed.


  Lemma univ_inv i C :
    rtc RERed.R (PUniv i) C
    C = PUniv i.
  Proof.
    move E : (PUniv i) u hu.
    move : i E. elim : u C / hu//=.
    hauto lq:on rew:off ctrs:rtc inv:RERed.R.
  Qed.


  Lemma var_inv (i : ) C :
    rtc RERed.R (VarPTm i) C
    C = VarPTm i.
  Proof.
    move E : (VarPTm i) u hu.
    move : i E. elim : u C /hu; hauto lq:on rew:off inv:RERed.R.
  Qed.


  Lemma hne_app_inv (a0 b0 C : PTm) :
    rtc RERed.R (PApp ) C
    ishne
     a1 b1, C = PApp
               rtc RERed.R
               rtc RERed.R .
  Proof.
    move E : (PApp ) u hu.
    move : E.
    elim : u C / hu.
    - hauto lq:on ctrs:rtc.
    - move a b c iha ?. subst.
      hauto lq:on rew:off ctrs:rtc, RERed.R use:RERed.hne_preservation inv:RERed.R.
  Qed.


  Lemma hne_proj_inv p (a C : PTm) :
    rtc RERed.R (PProj p a) C
    ishne a
     c, C = PProj p c rtc RERed.R a c.
  Proof.
    move E : (PProj p a) u hu.
    move : p a E.
    elim : u C /hu //=;
      scrush ctrs:rtc,RERed.R use:RERed.hne_preservation inv:RERed.R.
  Qed.


  Lemma hne_ind_inv P a b c (C : PTm) :
    rtc RERed.R (PInd P a b c) C ishne a
     P0 a0 b0 c0, C = PInd
                     rtc RERed.R P
                     rtc RERed.R a
                     rtc RERed.R b
                     rtc RERed.R c .
  Proof.
    move E : (PInd P a b c) u hu.
    move : P a b c E.
    elim : u C / hu //=;
      scrush ctrs:rtc,RERed.R use:RERed.hne_preservation inv:RERed.R.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    rtc RERed.R a b rtc RERed.R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    induction 1; hauto lq:on ctrs:rtc use:RERed.substing.
  Qed.


  Lemma cong_up (ρ0 ρ1 : PTm) :
    ( i, rtc RERed.R (ρ0 i) (ρ1 i))
    ( i, rtc RERed.R (up_PTm_PTm ρ0 i) (up_PTm_PTm ρ1 i)).
  Proof. move h [|i]; cycle 1.
         simpl. rewrite /funcomp.
         substify. by apply substing.
         apply rtc_refl.
  Qed.


  Lemma cong_up2 (ρ0 ρ1 : PTm) :
    ( i, rtc RERed.R (ρ0 i) (ρ1 i))
    ( i, rtc RERed.R (up_PTm_PTm (up_PTm_PTm ρ0) i) (up_PTm_PTm (up_PTm_PTm ρ1) i)).
  Proof. hauto l:on use:cong_up. Qed.

  Lemma cong (a : PTm) (ρ0 ρ1 : PTm) :
    ( i, rtc RERed.R (ρ0 i) (ρ1 i))
    rtc RERed.R (subst_PTm ρ0 a) (subst_PTm ρ1 a).
  Proof.
    move : ρ0 ρ1. elim : a /=;
      eauto 20 using AppCong, AbsCong, BindCong, ProjCong, PairCong, cong_up, rtc_refl, IndCong, SucCong, cong_up2.
  Qed.


  Lemma cong' (a b : PTm) (ρ0 ρ1 : PTm) :
    rtc RERed.R a b
    ( i, rtc RERed.R (ρ0 i) (ρ1 i))
    rtc RERed.R (subst_PTm ρ0 a) (subst_PTm ρ1 b).
  Proof.
    move .
    have : rtc RERed.R (subst_PTm ρ0 a) (subst_PTm ρ1 a) by eauto using cong.
    move ?. apply : relations.rtc_transitive; eauto.
    hauto l:on use:substing.
  Qed.


  Lemma ToEReds (a b : PTm) :
    nf a
    rtc RERed.R a b rtc ERed.R a b.
  Proof.
    move + h.
    induction h; hauto lq:on rew:off ctrs:rtc use:RERed.ToBetaEta, RReds.nf_refl, @rtc_once, ERed.nf_preservation.
  Qed.


  Lemma zero_inv (C : PTm) :
    rtc RERed.R PZero C C = PZero.
    move E : PZero u hu.
    move : E. elim : u C /hu//=.
    - hauto lq:on rew:off ctrs:rtc, RERed.R inv:RERed.R, rtc.
  Qed.

  Lemma suc_inv (a : PTm) C :
    rtc RERed.R (PSuc a) C
     b, rtc RERed.R a b C = PSuc b.
  Proof.
    move E : (PSuc a) u hu.
    move : a E.
    elim : u C / hu//=.
    - hauto l:on.
    - hauto lq:on rew:off ctrs:rtc, RERed.R inv:RERed.R, rtc.
  Qed.


  Lemma nat_inv C :
    rtc RERed.R PNat C
    C = PNat.
  Proof.
    move E : PNat u hu. move : E.
    elim : u C / hu=>//=.
    hauto lq:on rew:off ctrs:rtc, RERed.R inv:RERed.R.
  Qed.


End REReds.

Leftmost-outermost beta-reduction

Module LoRed.
  Inductive R : PTm PTm Prop :=
  (****************** Beta ***********************)
  | AppAbs a b :
    R (PApp (PAbs a) b) (subst_PTm (scons b VarPTm) a)

  | ProjPair p a b :
    R (PProj p (PPair a b)) (if p is PL then a else b)

  | IndZero P b c :
      R (PInd P PZero b c) b

  | IndSuc P a b c :
    R (PInd P (PSuc a) b c) (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)


  (*************** Congruence ********************)
  | AbsCong a0 a1 :
    R
    R (PAbs ) (PAbs )
  | AppCong0 a0 a1 b :
    ~~ ishf
    R
    R (PApp b) (PApp b)
  | AppCong1 a b0 b1 :
    ne a
    R
    R (PApp a ) (PApp a )
  | PairCong0 a0 a1 b :
    R
    R (PPair b) (PPair b)
  | PairCong1 a b0 b1 :
    nf a
    R
    R (PPair a ) (PPair a )
  | ProjCong p a0 a1 :
    ~~ ishf
    R
    R (PProj p ) (PProj p )
  | BindCong0 p A0 A1 B :
    R
    R (PBind p B) (PBind p B)
  | BindCong1 p A B0 B1 :
    nf A
    R
    R (PBind p A ) (PBind p A )
  | IndCong0 P0 P1 a b c :
    ne a
    R
    R (PInd a b c) (PInd a b c)
  | IndCong1 P a0 a1 b c :
    ~~ ishf
    R
    R (PInd P b c) (PInd P b c)
  | IndCong2 P a b0 b1 c :
    nf P
    ne a
    R
    R (PInd P a c) (PInd P a c)
  | IndCong3 P a b c0 c1 :
    nf P
    ne a
    nf b
    R
    R (PInd P a b ) (PInd P a b )
  | SucCong a0 a1 :
    R
    R (PSuc ) (PSuc ).

  Lemma hf_preservation (a b : PTm) :
    LoRed.R a b
    ishf a
    ishf b.
  Proof.
    move h. elim : a b /h//=.
  Qed.


  Lemma ToRRed (a b : PTm) :
    LoRed.R a b
    RRed.R a b.
  Proof. induction 1; hauto lq:on ctrs:RRed.R. Qed.

  Lemma AppAbs' a (b : PTm) u :
    u = (subst_PTm (scons b VarPTm) a)
    R (PApp (PAbs a) b) u.
  Proof. move . apply AppAbs. Qed.

  Lemma IndSuc' P a b c u :
    u = (subst_PTm (scons (PInd P a b c) (scons a VarPTm)) c)
    R (@PInd P (PSuc a) b c) u.
  Proof. move . apply IndSuc. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    R a b R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ.
    elim : a b /h.

    move a b ξ /=.
    apply AppAbs'. by asimpl.
    all : try qauto ctrs:R use:ne_nf_ren, ishf_ren.
    move * /=; apply IndSuc'. by asimpl.
  Qed.


End LoRed.

Module LoReds.
  Lemma hf_preservation (a b : PTm) :
    rtc LoRed.R a b
    ishf a
    ishf b.
  Proof.
    induction 1; eauto using LoRed.hf_preservation.
  Qed.


  Lemma hf_ne_imp (a b : PTm) :
    rtc LoRed.R a b
    ne b
    ~~ ishf a.
  Proof.
    move : hf_preservation. repeat move/[apply].
    case : a; case : b //=; sfirstorder b:on.
  Qed.


  #[local]Ltac solve_s_rec :=
  move *; eapply rtc_l; eauto;
         hauto lq:on ctrs:LoRed.R, rtc use:hf_ne_imp.

  #[local]Ltac solve_s :=
    repeat (induction 1; last by solve_s_rec); (move *; apply rtc_refl).

  Lemma AbsCong (a b : PTm) :
    rtc LoRed.R a b
    rtc LoRed.R (PAbs a) (PAbs b).
  Proof. solve_s. Qed.

  Lemma AppCong (a0 a1 b0 b1 : PTm) :
    rtc LoRed.R
    rtc LoRed.R
    ne
    rtc LoRed.R (PApp ) (PApp ).
  Proof. solve_s. Qed.

  Lemma PairCong (a0 a1 b0 b1 : PTm) :
    rtc LoRed.R
    rtc LoRed.R
    nf
    rtc LoRed.R (PPair ) (PPair ).
  Proof. solve_s. Qed.

  Lemma ProjCong p (a0 a1 : PTm) :
    rtc LoRed.R
    ne
    rtc LoRed.R (PProj p ) (PProj p ).
  Proof. solve_s. Qed.

  Lemma BindCong p (A0 A1 : PTm) B0 B1 :
    rtc LoRed.R
    rtc LoRed.R
    nf
    rtc LoRed.R (PBind p ) (PBind p ).
  Proof. solve_s. Qed.

  Lemma IndCong P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 :
    rtc LoRed.R
    rtc LoRed.R
    rtc LoRed.R
    rtc LoRed.R
    ne
    nf
    nf
    rtc LoRed.R (PInd ) (PInd ).
  Proof. solve_s. Qed.

  Lemma SucCong (a0 a1 : PTm) :
    rtc LoRed.R
    rtc LoRed.R (PSuc ) (PSuc ).
  Proof. solve_s. Qed.

  Local Ltac triv := simpl in *; sfirstorder b:on.

  Lemma FromSN_mutual :
    ( (a : PTm) (_ : SNe a), v, rtc LoRed.R a v ne v)
    ( (a : PTm) (_ : SN a), v, rtc LoRed.R a v nf v)
    ( (a b : PTm) (_ : TRedSN a b), LoRed.R a b).
  Proof.
    apply sn_mutual.
    - hauto lq:on ctrs:rtc.
    - hauto lq:on rew:off use:LoReds.AppCong solve+:triv.
    - hauto l:on use:LoReds.ProjCong solve+:triv.
    - hauto lq:on use:LoReds.IndCong solve+:triv.
    - hauto q:on use:LoReds.PairCong solve+:triv.
    - hauto q:on use:LoReds.AbsCong solve+:triv.
    - sfirstorder use:ne_nf.
    - hauto lq:on ctrs:rtc.
    - hauto lq:on use:LoReds.BindCong solve+:triv.
    - hauto lq:on ctrs:rtc.
    - hauto lq:on ctrs:rtc.
    - hauto lq:on ctrs:rtc.
    - hauto l:on use:SucCong.
    - qauto ctrs:LoRed.R.
    - move b hb ihb h.
      have : ~~ ishf by inversion h.
      hauto lq:on ctrs:LoRed.R.
    - qauto ctrs:LoRed.R.
    - qauto ctrs:LoRed.R.
    - move p a b h.
      have : ~~ ishf a by inversion h.
      hauto lq:on ctrs:LoRed.R.
    - sfirstorder.
    - sfirstorder.
    - move P b c hP ihP hb ihb hc ihc hr.
      have : ~~ ishf by inversion hr.
      hauto q:on ctrs:LoRed.R.
  Qed.


  Lemma FromSN : a, SN a v, rtc LoRed.R a v nf v.
  Proof. firstorder using FromSN_mutual. Qed.

  Lemma ToRReds : (a b : PTm), rtc LoRed.R a b rtc RRed.R a b.
  Proof. induction 1; hauto lq:on ctrs:rtc use:LoRed.ToRRed. Qed.
End LoReds.

Fixpoint size_PTm (a : PTm) :=
  match a with
  | VarPTm _ 1
  | PAbs a 3 + size_PTm a
  | PApp a b 1 + Nat.add (size_PTm a) (size_PTm b)
  | PProj p a 1 + size_PTm a
  | PPair a b 3 + Nat.add (size_PTm a) (size_PTm b)
  | PUniv _ 3
  | PBind p A B 3 + Nat.add (size_PTm A) (size_PTm B)
  | PInd P a b c 3 + size_PTm P + size_PTm a + size_PTm b + size_PTm c
  | PNat 3
  | PSuc a 3 + size_PTm a
  | PZero 3
  end.

Lemma size_PTm_ren (ξ : ) a : size_PTm (ren_PTm ξ a) = size_PTm a.
Proof.
  move : ξ. elim : a //=; scongruence.
Qed.


#[export]Hint Rewrite size_PTm_ren : sizetm.

Lemma ered_size (a b : PTm) :
  ERed.R a b
  size_PTm b < size_PTm a.
Proof.
  move h. elim : a b /h; try hauto l:on rew:db:sizetm solve+:lia.
Qed.


Lemma ered_sn (a : PTm) : sn ERed.R a.
Proof.
  hauto lq:on rew:off use:size_PTm_ren, ered_size,
          well_founded_lt_compat unfold:well_founded.
Qed.


Lemma ered_local_confluence (a b c : PTm) :
  ERed.R a b
  ERed.R a c
   d, rtc ERed.R b d rtc ERed.R c d.
Proof.
  move h. move : c.
  elim : a b / h.
  - move a c.
    elim /ERed.inv //= _.
    + move [+ ?]. subst h.
      apply f_equal with (f := subst_PTm (scons (PAbs (VarPTm var_zero)) VarPTm)) in h.
      move : h. asimpl ?. subst.
      eauto using rtc_refl.
    + move ha [*]. subst.
      elim /ERed.inv : ha //= _.
      * move ha [*]. subst. rename into .
        move /ERed.antirenaming : ha.
        move /(_ ltac:(hauto lq:on)) [a' [ ]]. subst.
        hauto lq:on ctrs:rtc, ERed.R.
      * hauto q:on ctrs:rtc, ERed.R inv:ERed.R.
  - move a c ha.
    elim /ERed.inv : ha //= _.
    + hauto l:on.
    + move ha [*]. subst.
      elim /ERed.inv : ha //= _.
      move p ha [*]. subst.
      hauto q:on ctrs:rtc, ERed.R.
    + move ha [*]. subst.
      elim /ERed.inv : ha //= _.
      move p ha [*]. subst.
      hauto q:on ctrs:rtc, ERed.R.
  - move ha iha c.
    elim /ERed.inv //= _.
    + move [*]. subst.
      elim /ERed.inv : ha //=_.
      * move ha [*] {iha}. subst.
        have [ [ ]] : a0, ERed.R c = ren_PTm shift by hauto lq:on use:ERed.antirenaming. subst.
        exists . split; last by apply relations.rtc_once.
        apply relations.rtc_once. apply ERed.AppEta.
      * hauto q:on inv:ERed.R.
    + hauto lq:on use:EReds.AbsCong.
  - move b ha iha c.
    elim /ERed.inv //= _.
    + hauto lq:on ctrs:rtc use:EReds.AppCong.
    + hauto lq:on use:@relations.rtc_once ctrs:ERed.R.
  - move a hb ihb c.
    elim /ERed.inv //=_.
    + move ha [*]. subst.
      move {ihb}. exists (PApp ).
      hauto lq:on use:@relations.rtc_once ctrs:ERed.R.
    + hauto lq:on ctrs:rtc use:EReds.AppCong.
  - move b ha iha c.
    elim /ERed.inv //= _.
    + sauto lq:on.
    + hauto lq:on ctrs:rtc use:EReds.PairCong.
    + hauto lq:on ctrs:ERed.R use:@relations.rtc_once.
  - move a hb hc c. elim /ERed.inv //= _.
    + move ? [*]. subst.
      sauto lq:on.
    + hauto lq:on ctrs:ERed.R use:@relations.rtc_once.
    + hauto lq:on ctrs:rtc use:EReds.PairCong.
  - qauto l:on inv:ERed.R use:EReds.ProjCong.
  - move p B hA ihA u.
    elim /ERed.inv //=_;
      hauto lq:on ctrs:rtc use:EReds.BindCong.
  - move p A hB ihB u.
    elim /ERed.inv //=_;
                     hauto lq:on ctrs:rtc use:EReds.BindCong.
  - move a b c hP ihP u.
    elim /ERed.inv //=_.
    + move hP' [*]. subst.
      move : ihP hP' /[apply]. move [][].
      eauto using EReds.IndCong, rtc_refl.
    + move + [*]. subst.
      eauto 20 using rtc_refl, EReds.IndCong, rtc_l.
    + move hb [*] {ihP}. subst.
      eauto 20 using rtc_refl, EReds.IndCong, rtc_l.
    + move h [*] {ihP}. subst.
      eauto 20 using rtc_refl, EReds.IndCong, rtc_l.
  - move P b c ha iha u.
    elim /ERed.inv //=_;
                     try solve [move hP[*]; subst;
      eauto 20 using rtc_refl, EReds.IndCong, rtc_l].
    move hP[*]. subst.
    move : iha hP /[apply].
    move [? [*]].
    eauto 20 using rtc_refl, EReds.IndCong, rtc_l.
  - move P a c hb ihb u.
    elim /ERed.inv //=_;
      try solve [
          move hP [*]; subst;
          eauto 20 using rtc_refl, EReds.IndCong, rtc_l].
    move h [*]. subst.
    move : ihb h /[apply]. move [ [*]].
    eauto 20 using rtc_refl, EReds.IndCong, rtc_l.
  - move P a b hc ihc u.
    elim /ERed.inv //=_;
      try solve [
          move c hP [*]; subst;
          eauto 20 using rtc_refl, EReds.IndCong, rtc_l].
    move h [*]. subst.
    move : ihc h /[apply]. move [][*].
    eauto 20 using rtc_refl, EReds.IndCong, rtc_l.
  - qauto l:on inv:ERed.R ctrs:rtc use:EReds.SucCong.
Qed.


Lemma ered_confluence (a b c : PTm) :
  rtc ERed.R a b
  rtc ERed.R a c
   d, rtc ERed.R b d rtc ERed.R c d.
Proof.
  sfirstorder use:relations.locally_confluent_confluent, ered_sn, ered_local_confluence.
Qed.


Lemma red_confluence (a b c : PTm) :
  rtc RRed.R a b
  rtc RRed.R a c
   d, rtc RRed.R b d rtc RRed.R c d.
  suff : rtc RPar.R a b rtc RPar.R a c d : PTm, rtc RPar.R b d rtc RPar.R c d
             by hauto lq:on use:RReds.RParIff.
  apply relations.diamond_confluent.
  rewrite /relations.diamond.
  eauto using RPar.diamond.
Qed.

Lemma red_uniquenf (a b c : PTm) :
  rtc RRed.R a b
  rtc RRed.R a c
  nf b
  nf c
  b = c.
Proof.
  move : red_confluence; repeat move/[apply].
  move [d [ ]].
  move *.
  suff [] : b = d c = d by congruence.
  sfirstorder use:RReds.nf_refl.
Qed.


Module NeEPars.
  Lemma R_nonelim_nf (a b : PTm) :
    rtc NeEPar.R_nonelim a b
    nf b
    nf a.
  Proof. induction 1; sfirstorder use:NeEPar.R_elim_nf. Qed.

  Lemma ToEReds : ( (a b : PTm), rtc NeEPar.R_nonelim a b rtc ERed.R a b).
  Proof.
    induction 1; hauto l:on use:NeEPar.ToEPar, EReds.FromEPar, @relations.rtc_transitive.
  Qed.

End NeEPars.

Lemma rered_standardization (a c : PTm) :
  SN a
  rtc RERed.R a c
   b, rtc RRed.R a b rtc NeEPar.R_nonelim b c.
Proof.
  move + h. elim : a c /h.
  by eauto using rtc_refl.
  move a b c.
  move /RERed.ToBetaEtaPar.
  case.
  - move ih hP.
    have : SN b by qauto use:epar_sn_preservation.
    move {}/ih [b' [ ]].
    hauto lq:on ctrs:rtc use:SN_UniqueNF.η_postponement_star.
  - hauto lq:on ctrs:rtc use:red_sn_preservation, RPar.FromRRed.
Qed.


Lemma rered_standardization' (a u : PTm) :
  SN a
  rtc RERed.R a u
  nf u
   u0, nf rtc RRed.R a rtc ERed.R u.
Proof.
  move + h. elim : a u /h.
  by eauto using rtc_refl.
  move a b c /RERed.ToBetaEtaPar.
  case.
  - move ih hP.
    have : SN b by qauto use:epar_sn_preservation.
    move : ih; repeat move/[apply].
    move [ [hnf [ ]]].
    move : SN_UniqueNF.η_postponement_star hP . repeat move/[apply].
    move [d [ ]]. exists d. split //.
    sfirstorder use:NeEPar.R_elim_nf.
    qauto use:NeEPar.ToEPar, EReds.FromEPar, @relations.rtc_transitive.
  - hauto lq:on lq:on ctrs:rtc use:red_sn_preservation, RPar.FromRRed.
Qed.


Lemma rered_confluence (a b c : PTm) :
  SN a
  rtc RERed.R a b
  rtc RERed.R a c
   d, rtc RERed.R b d rtc RERed.R c d.
Proof.
  move hP hb hc.
  have [] : SN b SN c by qauto use:REReds.sn_preservation.
  move /LoReds.FromSN [bv [/LoReds.ToRReds /REReds.FromRReds hbv hbv']].
  move => /LoReds.FromSN [cv [/LoReds.ToRReds /REReds.FromRReds hcv hcv']].
  have {}hb : rtc RERed.R a bv by eauto using relations.rtc_transitive.
  have {}hc : rtc RERed.R a cv by eauto using relations.rtc_transitive.
  move : rered_standardization' (hP) (hb) (hbv'); repeat move/[apply]. move => [bv' [nfbv' [ ]]].
  move : rered_standardization' (hP) (hc) (hcv'); repeat move/[apply]. move => [cv' [nfcv' [ ]]].
  have ? : bv' = cv' by eauto using red_uniquenf. subst.
  move : ered_confluence () (); repeat move/[apply].
  move => [v [hv hv']].
  exists v.
  hauto use:REReds.FromEReds,REReds.FromEReds,@relations.rtc_transitive.
Qed.


betaeta-joinability

Module DJoin.
  Definition R (a b : PTm) := c, rtc RERed.R a c rtc RERed.R b c.

  Lemma refl (a : PTm) : R a a.
  Proof. sfirstorder use:@rtc_refl unfold:R. Qed.

  Lemma FromEJoin (a b : PTm) : EJoin.R a b DJoin.R a b.
  Proof. hauto q:on use:REReds.FromEReds. Qed.

  Lemma ToEJoin (a b : PTm) : nf a nf b DJoin.R a b EJoin.R a b.
  Proof. hauto q:on use:REReds.ToEReds. Qed.

  Lemma symmetric (a b : PTm) : R a b R b a.
  Proof. sfirstorder unfold:R. Qed.

  Lemma transitive (a b c : PTm) : SN b R a b R b c R a c.
  Proof.
    rewrite /R.
    move + [ab [ha +]] [bc [+ hc]].
    move : rered_confluence; repeat move/[apply].
    move [v [ ]].
    exists v. sfirstorder use:@relations.rtc_transitive.
  Qed.


  Lemma AbsCong (a b : PTm) :
    R a b
    R (PAbs a) (PAbs b).
  Proof. hauto lq:on use:REReds.AbsCong unfold:R. Qed.

  Lemma AppCong (a0 a1 b0 b1 : PTm) :
    R
    R
    R (PApp ) (PApp ).
  Proof. hauto lq:on use:REReds.AppCong unfold:R. Qed.

  Lemma PairCong (a0 a1 b0 b1 : PTm) :
    R
    R
    R (PPair ) (PPair ).
  Proof. hauto q:on use:REReds.PairCong. Qed.

  Lemma ProjCong p (a0 a1 : PTm) :
    R
    R (PProj p ) (PProj p ).
  Proof. hauto q:on use:REReds.ProjCong. Qed.

  Lemma BindCong p (A0 A1 : PTm) B0 B1 :
    R
    R
    R (PBind p ) (PBind p ).
  Proof. hauto q:on use:REReds.BindCong. Qed.

  Lemma IndCong P0 P1 (a0 a1 : PTm) b0 b1 c0 c1 :
    R
    R
    R
    R
    R (PInd ) (PInd ).
  Proof. hauto q:on use:REReds.IndCong. Qed.

  Lemma SucCong (a0 a1 : PTm) :
    R
    R (PSuc ) (PSuc ).
  Proof. qauto l:on use:REReds.SucCong. Qed.

  Lemma FromRedSNs (a b : PTm) :
    rtc TRedSN a b
    R a b.
  Proof.
    move /RReds.FromRedSNs /REReds.FromRReds.
    sfirstorder use:@rtc_refl unfold:R.
  Qed.


  Lemma sne_nat_noconf (a b : PTm) :
    R a b SNe a isnat b False.
  Proof.
    move [c [? ?]] *.
    have : SNe c isnat c by sfirstorder use:REReds.sne_preservation, REReds.nat_preservation.
    qauto l:on inv:SNe.
  Qed.


  Lemma sne_bind_noconf (a b : PTm) :
    R a b SNe a isbind b False.
  Proof.
    move [c [? ?]] *.
    have : SNe c isbind c by sfirstorder use:REReds.sne_preservation, REReds.bind_preservation.
    qauto l:on inv:SNe.
  Qed.


  Lemma sne_univ_noconf (a b : PTm) :
    R a b SNe a isuniv b False.
  Proof.
    hauto q:on use:REReds.sne_preservation,
          REReds.univ_preservation inv:SNe.
  Qed.


  Lemma bind_univ_noconf (a b : PTm) :
    R a b isbind a isuniv b False.
  Proof.
    move [c [ ]] .
    have { } : isbind c isuniv c by
      hauto l:on use:REReds.bind_preservation,
          REReds.univ_preservation.
    case : c //=; sfirstorder b:on.
  Qed.


  Lemma hne_univ_noconf (a b : PTm) :
    R a b ishne a isuniv b False.
  Proof.
    move [c [ ]] .
    have { } : ishne c isuniv c by
      hauto l:on use:REReds.hne_preservation,
          REReds.univ_preservation.
    move [].
    case : c //=.
  Qed.


  Lemma hne_bind_noconf (a b : PTm) :
    R a b ishne a isbind b False.
  Proof.
    move [c [ ]] .
    have { } : ishne c isbind c by
      hauto l:on use:REReds.hne_preservation,
          REReds.bind_preservation.
    move [].
    case : c //=.
  Qed.


  Lemma hne_nat_noconf (a b : PTm) :
    R a b ishne a isnat b False.
  Proof.
    move [c [ ]] .
    have : ishne c isnat c by sfirstorder use:REReds.hne_preservation, REReds.nat_preservation.
    clear. case : c //=; sfirstorder b:on.
  Qed.


  Lemma bind_inj p0 p1 (A0 A1 : PTm) B0 B1 :
    DJoin.R (PBind ) (PBind )
     = DJoin.R DJoin.R .
  Proof.
    rewrite /R.
    hauto lq:on rew:off use:REReds.bind_inv.
  Qed.


  Lemma var_inj (i j : ) :
    R (VarPTm i) (VarPTm j) i = j.
  Proof. sauto lq:on rew:off use:REReds.var_inv unfold:R. Qed.

  Lemma univ_inj i j :
    @R (PUniv i) (PUniv j) i = j.
  Proof.
    sauto lq:on rew:off use:REReds.univ_inv.
  Qed.


  Lemma suc_inj (A0 A1 : PTm) :
    R (PSuc ) (PSuc )
    R .
  Proof.
    hauto lq:on rew:off use:REReds.suc_inv.
  Qed.


  Lemma hne_app_inj (a0 b0 a1 b1 : PTm) :
    R (PApp ) (PApp )
    ishne
    ishne
    R R .
  Proof.
    hauto lq:on use:REReds.hne_app_inv.
  Qed.


  Lemma hne_proj_inj p0 p1 (a0 a1 : PTm) :
    R (PProj ) (PProj )
    ishne
    ishne
     = R .
  Proof.
    sauto lq:on use:REReds.hne_proj_inv.
  Qed.


  Lemma FromRRed0 (a b : PTm) :
    RRed.R a b R a b.
  Proof.
    hauto lq:on ctrs:rtc use:RERed.FromBeta unfold:R.
  Qed.


  Lemma FromRRed1 (a b : PTm) :
    RRed.R b a R a b.
  Proof.
    hauto lq:on ctrs:rtc use:RERed.FromBeta unfold:R.
  Qed.


  Lemma FromRReds (a b : PTm) :
    rtc RRed.R b a R a b.
  Proof.
    hauto lq:on ctrs:rtc use:REReds.FromRReds unfold:R.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    R a b R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    hauto lq:on rew:off ctrs:rtc unfold:R use:REReds.substing.
  Qed.


  Lemma renaming (a b : PTm) (ρ : ) :
    R a b R (ren_PTm ρ a) (ren_PTm ρ b).
  Proof. substify. apply substing. Qed.

  Lemma weakening (a b : PTm) :
    R a b R (ren_PTm shift a) (ren_PTm shift b).
  Proof. apply renaming. Qed.

  Lemma cong (a : PTm) c d (ρ : PTm) :
    R c d R (subst_PTm (scons c ρ) a) (subst_PTm (scons d ρ) a).
  Proof.
    rewrite /R. move [cd [ ]].
    exists (subst_PTm (scons cd ρ) a).
    hauto q:on ctrs:rtc inv: use:REReds.cong.
  Qed.


  Lemma cong' (a b : PTm) c d (ρ : PTm) :
    R a b
    R c d R (subst_PTm (scons c ρ) a) (subst_PTm (scons d ρ) b).
  Proof.
    rewrite /R. move [ab [ ]] [cd [ ]].
    exists (subst_PTm (scons cd ρ) ab).
    hauto q:on ctrs:rtc inv: use:REReds.cong'.
  Qed.


  Lemma pair_inj (a0 a1 b0 b1 : PTm) :
    SN (PPair )
    SN (PPair )
    R (PPair ) (PPair )
    R R .
  Proof.
    move .
    have [? [? [? ?]]] : SN SN SN SN by sfirstorder use:SN_NoForbid.P_PairInv.
    have ? : SN (PProj PL (PPair )) by hauto lq:on db:sn.
    have ? : SN (PProj PR (PPair )) by hauto lq:on db:sn.
    have ? : SN (PProj PL (PPair )) by hauto lq:on db:sn.
    have ? : SN (PProj PR (PPair )) by hauto lq:on db:sn.
    have h0L : RRed.R (PProj PL (PPair )) by eauto with red.
    have h0R : RRed.R (PProj PR (PPair )) by eauto with red.
    have h1L : RRed.R (PProj PL (PPair )) by eauto with red.
    have h1R : RRed.R (PProj PR (PPair )) by eauto with red.
    move .
    move /ProjCong in .
    have h2L := PL.
    have {}h2R := PR.
    move /FromRRed1 in h0L.
    move /FromRRed0 in h1L.
    move /FromRRed1 in h0R.
    move /FromRRed0 in h1R.
    split; eauto using transitive.
  Qed.


  Lemma ejoin_pair_inj (a0 a1 b0 b1 : PTm) :
    nf nf nf nf
    EJoin.R (PPair ) (PPair )
    EJoin.R EJoin.R .
  Proof.
    move /FromEJoin.
    have [? ?] : SN (PPair ) SN (PPair ) by sauto lqb:on rew:off use:ne_nf_embed.
    move ?.
    have : R R by hauto l:on use:pair_inj.
    hauto l:on use:ToEJoin.
  Qed.


  Lemma abs_inj (a0 a1 : PTm) :
    SN SN
    R (PAbs ) (PAbs )
    R .
  Proof.
    move .
    move /weakening /=.
    move /AppCong. move /(_ (VarPTm var_zero) (VarPTm var_zero)).
    move /(_ ltac:(sfirstorder use:refl)).
    move h.
    have /FromRRed1 : RRed.R (PApp (PAbs (ren_PTm (upRen_PTm_PTm shift) )) (VarPTm var_zero)) . apply RRed.AppAbs'; asimpl. by rewrite subst_scons_id.
    have /FromRRed0 : RRed.R (PApp (PAbs (ren_PTm (upRen_PTm_PTm shift) )) (VarPTm var_zero)) by
      apply RRed.AppAbs'; eauto; by asimpl; rewrite ?subst_scons_id.
    have := .
    have := .
    eapply sn_renaming with (ξ := (upRen_PTm_PTm shift)) in .
    eapply sn_renaming with (ξ := (upRen_PTm_PTm shift)) in .
    apply : transitive; try apply .
    apply : N_Exp. apply N_β. sauto.
    asimpl. by rewrite subst_scons_id.
    apply : transitive; try apply .
    apply : N_Exp. apply N_β. sauto.
    by asimpl; rewrite subst_scons_id.
    eauto.
  Qed.


  Lemma ejoin_abs_inj (a0 a1 : PTm) :
    nf nf
    EJoin.R (PAbs ) (PAbs )
    EJoin.R .
  Proof.
    move .
    have [? [? [? ?]]] : SN SN SN (PAbs ) SN (PAbs ) by sauto lqb:on rew:off use:ne_nf_embed.
    move /FromEJoin.
    move /abs_inj.
    hauto l:on use:ToEJoin.
  Qed.


  Lemma standardization (a b : PTm) :
    SN a SN b R a b
     va vb, rtc RRed.R a va rtc RRed.R b vb nf va nf vb EJoin.R va vb.
  Proof.
    move [ab [ ]].
    have hv : SN ab by sfirstorder use:REReds.sn_preservation.
    have : v, rtc RRed.R ab v nf v by sfirstorder use:LoReds.FromSN, LoReds.ToRReds.
    move [v [ ]].
    have : rtc RERed.R a v by hauto q:on use:@relations.rtc_transitive, REReds.FromRReds.
    have : rtc RERed.R b v by hauto q:on use:@relations.rtc_transitive, REReds.FromRReds.
    move : . clear.
    move hv hbv hav.
    move : rered_standardization () hav. repeat move/[apply].
    move [ [ ]].
    move : rered_standardization () hbv. repeat move/[apply].
    move [ [ ]].
    have [*] : nf nf by sfirstorder use:NeEPars.R_nonelim_nf.
    hauto q:on use:NeEPars.ToEReds unfold:EJoin.R.
  Qed.


  Lemma standardization_lo (a b : PTm) :
    SN a SN b R a b
     va vb, rtc LoRed.R a va rtc LoRed.R b vb nf va nf vb EJoin.R va vb.
  Proof.
    move /[dup] sna + /[dup] snb.
    move : standardization; repeat move/[apply].
    move [va][vb][hva][hvb][nfva][nfvb]hj.
    move /LoReds.FromSN : sna [va' [hva' ]].
    move /LoReds.FromSN : snb [vb' [hvb' ]].
    exists va', vb'.
    repeat split => //=.
    have : va = va' vb = vb' by sfirstorder use:red_uniquenf, LoReds.ToRReds.
    case; congruence.
  Qed.

End DJoin.

One-step reductionless subtyping

Module Sub1.
  Inductive R : PTm PTm Prop :=
  | Refl a :
    R a a
  | Univ i j :
    i j
    R (PUniv i) (PUniv j)
  | PiCong A0 A1 B0 B1 :
    R
    R
    R (PBind PPi ) (PBind PPi )
  | SigCong A0 A1 B0 B1 :
    R
    R
    R (PBind PSig ) (PBind PSig ).

  Lemma transitive0 (A B C : PTm) :
    R A B (R B C R A C) (R C A R C B).
  Proof.
    move h. move : C.
    elim : A B /h; hauto lq:on ctrs:R inv:R solve+:lia.
  Qed.


  Lemma transitive (A B C : PTm) :
    R A B R B C R A C.
  Proof. hauto q:on use:transitive0. Qed.

  Lemma refl (A : PTm) : R A A.
  Proof. sfirstorder. Qed.

  Lemma commutativity0 (A B C : PTm) :
    R A B
    (RERed.R A C
     D, RERed.R B D R C D)
    (RERed.R B C
     D, RERed.R A D R D C).
  Proof.
    move h. move : C.
    elim : A B / h; hauto lq:on ctrs:RERed.R, R inv:RERed.R.
  Qed.


  Lemma commutativity_Ls (A B C : PTm) :
    R A B
    rtc RERed.R A C
     D, rtc RERed.R B D R C D.
  Proof.
    move + h. move : B.
    induction h; ecrush use:commutativity0.
  Qed.


  Lemma commutativity_Rs (A B C : PTm) :
    R A B
    rtc RERed.R B C
     D, rtc RERed.R A D R D C.
  Proof.
    move + h. move : A. induction h; ecrush use:commutativity0.
  Qed.


  Lemma sn_preservation :
  ( (a : PTm) (s : SNe a), b, R a b R b a a = b)
  ( (a : PTm) (s : SN a), b, R a b R b a SN b)
  ( (a b : PTm) (_ : TRedSN a b), c, R a c R c a a = c).
  Proof.
    apply sn_mutual; hauto lq:on inv:R ctrs:SN.
  Qed.


  Lemma bind_preservation (a b : PTm) :
    R a b isbind a = isbind b.
  Proof. hauto q:on inv:R. Qed.

  Lemma univ_preservation (a b : PTm) :
    R a b isuniv a = isuniv b.
  Proof. hauto q:on inv:R. Qed.

  Lemma sne_preservation (a b : PTm) :
    R a b SNe a SNe b.
  Proof. hfcrush use:sn_preservation. Qed.

  Lemma renaming (a b : PTm) (ξ : ) :
    R a b R (ren_PTm ξ a) (ren_PTm ξ b).
  Proof.
    move h. move : ξ.
    elim : a b /h; hauto lq:on ctrs:R.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    R a b R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof. move h. move : ρ. elim : a b /h; hauto lq:on ctrs:R. Qed.

  Lemma hne_refl (a b : PTm) :
    ishne a ishne b R a b a = b.
  Proof. hauto q:on inv:R. Qed.

End Sub1.

Module ESub.
  Definition R (a b : PTm) := c0 c1, rtc ERed.R a rtc ERed.R b Sub1.R .

  Lemma pi_inj (A0 A1 : PTm) B0 B1 :
    R (PBind PPi ) (PBind PPi )
    R R .
  Proof.
    move [ [ [ [ ]]]].
    move /EReds.bind_inv : [][][?][]. subst.
    move /EReds.bind_inv : [][][?][]. subst.
    sauto lq:on rew:off inv:Sub1.R.
  Qed.


  Lemma sig_inj (A0 A1 : PTm) B0 B1 :
    R (PBind PSig ) (PBind PSig )
    R R .
  Proof.
    move [ [ [ [ ]]]].
    move /EReds.bind_inv : [][][?][]. subst.
    move /EReds.bind_inv : [][][?][]. subst.
    sauto lq:on rew:off inv:Sub1.R.
  Qed.


  Lemma suc_inj (a b : PTm) :
    R (PSuc a) (PSuc b)
    R a b.
  Proof.
    sauto lq:on use:EReds.suc_inv inv:Sub1.R.
  Qed.


End ESub.

Untyped subtyping

Module Sub.
  Definition R (a b : PTm) := c d, rtc RERed.R a c rtc RERed.R b d Sub1.R c d.

  Lemma refl (a : PTm) : R a a.
  Proof. sfirstorder use:@rtc_refl unfold:R. Qed.

  Lemma ToJoin (a b : PTm) :
    ishne a ishne b
    R a b
    DJoin.R a b.
  Proof.
    move h [c][d][][].
    have : ishne c ishne d by hauto q:on use:REReds.hne_preservation.
    hauto lq:on rew:off use:Sub1.hne_refl.
  Qed.


  Lemma transitive (a b c : PTm) : SN b R a b R b c R a c.
  Proof.
    rewrite /R.
    move h [][][ha][hb]ha0b0 [][][hb'][hc]hb1c0.
    move : hb hb'.
    move : rered_confluence h. repeat move/[apply].
    move [b'][].
    have [a' ?] : a', rtc RERed.R a' Sub1.R a' b' by hauto l:on use:Sub1.commutativity_Rs.
    have [c' ?] : a', rtc RERed.R a' Sub1.R b' a' by hauto l:on use:Sub1.commutativity_Ls.
    exists a',c'; hauto l:on use:Sub1.transitive, @relations.rtc_transitive.
  Qed.


  Lemma FromJoin (a b : PTm) : DJoin.R a b R a b.
  Proof. sfirstorder. Qed.

  Lemma PiCong (A0 A1 : PTm) B0 B1 :
    R
    R
    R (PBind PPi ) (PBind PPi ).
  Proof.
    rewrite /R.
    move [A][A'][][].
    move [B][B'][][].
    exists (PBind PPi A' B), (PBind PPi A B').
    repeat split; eauto using REReds.BindCong, Sub1.PiCong.
  Qed.


  Lemma SigCong (A0 A1 : PTm) B0 B1 :
    R
    R
    R (PBind PSig ) (PBind PSig ).
  Proof.
    rewrite /R.
    move [A][A'][][].
    move [B][B'][][].
    exists (PBind PSig A B), (PBind PSig A' B').
    repeat split; eauto using REReds.BindCong, Sub1.SigCong.
  Qed.


  Lemma UnivCong i j :
    i j
    @R (PUniv i) (PUniv j).
  Proof. hauto lq:on ctrs:Sub1.R, rtc. Qed.

  Lemma sne_nat_noconf (a b : PTm) :
    R a b SNe a isnat b False.
  Proof.
    move [c [d [ [ ]]]] *.
    have : SNe c isnat d by sfirstorder use:REReds.sne_preservation, REReds.nat_preservation, Sub1.sne_preservation.
    move : . clear. hauto q:on inv:Sub1.R, SNe.
  Qed.


  Lemma nat_sne_noconf (a b : PTm) :
    R a b isnat a SNe b False.
  Proof.
    move [c [d [ [ ]]]] *.
    have : SNe d isnat c by sfirstorder use:REReds.sne_preservation, REReds.nat_preservation.
    move : . clear. hauto q:on inv:Sub1.R, SNe.
  Qed.


  Lemma sne_bind_noconf (a b : PTm) :
    R a b SNe a isbind b False.
  Proof.
    rewrite /R.
    move [c[d] [? []]] *.
    have : SNe c isbind c by
      hauto l:on use:REReds.sne_preservation, REReds.bind_preservation, Sub1.sne_preservation, Sub1.bind_preservation.
    qauto l:on inv:SNe.
  Qed.


  Lemma hne_bind_noconf (a b : PTm) :
    R a b ishne a isbind b False.
  Proof.
    rewrite /R.
    move [c[d] [? []]] .
    have : ishne c by eauto using REReds.hne_preservation.
    have : isbind d by eauto using REReds.bind_preservation.
    move : . clear. inversion 1; subst //=.
    clear. case : d //=.
  Qed.


  Lemma bind_sne_noconf (a b : PTm) :
    R a b SNe b isbind a False.
  Proof.
    rewrite /R.
    move [c[d] [? []]] *.
    have : SNe c isbind c by
      hauto l:on use:REReds.sne_preservation, REReds.bind_preservation, Sub1.sne_preservation, Sub1.bind_preservation.
    qauto l:on inv:SNe.
  Qed.


  Lemma sne_univ_noconf (a b : PTm) :
    R a b SNe a isuniv b False.
  Proof.
    hauto l:on use:REReds.sne_preservation,
          REReds.univ_preservation, Sub1.sne_preservation, Sub1.univ_preservation inv:SNe.
  Qed.


  Lemma univ_sne_noconf (a b : PTm) :
    R a b SNe b isuniv a False.
  Proof.
    move [c[d] [? []]] *.
    have ? : SNe d by eauto using REReds.sne_preservation.
    have : SNe c by sfirstorder use:Sub1.sne_preservation.
    have : isuniv c by sfirstorder use:REReds.univ_preservation.
    clear. case : c //=. inversion 2.
  Qed.


  Lemma univ_nat_noconf (a b : PTm) :
    R a b isuniv b isnat a False.
  Proof.
    move [c[d] [? []]] .
    have : isuniv d by eauto using REReds.univ_preservation.
    have : isnat c by sfirstorder use:REReds.nat_preservation.
    inversion ; subst //=.
    clear. case : d //=.
  Qed.


  Lemma nat_univ_noconf (a b : PTm) :
    R a b isnat b isuniv a False.
  Proof.
    move [c[d] [? []]] .
    have : isuniv c by eauto using REReds.univ_preservation.
    have : isnat d by sfirstorder use:REReds.nat_preservation.
    inversion ; subst //=.
    clear. case : d //=.
  Qed.


  Lemma bind_nat_noconf (a b : PTm) :
    R a b isbind b isnat a False.
  Proof.
    move [c[d] [? []]] .
    have : isbind d by eauto using REReds.bind_preservation.
    have : isnat c by sfirstorder use:REReds.nat_preservation.
    move : . clear.
    inversion 1; subst //=.
    case : d //=.
  Qed.


  Lemma nat_bind_noconf (a b : PTm) :
    R a b isnat b isbind a False.
  Proof.
    move [c[d] [? []]] .
    have : isbind c by eauto using REReds.bind_preservation.
    have : isnat d by sfirstorder use:REReds.nat_preservation.
    move : . clear.
    inversion 1; subst //=.
    case : d //=.
  Qed.


  Lemma bind_univ_noconf (a b : PTm) :
    R a b isbind a isuniv b False.
  Proof.
    move [c[d] [ [ ]]] .
    have : isbind c isuniv c by
      hauto l:on use:REReds.bind_preservation,
            REReds.univ_preservation, Sub1.bind_preservation, Sub1.univ_preservation.
    move : . clear.
    case : c //=; sfirstorder b:on.
  Qed.


  Lemma univ_bind_noconf (a b : PTm) :
    R a b isbind b isuniv a False.
  Proof.
    move [c[d] [ [ ]]] .
    have : isbind c isuniv c by
      hauto l:on use:REReds.bind_preservation,
            REReds.univ_preservation, Sub1.bind_preservation, Sub1.univ_preservation.
    move : . clear.
    case : c //=; sfirstorder b:on.
  Qed.


  Lemma bind_inj p0 p1 (A0 A1 : PTm) B0 B1 :
    R (PBind ) (PBind )
     = (if is PPi then R else R ) R .
  Proof.
    rewrite /R.
    move [u][v][][]h.
    move /REReds.bind_inv : [][][?][]. subst.
    move /REReds.bind_inv : [][][?][]. subst.
    inversion h; subst; sauto lq:on.
  Qed.


  Lemma univ_inj i j :
    @R (PUniv i) (PUniv j) i j.
  Proof.
    sauto lq:on rew:off use:REReds.univ_inv.
  Qed.


  Lemma suc_inj (A0 A1 : PTm) :
    R (PSuc ) (PSuc )
    R .
  Proof.
    sauto q:on use:REReds.suc_inv.
  Qed.


  Lemma cong (a b : PTm) c d (ρ : PTm) :
    R a b DJoin.R c d R (subst_PTm (scons c ρ) a) (subst_PTm (scons d ρ) b).
  Proof.
    rewrite /R.
    move [][][][].
    move [cd][].
    exists (subst_PTm (scons cd ρ) ), (subst_PTm (scons cd ρ) ).
    repeat split.
    hauto l:on use:REReds.cong' inv:.
    hauto l:on use:REReds.cong' inv:.
    eauto using Sub1.substing.
  Qed.


  Lemma substing (a b : PTm) (ρ : PTm) :
    R a b R (subst_PTm ρ a) (subst_PTm ρ b).
  Proof.
    rewrite /R.
    move [][][][].
    hauto ctrs:rtc use:REReds.cong', Sub1.substing.
  Qed.


  Lemma ToESub (a b : PTm) : nf a nf b R a b ESub.R a b.
  Proof. hauto q:on use:REReds.ToEReds. Qed.

  Lemma standardization (a b : PTm) :
    SN a SN b R a b
     va vb, rtc RRed.R a va rtc RRed.R b vb nf va nf vb ESub.R va vb.
  Proof.
    move hS.
    have : v, rtc RRed.R a v nf v by sfirstorder use:LoReds.FromSN, LoReds.ToRReds.
    move [v [ ]].
    have : v, rtc RRed.R b v nf v by sfirstorder use:LoReds.FromSN, LoReds.ToRReds.
    move [v' [ ]].
    move : () () *.
    apply DJoin.FromRReds in , .
    move/DJoin.symmetric in .
    apply FromJoin in , .
    have hv : R v v' by eauto using transitive.
    have {}hv : ESub.R v v' by hauto l:on use:ToESub.
    hauto lq:on.
  Qed.


  Lemma standardization_lo (a b : PTm) :
    SN a SN b R a b
     va vb, rtc LoRed.R a va rtc LoRed.R b vb nf va nf vb ESub.R va vb.
  Proof.
    move /[dup] sna + /[dup] snb.
    move : standardization; repeat move/[apply].
    move [va][vb][hva][hvb][nfva][nfvb]hj.
    move /LoReds.FromSN : sna => [va' [hva' ]].
    move /LoReds.FromSN : snb => [vb' [hvb' ]].
    exists va', vb'.
    repeat split => //=.
    have : va = va' vb = vb' by sfirstorder use:red_uniquenf, LoReds.ToRReds.
    case; congruence.
  Qed.


End Sub.