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

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

import Relation.Binary.Reasoning.Setoid as SetoidReasoning

open import PEUtil
import RUtil

open import IS4.Term.Base
open import IS4.Term.Conversion
open import IS4.Term.Reduction

----------------------
-- Substitution lemmas
----------------------

-- Left context of weakening and applying a substituion
-- is the same as the
-- Left context of applying and then weakening it
lCtxₛ-wkSub-comm : (e : CExt Γ ΓL ΓR) (s : Sub Δ Γ) (w : Δ  Δ')
   lCtxₛ e (wkSub w s)  lCtx (factorExtₛ e s) w
lCtxₛ-wkSub-comm nil       _s          _w = refl
lCtxₛ-wkSub-comm (ext   e) (s `, _t)   w  = lCtxₛ-wkSub-comm e s w
lCtxₛ-wkSub-comm (ext#- e) (lock s e') w  = trans˘
  (lCtxₛ-wkSub-comm e s (factorWk e' w))
  (lCtxPresTrans (factorExtₛ e _) e' _)

-- Right context of weakening and applying a substituion
-- is the same as the
-- Right context of applying and then weakening it
rCtxₛ-wkSub-comm : (e : CExt Γ ΓL ΓR) (s  : Sub Δ Γ) (w : Δ  Δ')
   rCtxₛ e (wkSub w s)  rCtx (factorExtₛ e s) w
rCtxₛ-wkSub-comm nil       _s          _w = refl
rCtxₛ-wkSub-comm (ext   e) (s `, _t)   w  = rCtxₛ-wkSub-comm e s w
rCtxₛ-wkSub-comm (ext#- e) (lock s e') w  = trans˘
  (cong1 _,,_ (rCtxₛ-wkSub-comm e s (factorWk e' w)))
  (rCtxPresTrans (factorExtₛ e _) e' _)

-- Weakening and factoring a subtitution can be achieved by factoring and then weakening it
factorSubₛ-wkSub-comm : (e : CExt Γ ΓL ΓR) (s : Sub Δ Γ) (w : Δ  Δ')
   subst1 Sub (lCtxₛ-wkSub-comm e s w) (factorSubₛ e (wkSub w s))  wkSub (factorWk (factorExtₛ e s) w) (factorSubₛ e s)
factorSubₛ-wkSub-comm nil       _s          _w = refl
factorSubₛ-wkSub-comm (ext   e) (s `, _t)   w  = factorSubₛ-wkSub-comm e s w
factorSubₛ-wkSub-comm (ext#- e) (lock s e') w  = let open ≡-Reasoning in begin
  subst1 Sub
    (trans˘ (lCtxₛ-wkSub-comm e _ _) (lCtxPresTrans _ e' _))
    (factorSubₛ e (wkSub (factorWk e' w) s))
    -- split `subst _ (trans p q) ...` to `subst _ q (subst _ p ...)`
    ≡˘⟨ subst-subst (lCtxₛ-wkSub-comm e _ _) 
  subst1˘ Sub
    (lCtxPresTrans _ e' _)
    (subst1 Sub (lCtxₛ-wkSub-comm e _ _)
      (factorSubₛ e (wkSub (factorWk e' w) s)))
    -- rewrite inner subst
    ≡⟨ cong (subst1 Sub _) (factorSubₛ-wkSub-comm e s (factorWk e' w)) 
  subst1˘ Sub
    (lCtxPresTrans _ e' _)
    (wkSub (factorWk (factorExtₛ e s) (factorWk e' w)) (factorSubₛ e s))
    -- commute subst with application
    ≡⟨ subst˘-application1′ wkSub (lCtxPresTrans _ e' _) 
  wkSub
    (subst˘ (_ ⊆_)
      (lCtxPresTrans _ e' _)
      (factorWk (factorExtₛ e s) (factorWk e' w)))
    (factorSubₛ e s)
    -- apply factorWkPresTrans
    ≡˘⟨ cong1 wkSub (subst-sym (lCtxPresTrans _ e' _) (factorWkPresTrans (factorExtₛ e s) e' w)) 
  wkSub (factorWk (extRAssoc (factorExtₛ e s) e') w) (factorSubₛ e s) 

lCtxₛ-factorExt-trimSub-assoc : (e : CExt Γ ΓL ΓR) (s : Sub Δ' Δ) (w : Γ  Δ)
    lCtxₛ e (trimSub w s)  lCtxₛ (factorExt e w) s
lCtxₛ-factorExt-trimSub-assoc nil       s          w
  = refl
lCtxₛ-factorExt-trimSub-assoc (ext e)   (s `, _)   (drop w)
  = lCtxₛ-factorExt-trimSub-assoc (ext e) s w
lCtxₛ-factorExt-trimSub-assoc (ext e)   (s `, _)   (keep w)
  = lCtxₛ-factorExt-trimSub-assoc e s w
lCtxₛ-factorExt-trimSub-assoc (ext#- e) (s `, t)   (drop w)
  = lCtxₛ-factorExt-trimSub-assoc (ext#- e) s w
lCtxₛ-factorExt-trimSub-assoc (ext#- e) (lock s _) (keep# w)
  = lCtxₛ-factorExt-trimSub-assoc e s w

rCtxₛ-factorExt-trimSub-assoc : (e : CExt Γ ΓL ΓR) (s : Sub Δ' Δ) (w : Γ  Δ)
    rCtxₛ e (trimSub w s)  rCtxₛ (factorExt e w) s
rCtxₛ-factorExt-trimSub-assoc nil       s          w
  = refl
rCtxₛ-factorExt-trimSub-assoc (ext e)   (s `, t)   (drop w)
  = rCtxₛ-factorExt-trimSub-assoc (ext e) s w
rCtxₛ-factorExt-trimSub-assoc (ext e)   (s `, t)   (keep w)
  = rCtxₛ-factorExt-trimSub-assoc e s w
rCtxₛ-factorExt-trimSub-assoc (ext#- e) (s `, t)   (drop w)
  = rCtxₛ-factorExt-trimSub-assoc (ext#- e) s w
rCtxₛ-factorExt-trimSub-assoc (ext#- e) (lock s _) (keep# w)
  = cong (_,, _) (rCtxₛ-factorExt-trimSub-assoc e s w)

factorSubₛ-trimSub-comm : (e : CExt Γ ΓL ΓR) (s : Sub Δ' Δ) (w : Γ  Δ)
   subst1 Sub (lCtxₛ-factorExt-trimSub-assoc e s w) (factorSubₛ e (trimSub w s))  trimSub (factorWk e w) (factorSubₛ (factorExt e w) s)
factorSubₛ-trimSub-comm nil       s          w
  = refl
factorSubₛ-trimSub-comm (ext e)   (s `, _)   (drop w)
  = factorSubₛ-trimSub-comm (ext e) s w
factorSubₛ-trimSub-comm (ext e)   (s `, _)   (keep w)
  = factorSubₛ-trimSub-comm e s w
factorSubₛ-trimSub-comm (ext#- e) (s `, t)   (drop w)
  = factorSubₛ-trimSub-comm (ext#- e) s w
factorSubₛ-trimSub-comm (ext#- e) (lock s _) (keep# w)
  = factorSubₛ-trimSub-comm e s w

---------------------------------------------
-- Factorisation of the identity substitution
---------------------------------------------

←#₁rCtx : (e : CExt Γ ΓL ΓR)  Ctx
←#₁rCtx nil             = []
←#₁rCtx (ext {a = a} e) = ←#₁rCtx e ,, rCtx′ (factorExtₛ e idₛ) freshExt[ a ]
←#₁rCtx (ext#- e)       = ←#₁rCtx e

private

  ex : {a b c : Ty}  CExt (ΓL `, a `, b # `, c #) ΓL ([] `, a `, b # `, c #)
  ex {Γ} {a} {b} {c} = ext#- (ext[ c ] (ext#- (ext[ b ] (ext[ a ] nil))))

  _ : ←#₁rCtx (ex {ΓL} {c = c})  [] `, a `, b
  _ = refl

-- Given `e` that ΓL extends Γ, ΓL is a lock-free extension of `lCtxₛ e idₛ`.
-- This means that ΓL ⊆ (lCtxₛ e idₛ), and thus applying `factorSubₛ e idₛ` weakens
-- a term with variables in `←#₁rCtx e`
factorSubₛIdWk : (e : CExt Γ ΓL ΓR)  LFExt (lCtxₛ e idₛ) ΓL (←#₁rCtx e)
factorSubₛIdWk                nil              = nil
factorSubₛIdWk {ΓR = ΓR `, a} (ext {a = .a} e) = subst˘
   Γ  LFExt Γ _ (←#₁rCtx (ext e))) (lCtxₛ-wkSub-comm e idₛ fresh)
  (extRAssoc (factorSubₛIdWk e) (factorDropsWk (factorExtₛ e idₛ) freshExt))
factorSubₛIdWk                (ext#- e)        = factorSubₛIdWk e

-- Obs: Deliberately named _Wk instead of _LFExt

--------------------
-- 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 `, t)   = cong1 _`,_ (trimSubPresId s)
trimSubPresId (lock s e) = cong1 lock (trimSubPresId s)

-- naturality of substVar
nat-substVar : (v : Var Γ a) (s : Sub Δ Γ) (w : Δ  Δ')
   substVar (wkSub w s) v  wkTm w (substVar s v)
nat-substVar zero     (s `, t) w = refl
nat-substVar (succ v) (s `, t) w = nat-substVar v 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' = cong1 _`,_ (nat-trimSub s w w')
nat-trimSub (lock s e) (keep# w) w' = cong1 lock (nat-trimSub s w (factorWk e w'))

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

---------------------------
-- Hell Of Syntactic Lemmas
---------------------------

-- 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.

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

cong-unbox≡ : (t≡t' : t  t')  unbox t e  unbox t' e
cong-unbox≡ = cong-unbox≡′′ refl

cong-unbox2≡ : unbox t e  unbox t e'
cong-unbox2≡ = cong-unbox≡′′ refl refl

cong-unbox≡′ : (t≡t' : t  t')  unbox t e  unbox t' e'
cong-unbox≡′ = cong-unbox≡′′ refl

module _ {e : CExt Δ Γ ΓR} {e' : CExt Δ Γ' ΓR'} where
  cong-lock≡′′ : (Γ≡Γ' : Γ  Γ')
     (s≡s' : subst1 Sub Γ≡Γ' s  s')
     lock s e  lock s' e'
  cong-lock≡′′ Γ≡Γ' s≡s' =
    idcong₄ lock Γ≡Γ' (extRUniq′ Γ≡Γ' e e') s≡s' (ExtIsProp _ _)

cong-lock≡ : (s≡s' : s  s')  lock s e  lock s' e
cong-lock≡ = cong-lock≡′′ refl

cong-lock2≡ : lock s e  lock s e'
cong-lock2≡ = cong-lock≡′′ refl refl

cong-lock≡′ : (s≡s' : s  s')  lock s e  lock s' e'
cong-lock≡′ = cong-lock≡′′ refl

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) = let open ≡-Reasoning in begin
  wkTm idWk (unbox t e)
    ≡⟨⟩
  unbox (wkTm (factorWk e idWk) t) (factorExt e idWk)
    ≡⟨ cong-unbox≡′′
         (lCtxPresId e)
         (trans
           (subst-application1′ wkTm (lCtxPresId e))
           (cong1 wkTm (factorWkPresId e)))
     
  unbox (wkTm idWk t) e
    ≡⟨ cong1 unbox (wkTmPresId t) 
  unbox t e 

wkSubPresId : (s : Sub Δ Γ)  wkSub idWk s  s
wkSubPresId []         = refl
wkSubPresId (s `, t)   = cong₂ _`,_ (wkSubPresId s) (wkTmPresId t)
wkSubPresId (lock s e) = let open ≡-Reasoning in begin
  wkSub idWk (lock s e)
    ≡⟨⟩
  lock (wkSub (factorWk e idWk) s) (factorExt e idWk)
    ≡⟨ cong-lock≡′′
         (lCtxPresId e)
         (trans
           (subst-application1′ wkSub (lCtxPresId e))
           (cong1 wkSub (factorWkPresId e)))
     
  lock (wkSub idWk s) e
    ≡⟨ cong1 lock (wkSubPresId s) 
  lock s e 

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) = let open ≡-Reasoning in begin
  wkTm w' (wkTm w (unbox t e))
    ≡⟨⟩
  unbox
    (wkTm (factorWk (factorExt e w) w') (wkTm (factorWk e w) t))
    (factorExt (factorExt e w) w')
    ≡⟨ cong-unbox≡′ (wkTmPres∙ _ _ t) 
  unbox
    (wkTm (factorWk e w  factorWk (factorExt e w) w') t)
    (factorExt (factorExt e w) w')
    ≡˘⟨ cong-unbox≡′′
          (lCtxPres∙ e w w')
          (trans
            (subst-application1′ wkTm (lCtxPres∙ e w w'))
            (cong1 wkTm (factorWkPres∙ e w w')))
      
  unbox
    (wkTm (factorWk e (w  w')) t)
    (factorExt e (w  w'))
    ≡⟨⟩
  wkTm (w  w') (unbox t e) 

wkSubPres∙ : (w : Δ  Δ') (w' : Δ'  Δ'') (s : Sub Δ Γ)
   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) = let open ≡-Reasoning in begin
  wkSub w' (wkSub w (lock s e))
    ≡⟨⟩
  lock
    (wkSub (factorWk (factorExt e w) w') (wkSub (factorWk e w) s))
    (factorExt (factorExt e w) w')
    ≡⟨ cong1 lock (wkSubPres∙ _ _ _ ) 
  lock
    (wkSub (factorWk e w  factorWk (factorExt e w) w') s)
    (factorExt (factorExt e w) w')
    ≡˘⟨ cong-lock≡′′
          (lCtxPres∙ e w w')
          (trans
            (subst-application1′ wkSub (lCtxPres∙ e w w'))
            (cong1 wkSub (factorWkPres∙ e w w')))
     
  lock
    (wkSub (factorWk e (w  w')) s)
    (factorExt e (w  w'))
    ≡⟨⟩
  wkSub (w  w') (lock s 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˘
      (cong1 wkSub (cong drop (rightIdWk _)) )
      (trans
        (wkSubPres∙ _ _ _)
        (cong1 wkSub (cong drop (leftIdWk _)))))

nat-substTm : (t : Tm Γ a) (s : Sub Δ Γ) (w : Δ  Δ')
   substTm (wkSub w s) t  wkTm w (substTm s t)
nat-substTm (var   v)   s w
  = nat-substVar v s w
nat-substTm (lam   t)   s w
  = cong lam
    (trans (cong  s  substTm (s `, var zero) t) wkSubFreshLemma)
    (nat-substTm t (keepₛ s) (keep w)))
nat-substTm (app   t u) s w
  = cong₂ app (nat-substTm t s w) (nat-substTm u s w)
nat-substTm (box   t)   s w
  = cong box (nat-substTm t (lock s (ext#- nil)) (keep# w))
nat-substTm (unbox t e) s w
  = let open ≡-Reasoning in begin
      substTm (wkSub w s) (unbox t e)
        ≡⟨⟩
      unbox
        (substTm (factorSubₛ e (wkSub w s)) t)
        (factorExtₛ e (wkSub w s))
        ≡⟨ cong-unbox≡′′
             (lCtxₛ-wkSub-comm e s w)
             (trans
               (subst-application1′ substTm {z = t} (lCtxₛ-wkSub-comm e s w))
               (cong1 substTm {y = t} (factorSubₛ-wkSub-comm e s w)))
         
     unbox
        (substTm (wkSub (factorWk (factorExtₛ e s) w) (factorSubₛ e s)) t)
        (factorExt (factorExtₛ e s) w)
        ≡⟨ cong-unbox≡ (nat-substTm t _ _) 
      unbox
        (wkTm (factorWk (factorExtₛ e s) w) (substTm (factorSubₛ e s) t))
        (factorExt (factorExtₛ e s) w)
        ≡⟨⟩
      wkTm w (substTm s (unbox t e)) 

coh-wkSub-∙ₛ  : {Δ'' : Ctx} (s : Sub Δ Γ) (s' : Sub Δ' Δ) (w : Δ'  Δ'')
          wkSub w (s ∙ₛ s')  s ∙ₛ wkSub w s'
coh-wkSub-∙ₛ []         _s' _w = refl
coh-wkSub-∙ₛ (s `, t)   s'  w  = cong₂ _`,_ (coh-wkSub-∙ₛ s s' w) (sym (nat-substTm t s' w))
coh-wkSub-∙ₛ (lock s e) s'  w  = let open ≡-Reasoning in begin
  wkSub w (lock s e ∙ₛ s')
    ≡⟨⟩
  lock
    (wkSub (factorWk (factorExtₛ e s') w) (s ∙ₛ factorSubₛ e s'))
    (factorExt (factorExtₛ e s') w)
    -- apply IH
    ≡⟨ cong1 lock (coh-wkSub-∙ₛ _ _ _) 
 lock
   (s ∙ₛ wkSub (factorWk (factorExtₛ e s') w) (factorSubₛ e s'))
   (factorExt (factorExtₛ e s') w)
   -- applying factoring equalities
   ≡˘⟨ cong-lock≡′′
         (lCtxₛ-wkSub-comm e s' w)
         (trans
           (subst-application′ (s ∙ₛ_) (lCtxₛ-wkSub-comm e s' w))
           (cong (s ∙ₛ_) (factorSubₛ-wkSub-comm e s' w))) 
 lock
   (s ∙ₛ factorSubₛ e (wkSub w s'))
   (factorExtₛ e (wkSub w s'))
   ≡⟨⟩
 lock s e ∙ₛ wkSub w s' 

coh-trimSub-wkTm : (t : Tm Γ a) (s : Sub Δ' Δ) (w : Γ  Δ)
   substTm (trimSub w s) t  substTm s (wkTm w t)
coh-trimSub-wkTm (var   v)   s w
  = coh-trimSub-wkVar v 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 (ext#- nil)) (keep# w))
coh-trimSub-wkTm (unbox t e) s w
  = let open ≡-Reasoning in begin
      substTm (trimSub w s) (unbox t e)
        ≡⟨⟩
      unbox
        (substTm (factorSubₛ e (trimSub w s)) t)
        (factorExtₛ e (trimSub w s))
        -- apply factoring equalities
        ≡⟨ cong-unbox≡′′
             (lCtxₛ-factorExt-trimSub-assoc e s w)
             (trans
               (subst-application1′ substTm {z = t} (lCtxₛ-factorExt-trimSub-assoc e s w))
               (cong1 substTm {y = t} (factorSubₛ-trimSub-comm e s w)))
         
      unbox
        (substTm (trimSub (factorWk e w) (factorSubₛ (factorExt e w) s)) t)
        (factorExtₛ (factorExt e w) s)
        -- aplpy IH
        ≡⟨ cong1 unbox (coh-trimSub-wkTm t _ _) 
      unbox
        (substTm (factorSubₛ (factorExt e w) s) (wkTm (factorWk e w) t))
        (factorExtₛ (factorExt e w) s)
        ≡⟨⟩
      substTm s (wkTm w (unbox t e)) 

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 `, t)   s'  w
  = cong₂ _`,_ (coh-trimSub-wkSub s s' w) (coh-trimSub-wkTm t s' w)
coh-trimSub-wkSub (lock s e) s'  w
  = let open ≡-Reasoning in begin
      lock s e ∙ₛ trimSub w s'
        ≡⟨⟩
      lock
        (s ∙ₛ factorSubₛ e (trimSub w s'))
        (factorExtₛ e (trimSub w s'))
        -- apply factoring equalities
        ≡⟨ cong-lock≡′′
             (lCtxₛ-factorExt-trimSub-assoc e s' w)
             (trans
               (subst-application′ (s ∙ₛ_) (lCtxₛ-factorExt-trimSub-assoc e s' w))
               (cong (s ∙ₛ_) (factorSubₛ-trimSub-comm e s' w)))
         
      lock
         (s ∙ₛ trimSub (factorWk e w) (factorSubₛ (factorExt e w) s'))
         (factorExtₛ (factorExt e w) s')
        -- apply IH
        ≡⟨ cong1 lock (coh-trimSub-wkSub _ _ _) 
      lock
        (wkSub (factorWk e w) s ∙ₛ factorSubₛ (factorExt e w) s')
        (factorExtₛ (factorExt e w) s')
        ≡⟨⟩
      wkSub w (lock s e) ∙ₛ s' 

lCtxₛPresTrans :  {ΓLL ΓLR : Ctx} (e : CExt ΓL ΓLL ΓLR) (e' : CExt Γ ΓL ΓR) (s : Sub Δ Γ)
   lCtxₛ e (factorSubₛ e' s)  lCtxₛ (extRAssoc e e') s
lCtxₛPresTrans _e nil        _s            = refl
lCtxₛPresTrans e  (ext   e') (s `, _t)     = lCtxₛPresTrans e e' s
lCtxₛPresTrans e  (ext#- e') (lock s _e'') = lCtxₛPresTrans e e' s

rCtxₛPresTrans :  {ΓLL ΓLR : Ctx} (e : CExt ΓL ΓLL ΓLR) (e' : CExt Γ ΓL ΓR) (s : Sub Δ Γ)
   rCtxₛ e (factorSubₛ e' s) ,, rCtxₛ e' s  rCtxₛ (extRAssoc e e') s
rCtxₛPresTrans _e nil        _s                      = refl
rCtxₛPresTrans e  (ext   e') (s `, _t)               = rCtxₛPresTrans e e' s
rCtxₛPresTrans e  (ext#- e') (lock {ΔR = ΔR} s _e'') = ˘trans (,,-assoc {ΓR = ΔR}) (cong (_,, ΔR) (rCtxₛPresTrans e e' s))

lCtxₛPres∙ₛ : (e : CExt Γ ΓL ΓR) (s : Sub Γ' Γ) (s' : Sub Δ Γ')
   lCtxₛ e (s ∙ₛ s')  lCtxₛ (factorExtₛ e s) s'
lCtxₛPres∙ₛ nil       _s          _s' = refl
lCtxₛPres∙ₛ (ext   e) (s `, _t)   s'  = lCtxₛPres∙ₛ e s s'
lCtxₛPres∙ₛ (ext#- e) (lock s e') s'  = trans (lCtxₛPres∙ₛ e _ _) (lCtxₛPresTrans _ e' _)

rCtxₛPres∙ₛ : (e : CExt Γ ΓL ΓR) (s : Sub Γ' Γ) (s' : Sub Δ Γ')
   rCtxₛ e (s ∙ₛ s')  rCtxₛ (factorExtₛ e s) s'
rCtxₛPres∙ₛ nil       _s          _s' = refl
rCtxₛPres∙ₛ (ext   e) (s `, _t)   s'  = rCtxₛPres∙ₛ e s s'
rCtxₛPres∙ₛ (ext#- e) (lock s e') s'  = trans (cong (_,, _) (rCtxₛPres∙ₛ e _ _)) (rCtxₛPresTrans _ e' _)

factorSubPresTrans :  {ΓLL ΓLR : Ctx} (e : CExt ΓL ΓLL ΓLR) (e' : CExt Γ ΓL ΓR) (s : Sub Δ Γ)
   subst1 Sub (lCtxₛPresTrans e e' s) (factorSubₛ e (factorSubₛ e' s))  factorSubₛ (extRAssoc e e') s
factorSubPresTrans _e nil        _s            = refl
factorSubPresTrans e  (ext   e') (s `, _t)     = factorSubPresTrans e e' s
factorSubPresTrans e  (ext#- e') (lock s _e'') = factorSubPresTrans e e' s

factorSubPres∙ₛ : (e : CExt Γ ΓL ΓR) (s : Sub Γ' Γ) (s' : Sub Δ Γ')
   subst1 Sub (lCtxₛPres∙ₛ e s s') (factorSubₛ e (s ∙ₛ s'))   factorSubₛ e s ∙ₛ factorSubₛ (factorExtₛ e s) s'
factorSubPres∙ₛ nil       _s          _s' = refl
factorSubPres∙ₛ (ext   e) (s `, _t)   s'  = factorSubPres∙ₛ e s s'
factorSubPres∙ₛ (ext#- e) (lock s e') s'  = let open ≡-Reasoning in begin
  subst1 Sub
    (lCtxₛPres∙ₛ (ext#- e) (lock s e') s')
    (factorSubₛ (ext#- e) (lock s e' ∙ₛ s'))
    ≡⟨⟩
  subst1 Sub
    (trans (lCtxₛPres∙ₛ e s (factorSubₛ e' s')) (lCtxₛPresTrans (factorExtₛ e s) e' s'))
    (factorSubₛ e (s ∙ₛ factorSubₛ e' s'))
    -- split `subst _ (trans p q) ...` to `subst _ q (subst _ p ...)`
    ≡˘⟨ subst-subst (lCtxₛPres∙ₛ e s (factorSubₛ e' s')) 
  subst1 Sub
    (lCtxₛPresTrans (factorExtₛ e s) e' s')
    (subst1 Sub
      (lCtxₛPres∙ₛ e s (factorSubₛ e' s'))
      (factorSubₛ e (s ∙ₛ factorSubₛ e' s')))
    -- rewrite (remove) inner subst with IH
    ≡⟨ cong (subst1 Sub _) (factorSubPres∙ₛ e s (factorSubₛ e' s')) 
  subst1 Sub
    (lCtxₛPresTrans (factorExtₛ e s) e' s')
    (factorSubₛ e s ∙ₛ factorSubₛ (factorExtₛ e s) (factorSubₛ e' s'))
    -- push subst inside application of (_ ∙ₛ_)
    ≡⟨ subst-application′ (factorSubₛ e s ∙ₛ_) (lCtxₛPresTrans (factorExtₛ e s) e' s') 
  factorSubₛ e s ∙ₛ subst1 Sub (lCtxₛPresTrans (factorExtₛ e s) e' s') (factorSubₛ (factorExtₛ e s) (factorSubₛ e' s'))
    -- apply factorSubPresTrans
    ≡⟨ cong (_ ∙ₛ_) (factorSubPresTrans (factorExtₛ e s) e' s') 
  factorSubₛ e s ∙ₛ factorSubₛ (extRAssoc (factorExtₛ e s) e') s'
    ≡⟨⟩
  factorSubₛ (ext#- e) (lock s e') ∙ₛ factorSubₛ (factorExtₛ (ext#- e) (lock s e')) s'   

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)))

-- 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' ∙ₛ_) (trimSubPresId (dropₛ s)))
      (coh-trimSub-wkSub s' (keepₛ s) fresh))

substTmPres∙ : (s : Sub Γ' Γ) (s' : Sub Δ Γ') (t : Tm Γ a)
   substTm s' (substTm s t)  substTm (s ∙ₛ s') t
substTmPres∙ s s' (var   v)  = substVarPres∙ s s' v
substTmPres∙ s s' (lam   t)  = cong lam
  (trans˘
    (substTmPres∙ (keepₛ s) (keepₛ s') t)
    (cong  s  substTm (s `, var zero) t) (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∙ (keep#ₛ s) (keep#ₛ s') t)
substTmPres∙ s s' (unbox t e) = let open ≡-Reasoning in begin
  substTm s' (substTm s (unbox t e))
    ≡⟨⟩
  unbox
    (substTm (factorSubₛ (factorExtₛ e s) s') (substTm (factorSubₛ e s) t))
    (factorExtₛ (factorExtₛ e s) s')
    -- apply IH
    ≡⟨ cong1 unbox (substTmPres∙ _ _ t) 
  unbox
    (substTm (factorSubₛ e s ∙ₛ factorSubₛ (factorExtₛ e s) s') t)
    (factorExtₛ (factorExtₛ e s) s')
    -- apply factoring equalities
    ≡˘⟨ cong-unbox≡′′
          (lCtxₛPres∙ₛ e s s')
          (trans
            (subst-application1′ substTm {z = t} (lCtxₛPres∙ₛ e s s'))
            (cong1 substTm {y = t} (factorSubPres∙ₛ e s s')))
      
  unbox
    (substTm (factorSubₛ e (s ∙ₛ s')) t)
    (factorExtₛ e (s ∙ₛ s'))
    ≡⟨⟩
  substTm (s ∙ₛ s') (unbox t e) 

assocSub :  (s : Sub Δ Γ) (s' : Sub Θ Δ) (s'' : Sub Ξ Θ)
   (s ∙ₛ s') ∙ₛ s''  s ∙ₛ (s' ∙ₛ s'')
assocSub []         _s' _s'' = refl
assocSub (s `, t)   s'  s''  = cong₂ _`,_ (assocSub s s' s'') (substTmPres∙ s' s'' t)
assocSub (lock s e) s'  s''  = let open ≡-Reasoning in begin
  (lock s e ∙ₛ s') ∙ₛ s''
    ≡⟨⟩
  lock
    ((s ∙ₛ factorSubₛ e s') ∙ₛ factorSubₛ (factorExtₛ e s') s'')
    (factorExtₛ (factorExtₛ e s') s'')
    -- apply IH
    ≡⟨ cong1 lock (assocSub _ _ _) 
  lock
    (s ∙ₛ factorSubₛ e s' ∙ₛ factorSubₛ (factorExtₛ e s') s'')
    (factorExtₛ (factorExtₛ e s') s'')
    -- apply factoring equalities
    ≡˘⟨ cong-lock≡′′
          (lCtxₛPres∙ₛ e s' s'')
          (trans
            (subst-application′ (s ∙ₛ_) (lCtxₛPres∙ₛ e s' s''))
            (cong (s ∙ₛ_) (factorSubPres∙ₛ e _ _ )))
      
  lock
    (s ∙ₛ factorSubₛ e (s' ∙ₛ s''))
    (factorExtₛ e (s' ∙ₛ s''))
    ≡⟨⟩
  lock s e ∙ₛ (s' ∙ₛ s'') 

leftIdSub : (s : Sub Γ Γ')  idₛ ∙ₛ s  s
leftIdSub []         = refl
leftIdSub (s `, t)   = let open ≡-Reasoning in begin
  idₛ ∙ₛ (s `, t)
    ≡⟨⟩
  wkSub fresh idₛ ∙ₛ (s `, t) `, t
    ≡˘⟨ cong (_`, _) (coh-trimSub-wkSub idₛ (s `, t) fresh) 
  idₛ ∙ₛ trimSub fresh (s `, t) `, t
    ≡⟨ cong (_`, _) (trans (leftIdSub _) (trimSubPresId _)) 
  (s `, t) 
leftIdSub (lock s e) = let open ≡-Reasoning in begin
  lock (idₛ ∙ₛ s) (extRAssoc nil e)
    ≡⟨ cong-lock≡′ (leftIdSub s) 
  lock s e 

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

private
  -- just a helper to reduce redundancy, nothing too interesting
  auxLemma : (w : Γ  Δ)  wkSub (drop[ a ] (w  idWk)) idₛ  dropₛ (embWk w)
  auxLemma w = ˘trans (wkSubPres∙ w fresh idₛ) (cong (wkSub fresh) (wkSubId w))

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

-- Outcast lemmas

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

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∙ _ _ _)
  (trans
    (wkTmPres∙ _ _ _)
    (cong1 wkTm (slicingLemma w e)))

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
  (coh-trimSub-wkTm t _ (keep w))
  (trans
    (cong
       p  substTm (p `, wkTm w u) t)
      (trans˘ (trimSubId w) (wkSubId w)))
    (nat-substTm t _ _))

-- factorising the identity substituion yields a weakening that only drops
factorSubₛIdWkIsFactorSubₛId : (e : CExt Γ ΓL ΓR)  factorSubₛ e idₛ  embWk (LFExtToWk (factorSubₛIdWk e))
factorSubₛIdWkIsFactorSubₛId nil             = refl
factorSubₛIdWkIsFactorSubₛId (ext#- e)       = factorSubₛIdWkIsFactorSubₛId e
factorSubₛIdWkIsFactorSubₛId (ext {a = a} e) = let open ≡-Reasoning in begin
  factorSubₛ e (wkSub fresh idₛ)
    -- apply `factorSubₛ-wkSub-comm`
    ≡⟨ subst-sym (lCtxₛ-wkSub-comm e idₛ fresh) (factorSubₛ-wkSub-comm e idₛ fresh)  
  subst1˘ Sub (lCtxₛ-wkSub-comm e idₛ fresh)
    (wkSub (factorWk (factorExtₛ e idₛ) fresh) (factorSubₛ e idₛ))
    -- apply IH
    ≡⟨ cong
          z  subst1˘ Sub (lCtxₛ-wkSub-comm e idₛ fresh) (wkSub (factorWk (factorExtₛ e idₛ) fresh) z))
         (factorSubₛIdWkIsFactorSubₛId e) 
  subst1˘ Sub (lCtxₛ-wkSub-comm e idₛ fresh)
    (wkSub (factorWk (factorExtₛ e idₛ) fresh) (embWk (LFExtToWk (factorSubₛIdWk e))))
    -- apply `substCrunch` which crunches substitution with substitution and weakening equalities
    ≡⟨ cong
         (subst1˘ Sub (lCtxₛ-wkSub-comm e idₛ fresh))
         substCrunch 
  subst1˘ Sub (lCtxₛ-wkSub-comm e idₛ fresh)
    (embWk (LFExtToWk (extRAssoc (factorSubₛIdWk e) (factorDropsWk (factorExtₛ e idₛ) freshExt))))
    -- pull out subst
    ≡⟨ subst˘-application′
          z  embWk (LFExtToWk z))
         (lCtxₛ-wkSub-comm e idₛ fresh) 
  embWk (LFExtToWk
    (subst˘  Γ  LFExt Γ _ (←#₁rCtx e ,, rCtx′ (factorExtₛ e idₛ) freshExt)) (lCtxₛ-wkSub-comm e idₛ fresh)
      (extRAssoc (factorSubₛIdWk e) (factorDropsWk (factorExtₛ e idₛ) freshExt))))
    ≡⟨⟩
  embWk (LFExtToWk (factorSubₛIdWk (ext e))) 
  where
  --
  coh-wkSub-embwk : (w : Γ'  Γ'') (w' : Γ  Γ')  wkSub w (embWk w')  embWk (w'  w)
  coh-wkSub-embwk w w' = let open ≡-Reasoning in begin
    wkSub w (embWk w')
      ≡˘⟨ cong (wkSub w) (wkSubId _) 
    wkSub w (wkSub w' idₛ)
      ≡⟨ wkSubPres∙ _ _ _ 
    wkSub (w'  w) idₛ
      ≡⟨ wkSubId _ 
    embWk (w'  w) 
  --
  substCrunch : wkSub (factorWk (factorExtₛ e idₛ) fresh[ a ]) (embWk (LFExtToWk (factorSubₛIdWk e)))
     embWk (LFExtToWk (extRAssoc (factorSubₛIdWk e) (factorDropsWk (factorExtₛ e idₛ) freshExt)))
  substCrunch = let open ≡-Reasoning in begin
    wkSub (factorWk (factorExtₛ e idₛ) fresh[ a ]) (embWk (LFExtToWk (factorSubₛIdWk e)))
      ≡⟨ coh-wkSub-embwk (factorWk (factorExtₛ e idₛ) fresh[ a ]) (LFExtToWk (factorSubₛIdWk e)) 
    embWk (LFExtToWk (factorSubₛIdWk e)  factorWk (factorExtₛ e idₛ) fresh)
      ≡˘⟨ cong  x  embWk (LFExtToWk (factorSubₛIdWk e)  x)) (factorDropsWkIsfactorWk (factorExtₛ e idₛ) freshExt) 
    embWk (LFExtToWk (factorSubₛIdWk e)  LFExtToWk (factorDropsWk (factorExtₛ e idₛ) freshExt))
      ≡˘⟨ cong embWk (LFExtToWkPresTrans (factorSubₛIdWk e) (factorDropsWk (factorExtₛ e idₛ) freshExt)) 
    embWk
      (LFExtToWk (extRAssoc (factorSubₛIdWk e) (factorDropsWk (factorExtₛ e idₛ) freshExt))) 

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

wkTmPres⟶ : (w : Γ  Γ')  t  t'  wkTm w t  wkTm w t'
wkTmPres⟶ w (red-fun t u)
  = step-≡ (red-fun _ _) (beta-wk-lemma w u t)
wkTmPres⟶ w (exp-fun _)
  = step-≡ (exp-fun _) (cong lam (cong1 app keepFreshLemma))
wkTmPres⟶ w (red-box t e)
  = step-≡
    (red-box _ _)
    (trans
      (˘trans
        (coh-trimSub-wkTm t _ _)
        (cong
           s  substTm (lock s (factorExt e w)) t)
          (trans˘
            (trimSubId (factorWk e w))
            (wkSubId _))))
      (nat-substTm t _ _))
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 {e = e} r)
  = cong-unbox (wkTmPres⟶ (factorWk e w ) r)
wkTmPres⟶ w (cong-app1 r)
  = cong-app1 (wkTmPres⟶ w r)
wkTmPres⟶ w (cong-app2 r)
  = cong-app2 (wkTmPres⟶ w r)
wkTmPres⟶ w (shift-unbox t w' e)
  = ≡-step-≡
      (let open ≡-Reasoning in begin
        wkTm w (unbox t (extRAssoc (upLFExt w') e))
          ≡⟨⟩
        unbox
          (wkTm (factorWk (extRAssoc (upLFExt w') e) w) t)
          (factorExt (extRAssoc (upLFExt w') e) w)
          -- factorisation preserves transitivity
          ≡⟨ cong-unbox≡′′
               (lCtxPresTrans (upLFExt w') e w)
               (trans
                 (subst-application1′ wkTm (lCtxPresTrans (upLFExt w') e w))
                 (cong1 wkTm (factorWkPresTrans (upLFExt w') e w)))
           
        unbox
          (wkTm (factorWk (upLFExt w') (factorWk e w)) t)
          (extRAssoc (factorExt (upLFExt w') (factorWk e w)) (factorExt e w))
          -- apply equalities for absorption of upLFExt
          ≡˘⟨ cong-unbox≡′′
                (lCtxAbsorbsUpLFExt w' (factorWk e w))
                (trans
                  (subst-application1′ wkTm (lCtxAbsorbsUpLFExt w' (factorWk e w)))
                  (cong1 wkTm (factorWkAbsorbsUpLFExt w' (factorWk e w))))
            
        unbox
          (wkTm (factorWk w' (factorWk e w)) t)
          (extRAssoc (upLFExt (factorExt w' (factorWk e w))) (factorExt e w)) )
      (shift-unbox _ _ _)
      (let open ≡-Reasoning in begin
        unbox
          (wkTm (LFExtToWk (factorExt w' (factorWk e w))) (wkTm (factorWk w' (factorWk e w)) t))
          (factorExt e w)
          -- wkTm preserves composition
          ≡⟨ cong1 unbox (wkTmPres∙ _ _ _) 
        unbox
          (wkTm (factorWk w' (factorWk e w)  LFExtToWk (factorExt w' (factorWk e w))) t)
          (factorExt e w)
          -- apply factorisationLemma
          ≡˘⟨ cong1 unbox (cong1 wkTm (factorisationLemma w' _)) 
        unbox
          (wkTm (LFExtToWk w'  factorWk e w) t)
          (factorExt e w)
          -- wkTm preserves composition
          ≡˘⟨ cong1 unbox (wkTmPres∙ _ _ _) 
        unbox
          (wkTm (factorWk e w) (wkTm (LFExtToWk w') t))
          (factorExt e w)
          ≡⟨⟩
        wkTm w (unbox (wkTm (LFExtToWk w') t) e) )

wkTmPres≈ : (w : Γ  Γ')  t  t'  wkTm w t  wkTm w t'
wkTmPres≈ w = cong-⟶-to-cong-≈ (wkTmPres⟶ w)

wkSubPres⟶ : (w : Δ  Δ')  σ ⟶ₛ σ'  wkSub w σ ⟶ₛ wkSub w σ'
wkSubPres⟶ w (cong-`,⟶ₛ1 r) = cong-`,⟶ₛ1 (wkSubPres⟶ w r)
wkSubPres⟶ w (cong-`,⟶ₛ2 r) = cong-`,⟶ₛ2 (wkTmPres≈ w r)
wkSubPres⟶ w (cong-lock⟶ₛ r) = cong-lock⟶ₛ (wkSubPres⟶ _ r)
wkSubPres⟶ w (shift-lock⟶ₛ {s = s} w' {e}) = RUtil.≡-step-≡ _⟶ₛ_
  (let open ≡-Reasoning in begin
    wkSub w (lock s (extRAssoc (upLFExt w') e))
      ≡⟨⟩
    lock
      (wkSub (factorWk (extRAssoc (upLFExt w') e) w) s)
      (factorExt (extRAssoc (upLFExt w') e) w)
      -- factorisation preserves transitivity
      ≡⟨ cong-lock≡′′
           (lCtxPresTrans (upLFExt w') e w)
           (trans
             (subst-application1′ wkSub (lCtxPresTrans (upLFExt w') e w))
             (cong1 wkSub (factorWkPresTrans (upLFExt w') e w)))
       
    lock
      (wkSub (factorWk (upLFExt w') (factorWk e w)) s)
      (extRAssoc (factorExt (upLFExt w') (factorWk e w)) (factorExt e w))
      -- apply equalities for absorption of upLFExt
      ≡˘⟨ cong-lock≡′′
            (lCtxAbsorbsUpLFExt w' (factorWk e w))
            (trans
              (subst-application1′ wkSub (lCtxAbsorbsUpLFExt w' (factorWk e w)))
              (cong1 wkSub (factorWkAbsorbsUpLFExt w' (factorWk e w))))
        
    lock
      (wkSub (factorWk w' (factorWk e w)) s)
      (extRAssoc (upLFExt (factorExt w' (factorWk e w))) (factorExt e w)) )
  (shift-lock⟶ₛ _)
  (let open ≡-Reasoning in begin
    lock
      (wkSub (LFExtToWk (factorExt w' (factorWk e w))) (wkSub (factorWk w' (factorWk e w)) s))
      (factorExt e w)
      -- wkSub preserves composition
      ≡⟨ cong1 lock (wkSubPres∙ _ _ _) 
    lock
      (wkSub (factorWk w' (factorWk e w)  LFExtToWk (factorExt w' (factorWk e w))) s)
      (factorExt e w)
      -- apply factorisation lemma
      ≡˘⟨ cong1 lock (cong1 wkSub (factorisationLemma w' _)) 
    lock
      (wkSub (LFExtToWk w'  factorWk e w) s)
      (factorExt e w)
      -- wkSub preserves composition
      ≡˘⟨ cong1 lock (wkSubPres∙ _ _ _) 
    lock
      (wkSub (factorWk e w) (wkSub (LFExtToWk w') s))
      (factorExt e w)
      ≡⟨⟩
    wkSub w (lock (wkSub (LFExtToWk w') s) e) )

wkSubPres≈ : (w : Δ  Δ')  σ ≈ₛ σ'  wkSub w σ ≈ₛ wkSub w σ'
wkSubPres≈ w = cong-⟶ₛ-to-cong-≈ₛ (wkSubPres⟶ w)

substTmPresId : (t : Tm Γ a)  t  substTm idₛ t
substTmPresId (var x)     = ≈-reflexive˘ (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 e) = fact-unbox≈ t e
  where
  --
  coh-wkTm-substTm : (t : Tm Γ a) (w : Γ  Γ')  wkTm w t  substTm (embWk w) t
  coh-wkTm-substTm {a = a} {Γ' = Γ'} t w = begin
    wkTm w t
      -- apply IH
      ≈⟨ wkTmPres≈ w (substTmPresId t) 
    wkTm w (substTm idₛ t)
      -- apply naturality of substTm
      ≡˘⟨ nat-substTm t idₛ w 
    substTm (wkSub w idₛ) t
      -- weakening id subst is same as embedding the weakening into a subst
      ≡⟨ cong1 substTm {y = t} (wkSubId w) 
    substTm (embWk w) t 
    where
    open SetoidReasoning (Tm-setoid Γ' a)
  --
  fact-unbox≈ : (t : Tm ΓL ( a)) (e : CExt Γ ΓL ΓR)
     unbox t e  unbox (substTm (factorSubₛ e idₛ) t) (factorExtₛ e idₛ)
  fact-unbox≈ {a = a} {Γ = Γ} t e = begin
    unbox t e
      -- expand extension e
      ≡⟨ cong-unbox2≡ 
    unbox t (extRAssoc (upLFExt (factorSubₛIdWk e)) (factorExtₛ e idₛ))
      -- apply shift-unbox
      ≈⟨ ⟶-to-≈ (shift-unbox _ _ _) 
    unbox (wkTm (LFExtToWk (factorSubₛIdWk e)) t) (factorExtₛ e idₛ)
      -- rewrite wkTm to substTm
      ≈⟨ cong-unbox≈ (coh-wkTm-substTm t _) 
    unbox (substTm (embWk (LFExtToWk (factorSubₛIdWk e))) t) (factorExtₛ e idₛ)
      -- show that the subst is the factorisation of the id subst
      ≡˘⟨ cong1 unbox (cong1 substTm {y = t} (factorSubₛIdWkIsFactorSubₛId e)) 
    unbox (substTm (factorSubₛ e idₛ) t) (factorExtₛ e idₛ) 
    where
    open SetoidReasoning (Tm-setoid Γ a)

rightIdSub : (s : Sub Γ Γ')  s ≈ₛ s ∙ₛ idₛ
rightIdSub []         = ≈ₛ-refl
rightIdSub (s `, t)   = cong-`,≈ₛ (rightIdSub s) (substTmPresId t)
rightIdSub (lock s e) = fact-lock≈ s e
  where
  --
  fact-lock≈ : (s : Sub ΓL Δ) (e : CExt Γ ΓL ΓR)
     lock s e ≈ₛ lock (s ∙ₛ factorSubₛ e idₛ) (factorExtₛ e idₛ)
  fact-lock≈ {Δ = Δ} {Γ = Γ} s e = begin
    lock s e
      -- expand extension e
      ≡⟨ cong-lock2≡ 
    lock s (extRAssoc (upLFExt (factorSubₛIdWk e)) (factorExtₛ e idₛ))
      -- apply shift-lock≈ₛ
      ≈⟨ shift-lock≈ₛ _ 
    lock (wkSub (LFExtToWk (factorSubₛIdWk e)) s) (factorExtₛ e idₛ)
      -- apply IH
      ≈⟨ cong-lock≈ₛ (wkSubPres≈ _ (rightIdSub s)) 
    lock (wkSub (LFExtToWk (factorSubₛIdWk e)) (s ∙ₛ idₛ)) (factorExtₛ e idₛ)
      -- rewrite using coherence between weakening and composing substs (associativity, really)
      ≡⟨ cong1 lock (coh-wkSub-∙ₛ s idₛ (LFExtToWk (factorSubₛIdWk e))) 
    lock (s ∙ₛ wkSub (LFExtToWk (factorSubₛIdWk e)) idₛ) (factorExtₛ e idₛ)
      --  weakening of id subst is itself a weakening
      ≡⟨ cong1 lock (cong (s ∙ₛ_) (wkSubId _)) 
    lock (s ∙ₛ embWk (LFExtToWk (factorSubₛIdWk e))) (factorExtₛ e idₛ)
      -- show that the weakening subst is the factorisation of the id subst
      ≡˘⟨ cong1 lock (cong (s ∙ₛ_) (factorSubₛIdWkIsFactorSubₛId e)) 
    lock (s ∙ₛ factorSubₛ e idₛ) (factorExtₛ e idₛ) 
    where
    open SetoidReasoning (Sub-setoid Γ (Δ #))

substVarPres⟶ : (v : Var Γ a)  σ ⟶ₛ σ'  substVar σ v  substVar σ' v
substVarPres⟶ zero     (cong-`,⟶ₛ1 s⟶s') = ≈-refl
substVarPres⟶ zero     (cong-`,⟶ₛ2 t≈t') = t≈t'
substVarPres⟶ (succ v) (cong-`,⟶ₛ1 s⟶s') = substVarPres⟶ v s⟶s'
substVarPres⟶ (succ v) (cong-`,⟶ₛ2 t≈t') = ≈-refl

substVarPres≈ : (v : Var Γ a)  σ ≈ₛ σ'  substVar σ v  substVar σ' v
substVarPres≈ v ε                    = ≈-refl
substVarPres≈ v (inj₁ σ⟶σ'  σ'≈σ'') = ≈-trans (substVarPres⟶ v σ⟶σ') (substVarPres≈ v σ'≈σ'')
substVarPres≈ v (inj₂ σ'⟶σ  σ≈σ'')  = ≈-˘trans (substVarPres⟶ v σ'⟶σ) (substVarPres≈ v σ≈σ'')

substTmPres⟶ : (t : Tm Γ a)  σ ⟶ₛ σ'  substTm σ t  substTm σ' t
substTmPres⟶ (var v)     r = substVarPres⟶ v r
substTmPres⟶ (lam t)     r = cong-lam≈ (substTmPres⟶ t (cong-`,⟶ₛ1 (wkSubPres⟶ fresh r)))
substTmPres⟶ (app t u)   r = cong-app≈ (substTmPres⟶ t r) (substTmPres⟶ u r)
substTmPres⟶ (box t)     r = cong-box≈ (substTmPres⟶ t (cong-lock⟶ₛ r))
substTmPres⟶ (unbox t e) r = h e r t
  where
    h :  (e    : CExt Γ ΓL ΓR)
          (σ⟶σ' : σ ⟶ₛ σ')
          (t    : Tm ΓL ( a))
          {e'   : CExt Θ _ ΔR}
          {e''  : CExt Θ _ ΔR'}
         unbox (substTm (factorSubₛ e σ)  t) e'
         unbox (substTm (factorSubₛ e σ') t) e''
    h nil        σ⟶ₛσ'                   t = cong-unbox≈′ (substTmPres⟶ t σ⟶ₛσ')
    h (ext e)    (cong-`,⟶ₛ1 σ⟶σ')       t = h e σ⟶σ' t
    h (ext e)    (cong-`,⟶ₛ2 t≈t')       t = cong-unbox2≈
    h (ext#- e) (cong-lock⟶ₛ σ⟶σ')       t = h e σ⟶σ' t
    h (ext#- e) (shift-lock⟶ₛ {s = σ} w) t {e'} {e''} = let open SetoidReasoning (Tm-setoid _ _) in
        begin
          unbox (substTm (factorSubₛ e σ) t) e'
        ≈⟨ shift-unbox≈ (substTm (factorSubₛ e σ) t) (factorDropsWk (factorExtₛ e σ) w) 
          unbox (wkTm (LFExtToWk (factorDropsWk (factorExtₛ e σ) w)) (substTm (factorSubₛ e σ) t)) (subst1 (CExt _) (lCtxₛ-wkSub-comm e σ (LFExtToWk w)) e'')
        ≡⟨ cong  w'  unbox (wkTm w' _) (subst1 (CExt _) (lCtxₛ-wkSub-comm e σ (LFExtToWk w)) e'')) (factorDropsWkIsfactorWk (factorExtₛ e σ) w) 
          unbox (wkTm (factorWk (factorExtₛ e σ) (LFExtToWk w)) (substTm (factorSubₛ e σ) t)) (subst1 (CExt _) (lCtxₛ-wkSub-comm e σ (LFExtToWk w)) e'')
        ≡˘⟨ cong1 unbox (nat-substTm t (factorSubₛ e σ) (factorWk (factorExtₛ e σ) (LFExtToWk w))) 
          unbox (substTm (wkSub (factorWk (factorExtₛ e σ) (LFExtToWk w)) (factorSubₛ e σ)) t) (subst1 (CExt _) (lCtxₛ-wkSub-comm e σ (LFExtToWk w)) e'')
        ≡˘⟨ dcong₃   s e  unbox (substTm s t) e) (lCtxₛ-wkSub-comm e σ (LFExtToWk w)) (factorSubₛ-wkSub-comm e σ (LFExtToWk w)) refl 
          unbox (substTm (factorSubₛ e (wkSub (LFExtToWk w) σ)) t) e''
        

substTmPres≈ : (t : Tm Γ a)  (σ≈σ' : σ ≈ₛ σ')  substTm σ t  substTm σ' t
substTmPres≈ t ε                    = ≈-refl
substTmPres≈ t (inj₁ σ⟶σ'  σ'≈σ'') = ≈-trans (substTmPres⟶ t σ⟶σ') (substTmPres≈ t σ'≈σ'')
substTmPres≈ t (inj₂ σ'⟶σ  σ≈σ'')  = ≈-˘trans (substTmPres⟶ t σ'⟶σ) (substTmPres≈ t σ≈σ'')

Generated from commit 2fd14c996b195ef101dff8919e837907ca0a08aa.