{-# OPTIONS --without-K --safe #-}

module STLCSubst.Statics.Properties.Ops where

open import Level using (0ℓ; _⊔_) renaming (suc to lsuc)
open import Relation.Binary using (Rel; Setoid; IsEquivalence)
open import Relation.Binary.PropositionalEquality hiding ([_])
open import Relation.Binary.PropositionalEquality using (_≗_) public

import Data.Nat.Properties as ℕₚ
import Relation.Binary.Reasoning.Setoid as SR

open import Lib
open import STLCSubst.Statics.Definitions

record AlgLemmas {i} (A : Set i)
   : Set (lsuc i) where

  infix 4 _≈_

  field
    {{has-id}}     : HasIdentity A
    {{composable}} : Composable A
    {{head-wk}}    : HeadWeaken A

    _≈_      : Rel A 0ℓ
    ≈-equiv  : IsEquivalence _≈_
    left-id  :  a  id  a  a
    right-id :  a  a  id  a
    assoc    :  a b c  (a  b)  c  a  (b  c)

    ∙-cong   :  a b c d  a  b  c  d  a  c  b  d

    q-id     : q id  id
    q-cong   :  {a b}  a  b  q a  q b
    q-∙-dist :  a b  q a  q b  q (a  b)

  ≈-setoid : Setoid _ _
  ≈-setoid = record { Carrier = _ ; _≈_ = _≈_ ; isEquivalence = ≈-equiv }

  module  = Setoid ≈-setoid
  module ≈-Reasoning = SR ≈-setoid

  repeat-q-∙-dist :  n a b  repeat q n a  repeat q n b  repeat q n (a  b)
  repeat-q-∙-dist zero a b    = ≈.refl
  repeat-q-∙-dist (suc n) a b = ≈.trans (q-∙-dist (repeat q n a) (repeat q n b)) (q-cong (repeat-q-∙-dist n a b))

  repeat-q-id :  n  repeat q n id  id
  repeat-q-id zero    = ≈.refl
  repeat-q-id (suc n) = ≈.trans (q-cong (repeat-q-id n)) q-id

open AlgLemmas {{...}} hiding (_≈_; has-id; composable; head-wk) public

---------------------------------------
-- properties of weakenings

module  {a b} {A : Set a} {B : Set b} = Setoid (A →-setoid B)

wk-q-cong : ϕ  ψ  q ϕ  q ψ
wk-q-cong eq zero    = refl
wk-q-cong eq (suc x) = cong suc (eq x)

wk-transp : (t : Exp)  ϕ  ψ  t [ ϕ ]  t [ ψ ]
wk-transp (v x) eq         = cong v (eq x)
wk-transp ze eq            = refl
wk-transp (su t) eq        = cong su (wk-transp t eq)
wk-transp (rec T u s t) eq
  rewrite wk-transp u eq
        | wk-transp s (wk-q-cong (wk-q-cong eq))
        | wk-transp t eq = refl
wk-transp (Λ t) eq         = cong Λ (wk-transp t (wk-q-cong eq))
wk-transp (t $ s) eq       = cong₂ _$_ (wk-transp t eq) (wk-transp s eq)

wk-q-∙-dist : (ϕ ψ : Wk)  q ϕ  q ψ  q (ϕ  ψ)
wk-q-∙-dist ϕ ψ zero    = refl
wk-q-∙-dist ϕ ψ (suc x) = refl

wk-qq-∙-dist : (ϕ ψ : Wk)  q (q ϕ)  q (q ψ)  q (q (ϕ  ψ))
wk-qq-∙-dist ϕ ψ = ≗.trans (wk-q-∙-dist (q ϕ) (q ψ)) (wk-q-cong (wk-q-∙-dist ϕ ψ))

wk-app-comb : (t : Exp) (ϕ ψ : Wk)  t [ ϕ ] [ ψ ]  t [ ϕ  ψ ]
wk-app-comb (v x) ϕ ψ       = refl
wk-app-comb ze ϕ ψ          = refl
wk-app-comb (su t) ϕ ψ      = cong su (wk-app-comb t ϕ ψ)
wk-app-comb (rec T u s t) ϕ ψ
  rewrite wk-app-comb u ϕ ψ
        | trans (wk-app-comb s (q (q ϕ)) (q (q ψ))) (wk-transp s (wk-qq-∙-dist ϕ ψ))
        | wk-app-comb t ϕ ψ = refl
wk-app-comb (Λ t) ϕ ψ       = cong Λ (trans (wk-app-comb t (q ϕ) (q ψ)) (wk-transp t (wk-q-∙-dist ϕ ψ)))
wk-app-comb (t $ s) ϕ ψ     = cong₂ _$_ (wk-app-comb t ϕ ψ) (wk-app-comb s ϕ ψ)

wk-comp-q : (n : ) (t : Exp) (ϕ : Wk)  t [ repeat q n  ] [ repeat q n (q ϕ) ]  t [ repeat q n ϕ ] [ repeat q n  ]
wk-comp-q n (v x) ϕ       = cong v (lem n x)
  where lem :  n x  repeat q n (q ϕ) (repeat q n  x)  repeat q n  (repeat q n ϕ x)
        lem zero x        = refl
        lem (suc n) zero  = refl
        lem (suc n) (suc x)
          rewrite lem n x = refl
wk-comp-q n ze ϕ          = refl
wk-comp-q n (su t) ϕ      = cong su (wk-comp-q n t ϕ)
wk-comp-q n (rec T u s t) ϕ
  rewrite wk-comp-q n u ϕ
        | wk-comp-q (2 + n) s ϕ
        | wk-comp-q n t ϕ = refl
wk-comp-q n (Λ t) ϕ       = cong Λ (wk-comp-q (1 + n) t ϕ)
wk-comp-q n (t $ s) ϕ     = cong₂ _$_ (wk-comp-q n t ϕ) (wk-comp-q n s ϕ)

wk-suc :  n x  wk (suc n) (suc x)  suc (wk n x)
wk-suc n x
  with n ≤? x | suc n ≤? suc x
... | yes p | yes p′       = refl
... | yes p | no ¬p′       = ⊥-elim (¬p′ (s≤s p))
... | no ¬p | yes (s≤s p′) = ⊥-elim (¬p p′)
... | no ¬p | no ¬p′       = refl

wk-repeat-q-eq :  n  repeat q n   wk n
wk-repeat-q-eq zero x       = refl
wk-repeat-q-eq (suc n) zero = refl
wk-repeat-q-eq (suc n) (suc x)
  rewrite wk-repeat-q-eq n x
        | wk-suc n x        = refl

wk-repeat-q-gen :  n m  repeat q n (wk m)  wk (n + m)
wk-repeat-q-gen zero m x             = refl
wk-repeat-q-gen (suc n) zero x
  rewrite ℕₚ.+-identityʳ n           = wk-repeat-q-eq (suc n) x
wk-repeat-q-gen (suc n) (suc m) zero = refl
wk-repeat-q-gen (suc n) (suc m) (suc x)
  rewrite wk-repeat-q-gen n (suc m) x
        | wk-suc (n + suc m) x       = refl

wk-q-wk0 : (n : ) (t : Exp)  t [ repeat q n  ] [  ]  t [  ] [ repeat q (1 + n)  ]
wk-q-wk0 n t = begin
  t [ repeat q n  ] [  ]       ≡⟨ cong (_[  ]) (wk-transp t (wk-repeat-q-eq n)) 
  t [ wk n ] [  ]                    ≡⟨ wk-app-comb t (wk n)  
  t [ wk n   ]                      ≡⟨ wk-transp t lem 
  t [   wk (1 + n) ]                ≡⟨ sym (wk-app-comb t  (wk (1 + n))) 
  t [  ] [ wk (1 + n) ]              ≡⟨ sym (wk-transp (t [  ]) (wk-repeat-q-eq (1 + n))) 
  t [  ] [ repeat q (1 + n)  ] 
  where open ≡-Reasoning
        lem : wk-compose (wk n)   wk-compose  (wk (suc n))
        lem x
          rewrite wk-suc n x = refl

wk-comp-q-equiv-gen : (n : ) (t : Exp) (σ : Subst)  t [ repeat q n  ] [ repeat q n (q σ) ]  t [ repeat q n σ ] [ repeat q n  ]
wk-comp-q-equiv-gen n (v x) σ       = lem n x
  where lem :  n x  repeat q n (q σ) (repeat q n  x)  (repeat q n σ x) [ repeat q n  ]
        lem zero x        = refl
        lem (suc n) zero  = refl
        lem (suc n) (suc x)
          rewrite lem n x = wk-q-wk0 n (repeat q n σ x)
wk-comp-q-equiv-gen n ze σ          = refl
wk-comp-q-equiv-gen n (su t) σ      = cong su (wk-comp-q-equiv-gen n t σ)
wk-comp-q-equiv-gen n (rec T u s t) σ
  rewrite wk-comp-q-equiv-gen n u σ
        | wk-comp-q-equiv-gen (2 + n) s σ
        | wk-comp-q-equiv-gen n t σ = refl
wk-comp-q-equiv-gen n (Λ t) σ       = cong Λ (wk-comp-q-equiv-gen (1 + n) t σ)
wk-comp-q-equiv-gen n (t $ s) σ     = cong₂ _$_ (wk-comp-q-equiv-gen n t σ) (wk-comp-q-equiv-gen n s σ)

wk-qid≈id : wk-q id  id
wk-qid≈id zero    = refl
wk-qid≈id (suc x) = refl

wk-qqid≈id : wk-q (q id)  id
wk-qqid≈id = ≗.trans (wk-q-cong wk-qid≈id) wk-qid≈id

wk-app-id : (t : Exp)  wk-app t id  t
wk-app-id (v x)       = refl
wk-app-id ze          = refl
wk-app-id (su t)      = cong su (wk-app-id t)
wk-app-id (rec T u s t)
  rewrite wk-app-id u
        | trans (wk-transp s wk-qqid≈id) (wk-app-id s)
        | wk-app-id t = refl
wk-app-id (Λ t)       = cong Λ (trans (wk-transp t wk-qid≈id) (wk-app-id t))
wk-app-id (t $ s)     = cong₂ _$_ (wk-app-id t) (wk-app-id s)

wk-∙-cong : (ϕ ϕ′ ψ ψ′ : Wk)  ϕ  ϕ′  ψ  ψ′  ϕ  ψ  ϕ′  ψ′
wk-∙-cong ϕ ϕ′ ψ ψ′ eq eq′ x
  rewrite eq x = eq′ _

---------------------------------------
-- properties of substitutions

subst-qid≈id : q v  id
subst-qid≈id zero    = refl
subst-qid≈id (suc _) = refl

subst-q-cong : σ  τ  q σ  q τ
subst-q-cong eq zero = refl
subst-q-cong eq (suc x) = cong  z  z [  ]) (eq x)

subst-qqid≈id : q (q v)  id
subst-qqid≈id = ≗.trans (subst-q-cong subst-qid≈id) subst-qid≈id

q-alt : Subst  Subst
q-alt σ = σ [  ]  v 0

conv-equiv-gen : (n : ) (t : Exp) (ϕ : Wk)  t [ repeat q n (conv ϕ) ]  t [ repeat q n ϕ ]
conv-equiv-gen n (v x) ϕ       = lem n x
  where lem :  n x  repeat subst-q n (conv ϕ) x  v (repeat wk-q n ϕ x)
        lem zero x        = refl
        lem (suc n) zero  = refl
        lem (suc n) (suc x)
          rewrite lem n x = refl
conv-equiv-gen n ze ϕ          = refl
conv-equiv-gen n (su t) ϕ      = cong su (conv-equiv-gen n t ϕ)
conv-equiv-gen n (rec T u s t) ϕ
  rewrite conv-equiv-gen n u ϕ
        | conv-equiv-gen (2 + n) s ϕ
        | conv-equiv-gen n t ϕ = refl
conv-equiv-gen n (Λ t) ϕ       = cong Λ (conv-equiv-gen (1 + n) t ϕ)
conv-equiv-gen n (t $ s) ϕ     = cong₂ _$_ (conv-equiv-gen n t ϕ) (conv-equiv-gen n s ϕ)

conv-equiv : (t : Exp) (ϕ : Wk)  t [ conv ϕ ]  t [ ϕ ]
conv-equiv = conv-equiv-gen 0

subst-q-equiv : (σ : Subst)  q σ  q-alt σ
subst-q-equiv σ zero    = refl
subst-q-equiv σ (suc x) = refl

wk-drop-ext : (σ : Subst) (t : Exp)  conv   (σ  t)  σ
wk-drop-ext _ _ _ = refl

subst-q-∙-dist : (σ σ′ : Subst)  q σ  q σ′  q (σ  σ′)
subst-q-∙-dist σ σ′ zero = refl
subst-q-∙-dist σ σ′ (suc x) = begin
  σ x [  ] [ q σ′ ] ≡⟨ wk-comp-q-equiv-gen 0 (σ x) σ′ 
  σ x [ σ′ ] [  ] 
  where open ≡-Reasoning

subst-qq-∙-dist : (σ σ′ : Subst)  q (q σ)  q (q σ′)  q (q (σ  σ′))
subst-qq-∙-dist σ σ′ = begin
  q (q σ)  q (q σ′) ≈⟨ subst-q-∙-dist (q σ) (q σ′) 
  q (q σ  q σ′)     ≈⟨ subst-q-cong (subst-q-∙-dist σ σ′) 
  q (q (σ  σ′))     
  where open SR ( →-setoid Exp)


subst-transp : (t : Exp)  σ  τ  t [ σ ]  t [ τ ]
subst-transp (v x) eq       = eq x
subst-transp ze eq          = refl
subst-transp (su t) eq      = cong su (subst-transp t eq)
subst-transp (rec T u s t) eq
  rewrite subst-transp u eq
        | subst-transp s (subst-q-cong (subst-q-cong eq))
        | subst-transp t eq = refl
subst-transp (Λ t) eq       = cong Λ (subst-transp t (subst-q-cong eq))
subst-transp (t $ s) eq     = cong₂ _$_ (subst-transp t eq) (subst-transp s eq)

subst-app-id : (t : Exp)  t [ v ]  t
subst-app-id (v _)       = refl
subst-app-id ze          = refl
subst-app-id (su t)      = cong su (subst-app-id t)
subst-app-id (rec T u s t)
  rewrite subst-app-id u
        | trans (subst-transp s subst-qqid≈id) (subst-app-id s)
        | subst-app-id t = refl
subst-app-id (Λ t)       = cong Λ (trans (subst-transp t subst-qid≈id) (subst-app-id t))
subst-app-id (t $ s)     = cong₂ _$_ (subst-app-id t) (subst-app-id s)

subst-app-comb : (t : Exp) (σ σ′ : Subst)  t [ σ ] [ σ′ ]  t [ σ  σ′ ]
subst-app-comb (v x) σ σ′       = refl
subst-app-comb ze σ σ′          = refl
subst-app-comb (su t) σ σ′      = cong su (subst-app-comb t σ σ′)
subst-app-comb (rec T u s t) σ σ′
  rewrite subst-app-comb u σ σ′
        | trans (subst-app-comb s (q (q σ)) (q (q σ′))) (subst-transp s (subst-qq-∙-dist σ σ′))
        | subst-app-comb t σ σ′ = refl
subst-app-comb (Λ t) σ σ′       = cong Λ (trans (subst-app-comb t (q σ) (q σ′)) (subst-transp t (subst-q-∙-dist σ σ′)))
subst-app-comb (t $ s) σ σ′     = cong₂ _$_ (subst-app-comb t σ σ′) (subst-app-comb s σ σ′)

subst-comp-assoc :  (σ σ′ σ″ : Subst)  (σ  σ′)  σ″  σ  (σ′  σ″)
subst-comp-assoc σ σ′ σ″ x = subst-app-comb (σ x) σ′ σ″

subst-∙-cong : (σ σ′ τ τ′ : Subst)  σ  σ′  τ  τ′  σ  τ  σ′  τ′
subst-∙-cong σ σ′ τ τ′ eq eq′ x
  rewrite eq x = subst-transp (σ′ x) eq′

subst-ext-η :  σ  σ  conv   σ  (v 0 [ σ ])
subst-ext-η σ zero    = refl
subst-ext-η σ (suc x) = refl

subst-ext-cong :  {σ σ′ : Subst} {t t′ : Exp}  σ  σ′  t  t′  σ  t  σ′  t′
subst-ext-cong eq refl zero    = refl
subst-ext-cong eq refl (suc x) = eq x

---------------------------------------
-- instances

instance
  Wk-AlgLemmas : AlgLemmas Wk
  Wk-AlgLemmas = record
    { _≈_      = _≗_
    ; ≈-equiv  = Setoid.isEquivalence ( →-setoid )
    ; left-id  = λ _ _  refl
    ; right-id = λ _ _  refl
    ; assoc    = λ _ _ _ _  refl
    ; ∙-cong   = wk-∙-cong
    ; q-id     = wk-qid≈id
    ; q-cong   = wk-q-cong
    ; q-∙-dist = wk-q-∙-dist
    }

  Subst-AlgLemmas : AlgLemmas Subst
  Subst-AlgLemmas = record
    { _≈_         = _≗_
    ; ≈-equiv     = Setoid.isEquivalence ( →-setoid Exp)
    ; left-id     = λ _ _  refl
    ; right-id    = λ σ x  subst-app-id (σ x)
    ; assoc       = subst-comp-assoc
    ; ∙-cong      = subst-∙-cong
    ; q-id        = subst-qid≈id
    ; q-cong      = subst-q-cong
    ; q-∙-dist    = subst-q-∙-dist
    }

---------------------------------------
-- more properties

conv-q :  ϕ  conv (q ϕ)  q (conv ϕ)
conv-q _ zero    = refl
conv-q _ (suc _) = refl

conv-∙ :  ϕ ψ  conv (ϕ  ψ)  conv ϕ  conv ψ
conv-∙ _ _ _ = refl

ext-lookup-v0 : (σ : Subst) (t : Exp)  v 0 [ σ  t ]  t
ext-lookup-v0 _ _ = refl

ext-lookup-v1 : (σ : Subst) (t : Exp) (n : )  v (suc n) [ σ  t ]  v n [ σ ]
ext-lookup-v1 _ _ _ = refl

ext-comp : (σ δ : Subst) (t : Exp)  σ  t  δ  σ  δ  (t [ δ ])
ext-comp σ δ t zero    = refl
ext-comp σ δ t (suc x) = refl

subst-wk-id : (σ : Subst)  σ [ id ]  σ
subst-wk-id σ x = wk-app-id (σ x)

wk-app-cong : (ϕ : Wk)  σ  τ  σ [ ϕ ]  τ [ ϕ ]
wk-app-cong ϕ eq x
  rewrite eq x = refl

ext-wk : (σ : Subst) (t : Exp) (ϕ : Wk)  (σ  t) [ ϕ ]  σ [ ϕ ]  (t [ ϕ ])
ext-wk σ t ϕ zero    = refl
ext-wk σ t ϕ (suc x) = refl

conv-equiv-subst : (σ : Subst) (ϕ : Wk)  σ  conv ϕ  σ [ ϕ ]
conv-equiv-subst σ ϕ x = conv-equiv (σ x) ϕ

wk-app-subst : (t : Exp) (σ : Subst) (ϕ : Wk)  t [ σ ] [ ϕ ]  t [ σ [ ϕ ] ]
wk-app-subst t σ ϕ = trans (sym (conv-equiv (subst-app t σ) ϕ))
                    (trans (subst-app-comb t σ (conv ϕ))
                           (subst-transp t (conv-equiv-subst σ ϕ)))

exp-wk-ext : (t : Exp) (σ : Subst) (s : Exp)  t [  ] [ σ  s ]  t [ σ ]
exp-wk-ext t σ s = trans (cong _[ σ  s ] (sym (conv-equiv t )))
                         (subst-app-comb t (conv ) (σ  s))

exp-wk-q : (t : Exp) (σ : Subst)  t [  ] [ q σ ]  t [ σ [  ] ]
exp-wk-q t σ = trans (cong _[ q σ ] (sym (conv-equiv t )))
                     (subst-app-comb t (conv ) (q σ))

exp-wk-q′ : (t : Exp) (σ : Subst)  t [  ] [ q σ ]  t [ σ ] [  ]
exp-wk-q′ t σ = trans (exp-wk-q t σ) (sym (wk-app-subst t σ ))

id-wk-conv : (ϕ : Wk)  id [ ϕ ]  conv ϕ
id-wk-conv ϕ = refl


wk-id-ext-equiv-gen : (n : ) (t : Exp) (ts : List Exp) (ϕ : Wk) 
                      t [ repeat q n (subst-exts id ts)  ] [ repeat q n ϕ ]  t [ repeat q (n + L.length ts) ϕ ] [ repeat q n (subst-exts id (L.map (_[ ϕ ]) ts)) ]
wk-id-ext-equiv-gen n (v x) ts ϕ       = helper n ts x
  where helper :  n ts x  repeat q n (subst-exts id ts) x [ repeat q n ϕ ]  repeat q n (subst-exts id (L.map (_[ ϕ ]) ts)) (repeat q (n + L.length ts) ϕ x)
        helper zero [] x               = refl
        helper zero (t  ts) zero      = refl
        helper zero (t  ts) (suc x)   = helper zero ts x
        helper (suc n) ts zero         = refl
        helper (suc n) ts (suc x)      = begin
          (repeat q n (subst-exts id ts) [  ]) x [ repeat q (1 + n) ϕ ]
            ≡⟨ wk-comp-q 0 (repeat subst-q n (subst-exts id ts) x) (repeat q n ϕ) 
          repeat q n (subst-exts id ts) x [ repeat q n ϕ ] [  ]
            ≡⟨ cong (_[  ]) (helper n ts x) 
          (repeat q n (subst-exts id (L.map (_[ ϕ ]) ts)) [  ]) (repeat q (n + L.length ts) ϕ x)
            
          where open ≡-Reasoning
wk-id-ext-equiv-gen n ze ts ϕ          = refl
wk-id-ext-equiv-gen n (su t) ts ϕ      = cong su (wk-id-ext-equiv-gen n t ts ϕ)
wk-id-ext-equiv-gen n (rec T s r t) ts ϕ
  rewrite wk-id-ext-equiv-gen n s ts ϕ
        | wk-id-ext-equiv-gen (2 + n) r ts ϕ
        | wk-id-ext-equiv-gen n t ts ϕ = refl
wk-id-ext-equiv-gen n (Λ t) ts ϕ       = cong Λ (wk-id-ext-equiv-gen (1 + n) t ts ϕ)
wk-id-ext-equiv-gen n (t $ s) ts ϕ     = cong₂ _$_ (wk-id-ext-equiv-gen n t ts ϕ) (wk-id-ext-equiv-gen n s ts ϕ)

wk-id-ext-equiv : (t : Exp) (ts : List Exp) (ϕ : Wk) 
                  t [ subst-exts id ts  ] [ ϕ ]  t [ repeat q (L.length ts) ϕ ] [ subst-exts id (L.map (_[ ϕ ]) ts) ]
wk-id-ext-equiv = wk-id-ext-equiv-gen 0

wk-id-ext₁ : (t s : Exp) (ϕ : Wk)  t [ id  s ] [ ϕ ]  t [ q ϕ ] [ id  (s [ ϕ ]) ]
wk-id-ext₁ t s = wk-id-ext-equiv t (s  [])

wk-id-ext₂ : (t s u : Exp) (ϕ : Wk)  t [ id  s  u ] [ ϕ ]  t [ q (q ϕ) ] [ id  (s [ ϕ ])  (u [ ϕ ]) ]
wk-id-ext₂ t s u = wk-id-ext-equiv t (u  s  [])

wk-subst-q : (σ : Subst) (ϕ : Wk)  q σ [ q ϕ ]  q (σ [ ϕ ])
wk-subst-q σ ϕ zero    = refl
wk-subst-q σ ϕ (suc x) = wk-comp-q 0 (σ x) ϕ

wk-subst-q₂ : (σ : Subst) (ϕ : Wk)  q (q σ) [ q (q ϕ) ]  q (q (σ [ ϕ ]))
wk-subst-q₂ σ ϕ = ≗.trans (wk-subst-q (q σ) (q ϕ)) (subst-q-cong (wk-subst-q σ ϕ))

subst-q-ext-gen : (n : ) (t : Exp) (ts : List Exp) (σ : Subst) 
                     t [ repeat q (n + L.length ts) σ ] [ repeat q n (subst-exts id ts) ]  t [ repeat q n (subst-exts σ ts) ]
subst-q-ext-gen n (v x) ts σ       = helper n ts x
  where helper :  n ts x  repeat q (n + L.length ts) σ x [ repeat q n (subst-exts id ts) ]  repeat q n (subst-exts σ ts) x
        helper zero [] x             = subst-app-id (σ x)
        helper zero (t  ts) zero    = refl
        helper zero (t  ts) (suc x) = trans (exp-wk-ext (repeat q (L.length ts) σ x) (subst-exts id ts) t) (helper zero ts x)
        helper (suc n) ts zero       = refl
        helper (suc n) ts (suc x)    = trans (exp-wk-q′ (repeat q (n + L.length ts) σ x) (repeat q n (subst-exts id ts))) (cong (_[  ]) (helper n ts x))
subst-q-ext-gen n ze ts σ          = refl
subst-q-ext-gen n (su t) ts σ      = cong su (subst-q-ext-gen n t ts σ)
subst-q-ext-gen n (rec T s r t) ts σ
  rewrite subst-q-ext-gen n s ts σ
        | subst-q-ext-gen (2 + n) r ts σ
        | subst-q-ext-gen n t ts σ = refl
subst-q-ext-gen n (Λ t) ts σ       = cong Λ (subst-q-ext-gen (1 + n) t ts σ)
subst-q-ext-gen n (t $ s) ts σ     = cong₂ _$_ (subst-q-ext-gen n t ts σ) (subst-q-ext-gen n s ts σ)

subst-q-ext : (t : Exp) (ts : List Exp) (σ : Subst)  t [ repeat q (L.length ts) σ ] [ subst-exts id ts ]  t [ subst-exts σ ts ]
subst-q-ext = subst-q-ext-gen 0

subst-q-ext₁ : (t s : Exp) (σ : Subst)  t [ q σ ] [ id  s ]  t [ σ  s ]
subst-q-ext₁ t s = subst-q-ext t (s  [])

subst-q-ext₂ : (t s s′ : Exp) (σ : Subst)  t [ q (q σ) ] [ id  s  s′ ]  t [ σ  s  s′ ]
subst-q-ext₂ t s s′ = subst-q-ext t (s′  s  [])

subst-exts-comb-gen : (σ τ : Subst) (ts : List Exp)  subst-exts σ ts  τ  subst-exts (σ  τ) (L.map (_[ τ ]) ts)
subst-exts-comb-gen σ τ [] = ≗.refl
subst-exts-comb-gen σ τ (s  ts) = ≗.trans (ext-comp (subst-exts σ ts) τ s) (subst-ext-cong (subst-exts-comb-gen σ τ ts) refl)

subst-ext-app-gen : (t : Exp) (σ τ : Subst) (ts : List Exp)  t [ subst-exts σ ts ] [ τ ]  t [ subst-exts (σ  τ) (L.map (_[ τ ]) ts) ]
subst-ext-app-gen t σ τ ts = trans (subst-app-comb t (subst-exts σ ts) τ) (subst-transp t (subst-exts-comb-gen σ τ ts))

subst-ext-app₁ : (t s : Exp) (σ τ : Subst)  t [ σ  s ] [ τ ]  t [ (σ  τ)  (s [ τ ]) ]
subst-ext-app₁ t s σ τ = subst-ext-app-gen t σ τ (s  [])

subst-ext-app₂ : (t s s′ : Exp) (σ τ : Subst)  t [ σ  s  s′ ] [ τ ]  t [ (σ  τ)  (s [ τ ])  (s′ [ τ ]) ]
subst-ext-app₂ t s s′ σ τ = subst-ext-app-gen t σ τ (s′  s  [])