module A201802.LR3 where

open import A201801.Prelude
open import A201801.Category
open import A201801.Fin
open import A201801.FinLemmas
open import A201801.Vec
open import A201801.VecLemmas
open import A201801.AllVec

open import A201802.LR0
open import A201802.LR0Lemmas
open import A201802.LR1
open import A201802.LR2

-- TODO: unfinished
postulate
  oops :  {} {X : Set }  X


--------------------------------------------------------------------------------


-- `SN _ A` is the strong normalisation predicate on terms at type `A`.
mutual
  SN : Term 0  Type  Set
  SN M A =   M  A × M  × SN! M A

  SN! : Term 0  Type  Set
  SN! M 𝔹       = 
  SN! M (A  B) = oops
  SN! M 𝟘       = 
  SN! M 𝟙       = 
  SN! M (A  B) = SN (FST M) A × SN (SND M) B
  SN! M (A  B) =  {N}  SN N A  SN (APP M N) B


-- Every SN term is well-typed.
derp :  {A M}  SN M A
                  M  A
derp (𝒟 , M⇓ , s!) = 𝒟


--------------------------------------------------------------------------------


-- `SNs _ Γ` is the strong normalisation predicate on substitutions at all types `Γ`.
SNs :  {g}  (τ : Terms 0 g)  Types g  Set
SNs τ Γ = All (\ { (M , A)  SN M A }) (zip τ Γ)


-- Every SN substitution is well-typed.
derps :  {g}  {τ : Terms 0 g} {Γ : Types g}
               SNs τ Γ
                 τ  Γ all
derps σ = maps derp σ


--------------------------------------------------------------------------------


-- Small-step reduction IN REVERSE preserves SN.
mutual
  snpr↦ :  {A M M′}  M  M′    M  A  SN M′ A
                       SN M A
  snpr↦ M↦M′ 𝒟 (𝒟′ , M′⇓ , s!′) = 𝒟 , hpr↦ M↦M′ M′⇓ , sn!pr↦ M↦M′ 𝒟 s!′

  sn!pr↦ :  {A M M′}  M  M′    M  A  SN! M′ A
                        SN! M A
  sn!pr↦ {𝔹}     M↦M′ 𝒟          = 
  sn!pr↦ {A  B} M↦M′ 𝒟 x         = oops
  sn!pr↦ {𝟘}     M↦M′ 𝒟 ()
  sn!pr↦ {𝟙}     M↦M′ 𝒟          = 
  sn!pr↦ {A  B} M↦M′ 𝒟 (s₁ , s₂) = snpr↦ (cong-fst M↦M′) (fst 𝒟) s₁ , snpr↦ (cong-snd M↦M′) (snd 𝒟) s₂
  sn!pr↦ {A  B} M↦M′ 𝒟 f s       = snpr↦ (cong-app₁ M↦M′) (app 𝒟 (derp s)) (f s)


-- Iterated small-step reduction IN REVERSE preserves SN.
snpr⤅ :  {A M M′}  M  M′    M  A  SN M′ A
                     SN M A
snpr⤅ done                𝒟 s = s
snpr⤅ (step M↦M″ M″⤅M′) 𝒟 s = snpr↦ M↦M″ 𝒟 (snpr⤅ M″⤅M′ (tp↦ M↦M″ 𝒟) s)


-- Big-step reduction IN REVERSE preserves SN.
snpr⇓ :  {A M M′}  M  M′    M  A  SN M′ A
                    SN M A
snpr⇓ (M⤅M′ , VM′) 𝒟 s = snpr⤅ M⤅M′ 𝒟 s


--------------------------------------------------------------------------------


-- Small-step reduction preserves SN.
mutual
  snp↦ :  {A M M′}  M  M′    M  A  SN M A
                      SN M′ A
  snp↦ M↦M′ 𝒟 (_ , M⇓ , s!) = tp↦ M↦M′ 𝒟 , hp↦ M↦M′ M⇓ , sn!p↦ M↦M′ 𝒟 s!

  sn!p↦ :  {A M M′}  M  M′    M  A  SN! M A
                       SN! M′ A
  sn!p↦ {𝔹}     M↦M′ 𝒟          = 
  sn!p↦ {A  B} M↦M′ 𝒟 x         = oops
  sn!p↦ {𝟘}     M↦M′ 𝒟 ()
  sn!p↦ {𝟙}     M↦M′ 𝒟          = 
  sn!p↦ {A  B} M↦M′ 𝒟 (s₁ , s₂) = snp↦ (cong-fst M↦M′) (fst 𝒟) s₁ , snp↦ (cong-snd M↦M′) (snd 𝒟) s₂
  sn!p↦ {A  B} M↦M′ 𝒟 f s       = snp↦ (cong-app₁ M↦M′) (app 𝒟 (derp s)) (f s)


-- Iterated small-step reduction preserves SN.
snp⤅ :  {A M M′}  M  M′    M  A  SN M A
                    SN M′ A
snp⤅ done                𝒟 s = s
snp⤅ (step M↦M″ M″⤅M′) 𝒟 s = snp⤅ M″⤅M′ (tp↦ M↦M″ 𝒟) (snp↦ M↦M″ 𝒟 s)


-- Big-step reduction preserves SN.
snp⇓ :  {A M M′}  M  M′    M  A  SN M A
                   SN M′ A
snp⇓ (M⤅M′ , VM′) 𝒟 s = snp⤅ M⤅M′ 𝒟 s


--------------------------------------------------------------------------------


-- If `M` is SN at type `A` and `N` is SN at type `B`, then `PAIR M N` is SN at type `A ∧ B`.
sn-pair :  {A B M N}  SN M A  SN N B
                       SN (PAIR M N) (A  B)
sn-pair s₁@(𝒟 , M⇓@(M′ , M⇓M′@(M⤅M′ , VM′)) , s!₁) s₂@( , N⇓@(N′ , N⇓N′@(N⤅N′ , VN′)) , s!₂)
  = pair 𝒟  ,
    halt-pair M⇓ N⇓ ,
    snpr⇓ (big-red-fst-pair {{VM′}} {{VN′}} (congs-pair {{VM′}} {{VN′}} M⤅M′ N⤅N′)) (fst (pair 𝒟 )) (snp⤅ M⤅M′ 𝒟 s₁) ,
    snpr⇓ (big-red-snd-pair {{VM′}} {{VN′}} ((congs-pair {{VM′}} {{VN′}} M⤅M′ N⤅N′))) (snd (pair 𝒟 )) (snp⤅ N⤅N′  s₂)


-- If `M` is SN at type `A ∧ B`, then `FST M` is SN at type `A`.
mutual
  sn-fst :  {A B M}  SN M (A  B)
                      SN (FST M) A
  sn-fst (𝒟 , M⇓ , s!) = fst 𝒟 , halt-fst 𝒟 M⇓ , sn!-fst s!

  sn!-fst :  {A B M}  SN! M (A  B)
                       SN! (FST M) A
  sn!-fst {𝔹}       _                      = 
  sn!-fst {A₁  A₂} x                      = oops
  sn!-fst {𝟘}       ()
  sn!-fst {𝟙}       _                      = 
  sn!-fst {A₁  A₂} (( , FST⇓ , s) , _)   = s
  sn!-fst {A₁  A₂} (( , FST⇓ , f) , _) s = f s


-- If `M` is SN at type `A ∧ B`, then `SND M` is SN at type `B`.
mutual
  sn-snd :  {A B M}  SN M (A  B)
                      SN (SND M) B
  sn-snd (𝒟 , M⇓ , s!) = snd 𝒟 , halt-snd 𝒟 M⇓ , sn!-snd s!

  sn!-snd :  {B A M}  SN! M (A  B)
                       SN! (SND M) B
  sn!-snd {𝔹}       _                      = 
  sn!-snd {B₁  B₂} x                      = oops
  sn!-snd {𝟘}       ()
  sn!-snd {𝟙}       _                      = 
  sn!-snd {B₁  B₂} (_ , ( , SND⇓ , s))   = s
  sn!-snd {B₁  B₂} (_ , ( , SND⇓ , f)) s = f s


-- If `M` is SN at type `𝟘`, then `ABORT M` is SN at type `C`.
mutual
  sn-abort :  {C M}  SN M 𝟘
                      SN (ABORT M) C
  sn-abort {C} (𝒟 , M⇓ , s!) = abort 𝒟 , halt-abort 𝒟 M⇓ , sn!-abort {C} s!

  sn!-abort :  {C M}  SN! M 𝟘
                       SN! (ABORT M) C
  sn!-abort {𝔹}     ()
  sn!-abort {A  B} x  = oops
  sn!-abort {𝟘}     ()
  sn!-abort {𝟙}     ()
  sn!-abort {A  B} ()
  sn!-abort {A  B} () _


-- If `M` is SN at type `A`, then `LEFT M` is SN at type `A ∨ B`.
sn-left :  {A B M}  SN M A
                     SN (LEFT M) (A  B)
sn-left s@(𝒟 , M⇓@(M′ , M⇓M′@(M⤅M′ , VM′)) , s!) = left 𝒟 , halt-left M⇓ , oops


-- If `M` is SN at type `B`, then `RIGHT M` is SN at type `A ∨ B`.
sn-right :  {A B M}  SN M B
                      SN (RIGHT M) (A  B)
sn-right s@(𝒟 , M⇓@(M′ , M⇓M′@(M⤅M′ , VM′)) , s!) = right 𝒟 , halt-right M⇓ , oops


-- If `M` is SN at type `𝔹` and `N` is SN at type `C` and `O` is SN at type `C`, then `IF M N O` is SN at type `C`.
mutual
  sn-if :  {C M N O}  SN M 𝔹  SN N C  SN O C
                       SN (IF M N O) C
  sn-if (𝒟 , M⇓@(M′       , M⤅M′    , VM′)       , ) _              _              with tp⤅ M⤅M′ 𝒟
  sn-if (𝒟 , M⇓@(LAM _    , _        , val-lam)   , ) _              _              | ()
  sn-if (𝒟 , M⇓@(PAIR _ _ , _        , val-pair)  , ) _              _              | ()
  sn-if (𝒟 , M⇓@(UNIT     , _        , val-unit)  , ) _              _              | ()
  sn-if (𝒟 , M⇓@(LEFT _   , _        , val-left)  , ) _              _              | ()
  sn-if (𝒟 , M⇓@(RIGHT _  , _        , val-right) , ) _              _              | ()
  sn-if (𝒟 , M⇓@(TRUE     , M⤅TRUE  , val-true)  , ) ( , N⇓ , s!₁) ( , O⇓ , s!₂) | true  = if 𝒟   , halt-if 𝒟 M⇓ N⇓ O⇓ , sn!-if-true M⤅TRUE 𝒟   s!₁
  sn-if (𝒟 , M⇓@(FALSE    , M⤅FALSE , val-false) , ) ( , N⇓ , s!₁) ( , O⇓ , s!₂) | false = if 𝒟   , halt-if 𝒟 M⇓ N⇓ O⇓ , sn!-if-false M⤅FALSE 𝒟   s!₂

  sn!-if-true :  {C M N O}  M  TRUE    M  𝔹    N  C    O  C  SN! N C
                             SN! (IF M N O) C
  sn!-if-true {𝔹}     M⤅TRUE 𝒟            = 
  sn!-if-true {A  B} M⤅TRUE 𝒟   x         = oops
  sn!-if-true {𝟘}     M⤅TRUE 𝒟   ()
  sn!-if-true {𝟙}     M⤅TRUE 𝒟            = 
  sn!-if-true {A  B} M⤅TRUE 𝒟   (s₁ , s₂) = snpr⤅ (congs-fst (reds-if-true M⤅TRUE done)) (fst (if 𝒟  )) s₁ ,
                                                snpr⤅ (congs-snd (reds-if-true M⤅TRUE done)) (snd (if 𝒟  )) s₂
  sn!-if-true {A  B} M⤅TRUE 𝒟   f s       = snpr⤅ (congs-app₁ (reds-if-true M⤅TRUE done)) (app (if 𝒟  ) (derp s)) (f s)

  sn!-if-false :  {C M N O}  M  FALSE    M  𝔹    N  C    O  C  SN! O C
                              SN! (IF M N O) C
  sn!-if-false {𝔹}     M⤅FALSE 𝒟            = 
  sn!-if-false {A  B} M⤅FALSE 𝒟   x         = oops
  sn!-if-false {𝟘}     M⤅FALSE 𝒟   ()
  sn!-if-false {𝟙}     M⤅FALSE 𝒟            = 
  sn!-if-false {A  B} M⤅FALSE 𝒟   (s₁ , s₂) = snpr⤅ (congs-fst (reds-if-false M⤅FALSE done)) (fst (if 𝒟  )) s₁ ,
                                                  snpr⤅ (congs-snd (reds-if-false M⤅FALSE done)) (snd (if 𝒟  )) s₂
  sn!-if-false {A  B} M⤅FALSE 𝒟   f s       = snpr⤅ (congs-app₁ (reds-if-false M⤅FALSE done)) (app (if 𝒟  ) (derp s)) (f s)


--------------------------------------------------------------------------------


-- The fundamental theorem and the troublecome `lam` case.
-- TODO: Clean this up.


gen-red-app-lam :  {g M N}  {τ : Terms 0 g}  {{_ : Val N}}
                             APP (LAM (SUB (LIFTS τ) M)) N  SUB (τ , N) M
gen-red-app-lam {M = M} {N} {τ} rewrite simp-CUT-SUB N τ M ⁻¹ = red app-lam


gen-big-red-app-lam :  {g M M′ N}  {τ : Terms 0 g}  {{_ : Vals τ}} {{_ : Val N}}
                                    SUB (τ , N) M  M′
                                    APP (LAM (SUB (LIFTS τ) M)) N  M′
gen-big-red-app-lam {M = M} (SUB⤅M′ , VM′) = step (gen-red-app-lam {M = M}) SUB⤅M′ , VM′


gen-halt-app-lam :  {g M N}  {τ : Terms 0 g}  {{_ : Vals τ}} {{_ : Val N}}
                              SUB (τ , N) M 
                              APP (LAM (SUB (LIFTS τ) M)) N 
gen-halt-app-lam {M = M} (M′ , SUB⇓M′) = M′ , gen-big-red-app-lam {M = M} SUB⇓M′


mutual
  gen-sn-app-lam :  {B g M N A}  {τ : Terms 0 g}  {{_ : Vals τ}} {{_ : Val N}}
                                    SUB τ (LAM M)  A  B    N  A  SN (SUB (τ , N) M) B
                                  SN (APP (LAM (SUB (LIFTS τ) M)) N) B
  gen-sn-app-lam {M = M} 𝒟  (𝒟′ , SUB⇓ , s!) = app 𝒟  , gen-halt-app-lam {M = M} SUB⇓ , gen-sn!-app-lam {M = M} 𝒟  s!

  gen-sn!-app-lam :  {B g M N A}  {τ : Terms 0 g}  {{_ : Vals τ}} {{_ : Val N}}
                                     SUB τ (LAM M)  A  B    N  A  SN! (SUB (τ , N) M) B
                                   SN! (APP (LAM (SUB (LIFTS τ) M)) N) B
  gen-sn!-app-lam {𝔹}       {M = M} 𝒟           = 
  gen-sn!-app-lam {B₁  B₂} {M = M} 𝒟  x         = oops
  gen-sn!-app-lam {𝟘}       {M = M} 𝒟  ()
  gen-sn!-app-lam {𝟙}       {M = M} 𝒟           = 
  gen-sn!-app-lam {B₁  B₂} {M = M} 𝒟  (s₁ , s₂) = snpr↦ (cong-fst (gen-red-app-lam {M = M})) (fst (app 𝒟 )) s₁ ,
                                                    snpr↦ (cong-snd (gen-red-app-lam {M = M})) (snd (app 𝒟 )) s₂
  gen-sn!-app-lam {B₁  B₂} {M = M} 𝒟  f s       = snpr↦ (cong-app₁ (gen-red-app-lam {M = M})) (app (app 𝒟 ) (derp s)) (f s)


mutual
  gen-sn :  {g M A}  {τ : Terms 0 g} {Γ : Types g}  {{_ : Vals τ}}
                      SNs τ Γ  Γ  M  A
                      SN (SUB τ M) A
  gen-sn σ (var i)      = get σ (zip∋₂ i)
  gen-sn σ (lam 𝒟)      = let 𝒟′ = sub (derps σ) (lam 𝒟) in
                            𝒟′ , (LAM _ , done , val-lam) , gen-sn-lam σ 𝒟 𝒟′
  gen-sn σ (app 𝒟 )    with gen-sn σ 𝒟
  gen-sn σ (app 𝒟 )    | 𝒟′ , M′⇓ , f = f (gen-sn σ )
  gen-sn σ (pair 𝒟 )   = sn-pair (gen-sn σ 𝒟) (gen-sn σ )
  gen-sn σ (fst 𝒟)      = sn-fst (gen-sn σ 𝒟)
  gen-sn σ (snd 𝒟)      = sn-snd (gen-sn σ 𝒟)
  gen-sn σ unit         = unit , (UNIT , done , val-unit) , 
  gen-sn σ (abort 𝒟)    = sn-abort (gen-sn σ 𝒟)
  gen-sn σ (left 𝒟)     = sn-left (gen-sn σ 𝒟)
  gen-sn σ (right 𝒟)    = sn-right (gen-sn σ 𝒟)
  gen-sn σ (case 𝒟  ) = oops
  gen-sn σ true         = true , (TRUE , done , val-true) , 
  gen-sn σ false        = false , (FALSE , done , val-false) , 
  gen-sn σ (if 𝒟  )   = sn-if (gen-sn σ 𝒟) (gen-sn σ ) (gen-sn σ )

  gen-sn-lam :  {g M N A B}  {τ : Terms 0 g} {Γ : Types g}  {{_ : Vals τ}}
                              SNs τ Γ  Γ , A  M  B    LAM (SUB (LIFTS τ) M)  A  B  SN N A
                              SN (APP (LAM (SUB (LIFTS τ) M)) N) B
  gen-sn-lam {M = M} {{}} σ 𝒟 𝒟′ s@( , (N′ , N⇓N′@(N⤅N′ , VN′)) , s!)
    = let s′ = snp⇓ N⇓N′  s in
        snpr⤅ (congs-app₂ N⤅N′) (app 𝒟′ ) (gen-sn-app-lam {M = M} {{}} {{VN′}} 𝒟′ (derp s′) (gen-sn {{ , VN′}} (σ , s′) 𝒟))


--------------------------------------------------------------------------------


-- Every well-typed term is SN.
sn :  {A M}    M  A
              SN M A
sn {A} {M} 𝒟 = subst (\ M′  SN M′ A) (id-SUB M) (gen-sn  𝒟)


-- Every SN term terminates.
herp :  {A M}  SN M A
                M 
herp (𝒟 , M⇓ , s!) = M⇓


-- Every well-typed term terminates.
halt :  {A M}    M  A
                M 
halt {A} 𝒟 = herp {A} (sn 𝒟)


--------------------------------------------------------------------------------