Algorithmic.Erasure.RenamingSubstitution

module Algorithmic.Erasure.RenamingSubstitution where
open import Relation.Binary.PropositionalEquality using (_≡_;refl;sym;trans;subst;cong;cong₂)
open import Function using (id;_∘_)
open import Data.Vec using (Vec;_∷_;lookup)
open import Data.Fin using (Fin;toℕ)

open import Utils using (Kind;*;Maybe;nothing;just)
open import Utils.List using (List;[];_∷_)
open import Type using (Ctx⋆;;_,⋆_;_∋⋆_;Z;S)
import Type.RenamingSubstitution as 
open import Type.BetaNormal using (_⊢Nf⋆_;_⊢Ne⋆_;weakenNf;renNf;lookup-renNf-VecList;embNf;embNf-VecList;lookup-embNf-VecList)
open _⊢Nf⋆_
open _⊢Ne⋆_
open import Type.BetaNBE using (lookup-eval-VecList;idEnv)
open import Type.BetaNormal.Equality using (renNf-id;renNf-comp)
open import Type.BetaNBE.RenamingSubstitution
                using (ren[]Nf;ren-nf-μ;SubNf;subNf-id;subNf;weakenNf[];weakenNf-subNf)
                using (sub-nf-Π;sub[]Nf';sub-nf-μ;subNf-cons;extsNf)
open import Algorithmic as A using (Ctx;_∋_;conv∋;_⊢_;conv⊢;Cases;[];_∷_)
open import Algorithmic.Signature using (btype-ren;btype-sub)
open Ctx
open _∋_
open _⊢_
import Algorithmic.RenamingSubstitution as A
open import Algorithmic.Erasure using (len;erase;eraseTC;eraseVar;lem-erase;erase-Cases;erase-ConstrArgs;lem-erase'')
open import Untyped using (_⊢)
open _⊢
import Untyped.RenamingSubstitution as U
open import Builtin.Constant.Type using (TyCon)
backVar⋆ : ∀{Φ}(Γ : Ctx Φ)  len Γ  Φ ⊢Nf⋆ *
backVar⋆ (Γ ,⋆ J) x        = weakenNf (backVar⋆ Γ x)
backVar⋆ (Γ , A)  nothing  = A
backVar⋆ (Γ , A)  (just x) = backVar⋆ Γ x

backVar : ∀{Φ}(Γ : Ctx Φ)(x : len Γ)  Γ  (backVar⋆ Γ x)
backVar (Γ ,⋆ J) x       = T (backVar Γ x)
backVar (Γ , A) nothing  = Z
backVar (Γ , A) (just x) = S (backVar Γ x)

backVar⋆-eraseVar : ∀{Φ}{Γ : Ctx Φ}{A : Φ ⊢Nf⋆ *}(x : Γ  A) 
  backVar⋆ Γ (eraseVar x)  A
backVar⋆-eraseVar (Z) = refl
backVar⋆-eraseVar (S x) = backVar⋆-eraseVar x
backVar⋆-eraseVar (T x) = cong weakenNf (backVar⋆-eraseVar x)

subst-S : ∀{Φ}{Γ : Ctx Φ}{B A A' : Φ ⊢Nf⋆ *}(p : A  A')(x : Γ  A) 
  conv∋ refl p (S {B = B} x)  S (conv∋ refl p x)
subst-S refl x = refl

subst-T : ∀{Φ}{Γ : Ctx Φ}{A A' : Φ ⊢Nf⋆ *}{K} 
  (p : A  A')(q : weakenNf {K = K} A  weakenNf A')  (x : Γ  A) 
  conv∋ refl q (T x)  T (conv∋ refl p x) --
subst-T refl refl x = refl

subst-T' : ∀{Φ}{Γ : Ctx Φ}{A A' : Φ ⊢Nf⋆ *}{K}{A'' : Φ ,⋆ K ⊢Nf⋆ *}
   (p : A  A')
   (q : weakenNf {K = K} A  A'')
   (r : weakenNf  {K = K} A'  A'')
   (x : Γ  A)
   conv∋ refl q (T x)  conv∋ refl r (T (conv∋ refl p x))
subst-T' refl refl refl x = refl

cong-erase-ren : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(ρ⋆ : ⋆.Ren Φ Ψ)
   (ρ : A.Ren ρ⋆ Γ Δ){A A' : Φ ⊢Nf⋆ *}(p : A'  A)
   (x : Γ  A)(x' : Γ  A')  conv∋ refl p x'  x
   eraseVar (ρ x)  eraseVar (ρ x')
cong-erase-ren ρ⋆ ρ refl x .x refl = refl

backVar-eraseVar : ∀{Φ}{Γ : Ctx Φ}{A : Φ ⊢Nf⋆ *}(x : Γ  A) 
  conv∋ refl (backVar⋆-eraseVar x) (backVar Γ (eraseVar x))  x
backVar-eraseVar Z = refl
backVar-eraseVar (S x) = trans
  (subst-S (backVar⋆-eraseVar x) (backVar _ (eraseVar x)))
  (cong S (backVar-eraseVar x))
backVar-eraseVar (T x) = trans (subst-T (backVar⋆-eraseVar x) (cong weakenNf (backVar⋆-eraseVar x)) (backVar _ (eraseVar x))) (cong T (backVar-eraseVar x))

eraseVar-backVar : ∀{Φ}(Γ : Ctx Φ)(x : len Γ)  eraseVar (backVar Γ x)  x
eraseVar-backVar        ()
eraseVar-backVar (Γ ,⋆ J) x        = eraseVar-backVar Γ x
eraseVar-backVar (Γ , A)  nothing  = refl
eraseVar-backVar (Γ , A)  (just x) = cong just (eraseVar-backVar Γ x)

--

erase-Ren : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(ρ⋆ : ⋆.Ren Φ Ψ)
   A.Ren ρ⋆ Γ Δ  U.Ren (len Γ) (len Δ)
erase-Ren ρ⋆ ρ i = eraseVar (ρ (backVar _ i))

ext-erase : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(ρ⋆ : ⋆.Ren Φ Ψ)
   (ρ : A.Ren ρ⋆ Γ Δ){A : Φ ⊢Nf⋆ *}(α : len (Γ , A))
   erase-Ren ρ⋆ (A.ext ρ⋆ ρ {B = A}) α  U.lift (erase-Ren ρ⋆ ρ) α
ext-erase ρ⋆ ρ nothing  = refl
ext-erase ρ⋆ ρ (just α) = refl

conv∋-erase : ∀{Φ}{Γ : Ctx Φ}{A A' : Φ ⊢Nf⋆ *}
   (p : A  A')
   (x : Γ  A)
   eraseVar (conv∋ refl p x)  eraseVar x
conv∋-erase refl x = refl

conv⊢-erase : ∀{Φ}{Γ : Ctx Φ}{A A' : Φ ⊢Nf⋆ *}
   (p : A  A')
   (t : Γ  A)
   erase (conv⊢ refl p t)  erase t
conv⊢-erase refl t = refl

ext⋆-erase : ∀{Φ Ψ K}{Γ : Ctx Φ}{Δ : Ctx Ψ}(ρ⋆ : ⋆.Ren Φ Ψ)
   (ρ : A.Ren ρ⋆ Γ Δ)(α : len Γ)
   erase-Ren (⋆.ext ρ⋆ {K = K}) (A.ext⋆ ρ⋆ ρ) α  erase-Ren ρ⋆ ρ α
ext⋆-erase {Γ = Γ} ρ⋆ ρ α = conv∋-erase
  (trans (sym (renNf-comp (backVar⋆ Γ α))) (renNf-comp (backVar⋆ Γ α)))
  (T (ρ (backVar Γ α)))

ren-erase : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(ρ⋆ : ⋆.Ren Φ Ψ)
   (ρ : A.Ren ρ⋆ Γ Δ){A : Φ ⊢Nf⋆ *}  (t : Γ  A)
    erase (A.ren ρ⋆ ρ t)  U.ren (erase-Ren ρ⋆ ρ) (erase t)

ren-erase-ConstrArgs-List :  {Φ} {Ψ} {Γ : Ctx Φ} {Δ : Ctx Ψ}
                              (ρ⋆ : ⋆.Ren Φ Ψ) (ρ : A.Ren ρ⋆ Γ Δ)
                              {As : List (Φ ⊢Nf⋆ *)}
                              (cs : A.ConstrArgs Γ As) 
                            erase-ConstrArgs ((A.ren-ConstrArgs-List ρ⋆ ρ cs)) 
                            U.renList (erase-Ren ρ⋆ ρ) (erase-ConstrArgs cs)
ren-erase-ConstrArgs-List ρ⋆ ρ [] = refl
ren-erase-ConstrArgs-List ρ⋆ ρ (x  cs) = cong₂ _∷_ (ren-erase ρ⋆ ρ x) (ren-erase-ConstrArgs-List ρ⋆ ρ cs)

ren-erase-ConstrArgs :  {Φ} {Ψ} {Γ : Ctx Φ} {Δ : Ctx Ψ}
                         (ρ⋆ : ⋆.Ren Φ Ψ) (ρ : A.Ren ρ⋆ Γ Δ) {n} (i : Fin n)
                         (Tss : Vec (List (Φ ⊢Nf⋆ *)) n)
                         (cs : A.ConstrArgs Γ (lookup Tss i)) 
                       erase-ConstrArgs (A.ren-ConstrArgs ρ⋆ ρ i Tss cs)
                        U.renList (erase-Ren ρ⋆ ρ)(erase-ConstrArgs cs)
ren-erase-ConstrArgs ρ⋆ ρ i Tss cs rewrite lookup-renNf-VecList ρ⋆ i Tss = ren-erase-ConstrArgs-List ρ⋆ ρ cs

ren-erase-Cases : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}
   (ρ⋆ : ⋆.Ren Φ Ψ)
   (ρ : A.Ren ρ⋆ Γ Δ)
    {n}{A : Φ ⊢Nf⋆ *}{Tss : Vec (List (Φ ⊢Nf⋆ *)) n}
   (cs : Cases Γ A Tss)
    erase-Cases (A.ren-Cases ρ⋆ ρ cs)  U.renList (erase-Ren ρ⋆ ρ) (erase-Cases cs)
ren-erase-Cases ρ⋆ ρ [] = refl
ren-erase-Cases ρ⋆ ρ {Tss = As  _}(c  cs) =
      cong₂ _∷_ (trans (sym (lem-erase'' (A.ren-mkCaseType ρ⋆ As) (A.ren ρ⋆ ρ c)))
                       (ren-erase ρ⋆ ρ c))
                (ren-erase-Cases ρ⋆ ρ cs)

ren-erase ρ⋆ ρ (` x) = cong
  `
  (cong-erase-ren
    ρ⋆
    ρ
    (backVar⋆-eraseVar x)
    x
    (backVar _ (eraseVar x))
    (backVar-eraseVar x))
ren-erase ρ⋆ ρ (ƛ t)            = cong ƛ
  (trans
    (ren-erase ρ⋆ (A.ext ρ⋆ ρ) t)
    (U.ren-cong (ext-erase ρ⋆ ρ) (erase t)))
ren-erase ρ⋆ ρ (t · u)            =
  cong₂ _·_ (ren-erase ρ⋆ ρ t) (ren-erase ρ⋆ ρ u)
ren-erase ρ⋆ ρ (Λ t)            = cong
  delay
  (trans (ren-erase (⋆.ext ρ⋆) (A.ext⋆ ρ⋆ ρ) t)
         (U.ren-cong (ext⋆-erase ρ⋆ ρ) (erase t)))
ren-erase ρ⋆ ρ (_·⋆_/_ {B = B} t A refl) = trans
  (conv⊢-erase (sym (ren[]Nf ρ⋆ B A)) (A.ren ρ⋆ ρ t ·⋆ renNf ρ⋆ A / refl ))
  (cong force (ren-erase ρ⋆ ρ t))
ren-erase ρ⋆ ρ (wrap A B t)  = trans
  (conv⊢-erase (ren-nf-μ ρ⋆ A B) (A.ren ρ⋆ ρ t))
  (ren-erase ρ⋆ ρ t)
ren-erase ρ⋆ ρ (unwrap {A = A}{B = B} t refl) = trans
  (conv⊢-erase (sym (ren-nf-μ ρ⋆ A B)) (unwrap (A.ren ρ⋆ ρ t) refl))
  (ren-erase ρ⋆ ρ t)
ren-erase ρ⋆ ρ (con c refl)            = refl
ren-erase ρ⋆ ρ (builtin b / refl)      =
  sym (lem-erase refl (btype-ren b ρ⋆) (builtin b / refl))
ren-erase ρ⋆ ρ (constr i A refl cs) = cong (constr (toℕ i)) (ren-erase-ConstrArgs ρ⋆ ρ i A cs)
ren-erase ρ⋆ ρ (case t cases) = cong₂ case (ren-erase ρ⋆ ρ t) (ren-erase-Cases ρ⋆ ρ cases)
ren-erase ρ⋆ ρ (error A)          = refl
--
erase-Sub : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(σ⋆ : SubNf Φ Ψ)
   A.Sub σ⋆ Γ Δ  U.Sub (len Γ) (len Δ)
erase-Sub σ⋆ σ i = erase (σ (backVar _ i))

cong-erase-sub : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(σ⋆ : SubNf Φ Ψ)
   (σ : A.Sub σ⋆ Γ Δ){A A' : Φ ⊢Nf⋆ *}(p : A'  A)
   (x : Γ  A)(x' : Γ  A')  conv∋ refl p x'  x
   erase (σ x)  erase (σ x')
cong-erase-sub σ⋆ σ refl x .x refl = refl

exts-erase :  {Φ Ψ}{Γ Δ}(σ⋆ : SubNf Φ Ψ)(σ : A.Sub σ⋆ Γ Δ)
   {B : Φ ⊢Nf⋆ *}
   (α : Maybe (len Γ))
   erase-Sub σ⋆ (A.exts σ⋆ σ {B}) α  U.lifts (erase-Sub σ⋆ σ) α
exts-erase σ⋆ σ nothing = refl
exts-erase {Γ = Γ}{Δ} σ⋆ σ {B} (just α) = trans
  (conv⊢-erase
    (renNf-id (subNf σ⋆ (backVar⋆ Γ α)))
    (A.ren id (conv∋ refl (sym (renNf-id _))  S) (σ (backVar Γ α))))
    (trans (ren-erase id (conv∋ refl (sym (renNf-id _))  S) (σ (backVar Γ α)))
           (U.ren-cong  β  trans
             (conv∋-erase (sym (renNf-id _)) (S (backVar Δ β)))
             (cong just (eraseVar-backVar Δ β)))
             (erase (σ (backVar Γ α)))))

exts⋆-erase :  {Φ Ψ}{Γ Δ}(σ⋆ : SubNf Φ Ψ)(σ : A.Sub σ⋆ Γ Δ)
   {B : Φ ⊢Nf⋆ *}
   ∀{K}
   (α : len Γ)
    erase-Sub (extsNf σ⋆ {K}) (A.exts⋆ σ⋆ σ) α  erase-Sub σ⋆ σ α
exts⋆-erase {Γ = Γ}{Δ} σ⋆ σ {B} α = trans
  (conv⊢-erase
    (weakenNf-subNf σ⋆ (backVar⋆ Γ α))
    (A.weaken⋆ (σ (backVar Γ α))))
  (trans
    (ren-erase S T (σ (backVar Γ α)))
    (trans
      (U.ren-cong (eraseVar-backVar Δ) (erase (σ (backVar Γ α))))
      (sym (U.ren-id (erase (σ (backVar Γ α)))))))

sub-erase : ∀{Φ Ψ}{Γ : Ctx Φ}{Δ : Ctx Ψ}(σ⋆ : SubNf Φ Ψ)
   (σ : A.Sub σ⋆ Γ Δ){A : Φ ⊢Nf⋆ *}  (t : Γ  A)
    erase (A.sub σ⋆ σ t)  U.sub (erase-Sub σ⋆ σ) (erase t)


sub-Erase-ConstrList :  {Φ}{Ψ}{Γ : Ctx Φ} {Δ : Ctx Ψ}
                         {As : List (Φ ⊢Nf⋆ *)}
                         (σ⋆ : SubNf Φ Ψ) (σ : A.Sub σ⋆ Γ Δ)
                         (cs : A.ConstrArgs Γ As)
          erase-ConstrArgs (A.sub-ConstrList σ⋆ σ cs)
          U.subList  i₁  erase (σ (backVar Γ i₁))) (erase-ConstrArgs cs)
sub-Erase-ConstrList σ⋆ σ [] = refl
sub-Erase-ConstrList σ⋆ σ (x  cs) = cong₂ _∷_ (sub-erase σ⋆ σ x) (sub-Erase-ConstrList σ⋆ σ cs)

sub-Erase-ConstrArgs :  {Φ} {Ψ} {Γ : Ctx Φ} {Δ : Ctx Ψ}
                         (σ⋆ : SubNf Φ Ψ) (σ : A.Sub σ⋆ Γ Δ) {n} (i : Fin n)
                         (Tss : Vec (List (Φ ⊢Nf⋆ *)) n)
                         (cs : A.ConstrArgs Γ (lookup Tss i))
                 erase-ConstrArgs (A.sub-VecList σ⋆ σ i Tss cs)  U.subList (erase-Sub σ⋆ σ) (erase-ConstrArgs cs)
sub-Erase-ConstrArgs σ⋆ σ i Tss cs
     rewrite lookup-eval-VecList i (⋆.sub-VecList  x₁  embNf (σ⋆ x₁)) (embNf-VecList Tss))
                                   (idEnv _)
            | ⋆.lookup-sub-VecList  x₁  embNf (σ⋆ x₁)) i (embNf-VecList Tss)
            | lookup-embNf-VecList i Tss = sub-Erase-ConstrList σ⋆ σ cs

sub-erase-Cases :  {Φ} {Ψ} {Γ : Ctx Φ} {Δ : Ctx Ψ}
                    (σ⋆ : SubNf Φ Ψ) (σ : A.Sub σ⋆ Γ Δ) {A : Φ ⊢Nf⋆ *} {n}
                    {Tss : Vec (List (Φ ⊢Nf⋆ *)) n}
                    (cases : Cases Γ A Tss) 
                  erase-Cases (A.sub-Cases σ⋆ σ cases) 
                  U.subList (erase-Sub σ⋆ σ) (erase-Cases cases)
sub-erase-Cases σ⋆ σ [] = refl
sub-erase-Cases {Δ = Δ} σ⋆ σ {Tss = As  _} (c  cases) = cong₂ _∷_ (trans (sym (lem-erase'' (A.sub-mkCaseType σ⋆ As) (A.sub σ⋆ σ c))) (sub-erase σ⋆ σ c))
                                             (sub-erase-Cases σ⋆ σ cases)

sub-erase σ⋆ σ (` x) =   cong-erase-sub
    σ⋆
    σ
    (backVar⋆-eraseVar x)
    x
    (backVar _ (eraseVar x))
    (backVar-eraseVar x)
sub-erase σ⋆ σ (ƛ t) = cong ƛ
  (trans (sub-erase σ⋆ (A.exts σ⋆ σ) t)
         (U.sub-cong (exts-erase σ⋆ σ) (erase t)))
sub-erase σ⋆ σ (t · u) = cong₂ _·_ (sub-erase σ⋆ σ t) (sub-erase σ⋆ σ u)
sub-erase σ⋆ σ (Λ {B = B} t) = cong
  delay
  (trans (conv⊢-erase (sub-nf-Π σ⋆ B) (A.sub (extsNf σ⋆) (A.exts⋆ σ⋆ σ) t))
         (trans (sub-erase (extsNf σ⋆) (A.exts⋆ σ⋆ σ) t)
                (U.sub-cong (exts⋆-erase σ⋆ σ {B = Π B}) (erase t))))
sub-erase σ⋆ σ (_·⋆_/_ {B = B} t A refl) =
  trans (conv⊢-erase (sym (sub[]Nf' σ⋆ A B)) (A.sub σ⋆ σ t ·⋆ subNf σ⋆ A / refl))
        (cong force (sub-erase σ⋆ σ t))
sub-erase σ⋆ σ (wrap A B t) = trans
  (conv⊢-erase (sub-nf-μ σ⋆ A B) (A.sub σ⋆ σ t))
  (sub-erase σ⋆ σ t)
sub-erase σ⋆ σ (unwrap {A = A}{B} t refl) = trans
  (conv⊢-erase (sym (sub-nf-μ σ⋆ A B)) (unwrap (A.sub σ⋆ σ t) refl))
  (sub-erase σ⋆ σ t)
sub-erase σ⋆ σ (con c refl) = refl
sub-erase σ⋆ σ (builtin b / refl) =
 sym (lem-erase refl (btype-sub b σ⋆) (builtin b / refl))
sub-erase σ⋆ σ (constr i A refl cs) = cong (constr (toℕ i)) (sub-Erase-ConstrArgs σ⋆ σ i A cs)
sub-erase σ⋆ σ (case t cases) = cong₂ case (sub-erase σ⋆ σ t) (sub-erase-Cases σ⋆ σ cases)
sub-erase σ⋆ σ (error A) = refl

lem[]⋆ : ∀{Φ}{Γ : Ctx Φ}{K}{B : Φ ,⋆ K ⊢Nf⋆ *}(N : Γ ,⋆ K  B)(A : Φ ⊢Nf⋆ K)
   erase N  erase (N A.[ A ]⋆)
lem[]⋆ {Γ = Γ} N A = trans
  (trans
    (U.sub-id (erase N))
    (U.sub-cong
       α  trans
        (cong ` (sym (eraseVar-backVar Γ α)))
        (sym (conv⊢-erase (weakenNf[] _ _) (` (backVar Γ α)))))
      (erase N)))
  (sym (sub-erase (subNf-cons (ne  `) A) A.lem N))


lem[] : ∀{Φ}{Γ : Ctx Φ}{A B : Φ ⊢Nf⋆ *}(N : Γ , A  B)(W : Γ  A)
   erase N U.[ erase W ]  erase (N A.[ W ])
lem[] {Γ = Γ}{A = A}{B} N W = trans
  (trans
    (U.sub-cong
      (λ{ nothing     sym (conv⊢-erase (sym (subNf-id A)) W)
        ; (just α)  trans
               (cong ` (sym (eraseVar-backVar Γ α)))
               (sym (conv⊢-erase (sym (subNf-id (backVar⋆ Γ α))) (` (backVar Γ α))))})
      (erase N))
    (sym (sub-erase (ne  `) (A.subcons (ne  `) (conv⊢ refl (sym (subNf-id _))  `) (conv⊢ refl (sym (subNf-id A)) W)) N)))
  (sym
    (conv⊢-erase
      (subNf-id B)
      (A.sub (ne  `)
         (A.subcons
           (ne  `)
           (conv⊢ refl (sym (subNf-id _))  `)
           (conv⊢ refl (sym (subNf-id A)) W))
         N)))