{-# OPTIONS --safe --without-K #-}
module IK.Term.Properties where

-- Welcome to the hell of mind-numbing syntactic lemmas.
-- No good ever comes from proving these lemmas, but no
-- good can happen without proving them.

open import Data.Product using (Σ ;  ; _×_ ; _,_ ; proj₁ ; proj₂)

open import Relation.Binary.PropositionalEquality using (_≡_ ; refl ; sym ; trans ; cong ; cong₂ ; subst ; subst-application)

open import PEUtil

open import IK.Term.Base
open import IK.Term.Reduction

--------------------
-- Substitution laws
--------------------

-- NOTE: these are only the laws that follow directly from the structure of substitutions
coh-trimSub-wkVar : (x : Var Γ a) (s : Sub Δ' Δ) (w : Γ  Δ)
   substVar (trimSub w s) x  substVar s (wkVar w x)
coh-trimSub-wkVar zero     (s `, x)  (drop w)
  = coh-trimSub-wkVar zero s w
coh-trimSub-wkVar zero     (s `, x)  (keep w)
  = refl
coh-trimSub-wkVar (succ x) (s `, x₁) (drop w)
  = coh-trimSub-wkVar (succ x) s w
coh-trimSub-wkVar (succ x) (s `, x₁) (keep w)
  = coh-trimSub-wkVar x s w

-- `trimSub` preserves the identity
trimSubPresId : (s : Sub Δ Γ)  trimSub idWk s  s
trimSubPresId []         = refl
trimSubPresId (s `, x)   = cong (_`, _) (trimSubPresId s)
trimSubPresId (lock s x) = cong₂ lock (trimSubPresId s) refl

-- naturality of substVar
nat-substVar : (x : Var Γ a) (s : Sub Δ Γ) (w : Δ  Δ')
   substVar (wkSub w s) x  wkTm w (substVar s x)
nat-substVar zero     (s `, t) w = refl
nat-substVar (succ x) (s `, t) w = nat-substVar x s w

-- naturality of trimSub
nat-trimSub : (s : Sub Γ Δ) (w : Δ'  Δ) (w' : Γ  Γ')
   wkSub w' (trimSub w s)  trimSub w (wkSub w' s)
nat-trimSub []         base      w' = refl
nat-trimSub (s `, t)   (drop w)  w' = nat-trimSub s w w'
nat-trimSub (s `, t)   (keep w)  w' = cong (_`, wkTm w' t) (nat-trimSub s w w')
nat-trimSub (lock s x) (keep# w) w' = cong₂ lock (nat-trimSub s w _) refl

-- `trimSub` on the identity substitution embeds the weakening
trimSubId : (w : Γ  Δ)  trimSub w idₛ  embWk w
trimSubId base      = refl
trimSubId (drop w)  = trans
  (sym (nat-trimSub idₛ w fresh))
  (cong (wkSub fresh) (trimSubId w))
trimSubId (keep w)  = cong (_`, var zero) (trans
  (sym (nat-trimSub idₛ w fresh))
  (cong (wkSub fresh) (trimSubId w)))
trimSubId (keep# w) = cong₂ lock (trimSubId w) refl

---------------
-- Functor laws
---------------

-- Standard, standard, standard, oh wait...

wkTmPresId : (t : Tm Γ a)  wkTm idWk t  t
wkTmPresId (var   v)   = cong  var (wkVarPresId v)
wkTmPresId (lam   t)   = cong  lam (wkTmPresId  t)
wkTmPresId (app   t u) = cong₂ app (wkTmPresId  t) (wkTmPresId u)
wkTmPresId (box   t)   = cong  box (wkTmPresId  t)
wkTmPresId (unbox t e) with ←#IsPre# e | #→IsPost# e
wkTmPresId (unbox t e) | refl | refl = cong₂ unbox
  (trans (cong₂ wkTm (sliceLeftId e) refl) (wkTmPresId t))
  (wkLFExtPresId e)

wkSubPresId : (s : Sub Γ Δ)  wkSub idWk s  s
wkSubPresId []       = refl
wkSubPresId (s `, t) = cong₂ _`,_ (wkSubPresId s) (wkTmPresId t)
wkSubPresId (lock s e) with ←#IsPre# e | #→IsPost# e
... | refl | refl = cong₂ lock
  (trans (cong₂ wkSub (sliceLeftId e) refl) (wkSubPresId s))
  (wkLFExtPresId e)

-- weakening of terms (a functor map) preserves weakening composition
wkTmPres∙ : (w : Γ  Γ') (w' : Γ'  Δ) (t : Tm Γ a)
   wkTm w' (wkTm w t)  wkTm (w  w') t
wkTmPres∙ w w' (var   v)   = cong  var (wkVarPres∙ w w' v)
wkTmPres∙ w w' (lam   t)   = cong  lam (wkTmPres∙ (keep w) (keep  w') t)
wkTmPres∙ w w' (app   t u) = cong₂ app (wkTmPres∙ w w' t) (wkTmPres∙ w w' u)
wkTmPres∙ w w' (box   t)   = cong  box (wkTmPres∙ (keep# w) (keep# w') t)
wkTmPres∙ w w' (unbox t e) = cong₂ unbox
  (trans (wkTmPres∙ _ _ _) (cong₂ wkTm (sliceLeftPres∙ w w' e) refl)) (wkLFExtPres∙  w w' e)

-- weakening of substitutions preserves weakening compisition
wkSubPres∙ : (w : Γ  Γ') (w' : Γ'  Δ) (s : Sub Γ ΓR)
   wkSub w' (wkSub w s)  wkSub (w  w') s
wkSubPres∙ w w' []         = refl
wkSubPres∙ w w' (s `, t)   = cong₂ _`,_ (wkSubPres∙ w w' s) (wkTmPres∙ w w' t)
wkSubPres∙ w w' (lock s e) = cong₂ lock
  (trans  (wkSubPres∙ _ _ s) (cong₂ wkSub (sliceLeftPres∙ w w' e) refl))
  (wkLFExtPres∙ w w' e)

private
  wkSubFreshLemma : {s : Sub Δ Γ} {w : Δ  Δ'}
     wkSub fresh[ a ] (wkSub w s)  wkSub (keep w) (dropₛ s)
  wkSubFreshLemma {s = s} {w} = trans (wkSubPres∙ w fresh s) (trans
    (cong₂ wkSub (cong drop (rightIdWk _)) refl )
    (sym (trans
      (wkSubPres∙ _ _ _)
      (cong₂ wkSub (cong drop (leftIdWk _)) refl))))

nat-subsTm : (t : Tm Γ a) (s : Sub Δ Γ) (w : Δ  Δ')
   substTm (wkSub w s) t  wkTm w (substTm s t)
nat-subsTm (var x)           s          w
  = nat-substVar x s w
nat-subsTm (lam {Γ} {a} t)   s          w
  = cong lam
    (trans (cong  s  substTm (s `, var zero) t) wkSubFreshLemma)
    (nat-subsTm t (keepₛ s) (keep w)))
nat-subsTm (app t u)         s          w
  = cong₂ app (nat-subsTm t s w) (nat-subsTm u s w)
nat-subsTm (box t)           s          w
  = cong box (nat-subsTm t (lock s nil) (keep# w))
nat-subsTm (unbox t nil)     (lock s x) w
  = cong₂ unbox (nat-subsTm t s _) refl
nat-subsTm (unbox t (ext x)) (s `, _)   w
  = nat-subsTm (unbox t x) s w

-- weakening a substitution composition is the same as weakening the first sub
-- (s ∙ₛ s') ₛ∙ₑ w ≡ s ∙ₛ (s' ₛ∙ₑ w)
coh-wkSub-∙ₛ  : {Δ'' : Ctx} (s : Sub Δ Γ) (s' : Sub Δ' Δ) (w : Δ'  Δ'')
          wkSub w (s ∙ₛ s')  s ∙ₛ (wkSub w s')
coh-wkSub-∙ₛ []           s'             w
  = refl
coh-wkSub-∙ₛ (s `, x)     s'             w
  = cong₂ _`,_  (coh-wkSub-∙ₛ s s' w) (sym (nat-subsTm x s' w))
coh-wkSub-∙ₛ (lock s nil) (lock s' x)    w
  = cong₂ lock (coh-wkSub-∙ₛ s s' _) refl
coh-wkSub-∙ₛ (lock s (ext x)) (s' `, x₁) w
  = coh-wkSub-∙ₛ (lock s x) s' w

-- applying a trimmed substitution is the same as weakening and substituting
coh-trimSub-wkTm : (t : Tm Γ a) (s : Sub Δ' Δ) (w : Γ  Δ)
   substTm (trimSub w s) t  substTm s (wkTm w t)
coh-trimSub-wkTm (var x)           s          w
  = coh-trimSub-wkVar x s w
coh-trimSub-wkTm (lam t)           s          w
  = cong lam (trans
    (cong  p  substTm (p `, var zero) t) (nat-trimSub s w fresh))
    (coh-trimSub-wkTm t (keepₛ s) (keep w)))
coh-trimSub-wkTm (app t u)         s          w
  = cong₂ app (coh-trimSub-wkTm t s w) (coh-trimSub-wkTm u s w)
coh-trimSub-wkTm (box t)           s          w
  = cong box (coh-trimSub-wkTm t (lock s nil) (keep# w))
coh-trimSub-wkTm (unbox t nil)     (s `, x)   (drop w)
  = coh-trimSub-wkTm (unbox t nil) s w
coh-trimSub-wkTm (unbox t (ext e)) (s `, x)   (drop w)
  = coh-trimSub-wkTm (unbox t (ext e)) s w
coh-trimSub-wkTm (unbox t (ext e)) (s `, x)   (keep w)
  = coh-trimSub-wkTm (unbox t e) s w
coh-trimSub-wkTm (unbox t nil)     (lock s x) (keep# w)
  = cong₂ unbox (coh-trimSub-wkTm t s w) refl

-- trimming the first sub in a composition is the same as weakening the second
-- s ∙ₛ (w ₑ∙ₛ s') ≡ (s ₛ∙ₑ w) ∙ₛ s'
coh-trimSub-wkSub  : {Δ₁ : Ctx} (s : Sub Δ Γ) (s' : Sub Δ₁ Δ') (w : Δ  Δ')
          s ∙ₛ (trimSub w s')  (wkSub w s) ∙ₛ s'
coh-trimSub-wkSub []               s'          w
  = refl
coh-trimSub-wkSub (s `, x)         s'          w
  = cong₂ _`,_ (coh-trimSub-wkSub s s' w) (coh-trimSub-wkTm x s' w)
coh-trimSub-wkSub (lock s nil)     (s' `, x)   (drop w)
  = coh-trimSub-wkSub (lock s nil) s' w
coh-trimSub-wkSub (lock s nil)     (lock s' x) (keep# w)
  = cong₂ lock (coh-trimSub-wkSub s s' w) refl
coh-trimSub-wkSub (lock s (ext e)) (s' `, x₁)  (drop w)
  = coh-trimSub-wkSub (lock s (ext e)) s' w
coh-trimSub-wkSub (lock s (ext x)) (s' `, x₁)  (keep w)
  = coh-trimSub-wkSub (lock s x) s' w

-- parallel substitution (substVar) preserves substitution composition
substVarPres∙ : (s : Sub Γ' Γ) (s' : Sub Δ Γ') (x : Var Γ a)
   substTm s' (substVar s x)  substVar (s ∙ₛ s') x
substVarPres∙ (s `, x) s' zero      = refl
substVarPres∙ (s `, x) s' (succ x₁) = substVarPres∙ s s' x₁

private
  dropKeepLemma : (s' : Sub Δ' Δ) (s : Sub Γ Δ')
             dropₛ (s' ∙ₛ s)  dropₛ {a = a} s' ∙ₛ keepₛ s
  dropKeepLemma s' s = trans (coh-wkSub-∙ₛ s' s fresh)
    (trans
      ((cong (s' ∙ₛ_) (sym (trimSubPresId (dropₛ s)))))
      (coh-trimSub-wkSub s' (keepₛ s) fresh))

substVarPresId : (x : Var Γ a)  substVar idₛ x  var x
substVarPresId zero     = refl
substVarPresId (succ x) = trans (nat-substVar x idₛ fresh) (trans
  (cong (wkTm fresh) (substVarPresId x))
  (cong var (wkIncr x)))

module _ {e : LFExt Δ (Γ #) ΓR} {e' : LFExt Δ (Γ' #) ΓR'} where
  cong-unbox≡ : (Γ≡Γ' : Γ  Γ')
     (t≡t' : subst  Γ  Tm Γ ( a)) Γ≡Γ' t  t')
     unbox t e  unbox t' e'
  cong-unbox≡ Γ≡Γ' t≡t' =
    idcong₄ unbox Γ≡Γ' (extRUniq′ (cong _# Γ≡Γ') e e') t≡t' (ExtIsProp _ _)

cong-unbox≡1 : (Γ≡Γ' : Γ  Γ')
   (t≡t' : subst  Γ  Tm Γ ( a)) Γ≡Γ' t  t')
   unbox t e  unbox t' e'
cong-unbox≡1 = cong-unbox≡

cong-unbox≡2 : unbox t e  unbox t e'
cong-unbox≡2 = cong-unbox≡ refl refl

module _ {Γ : Ctx} {a : Ty} where
  unbox-natural : (t : Tm Γ ( a))
     (e : LFExt Δ (Γ #) ΓR)
     (w : LFExt Δ' Δ ΔR)
     unbox t (e ∙Ext w)  wkTm (LFExtToWk w) (unbox t e)
  unbox-natural t e w with ←#IsPre# e | #→IsPost# e
  unbox-natural t e w | refl | refl = cong-unbox≡ (←#IsPre# w)
    (trans (cong˘ (subst  Γ  Tm Γ ( a)) (←#IsPre# w)) (wkTmPresId t))
      (trans (subst-application′  w  wkTm w t) (←#IsPre# w))
        (cong˘  w  wkTm w t) (sliceLeftDrop e w))))

unbox-universal : (t : Tm Γ ( a))
   (e : LFExt Δ (Γ #) ΓR)
   unbox t e  wkTm (LFExtToWk e) (unbox t new)
unbox-universal t e = trans cong-unbox≡2 (unbox-natural t new e)

substTmPresId : (t : Tm Γ a)  substTm idₛ t  t
substTmPresId (var x)       = substVarPresId x
substTmPresId (lam t)       = cong lam (substTmPresId t)
substTmPresId (app t u)     = cong₂ app (substTmPresId t) (substTmPresId u)
substTmPresId (box t)       = cong box (substTmPresId t)
substTmPresId (unbox t nil) = cong₂ unbox (substTmPresId t) refl
substTmPresId {Γ = Γ `, a} (unbox t (ext e)) with ←#IsPre# e | #→IsPost# e | substTmPresId (unbox t e)
... | refl | refl | rec = trans (nat-subsTm (unbox t e) idₛ fresh)
    (trans˘
      (cong (wkTm fresh) rec)
      (unbox-natural t e (ext nil)))

-- NOTE: the `rec` here is a hack required for/after the 2.6.1 update
-- c.f. "Termination checking" in http://hackage.haskell.org/package/Agda-2.6.1/changelog

-- parallel substitution (substTm) preserves substitution composition
substTmPres∙ : (s : Sub Γ' Γ) (s' : Sub Δ Γ') (t : Tm Γ a)
   substTm s' (substTm s t)  substTm (s ∙ₛ s') t
substTmPres∙ s s'             (var x)
  = substVarPres∙ s s' x
substTmPres∙ s s'             (lam t)
  = cong lam
    (trans (substTmPres∙ _ _ t)
    (cong ((λ s  substTm (s `, var zero) t)) (sym (dropKeepLemma s s'))))
substTmPres∙ s s'             (app t u)
  = cong₂ app (substTmPres∙ s s' t) (substTmPres∙ s s' u)
substTmPres∙ s s'             (box t)
  = cong box (substTmPres∙ (lock s nil) (lock s' nil) t)
substTmPres∙ (lock s nil)     (lock s' nil) (unbox t nil)
  = cong₂ unbox (substTmPres∙ s s' t) refl
substTmPres∙ (lock s nil)     (lock s' (ext x)) (unbox t nil)
  = cong₂ unbox (substTmPres∙ s s' t) refl
substTmPres∙ (lock s (ext e)) (s' `, _) (unbox t nil)
  = substTmPres∙ (lock s e) s' (unbox t nil)
substTmPres∙ (lock s nil) (lock s' _) (unbox t nil)
  = cong₂ unbox (substTmPres∙ s s' t) refl
substTmPres∙ (s `, e) s' (unbox t (ext e'))
  = substTmPres∙ s s' (unbox t e')

rightIdSub : (s : Sub Γ Γ')  s ∙ₛ idₛ  s
rightIdSub []           = refl
rightIdSub (s `, t)     = cong₂ _`,_ (rightIdSub s) (substTmPresId t)
rightIdSub (lock s nil) = cong₂ lock (rightIdSub s) refl
rightIdSub (lock s (ext e)) with ←#IsPre# e | #→IsPost# e
... | refl | refl = trans
  (sym (coh-wkSub-∙ₛ (lock s e) _ _))
  (trans
    (cong (wkSub fresh) (rightIdSub (lock s e)))
    (trans
      (cong₂ lock (cong₂ wkSub (sliceLeftId e) refl) (cong ext (wkLFExtPresId e)))
      (cong₂ lock (wkSubPresId s) refl)))


-- substitution composition is associative
assocSub : {Γ1 Γ2 Γ3 Γ4 : Ctx}  (s3 : Sub Γ3 Γ4) (s2 : Sub Γ2 Γ3)  (s1 : Sub Γ1 Γ2)
   (s3 ∙ₛ s2) ∙ₛ s1  s3 ∙ₛ (s2 ∙ₛ s1)
assocSub []                s2         s1
  = refl
assocSub (s3 `, t)         s2         s1
  = cong₂ _`,_ (assocSub s3 s2 s1) (substTmPres∙ s2 s1 t)
assocSub (lock s3 nil)     (lock s2 nil)     (lock s1 e)
  = cong  p  lock p e) (assocSub s3 s2 s1)
assocSub (lock s3 nil)     (lock s2 (ext e)) (s1 `, _)
  = assocSub (lock s3 nil) (lock s2 e) s1
assocSub (lock s3 (ext e)) (s2 `, _) s1
  = assocSub (lock s3 e) s2 s1


private
  -- just a helper to reduce redundancy, nothing too interesting
  auxLemma : (w : Γ  Δ)  wkSub (drop[ a ] (w  idWk)) idₛ  dropₛ (embWk w)

wkSubId : (w : Γ  Δ)  wkSub w idₛ  embWk w

auxLemma w = (trans
    (sym (wkSubPres∙ w fresh idₛ))
    (cong (wkSub fresh) (wkSubId w)))

wkSubId base      = refl
wkSubId (drop w)  = trans
  (cong  w'  wkSub (drop w') idₛ) (sym (rightIdWk w)))
  (auxLemma w)
wkSubId (keep w)  = cong (_`, var zero) (trans
  (wkSubPres∙ fresh (keep w) idₛ)
  (trans
    (cong₂ wkSub (cong drop (trans (leftIdWk _) (sym (rightIdWk _)))) refl)
    (auxLemma w)))
wkSubId (keep# w) = cong₂ lock (wkSubId w) refl

-- Outcast lemmas

keepFreshLemma : {w : Γ  Γ'} {t : Tm Γ a}
   wkTm fresh[ b ] (wkTm w t)  wkTm (keep w) (wkTm fresh t)
keepFreshLemma = trans (wkTmPres∙ _ _ _) (sym (trans
    (wkTmPres∙ _ _ _)
    (cong₂ wkTm (cong drop (trans (leftIdWk _) (sym (rightIdWk _)))) refl)))

sliceCompLemma : (w : Γ  Δ) (e : LFExt Γ (ΓL #) ΓR) (t : Tm (ΓL #) a)
   wkTm (LFExtToWk (wkLFExt e w)) (wkTm (keep# (sliceLeft e w)) t) 
      wkTm w (wkTm (LFExtToWk e) t)
sliceCompLemma w e t = (trans (wkTmPres∙ _ _ _) (sym (trans
  (wkTmPres∙ _ _ _)
  (cong₂ wkTm (slicingLemma w e) refl))))

beta-wk-lemma : (w  : Γ  Δ) (u : Tm Γ a) (t : Tm (Γ `, a) b)
   substTm (idₛ `, wkTm w u) (wkTm (keep w) t)  wkTm w (substTm (idₛ `, u) t)
beta-wk-lemma w u t = trans
  (sym (coh-trimSub-wkTm t _ (keep w)))
  (sym (trans
    (sym (nat-subsTm t _ _))
    (cong
       p  substTm (p `, wkTm w u) t)
      (sym (trans (trimSubId w) (sym (wkSubId w)))))))

-----------------------------------
--- Reduction and conversion lemmas
-----------------------------------

wkTmPres⟶ :  {t t' : Tm Γ a}
   (w : Γ  Δ)
   t  t'
   wkTm w t  wkTm w t'
wkTmPres⟶ w (red-fun {t = t} {u = u})
  = step-≡ red-fun (beta-wk-lemma w u t)
wkTmPres⟶ w exp-fun
  = step-≡ exp-fun (cong lam (cong₂ app keepFreshLemma refl))
wkTmPres⟶ w (red-box {e = e})
  = step-≡ red-box (sliceCompLemma w e _)
wkTmPres⟶ w exp-box
  = exp-box
wkTmPres⟶ w (cong-lam r)
  = cong-lam (wkTmPres⟶ (keep w) r)
wkTmPres⟶ w (cong-box r)
  = cong-box (wkTmPres⟶ (keep# w) r)
wkTmPres⟶ w (cong-unbox r)
  = cong-unbox (wkTmPres⟶ (sliceLeft _ w) r)
wkTmPres⟶ w (cong-app1 r)
  = cong-app1 (wkTmPres⟶ w r)
wkTmPres⟶ w (cong-app2 r)
  = cong-app2 (wkTmPres⟶ w r)

wkTmPres⟶* :  {t t' : Tm Γ a}
   (w : Γ  Δ)
   t ⟶* t'
   wkTm w t ⟶* wkTm w t'
wkTmPres⟶* w = cong-⟶-to-cong-⟶* (wkTmPres⟶ w)

Generated from commit 2fd14c996b195ef101dff8919e837907ca0a08aa.