{-# OPTIONS --without-K --safe #-}
module Source.Size.Substitution.Canonical where

open import Source.Size
open import Util.Prelude


infix  0 Sub⊢
infixl 5 _>>_


data Sub (Δ : Ctx) : (Ω : Ctx)  Set where
  [] : Sub Δ []
  Snoc : (σ : Sub Δ Ω) (n : Size Δ)  Sub Δ (Ω  m)


variable
  σ τ σ′ τ′ ι ι′ : Sub Δ Ω


subV : Sub Δ Ω  Var Ω  Size Δ
subV (Snoc σ n) zero = n
subV (Snoc σ n) (suc x) = subV σ x


sub : Sub Δ Ω  Size Ω  Size Δ
sub σ (var x) = subV σ x
sub σ  = 
sub σ zero = zero
sub σ (suc n) = suc (sub σ n)


data Sub⊢ Δ :  Ω (σ : Sub Δ Ω)  Set where
  [] : Sub⊢ Δ [] []
  Snoc : (⊢σ : Sub⊢ Δ Ω σ) (n<m : n < sub σ m)  Sub⊢ Δ (Ω  m) (Snoc σ n)


syntax Sub⊢ Δ Ω σ = σ  Δ  Ω


abstract
  sub-Snoc :  (σ : Sub Δ Ω) n o
     sub (Snoc {m = m} σ n) (wk o)  sub σ o
  sub-Snoc σ n (var x) = refl
  sub-Snoc σ n  = refl
  sub-Snoc σ n zero = refl
  sub-Snoc σ n (suc o) = cong suc (sub-Snoc σ n o)


  mutual
    subV-resp-< : σ  Δ  Ω  var x < n  subV σ x < sub σ n
    subV-resp-< {x = zero} (Snoc {σ = σ} {n} {m} ⊢σ n<m) (var refl)
      = subst (n <_) (sym (sub-Snoc σ n m)) n<m
    subV-resp-< {x = suc x} (Snoc {σ = σ} {n} {m} ⊢σ n<m) (var refl)
      = subst (subV σ x <_) (sym (sub-Snoc σ n (bound x)))
          (subV-resp-< ⊢σ (var refl))
    subV-resp-< ⊢σ <suc = <suc
    subV-resp-< ⊢σ (<-trans x<m m<n)
      = <-trans (subV-resp-< ⊢σ x<m) (sub-resp-< ⊢σ m<n)


    sub-resp-< : σ  Δ  Ω  n < m  sub σ n < sub σ m
    sub-resp-< ⊢σ (var p) = subV-resp-< ⊢σ (var p)
    sub-resp-< ⊢σ zero<suc = zero<suc
    sub-resp-< ⊢σ zero<∞ = zero<∞
    sub-resp-< ⊢σ (suc<suc n<m) = suc<suc (sub-resp-< ⊢σ n<m)
    sub-resp-< ⊢σ (suc<∞ n<∞) = suc<∞ (sub-resp-< ⊢σ n<∞)
    sub-resp-< ⊢σ (<-trans n<o o<m)
      = <-trans (sub-resp-< ⊢σ n<o) (sub-resp-< ⊢σ o<m)
    sub-resp-< ⊢σ <suc = <suc


Weaken : Sub Δ Ω  Sub (Δ  n) Ω
Weaken [] = []
Weaken (Snoc σ m) = Snoc (Weaken σ) (wk m)


abstract
  subV-Weaken :  (σ : Sub Δ Ω) x  subV (Weaken {n = o} σ) x  wk (subV σ x)
  subV-Weaken (Snoc σ n) zero = refl
  subV-Weaken (Snoc σ n) (suc x) = subV-Weaken σ x


  sub-Weaken :  (σ : Sub Δ Ω) n  sub (Weaken {n = o} σ) n  wk (sub σ n)
  sub-Weaken σ (var x) = subV-Weaken σ x
  sub-Weaken σ  = refl
  sub-Weaken σ zero = refl
  sub-Weaken σ (suc n) = cong suc (sub-Weaken σ n)


  Weaken⊢ : σ  Δ  Ω  Weaken σ  Δ  n  Ω
  Weaken⊢ [] = []
  Weaken⊢ (Snoc {σ = σ} {n} {m} ⊢σ n<m)
    = Snoc (Weaken⊢ ⊢σ)
        (subst (wk n <_) (sym (sub-Weaken σ m)) (wk-resp-< n<m))


Lift : (σ : Sub Δ Ω)  Sub (Δ  m) (Ω  n)
Lift σ = Snoc (Weaken σ) (var zero)


abstract
  Lift⊢ : σ  Δ  Ω  m  sub σ n  Lift σ  Δ  m  Ω  n
  Lift⊢ {Δ} {σ = σ} {n = n} ⊢σ refl
    = Snoc (Weaken⊢ ⊢σ) (var (sub-Weaken σ n))


mutual
  Id : Sub Δ Δ
  Id {[]} = []
  Id {Δ  n} = Lift Id


  abstract
    subV-Id :  x  subV (Id {Δ}) x  var x
    subV-Id zero = refl
    subV-Id (suc x) = trans (subV-Weaken Id x) (cong wk (subV-Id x))


    sub-Id :  n  σ  Id  sub σ n  n
    sub-Id (var x) refl  = subV-Id x
    sub-Id  _ = refl
    sub-Id zero _ = refl
    sub-Id (suc n) p = cong suc (sub-Id n p)


abstract
  Id⊢ : Id  Δ  Δ
  Id⊢ {[]} = []
  Id⊢ {Δ  n} = Lift⊢ Id⊢ (sym (sub-Id _ refl))


Wk : Sub (Δ  n) Δ
Wk = Weaken Id


abstract
  sub-Wk :  n  sub (Wk {Δ} {o}) n  wk n
  sub-Wk n = trans (sub-Weaken Id n) (cong wk (sub-Id _ refl))


  Wk⊢ : Wk  Δ  n  Δ
  Wk⊢ = Weaken⊢ Id⊢


Sing : Size Δ  Sub Δ (Δ  m)
Sing n = Snoc Id n


abstract
  Sing⊢ : n < m  Sing n  Δ  Δ  m
  Sing⊢ {n = n} n<m
    = Snoc Id⊢ (subst (n <_) (sym (sub-Id _ refl)) n<m)


_>>_ : Sub Δ Δ′  Sub Δ′ Δ″  Sub Δ Δ″
σ >> [] = []
σ >> Snoc τ n = Snoc (σ >> τ) (sub σ n)


abstract
  subV->> :  (σ : Sub Δ Δ′) (τ : Sub Δ′ Δ″) x
     subV (σ >> τ) x  sub σ (subV τ x)
  subV->> σ (Snoc τ n) zero = refl
  subV->> σ (Snoc τ n) (suc x) = subV->> σ τ x


  sub->> :  n  ι  σ >> τ
     sub ι n  sub σ (sub τ n)
  sub->> {σ = σ} {τ} (var x) refl = subV->> σ τ x
  sub->>  _ = refl
  sub->> zero _ = refl
  sub->> (suc n) p = cong suc (sub->> n p)


  sub->>′ : σ >> τ  σ′ >> τ′  sub σ (sub τ n)  sub σ′ (sub τ′ n)
  sub->>′ {σ = σ} {τ = τ} {σ′ = σ′} {τ′} {n} eq
    = trans (sym (sub->> n refl))
        (trans (cong  σ  sub σ n) eq) (sub->> n refl))


  >>⊢ : σ  Δ  Δ′  τ  Δ′  Δ″  σ >> τ  Δ  Δ″
  >>⊢ ⊢σ [] = []
  >>⊢ {σ = σ} ⊢σ (Snoc {σ = τ} {n} {m} ⊢τ n<m)
    = Snoc (>>⊢ ⊢σ ⊢τ)
        (subst (sub σ n <_) (sym (sub->> m refl)) (sub-resp-< ⊢σ n<m))


Skip : Sub (Δ  n  v0) (Δ  n)
Skip = Snoc (Weaken Wk) (var zero)


abstract
  Skip⊢ : Skip  Δ  n  v0  Δ  n
  Skip⊢ {n = n}
    = Snoc (Weaken⊢ Wk⊢)
        (<-trans (var refl)
          (var (trans (sub-Weaken Wk n) (cong wk (sub-Wk n)))))


  Weaken>> : Weaken σ >> τ  Weaken {n = n} (σ >> τ)
  Weaken>> {τ = []} = refl
  Weaken>> {σ = σ} {τ = Snoc τ n} = cong₂ Snoc Weaken>>  (sub-Weaken σ n)


  Snoc>>Weaken : Snoc {m = m} σ n >> Weaken τ  σ >> τ
  Snoc>>Weaken {τ = []} = refl
  Snoc>>Weaken {σ = σ} {n = n} {τ = Snoc τ k}
    = cong₂ Snoc Snoc>>Weaken (sub-Snoc σ n k)


  id-l : Id >> σ  σ
  id-l {σ = []} = refl
  id-l {σ = Snoc σ n} = cong₂ Snoc id-l (sub-Id n refl)


  id-r : {σ : Sub Δ Ω}  σ >> Id  σ
  id-r {σ = []} = refl
  id-r {σ = Snoc σ n} = cong₂ Snoc (trans Snoc>>Weaken id-r) refl


  >>-assoc : σ >> (τ >> ι)  σ >> τ >> ι
  >>-assoc {ι = []} = refl
  >>-assoc {σ = σ} {τ = τ} {ι = Snoc ι n}
    = cong₂ Snoc >>-assoc (sym (sub->> n refl))


  Wk>> : Wk >> σ  Weaken {n = n} σ
  Wk>> = trans Weaken>> (cong Weaken id-l)


  Snoc>>Wk : Snoc {m = m} σ n >> Wk  σ
  Snoc>>Wk = trans Snoc>>Weaken id-r


  Lift>>Weaken : Lift {m = m} {n} σ >> Weaken τ  Weaken (σ >> τ)
  Lift>>Weaken = trans Snoc>>Weaken Weaken>>


  Lift>>Wk : Lift {m = m} {n} σ >> Wk  Wk >> σ
  Lift>>Wk = trans Lift>>Weaken (trans (sym Wk>>) (cong (Wk >>_) id-r))


  Sing>>Weaken : Sing {m = m} n >> Weaken σ  σ
  Sing>>Weaken = trans Snoc>>Weaken id-l


  Sing>>Wk : Sing {m = m} n >> Wk  Id
  Sing>>Wk = trans Snoc>>Weaken id-r


  Sing>>Lift :  n  Sing (sub σ n) >> Lift {m = m} {o} σ  σ >> Sing n
  Sing>>Lift n = cong₂ Snoc (trans Sing>>Weaken (sym id-r)) refl


  Lift>>Lift : Lift {m = m} {n} σ >> Lift {n = o} τ  Lift (σ >> τ)
  Lift>>Lift = cong₂ Snoc Lift>>Weaken refl


  Skip>>Weaken : Skip {n = n} >> Weaken σ  Weaken (Weaken σ)
  Skip>>Weaken = trans Snoc>>Weaken (trans Weaken>> (cong Weaken Wk>>))


  Skip>>Lift : Skip >> Lift {m = m} {n} σ  Lift (Lift σ) >> Skip
  Skip>>Lift
    = cong₂ Snoc
        (trans Skip>>Weaken
          (sym (trans Lift>>Weaken (cong Weaken (trans Snoc>>Weaken id-r)))))
      refl


  Lift-Id : Lift {m = m} Id  Id
  Lift-Id = refl


  LiftSing>>Wk>>Wk : Lift {m = o} {m} (Sing n) >> (Wk >> Wk)  Wk
  LiftSing>>Wk>>Wk {n = n} {m} = let open ≡-Reasoning in
    begin
      Lift (Sing n) >> (Wk >> Wk)
    ≡⟨ cong (Lift (Sing n) >>_) Wk>> 
      Lift (Sing n) >> (Weaken Wk)
    ≡⟨ Lift>>Weaken 
      Weaken (Sing n >> Weaken Id)
    ≡⟨ cong Weaken Sing>>Weaken 
      Wk
    


  LiftSing>>Skip
    : Lift {m = m} (Sing {m = m} n) >> Skip  Sing {m = o} (var zero) >> Lift Wk
  LiftSing>>Skip {n = n} = cong₂ Snoc go refl
    where
      go : Lift (Sing n) >> Weaken Wk  Sing (var zero) >> Weaken Wk
      go = let open ≡-Reasoning in
        begin
          Lift (Sing n) >> Weaken Wk
        ≡⟨ Lift>>Weaken 
          Weaken (Sing n >> Weaken Id)
        ≡⟨ cong Weaken Sing>>Wk 
          Wk
        ≡⟨ sym Sing>>Weaken 
          Sing (var zero) >> Weaken Wk
        


  LiftLift>>Skip : Lift (Lift {m = m} {n} σ) >> Skip  Skip >> Lift σ
  LiftLift>>Skip
    = cong₂ Snoc (trans Lift>>Weaken
        (sym (trans Skip>>Weaken (cong Weaken (sym Snoc>>Wk))))) refl