Algorithmic.Completeness

module Algorithmic.Completeness where

open import Data.Nat using (zero;suc)
open import Data.Fin using (Fin)
open import Data.Vec using (Vec;[];_∷_;lookup) renaming (map to vmap)
open import Data.List.NonEmpty using (_∷_;toList)
open import Relation.Binary.PropositionalEquality using (_≡_;refl;cong;sym;trans;cong₂)
                                                  renaming (subst to substEq)
open Relation.Binary.PropositionalEquality.≡-Reasoning
open import Function using (_∘_)
open import Data.Product using (_×_) renaming (_,_ to _,,_)


open import Utils using (Kind;*;)
open import Utils.List using (List;IList;[];_∷_)
open import Type using (_⊢⋆_;_∋⋆_;Z;S;Ctx⋆;;_,⋆_)
open _⊢⋆_

open import Type.Equality using (_≡β_;≡2β)
open _≡β_
open import Type.RenamingSubstitution using (weaken;ren;_[_];sub-cons;Sub;sub;sub∅;sub-cong)
open import Builtin using (signature)
open import Builtin.Signature using (Sig;sig;_⊢♯;_/_⊢⋆;mkCtx⋆)
open _/_⊢⋆
open import Builtin.Constant.Type

import Declarative as Syn
import Algorithmic as Norm
import Algorithmic.Signature as Norm
open import Type.BetaNormal using (embNf;weakenNf;ne;_⊢Nf⋆_;_⊢Ne⋆_)
open _⊢Nf⋆_
open _⊢Ne⋆_
open import Type.BetaNBE using (nf;idEnv;reify;eval;exte;eval-VecList;eval-List;lookup-eval-VecList)
open import Type.BetaNBE.Completeness using (completeness;sub-eval;idCR;fund;symCR;reifyCR;exte-lem;idext)
open import Type.BetaNBE.RenamingSubstitution using (ren-nf;subNf;subNf-cong;subNf-cong';subNf-lemma';_[_]Nf;subNf-cons;subNf∅;subNf∅≡subNf;subNf-nf)
open import Type.BetaNBE.Soundness using (soundness)
open import Type.BetaNBE.Stability using (stability)

nfCtx :  {Φ}  Syn.Ctx Φ  Norm.Ctx Φ
nfCtx Syn.∅ = Norm.∅
nfCtx (Γ Syn.,⋆ K) = nfCtx Γ Norm.,⋆ K
nfCtx (Γ Syn., A) = nfCtx Γ Norm., nf A

nfTyVar : ∀{Φ Γ}
   {A : Φ ⊢⋆ *}
   Γ Syn.∋ A
   nfCtx Γ Norm.∋ nf A
nfTyVar Syn.Z             = Norm.Z
nfTyVar (Syn.S α)         = Norm.S (nfTyVar α)
nfTyVar (Syn.T {A = A} α) = Norm.conv∋ refl (ren-nf S A) (Norm.T (nfTyVar α))


lemΠ : ∀{Γ K }(B : Γ ,⋆ K ⊢⋆ *) 
       nf (Π B)  Π (nf B)
lemΠ B = cong Π (sym (subNf-lemma' B))



stability-μ : ∀{Φ K}(A :  Φ ⊢⋆ _)(B : Φ ⊢⋆ K) 
  nf (A · ƛ (μ (weaken A) (` Z)) · B)
  
  nf (embNf (nf A) · ƛ (μ (embNf (weakenNf (nf A))) (` Z)) · embNf (nf B))
stability-μ A B = completeness
  (·≡β
    (·≡β
      (soundness A)
      (ƛ≡β (μ≡β (trans≡β
        (soundness (ren S A))
        (≡2β (sym (cong embNf (ren-nf S A))))) (refl≡β (` Z)))))
    (soundness B))

lem[] : ∀{Γ K}(A : Γ ⊢⋆ K)(B : Γ ,⋆ K ⊢⋆ *) 
  (nf B [ nf A ]Nf)  nf (B [ A ])
lem[] A B = trans
  (sub-eval (embNf (nf B)) idCR (embNf  subNf-cons (ne  `) (nf A)))
  (trans
    (fund
       {Z  symCR (fund idCR (soundness A)) ; (S α)  idCR _})
      (sym≡β (soundness B)))
    (sym (sub-eval B idCR (sub-cons ` A))))

lemσ : ∀{Γ Δ Δ'}
   (σ : Sub Δ Γ)
   (C : Δ ⊢⋆ *)
   (C' : Δ' ⊢Nf⋆ *)
   (q : Δ'  Δ)
   nf C  substEq (_⊢Nf⋆ *) q C' 
  subNf
       {J} α 
         nf
          (σ (substEq (_∋⋆ J) q α)))
      C'
      
      nf (sub σ C)
lemσ σ C _ refl q = trans
  (subNf-cong' (nf  σ) (sym q))
  (trans
    (trans
      (sub-eval (embNf (nf C)) idCR (embNf  nf  σ))
      (fund  α  fund idCR (sym≡β (soundness (σ α)))) (sym≡β (soundness C))))
    (sym (sub-eval C idCR σ)))

-- this should be a lemma in NBE/RenSubst
-- subNf (nf ∘ σ) (nf C) ≡ nf (sub σ C)


nfList : ∀{Δ}  List (Δ ⊢⋆ *)  List (Δ ⊢Nf⋆ *)
nfList []       = []
nfList (A  As) = nf A  nfList As

We need to prove that the type of a builtin b for the Algorithmic system is the same as normalising the type of the Declarative system Norm.btype b ≡ nf (Syn.btype b)

This involves doing an analogous proof for every function in the definition of btype

subNf-sub∅ : ∀{Φ}{K} (A :  ⊢⋆ K)  nf {Φ} (sub∅ A)  subNf (λ()) (nf A)
subNf-sub∅ {Φ} A = begin
          nf (sub∅ A)
        ≡⟨⟩
          nf (sub  ()) A)
        ≡⟨ cong nf (sub-cong  ()) A) 
          nf (sub (embNf  λ ()) A)
        ≡⟨ subNf-nf ((λ ())) A 
          subNf  ()) (nf A)
        

subNf∅-sub∅ : ∀{Φ}{K} (A :  ⊢⋆ K)  nf {Φ} (sub∅ A)  subNf∅ (nf A)
subNf∅-sub∅ A = trans (subNf-sub∅ A) (sym subNf∅≡subNf)

con-injective :  {Φ}{n m : Φ ⊢Nf⋆ }  _≡_ {_} {Φ ⊢Nf⋆ *} (con n) (con m)  n  m
con-injective refl = refl

helper :  {n⋆} {n♯} (x : n♯ ⊢♯)  ne (Norm.⊢♯2TyNe♯ x)  eval (Syn.⊢♯2TyNe♯ x) (idEnv (Builtin.Signature.mkCtx⋆ n⋆ n♯))
helper (_⊢♯.` x) = refl
helper (_⊢♯.atomic x) = refl
helper (_⊢♯.list x) = cong ne (cong (^ list ·_) (helper x))
helper (_⊢♯.pair x y) = cong ne (cong₂  x y  ^ pair · x · y) (helper x) (helper y))

mkTy-lem :  {n⋆ n♯}(t : n⋆ / n♯ ⊢⋆)  Norm.mkTy t  nf (Syn.mkTy t)
mkTy-lem (` x) = refl
mkTy-lem (x ) = cong con (helper x)

sig2type⇒-lem : ∀{n⋆ n♯}{algRes}{synRes} (args : List (n⋆ / n♯ ⊢⋆))  (algRes  nf synRes) 
                          Norm.sig2type⇒ args algRes  nf (Syn.sig2type⇒ args synRes)
sig2type⇒-lem [] p = p
sig2type⇒-lem (x  args) p = sig2type⇒-lem args (cong₂ _⇒_ (mkTy-lem x) p)

sig2typeΠ-lem : ∀{n⋆ n♯}{t : mkCtx⋆ n⋆ n♯ ⊢⋆ *}{at : mkCtx⋆ n⋆ n♯ ⊢Nf⋆ *}  (at  nf t) 
                          Norm.sig2typeΠ at  nf (Syn.sig2typeΠ t)
sig2typeΠ-lem {zero} {zero} p = p
sig2typeΠ-lem {zero} {suc n♯} {t} refl = sig2typeΠ-lem {_} {n♯} (cong Π (sym (reifyCR (idext exte-lem t))))
sig2typeΠ-lem {suc n} {n♯} {t} refl = sig2typeΠ-lem {n} (cong Π (sym (reifyCR (idext exte-lem t))))

sig2type-lem :  s  Norm.sig2type s  nf (Syn.sig2type s)
sig2type-lem (sig zero zero a r) = sig2type⇒-lem (toList a) (mkTy-lem r)

sig2type-lem (sig zero (suc n) (head  tail) r) = sig2typeΠ-lem {t = Π (Syn.sig2type⇒ tail (Syn.mkTy head  Syn.mkTy r))}
                  {Π (Norm.sig2type⇒ tail (Norm.mkTy head  Norm.mkTy r))}
                  (cong Π (trans (sig2type⇒-lem {zero} {suc n} {Norm.mkTy r} {Syn.mkTy r} (head  tail) (mkTy-lem r))
                                 (sym (reifyCR (idext exte-lem (Syn.sig2type⇒ tail (Syn.mkTy head  Syn.mkTy r)))))
                   ))
sig2type-lem (sig (suc n) n♯ (head  tail) r) = sig2typeΠ-lem {t = Π (Syn.sig2type⇒ tail (Syn.mkTy head  Syn.mkTy r))}
                  {Π (Norm.sig2type⇒ tail (Norm.mkTy head  Norm.mkTy r))}
                  (cong Π (trans (sig2type⇒-lem {suc n} {n♯} {Norm.mkTy r} {Syn.mkTy r} (head  tail) (mkTy-lem r))
                                 (sym (reifyCR (idext exte-lem (Syn.sig2type⇒ tail (Syn.mkTy head  Syn.mkTy r)))))
                   ))

btype-lem :  {Φ} b  Norm.btype {Φ} b  nf (Syn.btype b)
btype-lem b = begin
        Norm.btype b
      ≡⟨⟩
        subNf∅ (Norm.sig2type (signature b))
      ≡⟨ cong subNf∅ (sig2type-lem (signature  b)) 
        subNf∅ (nf (Syn.sig2type (signature b)))
      ≡⟨ sym (subNf∅-sub∅ (Syn.sig2type (signature b))) 
         nf (sub∅ (Syn.sig2type (signature b)))
      ≡⟨⟩
       nf (Syn.btype b)
      
nfType : ∀{Φ Γ}
   {A : Φ ⊢⋆ *}
   Γ Syn.⊢ A
   nfCtx Γ Norm.⊢ nf A

nfType-ConstrArgs :  {Φ} {Γ : Syn.Ctx Φ}
                  {Ts : List (Φ ⊢⋆ *)}
                 (cs : Syn.ConstrArgs Γ Ts)
                 Norm.ConstrArgs (nfCtx Γ) (eval-List Ts (idEnv Φ))
nfType-ConstrArgs [] = []
nfType-ConstrArgs (c  cs) = (nfType c)  (nfType-ConstrArgs cs)

lemma-mkCaseType : ∀{Φ}{B} As 
   nf (Syn.mkCaseType B As)  Norm.mkCaseType (nf B) (eval-List As (idEnv Φ))
lemma-mkCaseType [] = refl
lemma-mkCaseType (A  As) = cong (eval A (idEnv _) ⇒_) (lemma-mkCaseType As)

nfType-Cases :  {Φ} {Γ : Syn.Ctx Φ} {A : Φ ⊢⋆ *} {n}
                 {Tss : Vec (List (Φ ⊢⋆ *)) n}
                 (cases : Syn.Cases Γ A Tss) 
               Norm.Cases (nfCtx Γ) (nf A) (eval-VecList Tss (idEnv Φ))
nfType-Cases Syn.[] = Norm.[]
nfType-Cases (Syn._∷_ {Ts = Ts} c cases) = substEq (nfCtx _ Norm.⊢_) (lemma-mkCaseType Ts)  (nfType c)
                                           Norm.∷ (nfType-Cases cases)

nfType (Syn.` α) = Norm.` (nfTyVar α)
nfType (Syn.ƛ t) = Norm.ƛ (nfType t)
nfType (t Syn.· u) = nfType t Norm.· nfType u
nfType (Syn.Λ {B = B} t) =
  Norm.Λ (Norm.conv⊢ refl (subNf-lemma' B) (nfType t))
nfType (Syn._·⋆_ {B = B} t A) = Norm.conv⊢
  refl
  (lem[] A B)
  (Norm._·⋆_/_ (Norm.conv⊢ refl (lemΠ B) (nfType t)) (nf A) refl)
nfType (Syn.wrap A B t) = Norm.wrap
  (nf A)
  (nf B)
  (Norm.conv⊢ refl (stability-μ A B) (nfType t))
nfType (Syn.unwrap {A = A}{B = B} t) = Norm.conv⊢
  refl
  (sym (stability-μ A B))
  (Norm.unwrap (nfType t) refl)
nfType (Syn.conv p t) = Norm.conv⊢ refl (completeness p) (nfType t)
nfType (Syn.con {A} t p) = Norm.con {A = nf A} t (trans (completeness p) (subNf∅-sub∅ A))
nfType (Syn.builtin b) = Norm.conv⊢ refl (btype-lem b) (Norm.builtin b / refl)
nfType (Syn.error A) = Norm.error (nf A)
nfType (Syn.constr e Tss refl cs) = Norm.constr e (eval-VecList Tss (idEnv _)) (sym (lookup-eval-VecList e Tss (idEnv _))) (nfType-ConstrArgs cs)
nfType (Syn.case t cases) = Norm.case (nfType t) (nfType-Cases cases)

completenessT : ∀{Φ Γ}{A : Φ ⊢⋆ *}  Γ Syn.⊢ A
   nfCtx Γ Norm.⊢ nf A × (A ≡β embNf (nf A))
completenessT {A = A} t = nfType t ,, soundness A