module A201607.WIP.BasicIPC.Syntax.GentzenNormalForm2 where

open import A201607.BasicIPC.Syntax.GentzenNormalForm public


data NfNe⁼ {A Γ} (t : Γ  A) : Set where
  nfⁿᶠⁿᵉ⁼ : (t′ : Γ ⊢ⁿᶠ A)  {{_ : nf→tm t′  t}}  NfNe⁼ t
  neⁿᶠⁿᵉ⁼ : (t′ : Γ ⊢ⁿᵉ A)  {{_ : ne→tm t′  t}}  NfNe⁼ t


mutual
  data Nf {Γ} :  {A}  Γ  A  Set where
    neⁿᶠ   :  {A} {t : Γ  A}  Ne t  Nf t
    lamⁿᶠ  :  {A B} {t : Γ , A  B}  Nf t  Nf (lam t)
    pairⁿᶠ :  {A B} {t : Γ  A} {u : Γ  B}  Nf t  Nf u  Nf (pair t u)
    unitⁿᶠ : Nf unit

  data Ne {Γ} :  {A}  Γ  A  Set where
    varⁿᵉ :  {A}  (i : A  Γ)  Ne (var i)
    appⁿᵉ :  {A B} {t : Γ  A  B} {u : Γ  A}  Ne t  Nf u  Ne (app t u)
    fstⁿᵉ :  {A B} {t : Γ  A  B}  Ne t  Ne (fst t)
    sndⁿᵉ :  {A B} {t : Γ  A  B}  Ne t  Ne (snd t)

data NfNe {A Γ} (t : Γ  A) : Set where
  nfⁿᶠⁿᵉ : Nf t  NfNe t
  neⁿᶠⁿᵉ : Ne t  NfNe t


mutual
  expⁿᶠ :  {A Γ} {t : Γ  A}  Nf t  Σ (Γ ⊢ⁿᶠ A)  t′  nf→tm t′  t)
  expⁿᶠ (neⁿᶠ d)     with expⁿᵉ d
  expⁿᶠ (neⁿᶠ d)     | t′ , refl = neⁿᶠ t′ , refl
  expⁿᶠ (lamⁿᶠ d)    with expⁿᶠ d
  expⁿᶠ (lamⁿᶠ d)    | t′ , refl = lamⁿᶠ t′ , refl
  expⁿᶠ (pairⁿᶠ d e) with expⁿᶠ d | expⁿᶠ e
  expⁿᶠ (pairⁿᶠ d e) | t′ , refl | u′ , refl = pairⁿᶠ t′ u′ , refl
  expⁿᶠ unitⁿᶠ       = unitⁿᶠ , refl

  expⁿᵉ :  {A Γ} {t : Γ  A}  Ne t  Σ (Γ ⊢ⁿᵉ A)  t′  ne→tm t′  t)
  expⁿᵉ (varⁿᵉ i)   = varⁿᵉ i , refl
  expⁿᵉ (appⁿᵉ d e) with expⁿᵉ d | expⁿᶠ e
  expⁿᵉ (appⁿᵉ d e) | t′ , refl | u′ , refl = appⁿᵉ t′ u′ , refl
  expⁿᵉ (fstⁿᵉ d)   with expⁿᵉ d
  expⁿᵉ (fstⁿᵉ d)   | t′ , refl = fstⁿᵉ t′ , refl
  expⁿᵉ (sndⁿᵉ d)   with expⁿᵉ d
  expⁿᵉ (sndⁿᵉ d)   | t′ , refl = sndⁿᵉ t′ , refl

expⁿᶠⁿᵉ :  {A Γ} {t : Γ  A}  NfNe t  NfNe⁼ t
expⁿᶠⁿᵉ (nfⁿᶠⁿᵉ d) with expⁿᶠ d
expⁿᶠⁿᵉ (nfⁿᶠⁿᵉ d) | t′ , refl = nfⁿᶠⁿᵉ⁼ t′
expⁿᶠⁿᵉ (neⁿᶠⁿᵉ d) with expⁿᵉ d
expⁿᶠⁿᵉ (neⁿᶠⁿᵉ d) | t′ , refl = neⁿᶠⁿᵉ⁼ t′


mutual
  impⁿᶠ :  {A Γ} {t : Γ  A}  (t′ : Γ ⊢ⁿᶠ A)  {{_ : nf→tm t′  t}}  Nf t
  impⁿᶠ (neⁿᶠ t′)      {{refl}} = neⁿᶠ (impⁿᵉ t′)
  impⁿᶠ (lamⁿᶠ t′)     {{refl}} = lamⁿᶠ (impⁿᶠ t′)
  impⁿᶠ (pairⁿᶠ t′ u′) {{refl}} = pairⁿᶠ (impⁿᶠ t′) (impⁿᶠ u′)
  impⁿᶠ unitⁿᶠ         {{refl}} = unitⁿᶠ

  impⁿᵉ :  {A Γ} {t : Γ  A}  (t′ : Γ ⊢ⁿᵉ A)  {{_ : ne→tm t′  t}}  Ne t
  impⁿᵉ (varⁿᵉ i)     {{refl}} = varⁿᵉ i
  impⁿᵉ (appⁿᵉ t′ u′) {{refl}} = appⁿᵉ (impⁿᵉ t′) (impⁿᶠ u′)
  impⁿᵉ (fstⁿᵉ t′)    {{refl}} = fstⁿᵉ (impⁿᵉ t′)
  impⁿᵉ (sndⁿᵉ t′)    {{refl}} = sndⁿᵉ (impⁿᵉ t′)

impⁿᶠⁿᵉ :  {A Γ} {t : Γ  A}  NfNe⁼ t  NfNe t
impⁿᶠⁿᵉ (nfⁿᶠⁿᵉ⁼ d) = nfⁿᶠⁿᵉ (impⁿᶠ d)
impⁿᶠⁿᵉ (neⁿᶠⁿᵉ⁼ d) = neⁿᶠⁿᵉ (impⁿᵉ d)


nf :  {A Γ} {t : Γ  A}  NfNe t  Nf t
nf (nfⁿᶠⁿᵉ d) = d
nf (neⁿᶠⁿᵉ d) = neⁿᶠ d


lamⁿᶠⁿᵉ :  {A B Γ} {t : Γ , A  B}  NfNe t  NfNe (lam t)
lamⁿᶠⁿᵉ = nfⁿᶠⁿᵉ  lamⁿᶠ  nf

unlamⁿᶠⁿᵉ :  {A B Γ} {t : Γ , A  B}  NfNe (lam t)  NfNe t
unlamⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (neⁿᶠ ()))
unlamⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (lamⁿᶠ t′)) = nfⁿᶠⁿᵉ t′
unlamⁿᶠⁿᵉ (neⁿᶠⁿᵉ ())


pairⁿᶠⁿᵉ :  {A B Γ} {t : Γ  A} {u : Γ  B}  NfNe t  NfNe u  NfNe (pair t u)
pairⁿᶠⁿᵉ (nfⁿᶠⁿᵉ t′) = nfⁿᶠⁿᵉ  pairⁿᶠ t′  nf
pairⁿᶠⁿᵉ (neⁿᶠⁿᵉ t′) = nfⁿᶠⁿᵉ  pairⁿᶠ (neⁿᶠ t′)  nf

unpairⁿᶠⁿᵉ₁ :  {A B Γ} {t : Γ  A} {u : Γ  B}  NfNe (pair t u)  NfNe t
unpairⁿᶠⁿᵉ₁ (nfⁿᶠⁿᵉ (neⁿᶠ ()))
unpairⁿᶠⁿᵉ₁ (nfⁿᶠⁿᵉ (pairⁿᶠ t′ u′)) = nfⁿᶠⁿᵉ t′
unpairⁿᶠⁿᵉ₁ (neⁿᶠⁿᵉ ())

unpairⁿᶠⁿᵉ₂ :  {A B Γ} {t : Γ  A} {u : Γ  B}  NfNe (pair t u)  NfNe u
unpairⁿᶠⁿᵉ₂ (nfⁿᶠⁿᵉ (neⁿᶠ ()))
unpairⁿᶠⁿᵉ₂ (nfⁿᶠⁿᵉ (pairⁿᶠ t′ u′)) = nfⁿᶠⁿᵉ u′
unpairⁿᶠⁿᵉ₂ (neⁿᶠⁿᵉ ())

pairⁿᶠⁿᵉ′ :  {A B Γ} {t : Γ  A} {u : Γ  B}  NfNe t × NfNe u  NfNe (pair t u)
pairⁿᶠⁿᵉ′ (d , e) = pairⁿᶠⁿᵉ d e

unpairⁿᶠⁿᵉ′ :  {A B Γ} {t : Γ  A} {u : Γ  B}  NfNe (pair t u)  NfNe t × NfNe u
unpairⁿᶠⁿᵉ′ d = unpairⁿᶠⁿᵉ₁ d , unpairⁿᶠⁿᵉ₂ d


appⁿᶠⁿᵉ :  {A B Γ} {t : Γ  A  B} {u : Γ  A}  Ne t  NfNe u  NfNe (app t u)
appⁿᶠⁿᵉ t′ = neⁿᶠⁿᵉ  appⁿᵉ t′  nf

unappⁿᶠⁿᵉ₁ :  {A B Γ} {t : Γ  A  B} {u : Γ  A}  NfNe (app t u)  NfNe t
unappⁿᶠⁿᵉ₁ (nfⁿᶠⁿᵉ (neⁿᶠ (appⁿᵉ t′ u′))) = neⁿᶠⁿᵉ t′
unappⁿᶠⁿᵉ₁ (neⁿᶠⁿᵉ (appⁿᵉ t′ u′))        = neⁿᶠⁿᵉ t′

unappⁿᶠⁿᵉ₂ :  {A B Γ} {t : Γ  A  B} {u : Γ  A}  NfNe (app t u)  NfNe u
unappⁿᶠⁿᵉ₂ (nfⁿᶠⁿᵉ (neⁿᶠ (appⁿᵉ t′ u′))) = nfⁿᶠⁿᵉ u′
unappⁿᶠⁿᵉ₂ (neⁿᶠⁿᵉ (appⁿᵉ t′ u′))        = nfⁿᶠⁿᵉ u′


unfstⁿᶠⁿᵉ :  {A B Γ} {t : Γ  A  B}  NfNe (fst t)  NfNe t
unfstⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (neⁿᶠ (fstⁿᵉ t′))) = neⁿᶠⁿᵉ t′
unfstⁿᶠⁿᵉ (neⁿᶠⁿᵉ (fstⁿᵉ t′))        = neⁿᶠⁿᵉ t′

unsndⁿᶠⁿᵉ :  {A B Γ} {t : Γ  A  B}  NfNe (snd t)  NfNe t
unsndⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (neⁿᶠ (sndⁿᵉ t′))) = neⁿᶠⁿᵉ t′
unsndⁿᶠⁿᵉ (neⁿᶠⁿᵉ (sndⁿᵉ t′))        = neⁿᶠⁿᵉ t′


¬applamⁿᶠⁿᵉ :  {A B Γ} {t : Γ , A  B} {u : Γ  A}  Not (NfNe (app (lam t) u))
¬applamⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (neⁿᶠ (appⁿᵉ () _)))
¬applamⁿᶠⁿᵉ (neⁿᶠⁿᵉ (appⁿᵉ () _))

¬fstpairⁿᶠⁿᵉ :  {A B Γ} {t : Γ  A} {u : Γ  B}  Not (NfNe (fst (pair t u)))
¬fstpairⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (neⁿᶠ (fstⁿᵉ ())))
¬fstpairⁿᶠⁿᵉ (neⁿᶠⁿᵉ (fstⁿᵉ ()))

¬sndpairⁿᶠⁿᵉ :  {A B Γ} {t : Γ  A} {u : Γ  B}  Not (NfNe (snd (pair t u)))
¬sndpairⁿᶠⁿᵉ (nfⁿᶠⁿᵉ (neⁿᶠ (sndⁿᵉ ())))
¬sndpairⁿᶠⁿᵉ (neⁿᶠⁿᵉ (sndⁿᵉ ()))


×Dec :  {p q} {P : Set p} {Q : Set q}  Dec P  Dec Q  Dec (P × Q)
×Dec (yes x) (yes y) = yes (x , y)
×Dec (no ¬x) _       = no (¬x  π₁)
×Dec _       (no ¬y) = no (¬y  π₂)


nfne? :  {A Γ}  (t : Γ  A)  Dec (NfNe t)
nfne? (var i)    = yes (neⁿᶠⁿᵉ (varⁿᵉ i))
nfne? (lam t)    = mapDec lamⁿᶠⁿᵉ unlamⁿᶠⁿᵉ (nfne? t)
nfne? (app t u)  with nfne? t
nfne? (app t u)  | yes (nfⁿᶠⁿᵉ (neⁿᶠ t′))  = mapDec (appⁿᶠⁿᵉ t′) unappⁿᶠⁿᵉ₂ (nfne? u)
nfne? (app _ u)  | yes (nfⁿᶠⁿᵉ (lamⁿᶠ t′)) = no ¬applamⁿᶠⁿᵉ
nfne? (app t u)  | yes (neⁿᶠⁿᵉ t′)         = mapDec (appⁿᶠⁿᵉ t′) unappⁿᶠⁿᵉ₂ (nfne? u)
nfne? (app t u)  | no  ¬nfne               = no (¬nfne  unappⁿᶠⁿᵉ₁)
nfne? (pair t u) = mapDec pairⁿᶠⁿᵉ′ unpairⁿᶠⁿᵉ′ (×Dec (nfne? t) (nfne? u))
nfne? (fst t)    with nfne? t
nfne? (fst t)    | yes (nfⁿᶠⁿᵉ (neⁿᶠ t′))      = yes (neⁿᶠⁿᵉ (fstⁿᵉ t′))
nfne? (fst _)    | yes (nfⁿᶠⁿᵉ (pairⁿᶠ t′ u′)) = no ¬fstpairⁿᶠⁿᵉ
nfne? (fst t)    | yes (neⁿᶠⁿᵉ t′)             = yes (neⁿᶠⁿᵉ (fstⁿᵉ t′))
nfne? (fst t)    | no  ¬nfne                   = no (¬nfne  unfstⁿᶠⁿᵉ)
nfne? (snd t)    with nfne? t
nfne? (snd t)    | yes (nfⁿᶠⁿᵉ (neⁿᶠ t′))      = yes (neⁿᶠⁿᵉ (sndⁿᵉ t′))
nfne? (snd _)    | yes (nfⁿᶠⁿᵉ (pairⁿᶠ t′ u′)) = no ¬sndpairⁿᶠⁿᵉ
nfne? (snd t)    | yes (neⁿᶠⁿᵉ t′)             = yes (neⁿᶠⁿᵉ (sndⁿᵉ t′))
nfne? (snd t)    | no  ¬nfne                   = no (¬nfne  unsndⁿᶠⁿᵉ)
nfne? unit       = yes (nfⁿᶠⁿᵉ unitⁿᶠ)