module Typed-Core where

open import Data.Empty
open import Data.List
open import Data.Nat
open import Data.List.Any as LAny
open LAny.Membership-≡
open import Relation.Nullary
open import Relation.Binary.Core
open import Relation.Binary.PropositionalEquality

open import Core
open import Core-Lemmas
open import Typing

data Λ : Type -> Set where
  bv :  {A} -> (i : ) -> Λ A
  fv :  {A} -> (x : Atom) -> Λ A
  lam :  {B} -> (A : Type) -> (e : Λ B) -> Λ (A  B)
  app :  {A B} -> (e₁ : Λ (A  B)) -> (e₂ : Λ A) -> Λ B
  Y : (t : Type) -> Λ ((t  t)  t)
------------------------------------------------------------------------------------

data _~_ : ∀{t} -> Λ t -> PTerm -> Set where
  bv :  {t i} -> (bv {t} i) ~ (bv i)
  fv :  {t x} -> (fv {t} x) ~ (fv x)
  lam :  {t s m m'} -> m ~ m' -> (lam {s} t m) ~ (lam m')
  app :  {t s m n m' n'} -> m ~ m' -> n ~ n' -> (app {t} {s} m n) ~ (app m' n')
  Y :  {t} -> (Y t) ~ (Y t)
------------------------------------------------------------------------------------

Λ[_<<_] :  {t} ->  -> Atom -> Λ t -> Λ t
Λ[ k << x ] (bv i) = bv i
Λ[ k << x ] (fv y) with x  y
... | yes _ = bv k
... | no _ = fv y
Λ[ k << x ] (lam t m) = lam t (Λ[ (suc k) << x ] m)
Λ[ k << x ] (app t1 t2) = app (Λ[ k << x ] t1) (Λ[ k << x ] t2)
Λ[ k << x ] (Y t) = Y t
------------------------------------------------------------------------------------

⊢->Λ :  {Γ m t} -> Γ  m  t -> Λ t
⊢->Λ {m = bv i} ()
⊢->Λ {m = fv x} {t} Γ⊢m∶t = fv {t} x
⊢->Λ {m = lam m} (abs {_} {τ₁} {τ₂} L cf) =
  lam τ₁ ( Λ[ 0 << ∃fresh (L ++ FV m) ] (⊢->Λ (cf (∉-cons-l _ _ (∃fresh-spec (L ++ FV m)) ))) )
⊢->Λ {m = app s t} (app Γ⊢s Γ⊢t) = app (⊢->Λ Γ⊢s) (⊢->Λ Γ⊢t)
⊢->Λ {m = Y τ} (Y _) = Y τ
------------------------------------------------------------------------------------

Λ*^-*^~ :  {τ x k} t t' -> _~_ {τ} t t' -> Λ[ k << x ] t ~ ([ k << x ] t')
Λ*^-*^~ _ _ bv = bv
Λ*^-*^~ {x = x} (fv y) _ fv with x  y
Λ*^-*^~ (fv x) .(fv x) fv | yes _ = bv
Λ*^-*^~ (fv y) .(fv y) fv | no _ = fv
Λ*^-*^~ _ _ (lam {m = m} {m'} t~t') = lam (Λ*^-*^~ m m' t~t')
Λ*^-*^~ _ _ (app {m = m} {n} {m'} {n'} t~t' t~t'') =
  app (Λ*^-*^~ m m' t~t') (Λ*^-*^~ n n' t~t'')
Λ*^-*^~ _ _ Y = Y
------------------------------------------------------------------------------------

⊢->Λ~ :  {Γ t τ} -> (Γ⊢t : Γ  t  τ) -> (⊢->Λ Γ⊢t) ~ t
⊢->Λ~ {t = bv i} ()
⊢->Λ~ {t = fv x} (var _ _) = fv
⊢->Λ~ {t = lam t} (abs {τ₁ = τ₁} {τ₂} L cf) = lam ih
  where
  x = ∃fresh (L ++ FV t)
  x∉ = ∃fresh-spec (L ++ FV t)
  x∷Γ⊢t^'x = cf (∉-cons-l _ _ x∉)

  sub :  {τ x m} -> x  FV t -> _~_ {τ} m t  m ~ (* x ^ (t ^' x))
  sub {_} {x} x∉ rewrite fv-^-*^-refl x t {0} x∉ = refl

  ih : Λ[ 0 << x ] (⊢->Λ x∷Γ⊢t^'x) ~ t
  ih rewrite sub {_} {x} {Λ[ 0 << x ] (⊢->Λ x∷Γ⊢t^'x)} (∉-cons-r L _ x∉) =
    Λ*^-*^~ (⊢->Λ x∷Γ⊢t^'x) (t ^' x) (⊢->Λ~ (cf (∉-cons-l _ _ x∉)))
⊢->Λ~ {t = app t t₁} (app Γ⊢t Γ⊢t₁) = app (⊢->Λ~ Γ⊢t) (⊢->Λ~ Γ⊢t₁)
⊢->Λ~ {t = Y t₁} (Y x) = Y
------------------------------------------------------------------------------------

Λ[_>>_] :  {τ τ'} ->  -> Λ τ' -> Λ τ -> Λ τ
Λ[ k >> u ] (bv i) with k  i
Λ[_>>_] {τ} {τ'} k u (bv .k) | yes refl with τ ≟T τ'
Λ[ k >> u ] (bv .k) | yes refl | yes refl = u
Λ[ k >> u ] (bv .k) | yes refl | no _ = bv k
Λ[ k >> u ] (bv i) | no _ = bv i
Λ[ k >> u ] (fv x) = fv x
Λ[ k >> u ] (lam A t) = lam A (Λ[ (suc k) >> u ] t)
Λ[ k >> u ] (app t1 t2) = app (Λ[ k >> u ] t1) (Λ[ k >> u ] t2)
Λ[ k >> u ] (Y t) = Y t
------------------------------------------------------------------------------------

_Λ[_::=_] :  {τ τ'} -> Λ τ -> Atom -> Λ τ' -> Λ τ
bv i Λ[ x ::= u ] = bv i
fv y Λ[ x ::= u ] with x  y
_Λ[_::=_] {τ} {τ'} (fv y) .y u | yes refl with τ ≟T τ'
fv y Λ[ .y ::= u ] | yes refl | yes refl = u
fv y Λ[ .y ::= u ] | yes refl | no _ = fv y
fv y Λ[ x ::= u ] | no _ = fv y
------------------------------------------------------------------------------------

lam A t Λ[ x ::= u ] = lam A (t Λ[ x ::= u ])
app t1 t2 Λ[ x ::= u ] = app (t1 Λ[ x ::= u ]) (t2 Λ[ x ::= u ])
Y t₁ Λ[ x ::= u ] = Y t₁
------------------------------------------------------------------------------------

ΛFV :  {τ} -> Λ τ -> FVars
ΛFV (bv i) = []
ΛFV (fv x) = x  []
ΛFV (lam A e) = ΛFV e
ΛFV (app e₁ e₂) = ΛFV e₁ ++ ΛFV e₂
ΛFV (Y t) = []
------------------------------------------------------------------------------------

data ΛTerm :  {τ} -> Λ τ -> Set where
  var :  {A x} -> ΛTerm (fv {A} x)
  lam :  {A B} (L : FVars) ->  {e : Λ B} ->
    (cf :  {x} -> (x∉L : x  L) -> ΛTerm (Λ[ 0 >> fv {A} x ] e)) -> ΛTerm (lam A e)
  app :  {A B} {e₁ : Λ (A  B)} {e₂ : Λ A} -> ΛTerm e₁ -> ΛTerm e₂ ->
    ΛTerm (app e₁ e₂)
  Y :  {t} -> ΛTerm (Y t)
------------------------------------------------------------------------------------

Λlam-inj :  {A B : Type} {t u : Λ B} -> (Λ.lam A t)  (Λ.lam A u) -> t  u
Λlam-inj refl = refl
------------------------------------------------------------------------------------

Λlam-eq : {A B : Type} {t u : Λ B} -> t  u -> (Λ.lam A t)  (Λ.lam A u)
Λlam-eq refl = refl
------------------------------------------------------------------------------------

Λapp-inj-l :  {A B : Type} {t₁ u₁ : Λ (A  B)} {t₂ u₂ : Λ A} ->
  (Λ.app t₁ t₂)  (Λ.app u₁ u₂) -> t₁  u₁
Λapp-inj-l refl = refl
------------------------------------------------------------------------------------

Λapp-inj-r :  {A B : Type} {t₁ u₁ : Λ (A  B)} {t₂ u₂ : Λ A} ->
  (Λ.app t₁ t₂)  (Λ.app u₁ u₂) -> t₂  u₂
Λapp-inj-r refl = refl
------------------------------------------------------------------------------------

Λapp-eq :  {A B : Type} {t₁ u₁ : Λ (A  B)} {t₂ u₂ : Λ A} -> t₁  u₁ -> t₂  u₂ ->
  (Λ.app t₁ t₂)  (Λ.app u₁ u₂)
Λapp-eq refl refl = refl
------------------------------------------------------------------------------------

^-ΛTerm-eq-aux :  {τ τ' τ''} j (t : Λ τ') i (u : Λ τ'') (e : Λ τ)  ¬ (i  j) ->
  Λ[ j >> t ] e  Λ[ i >> u ] (Λ[ j >> t ] e) ->
  e  Λ[ i >> u ] e
^-ΛTerm-eq-aux j t i u (bv k) i≠j eq with i  k
^-ΛTerm-eq-aux j t i u (bv ._) i≠j eq | yes refl with j  i
^-ΛTerm-eq-aux j t ._ u (bv ._) i≠j eq | yes refl | yes refl = ⊥-elim (i≠j refl)
^-ΛTerm-eq-aux j t i u (bv ._) i≠j eq | yes refl | no _ with i  i
^-ΛTerm-eq-aux j t i u (bv ._) i≠j eq | yes refl | no _ | yes refl = eq
^-ΛTerm-eq-aux j t i u (bv ._) i≠j eq | yes refl | no _ | no i≠i = ⊥-elim (i≠i refl)
^-ΛTerm-eq-aux j t i u (bv k) i≠j eq | no _ = refl
^-ΛTerm-eq-aux j t i u (fv x) i≠j eq = refl
^-ΛTerm-eq-aux j t i u (lam A e) i≠j eq
  with ^-ΛTerm-eq-aux (suc j) t (suc i) u e
            eq' -> i≠j (suc-inj i j eq')) (Λlam-inj eq)
... | eq'' = Λlam-eq eq''
^-ΛTerm-eq-aux j t i u (app e₁ e₂) i≠j eq
  with ^-ΛTerm-eq-aux j t i u e₁ i≠j (Λapp-inj-l eq)
     | ^-ΛTerm-eq-aux j t i u e₂ i≠j (Λapp-inj-r eq)
... | eq₁ | eq₂
  = Λapp-eq eq₁ eq₂
^-ΛTerm-eq-aux j t i u (Y s) i≠j eq = refl
------------------------------------------------------------------------------------

^-ΛTerm-eq :  {τ τ'} k (t : Λ τ') {e : Λ τ}  ΛTerm e  e  Λ[ k >> t ] e
^-ΛTerm-eq k t var = refl
^-ΛTerm-eq k t {lam A e} (lam L cf) = body
  where
    y = ∃fresh L

    y∉ : y  L
    y∉ = ∃fresh-spec L

    body : lam A e  Λ[ k >> t ] (lam A e)
    body with ^-ΛTerm-eq (suc k) t {Λ[ 0 >> fv y ] e} (cf y∉)
    ... | eq with ^-ΛTerm-eq-aux 0 (fv y) (suc k) t e  ()) eq
    ...   | eq' = Λlam-eq eq'
^-ΛTerm-eq k t (app trm-u trm-v) with
  ^-ΛTerm-eq k t trm-u | ^-ΛTerm-eq k t trm-v
... | e1 | e2 = Λapp-eq e1 e2
^-ΛTerm-eq k t₁ Y = refl
------------------------------------------------------------------------------------

Λsubst-open :  {τ τ' τ''} x (t : Λ τ') k (u : Λ τ'') (e : Λ τ) -> ΛTerm t ->
  (Λ[ k >> u ] e) Λ[ x ::= t ]  Λ[ k >> (u Λ[ x ::= t ]) ] (e Λ[ x ::= t ])
Λsubst-open x t k u (bv i) trm-t with k  i
Λsubst-open {τ} {τ'} {τ''} x t k u (bv .k) trm-t | yes refl with τ ≟T τ''
Λsubst-open x t k u (bv .k) trm-t | yes refl | yes refl = refl
Λsubst-open x t k u (bv .k) trm-t | yes refl | no _ = refl
Λsubst-open x t k u (bv i) trm-t | no _ = refl

Λsubst-open x t k u (fv y) trm-t with x  y
Λsubst-open {τ} {τ'} x t k u (fv .x) trm-t | yes refl with τ ≟T τ'
Λsubst-open x t k u (fv .x) trm-t | yes refl | yes refl =
  ^-ΛTerm-eq k (u Λ[ x ::= t ]) trm-t
Λsubst-open x t k u (fv .x) trm-t | yes refl | no _ = refl
Λsubst-open x t k u (fv y) trm-t | no _ = refl

Λsubst-open x t k u (lam A e) trm-t rewrite
  Λsubst-open x t (suc k) u e trm-t = refl
Λsubst-open x t k u (app v w) trm-t rewrite
  Λsubst-open x t k u v trm-t |
  Λsubst-open x t k u w trm-t = refl
Λsubst-open x t₁ k u (Y t) trm-t = refl
------------------------------------------------------------------------------------

Λsubst-open-var :  {τ τ' τ''} x y (u : Λ τ') (e : Λ τ) -> ¬ (x  y) -> ΛTerm u ->
  (Λ[ 0 >> fv {τ''} x ] (e Λ[ y ::= u ]))  (Λ[ 0 >> fv {τ''} x ] e) Λ[ y ::= u ]
Λsubst-open-var {τ'' = τ''} x y u e x≠y lu
  with Λsubst-open {τ'' = τ''} y u 0 (fv x) e lu
...  | eq with y  x
Λsubst-open-var x ._ u e x≠y lu | eq | yes refl = ⊥-elim (x≠y refl)
Λsubst-open-var x y u e x≠y lu | eq | no _ = sym eq

Λsubst-open-var2 :  {τ τ' τ''} x y (u : Λ τ') (e : Λ τ) -> ¬ (x  y) -> ΛTerm u ->
  (Λ[ 0 >> fv {τ''} x ] e) Λ[ y ::= u ]  (Λ[ 0 >> fv {τ''} x ] (e Λ[ y ::= u ]))
Λsubst-open-var2 {τ'' = τ''} x y u e x≠y lu rewrite
  Λsubst-open-var {τ'' = τ''} x y u e x≠y lu = refl
------------------------------------------------------------------------------------

Λsubst-fresh :  {τ τ'} x (t : Λ τ') (u : Λ τ) -> (x∉FVt : x  (ΛFV t)) ->
  (t Λ[ x ::= u ])  t
Λsubst-fresh x (bv i) u x∉FVt = refl
Λsubst-fresh x (fv y) u x∉FVt with x  y
Λsubst-fresh x (fv y) u x∉FVt | yes p = ⊥-elim (x∉FVt (here p))
Λsubst-fresh x (fv y) u x∉FVt | no ¬p = refl
Λsubst-fresh x (lam A s) u x∉FVt rewrite Λsubst-fresh x s u x∉FVt = refl
Λsubst-fresh x (app t1 t2) u x∉FVt rewrite
  Λsubst-fresh x t1 u (∉-cons-l (ΛFV t1) (ΛFV t2) x∉FVt) |
  Λsubst-fresh x t2 u (∉-cons-r (ΛFV t1) (ΛFV t2) x∉FVt) = refl
Λsubst-fresh x (Y t₁) u x∉FVt = refl
------------------------------------------------------------------------------------

Λsubst-intro :  {τ τ'} x (t : Λ τ') (e : Λ τ) -> x  ΛFV e -> ΛTerm t ->
  (Λ[ 0 >> t ] e)  (Λ[ 0 >> fv {τ'} x ] e) Λ[ x ::= t ]
Λsubst-intro {τ} {τ'} x t e x∉ lt with Λsubst-open x t 0 (fv {τ'} x) e lt
Λsubst-intro x t e x∉ lt | eq with x  x
Λsubst-intro {_} {τ'} x t e x∉ lt | eq | yes refl with τ' ≟T τ'
Λsubst-intro x t e x∉ lt | eq | yes refl | yes refl
  rewrite Λsubst-fresh x e t x∉ = sym eq
Λsubst-intro x t e x∉ lt | eq | yes refl | no τ'≠τ' = ⊥-elim (τ'≠τ' refl)
Λsubst-intro x t e x∉ lt | eq | no x≠x = ⊥-elim (x≠x refl)
------------------------------------------------------------------------------------

Λsubst-intro2 :  {τ τ'} x (t : Λ τ') (e : Λ τ) -> x  ΛFV e -> ΛTerm t ->
  (Λ[ 0 >> fv {τ'} x ] e) Λ[ x ::= t ]  (Λ[ 0 >> t ] e)
Λsubst-intro2 {τ} {τ'} x t e x∉ lt rewrite Λsubst-intro {τ} {τ'} x t e x∉ lt = refl
------------------------------------------------------------------------------------

Λ^-^-swap :  {τ τ' τ''} k n x y (m : Λ τ) -> ¬(k  n) -> ¬(x  y) ->
  Λ[ k >> fv {τ'} x ] (Λ[ n >> fv {τ''} y ] m)  Λ[ n >> fv {τ''} y ] (Λ[ k >> fv {τ'} x ] m)
Λ^-^-swap k n x y (bv i) k≠n x≠y with n  i
Λ^-^-swap {τ} {τ'} {τ''} k n x y (bv .n) k≠n x≠y | yes refl with τ ≟T τ''
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | yes refl with k  n
Λ^-^-swap n .n x y (bv .n) k≠n x≠y | yes refl | yes refl | yes refl = ⊥-elim (k≠n refl)
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | yes refl | no _ with n  n
Λ^-^-swap {τ} k n x y (bv .n) k≠n x≠y | yes refl | yes refl | no _ | yes refl with τ ≟T τ
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | yes refl | no _ | yes refl | yes refl = refl
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | yes refl | no _ | yes refl | no τ≠τ =
  ⊥-elim (τ≠τ refl)
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | yes refl | no _ | no n≠n = ⊥-elim (n≠n refl)

Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' with k  n
Λ^-^-swap n .n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' | yes refl = ⊥-elim (k≠n refl)
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' | no _ with n  n
Λ^-^-swap {τ} {τ'} {τ''} k n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' | no _ | yes refl with τ ≟T τ''
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' | no ¬p | yes refl | yes refl =
  ⊥-elim (τ≠τ'' refl)
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' | no ¬p₁ | yes refl | no _ = refl
Λ^-^-swap k n x y (bv .n) k≠n x≠y | yes refl | no τ≠τ'' | no _ | no n≠n =
  ⊥-elim (n≠n refl)

Λ^-^-swap k n x y (bv i) k≠n x≠y | no n≠i with k  n
Λ^-^-swap n .n x y (bv i) k≠n x≠y | no n≠i | yes refl = ⊥-elim (k≠n refl)
Λ^-^-swap k n x y (bv i) k≠n x≠y | no n≠i | no _ with k  i
Λ^-^-swap {τ} {τ'} k n x y (bv .k) k≠n x≠y | no n≠i | no _ | yes refl with τ ≟T τ'
Λ^-^-swap k n x y (bv .k) k≠n x≠y | no n≠i | no _ | yes refl | yes refl = refl
Λ^-^-swap k n x y (bv .k) k≠n x≠y | no n≠i | no _ | yes refl | no τ≠τ' with n  k
Λ^-^-swap k .k x y (bv .k) k≠n x≠y | no n≠i | no _ | yes refl | no τ≠τ' | yes refl =
  ⊥-elim (k≠n refl)
Λ^-^-swap k n x y (bv .k) k≠n x≠y | no n≠i | no _ | yes refl | no τ≠τ' | no _ = refl
Λ^-^-swap k n x y (bv i) k≠n x≠y | no n≠i | no _ | no k≠i with n  i
Λ^-^-swap k i x y (bv .i) k≠n x≠y | no n≠i | no _ | no k≠i | yes refl =
  ⊥-elim (n≠i refl)
Λ^-^-swap k n x y (bv i) k≠n x≠y | no n≠i | no _ | no k≠i | no _ = refl

Λ^-^-swap k n x y (fv z) k≠n x≠y = refl
Λ^-^-swap {_} {τ'} {τ''} k n x y (lam {B} A m) k≠n x≠y = cong
  (lam A)
  (Λ^-^-swap {B} {τ'} {τ''} (suc k) (suc n) x y m  sk≡sn  k≠n (≡-suc sk≡sn)) x≠y)
Λ^-^-swap {_} {τ'} {τ''} k n x y (app {τ₁} {τ₂} t1 t2) k≠n x≠y rewrite
  Λ^-^-swap {τ₁  τ₂} {τ'} {τ''} k n x y t1 k≠n x≠y |
  Λ^-^-swap {τ₁} {τ'} {τ''} k n x y t2 k≠n x≠y = refl
Λ^-^-swap k n x y (Y _) k≠n x≠y = refl
------------------------------------------------------------------------------------

ΛFV-^ :  {k x y A B} (m : Λ A) -> x  ΛFV m -> ¬(x  y) ->
  x  ΛFV (Λ[ k >> fv {B} y ] m)
ΛFV-^ {k} (bv i) x∉FVm x≠y x∈ with k  i
ΛFV-^ {_} {x} {y} {A} {B} (bv k) x∉FVm x≠y x∈ | yes refl with A ≟T B
ΛFV-^ {_} {x} {y} (bv k) x∉FVm x≠y x∈ | yes refl | yes refl = x≠y (fv-x≡y x y x∈)
ΛFV-^ (bv k) x∉FVm x≠y x∈ | yes refl | no _ = x∉FVm x∈
ΛFV-^ (bv i) x∉FVm x≠y x∈ | no k≠i = x∉FVm x∈
ΛFV-^ (fv _) x∉FVm x≠y x∈ = x∉FVm x∈
ΛFV-^ (lam A m) x∉FVm x≠y x∈ = ΛFV-^ m x∉FVm x≠y x∈
ΛFV-^ {k} {x} {y} (app t1 t2) x∉FVm x≠y =
  ∉-cons-intro (ΛFV (Λ[ k >> fv y ] t1)) (ΛFV (Λ[ k >> fv y ] t2))
     x∈t1  ΛFV-^ t1 (∉-cons-l _ _ x∉FVm) x≠y x∈t1)
     x∈t2  ΛFV-^ t2 (∉-cons-r (ΛFV t1) _ x∉FVm) x≠y x∈t2)
ΛFV-^ (Y t₁) x∉FVm x≠y x∈ = x∉FVm x∈