-- {-# OPTIONS --without-K #-}
module kripke where
open import common

infixl 2 _▻_
infixl 3 _‘’_
infixr 1 _‘→’_
infixr 1 _‘‘→’’_
infixr 1 _ww‘‘‘→’’’_
infixl 3 _‘’ₐ_
infixl 3 _w‘‘’’ₐ_
infixr 2 _‘∘’_
infixr 2 _‘×’_
infixr 2 _‘‘×’’_
infixr 2 _w‘‘×’’_

mutual
  data Context : Set where
    ε : Context
    _▻_ : (Γ : Context) → Type Γ → Context

  data Type : Context → Set where
    W : ∀ {Γ A} → Type Γ → Type (Γ ▻ A)
    W1 : ∀ {Γ A B} → Type (Γ ▻ B) → Type (Γ ▻ A ▻ (W {Γ = Γ} {A = A} B))
    _‘’_ : ∀ {Γ A} → Type (Γ ▻ A) → Term {Γ} A → Type Γ
    ‘Type’ : ∀ Γ → Type Γ
    ‘Term’ : ∀ {Γ} → Type (Γ ▻ ‘Type’ Γ)
    _‘→’_ : ∀ {Γ} → Type Γ → Type Γ → Type Γ
    _‘×’_ : ∀ {Γ} → Type Γ → Type Γ → Type Γ
    Quine : ∀ {Γ} → Type (Γ ▻ ‘Type’ Γ) → Type Γ
    ‘⊤’ : ∀ {Γ} → Type Γ
    ‘⊥’ : ∀ {Γ} → Type Γ

  data Term : {Γ : Context} → Type Γ → Set where
    ⌜_⌝ : ∀ {Γ} → Type Γ → Term {Γ} (‘Type’ Γ)
    ⌜_⌝t : ∀ {Γ T} → Term {Γ} T → Term {Γ} (‘Term’ ‘’ ⌜ T ⌝)
    ‘⌜‘VAR₀’⌝t’ : ∀ {Γ T} → Term {Γ ▻ ‘Term’ ‘’ ⌜ T ⌝} (W (‘Term’ ‘’ ⌜ ‘Term’ ‘’ ⌜ T ⌝ ⌝))
    ‘⌜‘VAR₀’⌝’ : ∀ {Γ} → Term {Γ ▻ ‘Type’ Γ} (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))
    ‘λ∙’ : ∀ {Γ A B} → Term {Γ ▻ A} (W B) → Term {Γ} (A ‘→’ B)
    ‘VAR₀’ : ∀ {Γ T} → Term {Γ ▻ T} (W T)
    _‘’ₐ_ : ∀ {Γ A B} → Term {Γ} (A ‘→’ B) → Term {Γ} A → Term {Γ} B
    ‘‘×'’’ : ∀ {Γ} → Term {Γ} (‘Type’ Γ ‘→’ ‘Type’ Γ ‘→’ ‘Type’ Γ)
    quine→ : ∀ {Γ φ} → Term {Γ} (Quine φ        ‘→’ φ ‘’ ⌜ Quine φ ⌝)
    quine← : ∀ {Γ φ} → Term {Γ} (φ ‘’ ⌜ Quine φ ⌝ ‘→’ Quine φ)
    ‘tt’ : ∀ {Γ} → Term {Γ} ‘⊤’
    SW : ∀ {Γ X A} {a : Term A} → Term {Γ} (W X ‘’ a) → Term X
    →SW1SV→W : ∀ {Γ T X A B} {x : Term X}
      → Term {Γ} (T ‘→’ (W1 A ‘’ ‘VAR₀’ ‘→’ W B) ‘’ x)
      → Term {Γ} (T ‘→’ A ‘’ x ‘→’ B)
    ←SW1SV→W : ∀ {Γ T X A B} {x : Term X}
      → Term {Γ} ((W1 A ‘’ ‘VAR₀’ ‘→’ W B) ‘’ x ‘→’ T)
      → Term {Γ} ((A ‘’ x ‘→’ B) ‘→’ T)
    →SW1SV→SW1SV→W : ∀ {Γ T X A B} {x : Term X}
      → Term {Γ} (T ‘→’ (W1 A ‘’ ‘VAR₀’ ‘→’ W1 A ‘’ ‘VAR₀’ ‘→’ W B) ‘’ x)
      → Term {Γ} (T ‘→’ A ‘’ x ‘→’ A ‘’ x ‘→’ B)
    ←SW1SV→SW1SV→W : ∀ {Γ T X A B} {x : Term X}
      → Term {Γ} ((W1 A ‘’ ‘VAR₀’ ‘→’ W1 A ‘’ ‘VAR₀’ ‘→’ W B) ‘’ x ‘→’ T)
      → Term {Γ} ((A ‘’ x ‘→’ A ‘’ x ‘→’ B) ‘→’ T)
    w : ∀ {Γ A T} → Term {Γ} A → Term {Γ ▻ T} (W A)
    w→ : ∀ {Γ A B X} → Term {Γ ▻ X} (W (A ‘→’ B)) → Term {Γ ▻ X} (W A ‘→’ W B)
    →w : ∀ {Γ A B X} → Term {Γ ▻ X} (W A ‘→’ W B) → Term {Γ ▻ X} (W (A ‘→’ B))
    ww→ : ∀ {Γ A B X Y} → Term {Γ ▻ X ▻ Y} (W (W (A ‘→’ B))) → Term {Γ ▻ X ▻ Y} (W (W A) ‘→’ W (W B))
    →ww : ∀ {Γ A B X Y} → Term {Γ ▻ X ▻ Y} (W (W A) ‘→’ W (W B)) → Term {Γ ▻ X ▻ Y} (W (W (A ‘→’ B)))
    _‘∘’_ : ∀ {Γ A B C} → Term {Γ} (B ‘→’ C) → Term {Γ} (A ‘→’ B) → Term {Γ} (A ‘→’ C)
    _w‘‘’’ₐ_ : ∀ {Γ A B T} → Term {Γ ▻ T} (W (‘Term’ ‘’ ⌜ A ‘→’ B ⌝)) → Term {Γ ▻ T} (W (‘Term’ ‘’ ⌜ A ⌝)) → Term {Γ ▻ T} (W (‘Term’ ‘’ ⌜ B ⌝))
    ‘‘’ₐ’ : ∀ {Γ A B} → Term {Γ} (‘Term’ ‘’ ⌜ A ‘→’ B ⌝ ‘→’ ‘Term’ ‘’ ⌜ A ⌝ ‘→’ ‘Term’ ‘’ ⌜ B ⌝)
    -- _w‘‘’’_ : ∀ {Γ A B T} → Term {Γ ▻ T} (‘Type’ (Γ ▻ T)) → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ T ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Type’ Γ)))
    ‘‘□’’ : ∀ {Γ A B} → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Type’ Γ)))
    -- ‘‘’’' : ∀ {Γ A} → Term {Γ ▻ A} (‘Type’ (Γ ▻ A) ‘→’ W (‘Term’ ‘’ ⌜ A ⌝) ‘→’ W (‘Type’ Γ))
    _‘‘→’’_ : ∀ {Γ} → Term {Γ} (‘Type’ Γ) → Term {Γ} (‘Type’ Γ) → Term {Γ} (‘Type’ Γ)
    _ww‘‘‘→’’’_ : ∀ {Γ A B} → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝)))
    _ww‘‘‘×’’’_ : ∀ {Γ A B} → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝)))

□ : Type ε → Set _
□ = Term {ε}

‘□’ : ∀ {Γ} → Type Γ → Type Γ
‘□’ T = ‘Term’ ‘’ ⌜ T ⌝

_‘‘×’’_ : ∀ {Γ} → Term {Γ} (‘Type’ Γ) → Term {Γ} (‘Type’ Γ) → Term {Γ} (‘Type’ Γ)
A ‘‘×’’ B = ‘‘×'’’ ‘’ₐ A ‘’ₐ B

max-level : Level
max-level = lzero

mutual
  Context⇓ : (Γ : Context) → Set (lsuc max-level)
  Context⇓ ε = ⊤
  Context⇓ (Γ ▻ T) = Σ (Context⇓ Γ) (Type⇓ {Γ} T)

  Type⇓ : {Γ : Context} → Type Γ → Context⇓ Γ → Set max-level
  Type⇓ (W T) Γ⇓ = Type⇓ T (Σ.proj₁ Γ⇓)
  Type⇓ (W1 T) Γ⇓ = Type⇓ T ((Σ.proj₁ (Σ.proj₁ Γ⇓)) , (Σ.proj₂ Γ⇓))
  Type⇓ (T ‘’ x) Γ⇓ = Type⇓ T (Γ⇓ , Term⇓ x Γ⇓)
  Type⇓ (‘Type’ Γ) Γ⇓ = Lifted (Type Γ)
  Type⇓ ‘Term’ Γ⇓ = Lifted (Term (lower (Σ.proj₂ Γ⇓)))
  Type⇓ (A ‘→’ B) Γ⇓ = Type⇓ A Γ⇓ → Type⇓ B Γ⇓
  Type⇓ (A ‘×’ B) Γ⇓ = Type⇓ A Γ⇓ × Type⇓ B Γ⇓
  Type⇓ ‘⊤’ Γ⇓ = ⊤
  Type⇓ ‘⊥’ Γ⇓ = ⊥
  Type⇓ (Quine φ) Γ⇓ = Type⇓ φ (Γ⇓ , (lift (Quine φ)))

  Term⇓ : ∀ {Γ : Context} {T : Type Γ} → Term T → (Γ⇓ : Context⇓ Γ) → Type⇓ T Γ⇓
  Term⇓ ⌜ x ⌝ Γ⇓ = lift x
  Term⇓ ⌜ x ⌝t Γ⇓ = lift x
  Term⇓ ‘⌜‘VAR₀’⌝t’ Γ⇓ = lift ⌜ (lower (Σ.proj₂ Γ⇓)) ⌝t
  Term⇓ ‘⌜‘VAR₀’⌝’ Γ⇓ = lift ⌜ (lower (Σ.proj₂ Γ⇓)) ⌝
  Term⇓ (f ‘’ₐ x) Γ⇓ = Term⇓ f Γ⇓ (Term⇓ x Γ⇓)
  Term⇓ ‘tt’ Γ⇓ = tt
  Term⇓ (quine→ {φ}) Γ⇓ x = x
  Term⇓ (quine← {φ}) Γ⇓ x = x
  Term⇓ (‘λ∙’ f) Γ⇓ x = Term⇓ f (Γ⇓ , x)
  Term⇓ ‘VAR₀’ Γ⇓ = Σ.proj₂ Γ⇓
  Term⇓ (SW t) = Term⇓ t
  Term⇓ (←SW1SV→W f) = Term⇓ f
  Term⇓ (→SW1SV→W f) = Term⇓ f
  Term⇓ (←SW1SV→SW1SV→W f) = Term⇓ f
  Term⇓ (→SW1SV→SW1SV→W f) = Term⇓ f
  Term⇓ (w x) Γ⇓ = Term⇓ x (Σ.proj₁ Γ⇓)
  Term⇓ (w→ f) Γ⇓ = Term⇓ f Γ⇓
  Term⇓ (→w f) Γ⇓ = Term⇓ f Γ⇓
  Term⇓ (ww→ f) Γ⇓ = Term⇓ f Γ⇓
  Term⇓ (→ww f) Γ⇓ = Term⇓ f Γ⇓
  Term⇓ ‘‘×'’’ Γ⇓ A B = lift (lower A ‘×’ lower B)
  Term⇓ (g ‘∘’ f) Γ⇓ x = Term⇓ g Γ⇓ (Term⇓ f Γ⇓ x)
  Term⇓ (f w‘‘’’ₐ x) Γ⇓ = lift (lower (Term⇓ f Γ⇓) ‘’ₐ lower (Term⇓ x Γ⇓))
  Term⇓ ‘‘’ₐ’ Γ⇓ f x = lift (lower f ‘’ₐ lower x)
  -- Term⇓ (f w‘‘’’ x) Γ⇓ = lift {!!} --(lower (Term⇓ f Γ⇓) ‘’ lower (Term⇓ x Γ⇓))
  Term⇓ (‘‘□’’ {Γ} T) Γ⇓ = lift (‘Term’ ‘’ lower (Term⇓ T Γ⇓))
  Term⇓ (A ‘‘→’’ B) Γ⇓ = lift ((lower (Term⇓ A Γ⇓)) ‘→’ (lower (Term⇓ B Γ⇓)))
  Term⇓ (A ww‘‘‘→’’’ B) Γ⇓ = lift ((lower (Term⇓ A Γ⇓)) ‘‘→’’ (lower (Term⇓ B Γ⇓)))
  Term⇓ (A ww‘‘‘×’’’ B) Γ⇓ = lift ((lower (Term⇓ A Γ⇓)) ‘‘×’’ (lower (Term⇓ B Γ⇓)))

module inner (‘X’ : Type ε) (‘f’ : Term {ε} (‘□’ ‘X’ ‘→’ ‘X’)) where
  ‘H’ : Type ε
  ‘H’ = Quine (W1 ‘Term’ ‘’ ‘VAR₀’ ‘→’ W ‘X’)

  ‘toH’ : □ ((‘□’ ‘H’ ‘→’ ‘X’) ‘→’ ‘H’)
  ‘toH’ = ←SW1SV→W quine←

  ‘fromH’ : □ (‘H’ ‘→’ (‘□’ ‘H’ ‘→’ ‘X’))
  ‘fromH’ = →SW1SV→W quine→

  ‘□‘H’→□‘X’’ : □ (‘□’ ‘H’ ‘→’ ‘□’ ‘X’)
  ‘□‘H’→□‘X’’ = ‘λ∙’ (w ⌜ ‘fromH’ ⌝t w‘‘’’ₐ ‘VAR₀’ w‘‘’’ₐ ‘⌜‘VAR₀’⌝t’)

  ‘h’ : Term ‘H’
  ‘h’ = ‘toH’ ‘’ₐ (‘f’ ‘∘’ ‘□‘H’→□‘X’’)

  Lӧb : □ ‘X’
  Lӧb = ‘fromH’ ‘’ₐ ‘h’ ‘’ₐ ⌜ ‘h’ ⌝t

Lӧb : ∀ {X} → Term {ε} (‘□’ X ‘→’ X) → Term {ε} X
Lӧb {X} f = inner.Lӧb X f

⌞_⌟ : Type ε → Set _
⌞ T ⌟ = Type⇓ T tt

‘¬’_ : ∀ {Γ} → Type Γ → Type Γ
‘¬’ T = T ‘→’ ‘⊥’

_w‘‘×’’_ : ∀ {Γ X} → Term {Γ ▻ X} (W (‘Type’ Γ)) → Term {Γ ▻ X} (W (‘Type’ Γ)) → Term {Γ ▻ X} (W (‘Type’ Γ))
A w‘‘×’’ B = w→ (w→ (w ‘‘×'’’) ‘’ₐ A) ‘’ₐ B

lӧb : ∀ {‘X’} → □ (‘□’ ‘X’ ‘→’ ‘X’) → ⌞ ‘X’ ⌟
lӧb f = Term⇓ (Lӧb f) tt

incompleteness : ¬ □ (‘¬’ (‘□’ ‘⊥’))
incompleteness = lӧb

soundness : ¬ □ ‘⊥’
soundness x = Term⇓ x tt

non-emptyness : Σ (Type ε) (λ T → □ T)
non-emptyness = ‘⊤’ , ‘tt’

module helpers where
  Wₕ : ∀ {Γ A} → Type Γ → Type (Γ ▻ A)
  Wₕ ‘⊤’ = ‘⊤’
  Wₕ ‘⊥’ = ‘⊥’
  Wₕ (T ‘→’ T₂) = Wₕ T ‘→’ Wₕ T₂
  Wₕ (T ‘×’ T₂) = Wₕ T ‘×’ Wₕ T₂
  Wₕ (W T₂) = W (Wₕ T₂)
  Wₕ T = W T

  _‘’ₕ_ : ∀ {Γ A} → Type (Γ ▻ A) → Term A → Type Γ
  ‘⊤’ ‘’ₕ x₁ = ‘⊤’
  ‘⊥’ ‘’ₕ x₁ = ‘⊥’
  W f ‘’ₕ x₁ = f
  ‘Term’ ‘’ₕ ⌜ ‘⊤’ ⌝ = ‘⊤’
  (f ‘→’ f₁) ‘’ₕ x₁ = (f ‘’ₕ x₁) ‘→’ (f₁ ‘’ₕ x₁)
  (f ‘×’ f₁) ‘’ₕ x₁ = (f ‘’ₕ x₁) ‘×’ (f₁ ‘’ₕ x₁)
  f ‘’ₕ x = f ‘’ x

  _‘→’ₕ_ : ∀ {Γ} → Type Γ → Type Γ → Type Γ
  ‘⊤’ ‘→’ₕ B = B
  ‘⊥’ ‘→’ₕ B = ‘⊤’
  A ‘→’ₕ ‘⊤’ = ‘⊤’
  A ‘→’ₕ B = A ‘→’ B

  _‘×’ₕ_ : ∀ {Γ} → Type Γ → Type Γ → Type Γ
  ‘⊤’ ‘×’ₕ B = B
  ‘⊥’ ‘×’ₕ B = ‘⊥’
  A ‘×’ₕ ‘⊤’ = A
  A ‘×’ₕ ‘⊥’ = ‘⊥’
  A ‘×’ₕ B = A ‘×’ B

open helpers

mutual
  simplify-type : ∀ {Γ} → Type Γ → Type Γ
  simplify-type ‘⊤’ = ‘⊤’
  simplify-type ‘⊥’ = ‘⊥’
  simplify-type (T ‘→’ T₁) = simplify-type T ‘→’ₕ simplify-type T₁
  simplify-type (T ‘×’ T₁) = simplify-type T ‘×’ₕ simplify-type T₁
  simplify-type (T₁ ‘’ x) = simplify-type T₁ ‘’ₕ simplify-term x
  simplify-type (W T₁) = Wₕ (simplify-type T₁)
  simplify-type (W1 T₂) = W1ₕ (simplify-type T₂)
    where W1ₕ : ∀ {Γ A B} → Type (Γ ▻ A) → Type (Γ ▻ B ▻ W A)
          W1ₕ ‘⊤’ = ‘⊤’
          W1ₕ ‘⊥’ = ‘⊥’
          W1ₕ (T ‘→’ T₂) = W1ₕ T ‘→’ W1ₕ T₂
          W1ₕ (T ‘×’ T₂) = W1ₕ T ‘×’ W1ₕ T₂
          W1ₕ (W T₂) = W (Wₕ T₂)
          W1ₕ (W1 T₂) = W1 (W1ₕ T₂)
          W1ₕ T = W1 T
  simplify-type (‘Type’ Γ) = ‘Type’ Γ
  simplify-type ‘Term’ = ‘Term’
  simplify-type (Quine T) = Quine (simplify-type T)

  simplify-term : ∀ {Γ T} → Term {Γ} T → Term T
  simplify-term ⌜ x ⌝ = ⌜ simplify-type x ⌝
  simplify-term ⌜ t ⌝t = ⌜ simplify-term t ⌝t
  simplify-term ‘⌜‘VAR₀’⌝t’ = ‘⌜‘VAR₀’⌝t’
  simplify-term ‘⌜‘VAR₀’⌝’ = ‘⌜‘VAR₀’⌝’
  simplify-term (‘λ∙’ t) = ‘λ∙’ (simplify-term t)
  simplify-term ‘VAR₀’ = ‘VAR₀’
  simplify-term (t ‘’ₐ t₁) = simplify-term t ‘’ₐₕ simplify-term t₁
    where _‘’ₐₕ_ : ∀ {Γ A T} → Term {Γ} (A ‘→’ T) → Term A → Term T
          ‘λ∙’ ‘⌜‘VAR₀’⌝t’ ‘’ₐₕ x = ⌜ x ⌝t
          ‘λ∙’ ‘⌜‘VAR₀’⌝’ ‘’ₐₕ x = ⌜ x ⌝t
          ‘λ∙’ ‘VAR₀’ ‘’ₐₕ x = x
          ‘λ∙’ (f ‘’ₐ f₁) ‘’ₐₕ x = ‘λ∙’ (f ‘’ₐₕ f₁) ‘’ₐ x
          ‘λ∙’ (w f) ‘’ₐₕ x = f
          (f ‘’ₐ f₁) ‘’ₐₕ x = (f ‘’ₐₕ f₁) ‘’ₐ x
          (f ‘∘’ f₁) ‘’ₐₕ x = f ‘’ₐₕ (f₁ ‘’ₐₕ x)
          f ‘’ₐₕ x = f ‘’ₐ x
  simplify-term ‘‘×'’’ = ‘‘×'’’
  simplify-term quine→ = quine→
  simplify-term quine← = quine←
  simplify-term ‘tt’ = ‘tt’
  simplify-term (SW t₁) = SW (simplify-term t₁)
  simplify-term (→SW1SV→W t₁) = →SW1SV→W (simplify-term t₁)
  simplify-term (←SW1SV→W t₁) = ←SW1SV→W (simplify-term t₁)
  simplify-term (→SW1SV→SW1SV→W t₁) = →SW1SV→SW1SV→W (simplify-term t₁)
  simplify-term (←SW1SV→SW1SV→W t₁) = ←SW1SV→SW1SV→W (simplify-term t₁)
  simplify-term (w t) = w (simplify-term t)
  simplify-term (w→ t) = w→ (simplify-term t)
  simplify-term (→w t) = →w (simplify-term t)
  simplify-term (ww→ t) = ww→ (simplify-term t)
  simplify-term (→ww t) = →ww (simplify-term t)
  simplify-term (t ‘∘’ t₁) = simplify-term t ‘∘’ simplify-term t₁
  simplify-term (t w‘‘’’ₐ t₁) = simplify-term t w‘‘’’ₐ simplify-term t₁
  simplify-term ‘‘’ₐ’ = ‘‘’ₐ’
  simplify-term (‘‘□’’ t) = ‘‘□’’ (simplify-term t)
  simplify-term (t ‘‘→’’ t₁) = simplify-term t ‘‘→’’ simplify-term t₁
  simplify-term (t ww‘‘‘→’’’ t₁) = simplify-term t ww‘‘‘→’’’ simplify-term t₁
  simplify-term (t ww‘‘‘×’’’ t₁) = simplify-term t ww‘‘‘×’’’ simplify-term t₁
{-
mutual
  simplify-type⇓→ : ∀ {Γ T Γ⇓} → Type⇓ {Γ} T Γ⇓ → Type⇓ {Γ} (simplify-type T) Γ⇓
  simplify-type⇓→ {T = T ‘→’ T₁} T⇓ = {!!}
  simplify-type⇓→ {T = W T₁} T⇓ = {!!}
  simplify-type⇓→ {T = W1 T₂} T⇓ = {!!}
  simplify-type⇓→ {T = T₁ ‘’ x} T⇓ = {!!}
  simplify-type⇓→ {T = ‘Type’ ._} T⇓ = {!!}
  simplify-type⇓→ {T = ‘Term’} T⇓ = {!!}
  simplify-type⇓→ {T = T ‘×’ T₁} T⇓ = {!!}
  simplify-type⇓→ {T = Quine T} T⇓ = {!!}
  simplify-type⇓→ {T = ‘⊤’} T⇓ = tt
  simplify-type⇓→ {T = ‘⊥’} T⇓ = T⇓

  simplify-type⇓← : ∀ {Γ T Γ⇓} → Type⇓ {Γ} (simplify-type T) Γ⇓ → Type⇓ {Γ} T Γ⇓
  simplify-type⇓← {T = W T₁} T⇓ = {!!}
  simplify-type⇓← {T = W1 T₂} T⇓ = {!!}
  simplify-type⇓← {T = T₁ ‘’ x} T⇓ = {!!}
  simplify-type⇓← {T = ‘Type’ ._} T⇓ = {!!}
  simplify-type⇓← {T = ‘Term’} T⇓ = {!!}
  simplify-type⇓← {T = T ‘→’ T₁} T⇓ x = {!!}
  simplify-type⇓← {Γ} {T ‘×’ T₁} {Γ⇓} T⇓ = helper {Γ} {simplify-type T} {simplify-type T₁} {T} {T₁} {Γ⇓} (simplify-type⇓← {T = T}) (simplify-type⇓← {T = T₁}) T⇓
    where helper : ∀ {Γ A B A' B'} {Γ⇓ : Context⇓ Γ}
                 → (Type⇓ A Γ⇓ → Type⇓ A' Γ⇓)
                 → (Type⇓ B Γ⇓ → Type⇓ B' Γ⇓)
                 → Type⇓ (A ‘×’ₕ B) Γ⇓
                 → Type⇓ (A' ‘×’ B') Γ⇓
          helper {A = ‘⊤’} f g x = f tt , g x
          helper {A = ‘⊥’} f g ()
          helper {A = W A₁} {‘⊤’} f g x = f x , g tt
          helper {A = W1 A₂} {‘⊤’} f g x = f x , g tt
          helper {A = A₁ ‘’ x} {‘⊤’} f g x₁ = f x₁ , g tt
          helper {A = ‘Type’ ._} {‘⊤’} f g x = f x , g tt
          helper {A = ‘Term’} {‘⊤’} f g x = f x , g tt
          helper {A = A ‘→’ A₁} {‘⊤’} f g x = f x , g tt
          helper {A = A ‘×’ A₁} {‘⊤’} f g x = f x , g tt
          helper {A = Quine A} {‘⊤’} f g x = f x , g tt
          helper {A = W A₁} {‘⊥’} {Γ⇓ = proj₁ , proj₂} f g ()
          helper {A = W1 A₂} {‘⊥’} {Γ⇓ = proj₁ , proj₂ , proj₃} f g ()
          helper {A = A₁ ‘’ x} {‘⊥’} f g ()
          helper {A = ‘Type’ ._} {‘⊥’} f g ()
          helper {A = ‘Term’} {‘⊥’} {Γ⇓ = proj₁ , proj₂} f g ()
          helper {A = A ‘→’ A₁} {‘⊥’} f g ()
          helper {A = A ‘×’ A₁} {‘⊥’} f g ()
          helper {A = Quine A} {‘⊥’} f g ()
          helper {A = W A₁} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {A = W1 A₂} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {A = A₂ ‘’ _} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = {!f (Σ.proj₁ x) , g (Σ.proj₂ x)!}
          helper {A = ‘Type’ ._} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {A = ‘Term’} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {A = A₁ ‘→’ A₂} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {A = A₁ ‘×’ A₂} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {A = Quine A₁} {W B₁} {Γ⇓ = proj₁ , proj₂} f g x = f (Σ.proj₁ x) , g (Σ.proj₂ x)
          helper {B = W1 B₂} f g x = {!!}
          helper {B = B₁ ‘’ x} f g x₁ = {!!}
          helper {B = ‘Type’ ._} f g x = {!!}
          helper {B = ‘Term’} f g x = {!!}
          helper {B = B ‘→’ B₁} f g x = {!!}
          helper {B = B ‘×’ B₁} f g x = {!!}
          helper {B = Quine B} f g x = {!!} {-
          helper {A = W A₁} f g x = {!!}
          helper {A = W1 A₂} f g x = {!!}
          helper {A = A₁ ‘’ x} f g x₁ = {!!}
          helper {A = ‘Type’ ._} f g x = {!!}
          helper {A = ‘Term’} f g x = {!!}
          helper {A = A ‘→’ A₁} f g x = {!!}
          helper {A = A ‘×’ A₁} f g x = {!!}
          helper {A = Quine A} f g x = {!!} -}
  simplify-type⇓← {T = Quine T} T⇓ = {!!}
  simplify-type⇓← {T = ‘⊤’} T⇓ = tt
  simplify-type⇓← {T = ‘⊥’} T⇓ = T⇓
-}

module modal-fixpoint where
  context-to-term : Context → Set
  context-to-term ε = ⊤
  context-to-term (Γ ▻ x) = Σ (context-to-term Γ) (λ _ → Term x)

  context-to-term⇓ : ∀ {Γ} → context-to-term Γ → Context⇓ Γ
  context-to-term⇓ {ε} v = tt
  context-to-term⇓ {Γ ▻ x} v = (context-to-term⇓ (Σ.proj₁ v)) , (Term⇓ (Σ.proj₂ v) (context-to-term⇓ (Σ.proj₁ v)))

  Type-in : ∀ {Γ} → Type Γ → context-to-term Γ → Type ε
  Type-in {ε} T v = T
  Type-in (T ‘→’ T₁) v = (Type-in T v) ‘→’ (Type-in T₁ v)
  Type-in ‘⊤’ v = ‘⊤’
  Type-in ‘⊥’ v = ‘⊥’
  Type-in {Γ ▻ x} T v = Type-in (T ‘’ Σ.proj₂ v) (Σ.proj₁ v)

  postulate hold : ∀ {Γ} → ℕ → Type Γ → Type Γ
  postulate hold-t : ∀ {Γ T} → ℕ → Term {Γ} T → Term T

  helper-ikf : ∀ {Γ A} world (T₁ : Type (Γ ▻ A)) (x : Term A) → Type Γ
  helper-ikf zero ‘Term’ x₁ = ‘⊤’
  helper-ikf world f (x₁ ‘‘→’’ x₂) = helper-ikf world f x₁ ‘→’ helper-ikf world f x₂
  helper-ikf (suc world) ‘Term’ ⌜ x₁ ⌝ = x₁
  helper-ikf (suc world) ‘Term’ (x₁ ‘’ₐ x₂) = hold (suc world) (‘Term’ ‘’ (x₁ ‘’ₐ x₂))
  helper-ikf (suc world) ‘Term’ (SW x₂) = hold (suc world) (‘Term’ ‘’ SW x₂)
  helper-ikf world (W T₂) x₁ = T₂
  helper-ikf world (W1 T₄) x₁ = hold world (W1 T₄ ‘’ x₁)
  helper-ikf world (T₃ ‘’ x₁) x₂ = hold world (T₃ ‘’ x₁ ‘’ x₂)
  helper-ikf world (‘Type’ ._) x₁ = hold world (‘Type’ _ ‘’ x₁)
  helper-ikf world (T₂ ‘→’ T₃) x₁ = helper-ikf world T₂ x₁ ‘→’ helper-ikf world T₃ x₁
  helper-ikf world (T₂ ‘×’ T₃) x₁ = helper-ikf world T₂ x₁ ‘×’ helper-ikf world T₃ x₁
  helper-ikf world (Quine T₂) x₁ = hold world (Quine T₂ ‘’ x₁)
  helper-ikf world ‘⊤’ x₁ = ‘⊤’
  helper-ikf world ‘⊥’ x₁ = ‘⊥’

  mutual
{-    _‘’ₐₕ_ : ∀ {Γ A B} {world} (f : Term {Γ} (A ‘→’ B)) (x : Term A) → Term B
    ‘λ∙’ ‘⌜‘VAR₀’⌝t’ ‘’ₐₕ x₂ = ⌜ x₂ ⌝t
    ‘λ∙’ ‘⌜‘VAR₀’⌝’ ‘’ₐₕ x₂ = ⌜ x₂ ⌝t
    ‘λ∙’ ‘VAR₀’ ‘’ₐₕ x₂ = x₂
    ‘λ∙’ (f ‘’ₐ f₁) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (SW f₁) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (w f) ‘’ₐₕ x₂ = f
    ‘λ∙’ (→w (‘λ∙’ ‘VAR₀’)) ‘’ₐₕ x₂ = ‘λ∙’ ‘VAR₀’
    ‘λ∙’ (→w (‘λ∙’ (f ‘’ₐ f₁))) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (‘λ∙’ (SW f₁))) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (‘λ∙’ (w f))) ‘’ₐₕ x₂ = ‘λ∙’ (w (‘λ∙’ f ‘’ₐₕ x₂))
    ‘λ∙’ (→w (‘λ∙’ (→ww f))) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (‘λ∙’ (‘‘□’’ f))) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (‘λ∙’ (f ww‘‘‘→’’’ f₁))) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (‘λ∙’ (f ww‘‘‘×’’’ f₁))) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (f ‘’ₐ f₁)) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (SW f₁)) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (w→ f)) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (ww→ f)) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→w (f ‘∘’ f₁)) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (→ww f) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (f w‘‘’’ₐ f₁) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (‘‘□’’ f) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (f ww‘‘‘→’’’ f₁) ‘’ₐₕ x₂ = {!!}
    ‘λ∙’ (f ww‘‘‘×’’’ f₁) ‘’ₐₕ x₂ = {!!}
    _‘’ₐₕ_ {world = world} (f ‘’ₐ f₁) x₂ = hold-t world ((_‘’ₐₕ_ {world = world} f f₁) ‘’ₐ x₂)
    _‘’ₐₕ_ {world = world} ‘‘×'’’ x₂ = {!!}
    _‘’ₐₕ_ {world = world} quine→ x₂ = {!!}
    _‘’ₐₕ_ {world = world} quine← x₂ = {!!}
    _‘’ₐₕ_ {world = world} (SW f₁) x₂ = {!!}
    _‘’ₐₕ_ {world = world} (→SW1SV→W f₁) x₂ = {!!}
    _‘’ₐₕ_ {world = world} (←SW1SV→W f₁) x₂ = {!!}
    →SW1SV→SW1SV→W (‘λ∙’ f₁) ‘’ₐₕ x₂ = {!!}
    →SW1SV→SW1SV→W (f₁ ‘’ₐ f₂) ‘’ₐₕ x₂ = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ⌜ x ⌝) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ⌜ x₃ ⌝t) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘⌜‘VAR₀’⌝t’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘⌜‘VAR₀’⌝’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ ‘⌜‘VAR₀’⌝t’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ ‘⌜‘VAR₀’⌝’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ ‘VAR₀’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (x₃ ‘’ₐ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (SW x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (w x₃)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ ‘VAR₀’))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (x₃ ‘’ₐ x₄)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (SW x₄)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w ‘⌜‘VAR₀’⌝t’)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w ‘⌜‘VAR₀’⌝’)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w ‘VAR₀’)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (x₃ ‘’ₐ x₄))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (SW x₄))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (‘λ∙’ x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ x₃ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (SW x₃ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→W (‘λ∙’ x₃) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→W (x₃ ‘’ₐ x₄) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→W (SW x₄) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→W (x₃ ‘∘’ x₄) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→SW1SV→W (‘λ∙’ x₃) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→SW1SV→W (x₃ ‘’ₐ x₄) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→SW1SV→W quine← ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x ⌝))))) = ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x ⌝))))
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→SW1SV→W (SW x₄) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→SW1SV→W (x₃ ‘∘’ x₄) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₁ ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ ((x₂ ‘∘’ x₃) ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x ⌝))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ⌜ x₃ ⌝t))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ‘⌜‘VAR₀’⌝t’))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ‘⌜‘VAR₀’⌝’))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (‘λ∙’ x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ‘VAR₀’))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (x₃ ‘’ₐ x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ‘‘×'’’))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w quine→))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w quine←))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ‘tt’))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (SW x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (→SW1SV→W x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (←SW1SV→W x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (→SW1SV→SW1SV→W x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (←SW1SV→SW1SV→W x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (w x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (w→ x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (→w x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (ww→ x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (→ww x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (x₃ ‘∘’ x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (x₃ w‘‘’’ₐ x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w ‘‘’ₐ’))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (‘‘□’’ x₃)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (x₃ ‘‘→’’ x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (x₃ ww‘‘‘→’’’ x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (w (x₃ ww‘‘‘×’’’ x₄)))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (→w x₃))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (→ww x₃))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (x₃ w‘‘’’ₐ x₄))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (‘‘□’’ x₃))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (x₃ ww‘‘‘→’’’ x₄))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (w (x₃ ww‘‘‘×’’’ x₄))))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (→ww x₃)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (‘‘□’’ x₃)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (x₃ ww‘‘‘→’’’ x₄)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (‘λ∙’ (x₃ ww‘‘‘×’’’ x₄)))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (x₃ ‘’ₐ x₄))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (SW x₄))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (w→ x₃))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (ww→ x₃))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→w (x₃ ‘∘’ x₄))) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (→ww x₃)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (x₃ w‘‘’’ₐ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (‘‘□’’ x₃)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (x₃ ww‘‘‘→’’’ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘λ∙’ (x₃ ww‘‘‘×’’’ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘VAR₀’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ (x₃ ‘’ₐ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘‘×'’’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ quine→) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ quine←) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘tt’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ SW x₄) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ →SW1SV→W x₄) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ←SW1SV→W x₄) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ →SW1SV→SW1SV→W x₄) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ←SW1SV→SW1SV→W x₄) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ w x₃) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ w→ x₃) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ →w x₃) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ww→ x₃) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ →ww x₃) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ (x₃ ‘∘’ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ (x₃ w‘‘’’ₐ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘‘’ₐ’) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ ‘‘□’’ x₃) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ (x₃ ‘‘→’’ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ (x₃ ww‘‘‘→’’’ x₄)) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (x₂ ‘’ₐ x₃ ww‘‘‘×’’’ x₄) = {!!}
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ SW x₃ = {!!}
    →SW1SV→SW1SV→W (SW f₂) ‘’ₐₕ x₂ = {!!}
    →SW1SV→SW1SV→W (←SW1SV→W f₂) ‘’ₐₕ x₂ = {!!}
    →SW1SV→SW1SV→W (←SW1SV→SW1SV→W f₂) ‘’ₐₕ x₂ = {!!}
    →SW1SV→SW1SV→W (f₁ ‘∘’ f₂) ‘’ₐₕ x₂ = {!!}
    ←SW1SV→SW1SV→W (‘λ∙’ f₁) ‘’ₐₕ x₂ = {!!}
    ←SW1SV→SW1SV→W (f₁ ‘’ₐ f₂) ‘’ₐₕ x₂ = {!!}
    ←SW1SV→SW1SV→W quine← ‘’ₐₕ x₂ = {!!} -- ←SW1SV→SW1SV→W quine← ‘’ₐ x₂
    ←SW1SV→SW1SV→W (SW f₂) ‘’ₐₕ x₂ = {!!}
    ←SW1SV→SW1SV→W (→SW1SV→W f₂) ‘’ₐₕ x₂ = {!!}
    ←SW1SV→SW1SV→W (→SW1SV→SW1SV→W f₂) ‘’ₐₕ x₂ = {!!}
    ←SW1SV→SW1SV→W (f₁ ‘∘’ f₂) ‘’ₐₕ x₂ = {!!}
    _‘’ₐₕ_ {world = world} (w→ f) x₂ = w→ f ‘’ₐ x₂
    _‘’ₐₕ_ {world = world} (ww→ f) x₂ = {!!}
    _‘’ₐₕ_ {world = world} (f ‘∘’ f₁) x₂ = _‘’ₐₕ_ {world = world} f (_‘’ₐₕ_ {world = world} f₁ x₂)
    _‘’ₐₕ_ {world = world} ‘‘’ₐ’ x₂ = {!!}

    ‘‘□’’ₕ : ∀ {Γ A B} → Term {Γ ▻ A ▻ B} (W (W (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝))) → Term {Γ ▻ A ▻ B} (W (W (‘Type’ Γ)))
    ‘‘□’’ₕ t = ‘‘□’’ t

    _‘∘’ₕ_ : ∀ {Γ A B C} {world} (g : Term {Γ} (B ‘→’ C)) (f : Term (A ‘→’ B)) → Term (A ‘→’ C)
    f ‘∘’ₕ g = f ‘∘’ g-}

    in-kripke-frame-c : ∀ (world : ℕ) → Context → Context
    in-kripke-frame : ∀ {Γ} (world : ℕ) → Type Γ → Type (in-kripke-frame-c world Γ)
    in-kripke-frame-t : ∀ {Γ} (world : ℕ) {T : Type Γ} → Term T → Term (in-kripke-frame world T)

    in-kripke-frame-c world ε = ε
    in-kripke-frame-c world (Γ ▻ x) = in-kripke-frame-c world Γ ▻ in-kripke-frame world x

    _‘’ₐₕ_ : ∀ {Γ A B} → Term {Γ} (A ‘→’ B) → Term {Γ} A → Term {Γ} B
    (f₁ ‘’ₐ f₂) ‘’ₐₕ x₁ = (f₁ ‘’ₐₕ f₂) ‘’ₐ x₁
    →SW1SV→SW1SV→W quine→ ‘’ₐₕ (←SW1SV→SW1SV→W quine← ‘’ₐ f) = f
    f ‘’ₐₕ x = {!!}

    in-kripke-frame world (T ‘→’ T₁) = (in-kripke-frame world T) ‘→’ (in-kripke-frame world T₁)
    in-kripke-frame world (‘Type’ Γ) = ‘Type’ (in-kripke-frame-c world Γ)
    in-kripke-frame world (W T₁) = W (in-kripke-frame world T₁)
    in-kripke-frame world (W1 T₂) = W1 (in-kripke-frame world T₂)
    in-kripke-frame zero (‘Term’ ‘’ x) = ‘⊤’
    in-kripke-frame {Γ = Γ} (suc world) (‘Term’ ‘’ (f ‘’ₐ x)) = helper helper'
      where helper' : Term (in-kripke-frame world (‘Type’ Γ))
            helper' = simplify-term (in-kripke-frame-t world f) ‘’ₐₕ simplify-term (in-kripke-frame-t world x)

            helper : ∀ {Γ} → Term (in-kripke-frame world (‘Type’ Γ)) → Type (in-kripke-frame-c (suc world) Γ)
            helper x = {!!}
    in-kripke-frame (suc world) (‘Term’ ‘’ x) = {!in-kripke-frame world x!}
    in-kripke-frame world (T₁ ‘’ x) = {!!}
    in-kripke-frame world ‘Term’ = {!!}
    in-kripke-frame world (T ‘×’ T₁) = {!!}
    in-kripke-frame world (Quine T) = {!!}
    in-kripke-frame world ‘⊤’ = ‘⊤’
    in-kripke-frame world ‘⊥’ = ‘⊥’

    in-kripke-frame-t world ⌜ x ⌝ = ⌜ (in-kripke-frame world x) ⌝
    in-kripke-frame-t zero ⌜ t ⌝t = ‘tt’
    in-kripke-frame-t (suc world) ⌜ t ⌝t = {!!}
    in-kripke-frame-t world ‘⌜‘VAR₀’⌝t’ = {!!}
    in-kripke-frame-t world ‘⌜‘VAR₀’⌝’ = {!!}
    in-kripke-frame-t world (‘λ∙’ t) = ‘λ∙’ (in-kripke-frame-t world t)
    in-kripke-frame-t world ‘VAR₀’ = ‘VAR₀’
    in-kripke-frame-t world (t ‘’ₐ t₁) = in-kripke-frame-t world t ‘’ₐₕ in-kripke-frame-t world t₁
    in-kripke-frame-t world ‘‘×'’’ = {!!}
    in-kripke-frame-t world quine→ = {!!}
    in-kripke-frame-t world quine← = {!!}
    in-kripke-frame-t world ‘tt’ = ‘tt’
    in-kripke-frame-t world (SW t₁) = {!!}
    in-kripke-frame-t world (→SW1SV→W t₁) = {!!}
    in-kripke-frame-t world (←SW1SV→W t₁) = {!!}
    in-kripke-frame-t world (→SW1SV→SW1SV→W t₁) = {!!}
    in-kripke-frame-t world (←SW1SV→SW1SV→W t₁) = {!!}
    in-kripke-frame-t world (w t) = w (in-kripke-frame-t world t)
    in-kripke-frame-t world (w→ t) = w→ (in-kripke-frame-t world t)
    in-kripke-frame-t world (→w t) = →w (in-kripke-frame-t world t)
    in-kripke-frame-t world (ww→ t) = ww→ (in-kripke-frame-t world t)
    in-kripke-frame-t world (→ww t) = →ww (in-kripke-frame-t world t)
    in-kripke-frame-t world (t ‘∘’ t₁) = {!!}
    in-kripke-frame-t world (t w‘‘’’ₐ t₁) = {!!}
    in-kripke-frame-t world ‘‘’ₐ’ = {!!}
    in-kripke-frame-t zero (‘‘□’’ t) = w (w ⌜ ‘⊤’ ⌝)
    in-kripke-frame-t {Γ = Γ ▻ A ▻ B} (suc world) (‘‘□’’ t) = helper {lzero} (‘‘□’’ₕ (in-kripke-frame-t world t))
      where ‘‘□’’ₕ : ∀ {Γ A B} → Term {in-kripke-frame-c world Γ ▻ A ▻ B} (W (W (in-kripke-frame world (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝)))) → Term {in-kripke-frame-c world Γ ▻ A ▻ B} (W (W (‘Type’ (in-kripke-frame-c world Γ))))
            ‘‘□’’ₕ T = {!!}

            helper : ∀ {ℓ ℓ′} {A : Set ℓ} {B : Set ℓ′} → A → B
            helper t = {!!}
    in-kripke-frame-t world (t ‘‘→’’ t₁) = {!!}
    in-kripke-frame-t world (t ww‘‘‘→’’’ t₁) = {!!}
    in-kripke-frame-t world (t ww‘‘‘×’’’ t₁) = {!!}
{-
    in-kripke-frame zero ‘Term’ = ‘⊤’
    in-kripke-frame {Γ} world (T ‘→’ T₁) = simplify-type (in-kripke-frame world T ‘→’ in-kripke-frame world T₁)
    in-kripke-frame {Γ} world (T ‘×’ T₁) = simplify-type (in-kripke-frame world T ‘×’ in-kripke-frame world T₁)
    in-kripke-frame world (W T) = simplify-type (W (in-kripke-frame world T))
    in-kripke-frame world (W1 T) = simplify-type (W1 (in-kripke-frame world T))
    in-kripke-frame world (T₁ ‘’ x) = simplify-type (helper-ikf world (simplify-type (in-kripke-frame world T₁)) (simplify-term (in-kripke-frame-t world x)))
    in-kripke-frame world ‘⊤’ = ‘⊤’
    in-kripke-frame world ‘⊥’ = ‘⊥’
    in-kripke-frame world (‘Type’ Γ) = hold world (‘Type’ Γ)
    in-kripke-frame world (Quine T) = hold world (Quine T)
    in-kripke-frame {Γ ▻ .(‘Type’ Γ)} (suc world) ‘Term’ = ‘Term’  {-helper (in-kripke-frame-t world T v) v
      where
        helper : ∀ {Γ} → Term (‘Type’ Γ) → context-to-term Γ → Type ε
        helper ⌜ x ⌝ v = Type-in x v
        helper (t ‘‘→’’ t₁) v = helper t v ‘→’ helper t₁ v
        helper (t ‘’ₐ t₁) v = {!!}
        helper (SW t₁) v = {!!}-}

    in-kripke-frame-t : ∀ {Γ} (world : ℕ) → {T : Type Γ} → Term T → Term (in-kripke-frame world T)
    in-kripke-frame-t world ⌜ x ⌝ = ⌜ in-kripke-frame world x ⌝
    in-kripke-frame-t world ⌜ x ⌝t = ⌜ in-kripke-frame-t world x ⌝t
    in-kripke-frame-t world ‘⌜‘VAR₀’⌝t’ = ‘⌜‘VAR₀’⌝t’
    in-kripke-frame-t world ‘⌜‘VAR₀’⌝’ = ‘⌜‘VAR₀’⌝’
    in-kripke-frame-t world (‘λ∙’ x) = ‘λ∙’ (in-kripke-frame-t world x)
    in-kripke-frame-t world ‘VAR₀’ = ‘VAR₀’
    in-kripke-frame-t world (x ‘’ₐ x₁) = _‘’ₐₕ_ {world = world} (simplify-term (in-kripke-frame-t world x)) (simplify-term (in-kripke-frame-t world x₁))
    in-kripke-frame-t world ‘‘×'’’ = ‘‘×'’’
    in-kripke-frame-t world quine→ = quine→
    in-kripke-frame-t world quine← = quine←
    in-kripke-frame-t world ‘tt’ = ‘tt’
    in-kripke-frame-t world (SW x₁) = SW (in-kripke-frame-t world x₁)
    in-kripke-frame-t world (→SW1SV→W x₁) = →SW1SV→W (in-kripke-frame-t world x₁)
    in-kripke-frame-t world (←SW1SV→W x₁) = ←SW1SV→W (in-kripke-frame-t world x₁)
    in-kripke-frame-t world (→SW1SV→SW1SV→W x₁) = →SW1SV→SW1SV→W (in-kripke-frame-t world x₁)
    in-kripke-frame-t world (←SW1SV→SW1SV→W x₁) = ←SW1SV→SW1SV→W (in-kripke-frame-t world x₁)
    in-kripke-frame-t world (w x) = w (in-kripke-frame-t world x)
    in-kripke-frame-t world (w→ x) = w→ (in-kripke-frame-t world x)
    in-kripke-frame-t world (→w x) = →w (in-kripke-frame-t world x)
    in-kripke-frame-t world (ww→ x) = ww→ (in-kripke-frame-t world x)
    in-kripke-frame-t world (→ww x) = →ww (in-kripke-frame-t world x)
    in-kripke-frame-t world (x ‘∘’ x₁) = _‘∘’ₕ_ {world = world} (simplify-term (in-kripke-frame-t world x)) (simplify-term (in-kripke-frame-t world x₁))
    in-kripke-frame-t world (x w‘‘’’ₐ x₁) = simplify-term (in-kripke-frame-t world x) w‘‘’’ₐ simplify-term (in-kripke-frame-t world x₁)
    in-kripke-frame-t world ‘‘’ₐ’ = ‘‘’ₐ’
    in-kripke-frame-t zero (‘‘□’’ x) = w (w ⌜ ‘⊤’ ⌝)
    in-kripke-frame-t (suc world) (‘‘□’’ x) = ‘‘□’’ₕ (simplify-term (in-kripke-frame-t world x))
    in-kripke-frame-t world (x ‘‘→’’ x₁) = {!!}
    in-kripke-frame-t world (x ww‘‘‘→’’’ x₁) = {!!}
    in-kripke-frame-t world (x ww‘‘‘×’’’ x₁) = {!!}
-}
  kripke-reduce : ∀ {Γ} → Type Γ → context-to-term Γ → Type ε
  kripke-reduce (W x₁) v = kripke-reduce x₁ (Σ.proj₁ v)
  kripke-reduce (W1 x₂) v = kripke-reduce x₂ ((Σ.proj₁ (Σ.proj₁ v)) , (‘λ∙’ (Σ.proj₂ v) ‘’ₐ Σ.proj₂ (Σ.proj₁ v)))
  kripke-reduce (T₁ ‘’ x) v = kripke-reduce T₁ (v , x)
  kripke-reduce (‘Type’ Γ) v = Type-in (‘Type’ Γ) v
  -- kripke-reduce ‘TVAR₀’ v = {!!}
  kripke-reduce ‘Term’ (v , T) = {!!} {- Type-in (lower (Term⇓ (Σ.proj₂ v) (context-to-term⇓ (Σ.proj₁ v)))) (Σ.proj₁ v) -}
  kripke-reduce (T ‘→’ T₁) v = kripke-reduce T v ‘→’ kripke-reduce T₁ v
  kripke-reduce (T ‘×’ T₁) v = kripke-reduce T v ‘×’ kripke-reduce T₁ v
  kripke-reduce (Quine x) v = {!!}
  kripke-reduce ‘⊤’ v = ‘⊤’
  kripke-reduce ‘⊥’ v = ‘⊥’

  kripke-eval' : ∀ {Γ} → ℕ → Type Γ → context-to-term Γ → Type ε
  kripke-eval' 0 T v = Type-in T v
  kripke-eval' (suc n) T v = kripke-eval' n (kripke-reduce T v) tt

  kripke-finalize : ∀ {Γ} → Type Γ → context-to-term Γ → Type ε
  kripke-finalize (W x₁) v = kripke-finalize x₁ (Σ.proj₁ v)
  kripke-finalize (W1 x₂) v = kripke-finalize x₂ ((Σ.proj₁ (Σ.proj₁ v)) , (‘λ∙’ (Σ.proj₂ v) ‘’ₐ Σ.proj₂ (Σ.proj₁ v)))
  kripke-finalize (T₁ ‘’ x) v = kripke-finalize T₁ (v , x)
  kripke-finalize (‘Type’ Γ) v = Type-in (‘Type’ Γ) v
  -- kripke-finalize ‘TVAR₀’ v = {!!}
  kripke-finalize ‘Term’ v = ‘⊤’
  kripke-finalize (T ‘→’ T₁) v = kripke-finalize T v ‘→’ kripke-finalize T₁ v
  kripke-finalize (T ‘×’ T₁) v = kripke-finalize T v ‘×’ kripke-finalize T₁ v
  kripke-finalize (Quine x) v = {!!}
  kripke-finalize ‘⊤’ v = ‘⊤’
  kripke-finalize ‘⊥’ v = ‘⊥’

  mutual
    kripke-count : ∀ {Γ} → Type Γ → ℕ
    kripke-count (W T₁) = kripke-count T₁
    kripke-count (W1 T₂) = kripke-count T₂
    kripke-count (T₁ ‘’ x) = kripke-count T₁ + kripke-count-t x
    kripke-count (‘Type’ Γ) = 0
    -- kripke-count ‘TVAR₀’ = 0
    kripke-count ‘Term’ = 1
    kripke-count (T ‘→’ T₁) = kripke-count T + kripke-count T₁
    kripke-count (T ‘×’ T₁) = kripke-count T + kripke-count T₁
    kripke-count (Quine T) = kripke-count T
    kripke-count ‘⊤’ = 0
    kripke-count ‘⊥’ = 0

    kripke-count-t : ∀ {Γ T} → Term {Γ} T → ℕ
    kripke-count-t ⌜ x ⌝ = kripke-count x
    kripke-count-t (x ‘’ₐ x₁) = kripke-count-t x + kripke-count-t x₁
    kripke-count-t ⌜ x ⌝t = kripke-count-t x
    kripke-count-t ‘⌜‘VAR₀’⌝t’ = 0
    kripke-count-t ‘⌜‘VAR₀’⌝’ = 0
    kripke-count-t (‘λ∙’ x) = kripke-count-t x
    kripke-count-t ‘VAR₀’ = 0
    kripke-count-t quine→ = 0
    kripke-count-t quine← = 0
    kripke-count-t ‘tt’ = 0
    kripke-count-t (SW x₁) = kripke-count-t x₁
    kripke-count-t (→SW1SV→W x₁) = kripke-count-t x₁
    kripke-count-t (←SW1SV→W x₁) = kripke-count-t x₁
    kripke-count-t (→SW1SV→SW1SV→W x₁) = kripke-count-t x₁
    kripke-count-t (←SW1SV→SW1SV→W x₁) = kripke-count-t x₁
    kripke-count-t (w x) = kripke-count-t x
    kripke-count-t (w→ x) = kripke-count-t x
    kripke-count-t (→w x) = kripke-count-t x
    kripke-count-t (ww→ x) = kripke-count-t x
    kripke-count-t (→ww x) = kripke-count-t x
    kripke-count-t ‘‘×'’’ = 0
    kripke-count-t (x ‘∘’ x₁) = kripke-count-t x + kripke-count-t x₁
    kripke-count-t (x w‘‘’’ₐ x₁) = kripke-count-t x + kripke-count-t x₁
    kripke-count-t ‘‘’ₐ’ = 0
    -- kripke-count-t (f w‘‘’’ x) = kripke-count-t f + kripke-count-t x
    kripke-count-t (‘‘□’’ T) = 1 + kripke-count-t T
    kripke-count-t (x ‘‘→’’ x₁) = kripke-count-t x + kripke-count-t x₁
    kripke-count-t (x ww‘‘‘→’’’ x₁) = kripke-count-t x + kripke-count-t x₁
    kripke-count-t (x ww‘‘‘×’’’ x₁) = kripke-count-t x + kripke-count-t x₁

  kripke-normalize' : ∀ {Γ} → Type Γ → context-to-term Γ → Type ε
  kripke-normalize' T v = kripke-eval' (kripke-count T) T v

  kripke-normalize'' : ∀ {Γ} → (T : Type Γ) → Type (in-kripke-frame-c (kripke-count T) Γ)
  kripke-normalize'' T = in-kripke-frame (kripke-count T) T

open modal-fixpoint

‘Bot’ : ∀ {Γ} → Type Γ
‘Bot’ {Γ} = Quine (W1 ‘Term’ ‘’ ‘VAR₀’ ‘→’ W1 ‘Term’ ‘’ ‘VAR₀’ ‘→’ W (‘Type’ Γ)) {- a bot takes in the source code for itself, for another bot, and spits out the assertion that it cooperates with this bot -}

_cooperates-with_ : □ ‘Bot’ → □ ‘Bot’ → Type ε
b1 cooperates-with b2 = lower (Term⇓ b1 tt (lift b1) (lift b2))

‘eval-bot'’ : ∀ {Γ} → Term {Γ} (‘Bot’ ‘→’ (‘□’ ‘Bot’ ‘→’ ‘□’ ‘Bot’ ‘→’ ‘Type’ Γ))
‘eval-bot'’ = →SW1SV→SW1SV→W quine→

‘‘eval-bot'’’ : ∀ {Γ} → Term {Γ} (‘□’ ‘Bot’ ‘→’ ‘□’ ({- other -} ‘□’ ‘Bot’ ‘→’ ‘Type’ Γ))
‘‘eval-bot'’’ = ‘λ∙’ (w ⌜ ‘eval-bot'’ ⌝t w‘‘’’ₐ ‘VAR₀’ w‘‘’’ₐ ‘⌜‘VAR₀’⌝t’)

‘other-cooperates-with’ : ∀ {Γ} → Term {Γ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘□’ ‘Bot’)) ‘→’ W (W (‘□’ (‘Type’ Γ))))
‘other-cooperates-with’ {Γ} = ‘eval-other'’ ‘∘’ w→ (w (w→ (w (‘λ∙’ ‘⌜‘VAR₀’⌝t’))))
  where
    ‘eval-other’ : Term {Γ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘□’ (‘□’ ‘Bot’ ‘→’ ‘Type’ Γ))))
    ‘eval-other’ = w→ (w (w→ (w ‘‘eval-bot'’’))) ‘’ₐ ‘VAR₀’

    ‘eval-other'’ : Term (W (W (‘□’ (‘□’ ‘Bot’))) ‘→’ W (W (‘□’ (‘Type’ Γ))))
    ‘eval-other'’ = ww→ (w→ (w (w→ (w ‘‘’ₐ’))) ‘’ₐ ‘eval-other’)

‘self’ : ∀ {Γ} → Term {Γ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘□’ ‘Bot’)))
‘self’ = w ‘VAR₀’

‘other’ : ∀ {Γ} → Term {Γ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘□’ ‘Bot’)))
‘other’ = ‘VAR₀’

make-bot : ∀ {Γ} → Term {Γ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘Type’ Γ))) → Term {Γ} ‘Bot’
make-bot t = ←SW1SV→SW1SV→W quine← ‘’ₐ ‘λ∙’ (→w (‘λ∙’ t))

ww‘‘‘¬’’’_ : ∀ {Γ A B}
  → Term {Γ ▻ A ▻ B} (W (W (‘□’ (‘Type’ Γ))))
  → Term {Γ ▻ A ▻ B} (W (W (‘□’ (‘Type’ Γ))))
ww‘‘‘¬’’’ T = T ww‘‘‘→’’’ w (w ⌜ ⌜ ‘⊥’ ⌝ ⌝t)

‘DefectBot’ : □ ‘Bot’
‘CooperateBot’ : □ ‘Bot’
‘FairBot’ : □ ‘Bot’
‘PrudentBot’ : □ ‘Bot’

‘DefectBot’ = make-bot (w (w ⌜ ‘⊥’ ⌝))
‘CooperateBot’ = make-bot (w (w ⌜ ‘⊤’ ⌝))
‘FairBot’ = make-bot (‘‘□’’ (‘other-cooperates-with’ ‘’ₐ ‘self’))
‘PrudentBot’ = make-bot (‘‘□’’ (φ₀ ww‘‘‘×’’’ (¬□⊥ ww‘‘‘→’’’ other-defects-against-DefectBot)))
  where
    φ₀ : ∀ {Γ} → Term {Γ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘□’ (‘Type’ Γ))))
    φ₀ = ‘other-cooperates-with’ ‘’ₐ ‘self’

    other-defects-against-DefectBot : Term {_ ▻ ‘□’ ‘Bot’ ▻ W (‘□’ ‘Bot’)} (W (W (‘□’ (‘Type’ _))))
    other-defects-against-DefectBot = ww‘‘‘¬’’’ (‘other-cooperates-with’ ‘’ₐ w (w ⌜ ‘DefectBot’ ⌝t))

    ¬□⊥ : ∀ {Γ A B} → Term {Γ ▻ A ▻ B} (W (W (‘□’ (‘Type’ Γ))))
    ¬□⊥ = w (w ⌜ ⌜ ‘¬’ (‘□’ ‘⊥’) ⌝ ⌝t)

data KnownBot : Set where
  DefectBot : KnownBot
  CooperateBot : KnownBot
  FairBot : KnownBot
  PrudentBot : KnownBot

KnownBot⇓ : KnownBot → □ ‘Bot’
KnownBot⇓ DefectBot = ‘DefectBot’
KnownBot⇓ CooperateBot = ‘CooperateBot’
KnownBot⇓ FairBot = ‘FairBot’
KnownBot⇓ PrudentBot = ‘PrudentBot’

DB-defects : ∀ {b} → □ (‘¬’ (‘DefectBot’ cooperates-with b))
CB-cooperates : ∀ {b} → □ (‘CooperateBot’ cooperates-with b)

FB-spec : KnownBot → □ ‘Bot’ → Type ε
FB-spec DefectBot b = ‘¬’ (‘FairBot’ cooperates-with b)
FB-spec CooperateBot b = ‘FairBot’ cooperates-with b
FB-spec FairBot b = ‘FairBot’ cooperates-with b
FB-spec PrudentBot b = ‘FairBot’ cooperates-with b

FB-good : ∀ {b} → □ (FB-spec b (KnownBot⇓ b))

DB-defects {b} = ‘λ∙’ ‘VAR₀’
CB-cooperates {b} = ‘tt’
FB-good {DefectBot} = {!!}
FB-good {CooperateBot} = {!!}
FB-good {FairBot} = {!!}
FB-good {PrudentBot} = {!!}

{-
data Bot : Set where
  DefectBot : Bot
  CooperateBot : Bot
  FairBot : Bot
  PrudentBot : Bot

data Opponent : Set where
  Self : Opponent
  Other : Type ε → Opponent

_cooperates-with-me : ∀ {Γ} → Term {Γ} (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝ ‘→’ ‘Type’ Γ) → Term {Γ ▻ ‘Type’ Γ} (W ‘Type’ Γ)
x cooperates-with-me = w→ (w x) ‘’ₐ ‘⌜‘VAR₀’⌝’

{-
‘cooperates-with'’ : □ (‘Bot’ ‘→’ ‘Bot’ ‘→’ ‘Type’ Γ)
‘cooperates-with'’ = ‘λ∙’ (→w ((‘eval-bot’ ‘VAR₀’) ‘∘’ (w→ (w {!!}))))
  where ‘eval-bot'’ : Term (‘Bot’ ‘→’ (‘□’ ‘Bot’ ‘→’ ‘Type’ Γ))
        ‘eval-bot'’ = →SW1SV→W quine→

        ‘eval-bot’_ : Term {ε ▻ ‘Bot’} (W ‘Bot’) → Term (W (‘□’ ‘Bot’) ‘→’ W ‘Type’ Γ)
        ‘eval-bot’ b = w→ (w→ (w ‘eval-bot'’) ‘’ₐ b)

        get-bot-source : Term {ε} (‘Bot’ ‘→’ ‘□’ ‘Bot’)
        get-bot-source = {!!}-}

_‘cooperates’ : Bot → Term {ε} (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝ ‘→’ ‘Type’ Γ)
DefectBot ‘cooperates’ = ‘λ∙’ (w ⌜ ‘⊥’ ⌝)
CooperateBot ‘cooperates’ = ‘λ∙’ (w ⌜ ‘⊤’ ⌝)
FairBot ‘cooperates’ = ‘λ∙’ {!!}
PrudentBot ‘cooperates’ = {!!}

_‘cooperates-with'’_ : Bot → Term {ε} (‘Term’ ‘’ ⌜ ‘Type’ Γ ⌝ ‘→’ ‘Type’ Γ) → Type ε
FairBot ‘cooperates-with'’ x = Quine (W1 ‘Term’ ‘’ (x cooperates-with-me))
DefectBot ‘cooperates-with'’ b2 = ‘⊥’
CooperateBot ‘cooperates-with'’ b2 = ‘⊤’
PrudentBot ‘cooperates-with'’ x = Quine (W1 ‘Term’ ‘’ (x cooperates-with-me w‘‘×’’ w ⌜ (‘¬’ (‘Term’ ‘’ ⌜ ‘⊥’ ⌝) ‘→’ ‘¬’ {!!}) ⌝))
-}

   {-

  box-free? : ∀ {Γ} → Type Γ → context-to-term Γ → Set
  box-free? (T₁ ‘’ x) v = box-free? T₁ (v , x)
  box-free? ‘Type’ Γ v = ⊤
  box-free? ‘Term’ v = ⊥
  box-free? (T ‘→’ T₁) v = box-free? T v × box-free? T₁ v
  box-free? ‘⊤’ v = ⊤
  box-free? ‘⊥’ v = ⊤

  mutual
    kripke-lift→ : ∀ (n : ℕ) (T : Type ε) → box-free? (kripke-eval' n T) tt
      → Term (kripke-eval' n T ‘→’ T)
    kripke-lift→ zero T H = ‘λx∙x’
    kripke-lift→ (suc n) (T₁ ‘’ x) H = {!!}
    kripke-lift→ (suc n) (T ‘→’ T₁) H = {!!} ‘∘’ (kripke-lift→ n (kripke-reduce (T ‘→’ T₁) tt) H)
    kripke-lift→ (suc n) ‘Type’ Γ H = kripke-lift→ n _ H
    kripke-lift→ (suc n) ‘⊤’ H = kripke-lift→ n _ H
    kripke-lift→ (suc n) ‘⊥’ H = kripke-lift→ n _ H

    kripke-lift← : ∀ (n : ℕ) (T : Type ε) → box-free? (kripke-eval' n T) tt
      → Term (kripke-eval' n T)
      → Term T
    kripke-lift← n T H t = {!!}
{-    kripke-lift : ∀ {Γ} (T : Type Γ) v → Term (kripke-reduce T v ‘→’ Type-in T v)
    kripke-lift (T₁ ‘’ x) = {!!}
    kripke-lift ‘Type’ Γ v = {!!}
    kripke-lift ‘Term’ v = {!!}
    kripke-lift {ε} (T ‘→’ T₁) v = {!!}
  kripke-lift {Γ ▻ x} (T ‘→’ T₁) v = {!!}
  kripke-lift ‘⊤’ v = {!!}
  kripke-lift ‘⊥’ v = {!!}-}

  Quine : Type (ε ▻ ‘Type’ Γ) → Type ε
  Quine φ = {!!}

  quine→ : ∀ {φ} → Term {ε} (Quine φ        ‘→’ φ ‘’ ⌜ Quine φ ⌝)
  quine← : ∀ {φ} → Term {ε} (φ ‘’ ⌜ Quine φ ⌝ ‘→’ Quine φ)

  quine→ = {!!}
  quine← = {!!}
-}


-- Binding signatures
module SOAS.Syntax.Signature (T : Set) where

open import SOAS.Syntax.Arguments {T}

open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable

open import SOAS.Families.Core {T}
open import SOAS.Families.BCCC {T} using (⊤ₘ)

open import SOAS.Coalgebraic.Strength
open import SOAS.Coalgebraic.Lift
open import SOAS.Coalgebraic.Map

open import SOAS.Abstract.Hom

open import SOAS.Abstract.ExpStrength
import SOAS.Abstract.Coalgebra as →□
open →□.Sorted
open →□.Unsorted renaming (Coalg to UCoalg ; Coalg⇒ to UCoalg⇒)

open import Data.List.Base using ([] ; _∷_ ; List)

private
  variable
    Γ Δ Θ : Ctx
    α τ : T

-- Binding signature for a second-order syntax, consisting of a set of operators
-- O and an arity assignment ∣_∣
record Signature (O : Set) : Set₁ where
  constructor sig
  field ∣_∣ : O → List (Ctx × T) × T

  -- Sort and arity of an operator
  Sort : O → T
  Sort o = proj₂ ∣ o ∣

  Arity : O → List (Ctx × T)
  Arity o = proj₁ ∣ o ∣

  -- Signature endofunctor
  ⅀F : Functor 𝔽amiliesₛ 𝔽amiliesₛ
  ⅀F  = record
    { F₀ = λ 𝒳 α Γ → Σ[ o ∈ O ] (α ≡ Sort o × Arg (Arity o) 𝒳 Γ)
    ; F₁ = λ{ f (o , e , ar) → o , e , (F₁ o f ar)}
    ; identity = λ{ {x = o , e , ar} → cong (λ - → o , e , -) (identity o) }
    ; homomorphism = λ{ {x = o , e , ar} → cong (λ - → o , e , -) (homomorphism o) }
    ; F-resp-≈ = λ{ p {x = o , e , ar} → cong (λ - → o , e , -) (F-resp-≈ o p) }
    } where open module AF o =  Functor (ArgF (Arity o))

  pattern _⋮_ o ar = (o , refl , ar)
  infix 1 _⋮_

  open import SOAS.Metatheory.Algebra {T} ⅀F public

  -- Coalgebraic and exponential strength for signature endofunctor
  private
    str : {𝒫 : Familyₛ}(𝒫ᴮ : Coalgₚ 𝒫)(𝒳 : Familyₛ)
          (as : List (Ctx × T))(σ : Γ ~[ 𝒫 ]↝ Δ)
        → Arg as 〖 𝒫 , 𝒳 〗 Γ → Arg as 𝒳 Δ
    str 𝒫ᴮ 𝒳 [] σ x = tt
    str 𝒫ᴮ 𝒳 ((Θ , τ) ∷ []) σ h = h (lift 𝒫ᴮ Θ σ)
    str 𝒫ᴮ 𝒳 ((Θ , τ) ∷ a ∷ as) σ (h , at) = h (lift 𝒫ᴮ Θ σ) , str 𝒫ᴮ 𝒳 (a ∷ as) σ at

    str-nat₁ : {𝒫 𝒬 𝒳 : Familyₛ} {𝒫ᴮ : Coalgₚ 𝒫} {𝒬ᴮ : Coalgₚ 𝒬}
             → {f : 𝒬 ⇾̣ 𝒫} (fᴮ⇒ : Coalgₚ⇒ 𝒬ᴮ 𝒫ᴮ f)
             → (as : List (Ctx × T))
             → (h : Arg as 〖 𝒫 , 𝒳 〗 Γ) (σ : Γ ~[ 𝒬 ]↝ Δ)
             → str 𝒫ᴮ 𝒳 as (λ x → f (σ x)) h
             ≡ str 𝒬ᴮ 𝒳 as σ (Arg₁ as (λ{ h′ ς → h′ (λ v → f (ς v))}) h)
    str-nat₁ fᴮ⇒ [] h σ = refl
    str-nat₁ {𝒳 = 𝒳} fᴮ⇒ ((Θ , τ) ∷ []) h σ = lift-comp 𝒳 Θ fᴮ⇒ h σ
    str-nat₁ {𝒳 = 𝒳} fᴮ⇒ ((Θ , τ) ∷ a ∷ as) (h , ap) σ =
      cong₂ _,_ (lift-comp 𝒳 Θ fᴮ⇒ h σ) (str-nat₁ fᴮ⇒ (a ∷ as) ap σ)

    str-nat₂ : {𝒫 𝒳 𝒴 : Familyₛ} {𝒫ᴮ : Coalgₚ 𝒫}
             → (f : 𝒳 ⇾̣ 𝒴)
             → (as : List (Ctx × T))
             → (h : Arg as 〖 𝒫 , 𝒳 〗 Γ) (σ : Γ ~[ 𝒫 ]↝ Δ)
             → str 𝒫ᴮ 𝒴 as σ (Arg₁ as (λ{ h′ ς → f (h′ ς)}) h)
             ≡ Arg₁ as f (str 𝒫ᴮ 𝒳 as σ h)
    str-nat₂ f [] h σ = refl
    str-nat₂ f ((Θ , τ) ∷ []) h σ = refl
    str-nat₂ f ((Θ , τ) ∷ a ∷ as) (h , ap) σ = cong (_ ,_) (str-nat₂ f (a ∷ as) ap σ)

    str-unit : (𝒳 : Familyₛ)
             → (as : List (Ctx × T))
             → (h : Arg as 〖 ℐ , 𝒳 〗 Γ)
             → str ℐᴮ 𝒳 as id h
             ≡ Arg₁ as (λ b → b id) h
    str-unit 𝒳 [] h = refl
    str-unit 𝒳 ((Θ , τ) ∷ []) h = rlift-id 𝒳 Θ h
    str-unit 𝒳 ((Θ , τ) ∷ a ∷ as) (h , ap) = cong₂ _,_ (rlift-id 𝒳 Θ h) (str-unit 𝒳 (a ∷ as) ap)

    str-assoc : (𝒳 : Familyₛ) {𝒫 𝒬 ℛ : Familyₛ}
                {𝒫ᴮ : Coalgₚ 𝒫} {𝒬ᴮ : Coalgₚ 𝒬} {ℛᴮ : Coalgₚ ℛ}
              → {f : 𝒫 ⇾̣ 〖 𝒬 , ℛ 〗} (fᶜ : Coalgebraic 𝒫ᴮ 𝒬ᴮ ℛᴮ f)
              → (open Coalgebraic fᶜ)
              → (as : List (Ctx × T))
              → (h : Arg as 〖 ℛ , 𝒳 〗 Γ) (σ : Γ ~[ 𝒫 ]↝ Δ) (ς : Δ ~[ 𝒬 ]↝ Θ)
              → str ℛᴮ 𝒳 as (λ v → f (σ v) ς) h
              ≡ str 𝒬ᴮ 𝒳 as ς (str 〖𝒫,𝒴〗ᴮ 〖 𝒬 , 𝒳 〗 as (f ∘ σ) (Arg₁ as (λ{ h ς σ → h (λ v → ς v σ)}) h))
    str-assoc 𝒳 fᶜ [] h σ ς = refl
    str-assoc 𝒳 fᶜ ((Ξ , τ) ∷ []) h σ ς = lift-assoc 𝒳 Ξ fᶜ h σ ς
    str-assoc 𝒳 fᶜ ((Ξ , τ) ∷ a ∷ as) (h , ap) σ ς = cong₂ _,_ (lift-assoc 𝒳 Ξ fᶜ h σ ς) (str-assoc 𝒳 fᶜ (a ∷ as) ap σ ς)

    estr : {X : Family}(Xᵇ : UCoalg X)(𝒴 : Familyₛ)
          (as : List (Ctx × T))
        → Arg as (X ⇨ 𝒴) Γ → (x : X Γ) → Arg as 𝒴 Γ
    estr Xᵇ 𝒴 [] at x = tt
    estr Xᵇ 𝒴 ((Θ , τ) ∷ []) e x = e (UCoalg.wkr Xᵇ Θ x)
    estr Xᵇ 𝒴 ((Θ , τ) ∷ a ∷ as) (e , at) x = (e (UCoalg.wkr Xᵇ Θ x)) , estr Xᵇ 𝒴 (a ∷ as) at x

    estr-nat₁ : {X X′ : Family} {Xᵇ : UCoalg X}
      {X′ᵇ : UCoalg X′} {𝒴 : Familyₛ} {f : X′ ⇾ X} →
      UCoalg⇒ X′ᵇ Xᵇ f →
      (as : List (Ctx × T))
      (h : Arg as (X ⇨ 𝒴) Γ)(x : X′ Γ)
      → estr Xᵇ 𝒴 as h (f x)
      ≡ estr X′ᵇ 𝒴 as (Arg₁ as (λ e x₁ → e (f x₁)) h) x
    estr-nat₁ fᵇ⇒ [] h x = refl
    estr-nat₁ fᵇ⇒ ((Θ , τ) ∷ []) h x = cong h (sym (UCoalg⇒.⟨r⟩ fᵇ⇒))
    estr-nat₁ fᵇ⇒ ((Θ , τ) ∷ a ∷ as) (h , at) x = cong₂ _,_ (cong h (sym (UCoalg⇒.⟨r⟩ fᵇ⇒))) (estr-nat₁ fᵇ⇒ (a ∷ as) at x)

    estr-nat₂ : {X : Family} {Xᵇ : UCoalg X}
        {𝒴 𝒴′ : Familyₛ} (g : 𝒴 ⇾̣ 𝒴′) (as : List (Ctx × T))(at : Arg as (X ⇨ 𝒴) Γ) (x : X Γ)
        → estr Xᵇ 𝒴′ as (Arg₁ as (λ e x → g (e x)) at) x
        ≡ Arg₁ as g (estr Xᵇ 𝒴 as at x)
    estr-nat₂ g [] at x = refl
    estr-nat₂ g ((Θ , τ) ∷ []) h x = refl
    estr-nat₂ g ((Θ , τ) ∷ a ∷ as) (h , at) x = cong (_ ,_) (estr-nat₂ g (a ∷ as) at x)

    estr-unit : {𝒴 : Familyₛ} (as : List (Ctx × T)) {at : Arg as (⊤ₘ ⇨ 𝒴) Γ}
        → estr ⊤ᵇ 𝒴 as at tt ≡ Arg₁ as (λ e′ → e′ tt) at
    estr-unit [] = refl
    estr-unit ((Θ , τ) ∷ []) = refl
    estr-unit ((Θ , τ) ∷ a ∷ as) = cong (_ ,_) (estr-unit (a ∷ as))

  -- Compatible strengths for the signature endofunctor
  ⅀:CompatStr : CompatStrengths ⅀F
  ⅀:CompatStr = record
    { CoalgStr = record
      { str = λ{ 𝒫ᴮ 𝒳 (o , e , ap) σ → o , (e , str 𝒫ᴮ 𝒳 (Arity o) σ ap) }
      ; str-nat₁ = λ{ fᴮ⇒ (o , e , ap) σ → cong (λ - → o , e , -) (str-nat₁ fᴮ⇒ (Arity o) ap σ)}
      ; str-nat₂ = λ{ f (o , e , ap) σ → cong (λ - → o , e , -) (str-nat₂ f (Arity o) ap σ)}
      ; str-unit = λ{ 𝒳 (o , e , ap) → cong (λ - → o , e , -) (str-unit 𝒳 (Arity o) ap)}
      ; str-assoc = λ{ 𝒳 fᶜ (o , e , ap) σ ς → cong (λ - → o , e , -) (str-assoc 𝒳 fᶜ (Arity o) ap σ ς)}
      }
    ; ExpStr = record
      { estr = λ{ Xᵇ 𝒴 (o , refl , at) x → o , refl , estr Xᵇ 𝒴 (Arity o) at x }
      ; estr-nat₁ = λ{ fᵇ⇒ (o , refl , at) x → cong (λ - → o , refl , -) (estr-nat₁ fᵇ⇒ (Arity o) at x)}
      ; estr-nat₂ = λ{ g (o , refl , at) x → cong (λ - → o , refl , -) (estr-nat₂ g (Arity o) at x) }
      ; estr-unit = λ{ {e = (o , refl , at)} → cong (λ - → o , refl , -) (estr-unit (Arity o)) } }
    }


open import Oscar.Class.Similarity
open import Oscar.Class.Reflexivity
open import Oscar.Class.Surjection
open import Oscar.Class.Leftunit
open import Oscar.Class.Smap
open import Oscar.Class.Symmetrical
open import Oscar.Class.Unit
open import Oscar.Data.Surjcollation
open import Oscar.Prelude

module Oscar.Class.Smapoid where

module _
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  {𝔯} (ℜ : 𝔛₁ → 𝔛₁ → Ø 𝔯)
  {𝔭₁} (𝔓₁ : 𝔛₂ → Ø 𝔭₁)
  {𝔭₂} (𝔓₂ : 𝔛₂ → Ø 𝔭₂)
  {𝔯̇} (ℜ̇ : ∀ {x y} → ℜ x y → ℜ x y → Ø 𝔯̇)
  {𝔭̇₁₁} (𝔓̇₁₁ : ∀ {x} → 𝔓₁ x → 𝔓₁ x → Ø 𝔭̇₁₁)
  {𝔭̇₂₂} (𝔓̇₂₂ : ∀ {x} → 𝔓₂ x → 𝔓₂ x → Ø 𝔭̇₂₂)
  {𝔭̇₁₂} (𝔓̇₁₂ : ∀ {x} → 𝔓₁ x → 𝔓₂ x → Ø 𝔭̇₁₂)
  where
  record IsSmapoid : Ø 𝔵₁ ∙̂ 𝔵₂ ∙̂ 𝔯 ∙̂ 𝔯̇ ∙̂ 𝔭₁ ∙̂ 𝔭₂ ∙̂ 𝔭̇₁₁ ∙̂ 𝔭̇₂₂ ∙̂ 𝔭̇₁₂ where
    constructor ∁
    field

      ⦃ `Surjection ⦄ : Surjection.class 𝔛₁ 𝔛₂

      -- ℜ 𝓂 𝓃 → 𝔓₁ (surjection 𝓂) → 𝔓₂ (surjection 𝓃)
      ⦃ `Smaparrow ⦄ : Smaparrow.class ℜ 𝔓₁ 𝔓₂ surjection surjection -- epfs, Smap

      -- 𝒫 ₁≈₁ 𝒬 → 𝒻 ◃ 𝒫 ₂≈₂ 𝒻 ◃ 𝒬
      ⦃ `leftSim ⦄ : Similarity,smaparrow!.class ℜ 𝔓₁ 𝔓₂ 𝔓̇₁₁ 𝔓̇₂₂ -- fact5, Similarity, Smaparrowleftsimilarity

      ⦃ `𝓡eflexivity ⦄ : Reflexivity.class ℜ

      -- 𝒫 ₁≈₂ 𝒖 ◃ 𝒫
      ⦃ `leftyunit ⦄ : ∀ {x} {p : 𝔓₁ (surjection x)}
                     → Leftunit.class (flip (𝔓̇₁₂ {surjection x})) ε smap p

module _
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  {𝔯} (ℜ : 𝔛₁ → 𝔛₁ → Ø 𝔯)
  {𝔭₁} (𝔓₁ : 𝔛₂ → Ø 𝔭₁)
  {𝔭₂} (𝔓₂ : 𝔛₂ → Ø 𝔭₂)
  {𝔯̇} (ℜ̇ : ∀ {x y} → ℜ x y → ℜ x y → Ø 𝔯̇)
  {𝔭̇₁₁} (𝔓̇₁₁ : ∀ {x} → 𝔓₁ x → 𝔓₁ x → Ø 𝔭̇₁₁)
  {𝔭̇₂₂} (𝔓̇₂₂ : ∀ {x} → 𝔓₂ x → 𝔓₂ x → Ø 𝔭̇₂₂)
  {𝔭̇₁₂} (𝔓̇₁₂ : ∀ {x} → 𝔓₁ x → 𝔓₂ x → Ø 𝔭̇₁₂)
  where
  record IsSmapoidR : Ø 𝔵₁ ∙̂ 𝔵₂ ∙̂ 𝔯 ∙̂ 𝔯̇ ∙̂ 𝔭₁ ∙̂ 𝔭₂ ∙̂ 𝔭̇₁₁ ∙̂ 𝔭̇₂₂ ∙̂ 𝔭̇₁₂ where
    constructor ∁
    field

      ⦃ `IsSmapoid ⦄ : IsSmapoid ℜ 𝔓₁ 𝔓₂ ℜ̇ 𝔓̇₁₁ 𝔓̇₂₂ 𝔓̇₁₂

      -- 𝒻 ᵣ≈ᵣ ℊ → 𝒻 ◃ 𝒫 ₂≈₂ ℊ ◃ 𝒫
      ⦃ `rightSim ⦄ : Similarity,cosmaparrow!.class ℜ 𝔓₁ 𝔓₂ ℜ̇ 𝔓̇₂₂ -- fact6, Similarity, Smaparrowrightsimilarity

-- TODO add these somewhere?
-- fact4 needs Propergroup's Nothing -- ⦃ `Leftstar ⦄ : Leftstar.class
-- left-identity-∧ needs Propergroup
-- fact1 -- ⦃ `Symmetrical ⦄ : ∀ {m} → Symmetrical (surjcollation[ 𝔓 ]⟦ ℜ / 𝔓̇ ⟧ {m}) (λ { (∁ f) (∁ g) → {!f {m}!}})
-- Properties-fact1


module index where

-- A formalisation of Haskell B. Curry’s thesis Grundlagen der Kombinatorischen
-- Logik. See <https://www.jstor.org/stable/2370619> (part 1) and
-- <https://www.jstor.org/stable/2370716> (part 2).
import CombinatoryLogic.Equality
import CombinatoryLogic.Forest
import CombinatoryLogic.Semantics
import CombinatoryLogic.Syntax

-- A formalisation of the theory and problems presented in To Mock a Mockingbird
-- by Raymond Smullyan.
import Mockingbird.Forest
import Mockingbird.Forest.Base
import Mockingbird.Forest.Birds
import Mockingbird.Forest.Combination
import Mockingbird.Forest.Combination.Base
import Mockingbird.Forest.Combination.Properties
import Mockingbird.Forest.Combination.Vec
import Mockingbird.Forest.Combination.Vec.Base
import Mockingbird.Forest.Combination.Vec.Properties
import Mockingbird.Forest.Extensionality
import Mockingbird.Problems.Chapter09
import Mockingbird.Problems.Chapter10
import Mockingbird.Problems.Chapter11
import Mockingbird.Problems.Chapter12
import Mockingbird.Problems.Chapter13
import Mockingbird.Problems.Chapter14
import Mockingbird.Problems.Chapter15
import Mockingbird.Problems.Chapter16
import Mockingbird.Problems.Chapter17
import Mockingbird.Problems.Chapter18
import Mockingbird.Problems.Chapter19
import Mockingbird.Problems.Chapter20


------------------------------------------------------------------------
-- Application of substitutions
------------------------------------------------------------------------

-- Given an operation which applies a substitution to a term, subject
-- to some conditions, more operations and lemmas are defined/proved.

-- (This module reexports various other modules.)

open import Data.Universe.Indexed

module deBruijn.Substitution.Data.Application
  {i u e} {Uni : IndexedUniverse i u e} where

import deBruijn.Substitution.Data.Application.Application
open deBruijn.Substitution.Data.Application.Application
  {_} {_} {_} {Uni} public
import deBruijn.Substitution.Data.Application.Application21
open deBruijn.Substitution.Data.Application.Application21
  {_} {_} {_} {Uni} public
import deBruijn.Substitution.Data.Application.Application22
open deBruijn.Substitution.Data.Application.Application22
  {_} {_} {_} {Uni} public
import deBruijn.Substitution.Data.Application.Application1
open deBruijn.Substitution.Data.Application.Application1
  {_} {_} {_} {Uni} public


------------------------------------------------------------------------
-- Some results related to expansion for the delay monad
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

open import Prelude

module Expansion.Delay-monad {a} {A : Type a} where

open import Delay-monad
open import Delay-monad.Bisimilarity as D using (force)
import Delay-monad.Bisimilarity.Negative as DN
open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude.Size

open import Function-universe equality-with-J hiding (id; _∘_)
open import Function-universe.Size equality-with-J

import Bisimilarity.Delay-monad as SD
open import Equational-reasoning
import Expansion.Equational-reasoning-instances
open import Labelled-transition-system.Delay-monad A

open import Bisimilarity delay-monad using ([_]_∼_)
open import Expansion delay-monad

------------------------------------------------------------------------
-- The direct and the indirect definitions of expansion are pointwise
-- logically equivalent

-- Emulations of the constructors D.later and D.laterˡ.

later-cong : ∀ {i x y} →
             [ i ] force x ≳′ force y → [ i ] later x ≳ later y
later-cong x≳′y =
  ⟨ (λ { later → _ , ⟶→⟶̂   later , x≳′y })
  , (λ { later → _ , ⟶→[]⇒ later , x≳′y })
  ⟩

laterˡ : ∀ {i x y} → [ i ] force x ≳ y → [ i ] later x ≳ y
laterˡ x≳y =
  ⟨ (λ { later → _ , done _ , convert {a = a} x≳y })
  , Σ-map id (Σ-map later[]⇒ id) ∘ right-to-left x≳y
  ⟩

-- The direct definition of expansion is contained in the one
-- obtained from the transition relation.

direct→indirect : ∀ {i x y} →
                  D.[ i ] x ≳ y → [ i ] x ≳ y
direct→indirect D.now        = reflexive
direct→indirect (D.later  p) = later-cong λ { .force →
                                 direct→indirect (force p) }
direct→indirect (D.laterˡ p) = laterˡ (direct→indirect p)

-- If x makes a sequence of zero or more silent transitions to y, then
-- x is an expansion of y.

⇒→≳ : ∀ {i x y} → x ⇒ y → D.[ i ] x ≳ y
⇒→≳ done               = D.reflexive _
⇒→≳ (step _  later tr) = D.laterˡ (⇒→≳ tr)
⇒→≳ (step () now   tr)

-- If x makes a non-silent transition with the label y, then x is an
-- expansion of now y.

[just]⟶→≳now : ∀ {i x x′ y} → x [ just y ]⟶ x′ → D.[ i ] x ≳ now y
[just]⟶→≳now now = D.reflexive _

[just]⇒→≳now : ∀ {i x x′ y} → x [ just y ]⇒ x′ → D.[ i ] x ≳ now y
[just]⇒→≳now (steps tr now _) = ⇒→≳ tr

[just]⇒̂→≳now : ∀ {i x x′ y} → x [ just y ]⇒̂ x′ → D.[ i ] x ≳ now y
[just]⇒̂→≳now (silent () _)
[just]⇒̂→≳now (non-silent _ tr) = [just]⇒→≳now tr

[just]⟶̂→≳now : ∀ {i x x′ y} → x [ just y ]⟶̂ x′ → D.[ i ] x ≳ now y
[just]⟶̂→≳now (done ())
[just]⟶̂→≳now (step tr) = [just]⇒̂→≳now (⟶→⇒̂ tr)

-- The definition of expansion obtained from the transition relation
-- is contained in the direct one.

indirect→direct : ∀ {i} x y → [ i ] x ≳ y → D.[ i ] x ≳ y
indirect→direct {i} (now x) y =
  ([ i ] now x ≳ y)                                ↝⟨ (λ p → left-to-right p now) ⟩
  (∃ λ y′ → y [ just x ]⟶̂ y′ × [ i ] now x ≳′ y′)  ↝⟨ sym ∘ [just]⟶̂→≡now ∘ proj₁ ∘ proj₂ ⟩
  now x ≡ y                                        ↝⟨ (λ { refl → D.reflexive _ }) ⟩□
  D.[ i ] now x ≳ y                                □

indirect→direct {i} x (now y) =
  [ i ] x ≳ now y                                  ↝⟨ (λ p → right-to-left p now) ⟩
  (∃ λ x′ → x [ just y ]⇒ x′ × [ i ] x′ ≳′ now y)  ↝⟨ [just]⇒→≳now ∘ proj₁ ∘ proj₂ ⟩□
  D.[ i ] x ≳ now y                                □

indirect→direct (later x) (later y) lx≳ly =
  case left-to-right lx≳ly later of λ where
    (_ , step later , x≳′y) → D.later λ { .force {j} →
                                indirect→direct _ _
                                  (force x≳′y {j = j}) }
    (_ , done _     , x≳′ly) →
      let x′ , lx⇒x′ , x′≳′y = right-to-left lx≳ly later
      in lemma x≳′ly ([]⇒→⇒ _ lx⇒x′) x′≳′y
  where
  indirect→direct′ : ∀ {i x x′ y} →
                     x ⇒ x′ → [ i ] x′ ≳ y → D.[ i ] x ≳ y
  indirect→direct′ done               p = indirect→direct _ _ p
  indirect→direct′ (step _  later tr) p = D.laterˡ
                                            (indirect→direct′ tr p)
  indirect→direct′ (step () now   _)

  lemma :
    ∀ {i x x′} →
    [ i ] force x ≳′ later y →
    later x ⇒ x′ → [ i ] x′ ≳′ force y →
    D.[ i ] later x ≳ later y
  lemma x≳′ly done lx≳′y =
    D.later λ { .force →
      D.laterˡʳ⁻¹
        (indirect→direct _ _ (force lx≳′y))
        (indirect→direct _ _ (force x≳′ly)) }
  lemma x≳′ly (step _ later x⇒x′) x′≳′y =
    D.later λ { .force →
      indirect→direct′ x⇒x′ (force x′≳′y) }

-- The direct definition of the expansion relation is logically
-- equivalent to the one obtained from the transition relation.
--
-- TODO: Are the two definitions isomorphic?

direct⇔indirect : ∀ {i x y} → D.[ i ] x ≳ y ⇔ [ i ] x ≳ y
direct⇔indirect = record
  { to   = direct→indirect
  ; from = indirect→direct _ _
  }

------------------------------------------------------------------------
-- Some non-existence results

Now≳later-now = ∀ x → now x ≳ later (record { force = now x })

now≳later-now⇔uninhabited : Now≳later-now ⇔ ¬ A
now≳later-now⇔uninhabited =
  Now≳later-now     ↝⟨ inverse $ ∀-cong _ (λ _ → direct⇔indirect) ⟩
  DN.Now≳later-now  ↝⟨ DN.now≳later-now⇔uninhabited ⟩□
  ¬ A               □

Laterˡ⁻¹ = ∀ {x y} → later x ≳ y → force x ≳ y

laterˡ⁻¹⇔uninhabited : Laterˡ⁻¹ ⇔ ¬ A
laterˡ⁻¹⇔uninhabited =
  Laterˡ⁻¹       ↝⟨ inverse $ implicit-∀-cong _ $ implicit-∀-cong _ $ →-cong _ direct⇔indirect direct⇔indirect ⟩
  DN.Laterˡ⁻¹-≳  ↝⟨ DN.laterˡ⁻¹-≳⇔uninhabited ⟩□
  ¬ A            □

Laterʳ⁻¹-∼≳ =
  ∀ {i x} →
  [ i ] never ∼ later (record { force = now x }) →
  [ i ] never ≳ now x

size-preserving-laterʳ⁻¹-∼≳⇔uninhabited : Laterʳ⁻¹-∼≳ ⇔ ¬ A
size-preserving-laterʳ⁻¹-∼≳⇔uninhabited =
  Laterʳ⁻¹-∼≳     ↝⟨ inverse $ implicit-∀-size-cong _ $ implicit-∀-cong _ $ →-cong _ SD.direct⇔indirect direct⇔indirect ⟩
  DN.Laterʳ⁻¹-∼≳  ↝⟨ DN.size-preserving-laterʳ⁻¹-∼≳⇔uninhabited ⟩□
  ¬ A             □

Laterʳ⁻¹ = ∀ {i x y} → [ i ] x ≳ later y → [ i ] x ≳ force y

size-preserving-laterʳ⁻¹⇔uninhabited : Laterʳ⁻¹ ⇔ ¬ A
size-preserving-laterʳ⁻¹⇔uninhabited =
  Laterʳ⁻¹       ↝⟨ inverse $ implicit-∀-size-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $ →-cong _ direct⇔indirect direct⇔indirect ⟩
  DN.Laterʳ⁻¹-≳  ↝⟨ DN.size-preserving-laterʳ⁻¹-≳⇔uninhabited ⟩□
  ¬ A            □

Transitivity-∼≳ˡ =
  ∀ {i} {x y z : Delay A ∞} →
  [ i ] x ∼ y → y ≳ z → [ i ] x ≳ z

size-preserving-transitivity-∼≳ˡ⇔uninhabited : Transitivity-∼≳ˡ ⇔ ¬ A
size-preserving-transitivity-∼≳ˡ⇔uninhabited =
  Transitivity-∼≳ˡ     ↝⟨ inverse $ implicit-∀-size-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $
                          →-cong _ SD.direct⇔indirect (→-cong _ direct⇔indirect direct⇔indirect) ⟩
  DN.Transitivity-∼≳ˡ  ↝⟨ DN.size-preserving-transitivity-∼≳ˡ⇔uninhabited ⟩□
  ¬ A                  □

Transitivityˡ =
  ∀ {i} {x y z : Delay A ∞} →
  [ i ] x ≳ y → y ≳ z → [ i ] x ≳ z

size-preserving-transitivityˡ⇔uninhabited : Transitivityˡ ⇔ ¬ A
size-preserving-transitivityˡ⇔uninhabited =
  Transitivityˡ       ↝⟨ inverse $ implicit-∀-size-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $
                         →-cong _ direct⇔indirect (→-cong _ direct⇔indirect direct⇔indirect) ⟩
  DN.Transitivity-≳ˡ  ↝⟨ DN.size-preserving-transitivity-≳ˡ⇔uninhabited ⟩□
  ¬ A                 □

Transitivity =
  ∀ {i} {x y z : Delay A ∞} →
  [ i ] x ≳ y → [ i ] y ≳ z → [ i ] x ≳ z

size-preserving-transitivity⇔uninhabited : Transitivity ⇔ ¬ A
size-preserving-transitivity⇔uninhabited =
  Transitivity       ↝⟨ inverse $ implicit-∀-size-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $ implicit-∀-cong _ $
                        →-cong _ direct⇔indirect $ →-cong _ direct⇔indirect direct⇔indirect ⟩
  DN.Transitivity-≳  ↝⟨ DN.size-preserving-transitivity-≳⇔uninhabited ⟩□
  ¬ A                □


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- Overloading ⟦_⟧ notation
--
-- This module defines a general mechanism for overloading the
-- ⟦_⟧ notation, using Agda’s instance arguments.
------------------------------------------------------------------------

module Base.Denotation.Notation where

open import Level

record Meaning (Syntax : Set) {ℓ : Level} : Set (suc ℓ) where
  constructor
    meaning
  field
    {Semantics} : Set ℓ
    ⟨_⟩⟦_⟧ : Syntax → Semantics

open Meaning {{...}} public
  renaming (⟨_⟩⟦_⟧ to ⟦_⟧)

open Meaning public
  using (⟨_⟩⟦_⟧)


{-# OPTIONS --without-K --rewriting --termination-depth=2 #-}

open import HoTT
open import cohomology.ChainComplex
open import cohomology.Theory
open import groups.KernelImage
open import cw.CW

module cw.cohomology.ReconstructedHigherCohomologyGroups {i : ULevel} (OT : OrdinaryTheory i) where

  open OrdinaryTheory OT
  import cw.cohomology.HigherCoboundary OT as HC
  open import cw.cohomology.WedgeOfCells OT
  open import cw.cohomology.Descending OT
  open import cw.cohomology.ReconstructedCochainComplex OT
  import cw.cohomology.HigherCohomologyGroups OT as HCG
  import cw.cohomology.HigherCohomologyGroupsOnDiag OT as HCGD
  import cw.cohomology.CohomologyGroupsTooHigh OT as CGTH

  private
    ≤-dec-has-all-paths : {m n : ℕ} → has-all-paths (Dec (m ≤ n))
    ≤-dec-has-all-paths = prop-has-all-paths (Dec-level ≤-is-prop)

  private
    abstract
      higher-cohomology-group-descend : ∀ {n} (⊙skel : ⊙Skeleton {i} (2 + n)) {m} (Sm<n : S m < n)
        →  cohomology-group (cochain-complex ⊙skel) (ℕ-to-ℤ (2 + m))
        == cohomology-group (cochain-complex (⊙cw-init ⊙skel)) (ℕ-to-ℤ (2 + m))
      higher-cohomology-group-descend ⊙skel {m} ltS -- n = 2 + m
        = ap3
            (λ Sm≤4+m? SSm≤4+m? SSSm≤4+m? → Ker/Im
              (coboundary-template ⊙skel SSm≤4+m? SSSm≤4+m?)
              (coboundary-template ⊙skel Sm≤4+m? SSm≤4+m?)
              (cochain-is-abelian-template ⊙skel SSm≤4+m?))
            (≤-dec-has-all-paths (≤-dec (1 + m) (4 + m)) (inl (lteSR $ lteSR lteS)))
            (≤-dec-has-all-paths (≤-dec (2 + m) (4 + m)) (inl (lteSR lteS)))
            (≤-dec-has-all-paths (≤-dec (3 + m) (4 + m)) (inl lteS))
        ∙ ap2 (λ δ₁ δ₂ → Ker/Im δ₂ δ₁ (CXₙ/Xₙ₋₁-is-abelian (⊙cw-take (lteSR lteS) ⊙skel) (ℕ-to-ℤ (2 + m))))
            (coboundary-higher-template-descend-from-far {n = 3 + m} ⊙skel {m = m} (ltSR ltS) ltS)
            (coboundary-higher-template-descend-from-one-above ⊙skel)
        ∙ ap3
            (λ Sm≤3+m? SSm≤3+m? SSSm≤3+m? → Ker/Im
              (coboundary-template (⊙cw-init ⊙skel) SSm≤3+m? SSSm≤3+m?)
              (coboundary-template (⊙cw-init ⊙skel) Sm≤3+m? SSm≤3+m?)
              (cochain-is-abelian-template (⊙cw-init ⊙skel) SSm≤3+m?))
            (≤-dec-has-all-paths (inl (lteSR lteS)) (≤-dec (1 + m) (3 + m)))
            (≤-dec-has-all-paths (inl lteS)         (≤-dec (2 + m) (3 + m)))
            (≤-dec-has-all-paths (inl lteE)         (≤-dec (3 + m) (3 + m)))
      higher-cohomology-group-descend {n = S n} ⊙skel {m} (ltSR Sm<n) -- n = S n
        = ap3
            (λ Sm≤3+n? SSm≤3+n? SSSm≤3+n? → Ker/Im
              (coboundary-template ⊙skel SSm≤3+n? SSSm≤3+n?)
              (coboundary-template ⊙skel Sm≤3+n? SSm≤3+n?)
              (cochain-is-abelian-template ⊙skel SSm≤3+n?))
            (≤-dec-has-all-paths (≤-dec (1 + m) (3 + n)) (inl (lteSR $ lteSR $ lteSR (inr Sm<n))))
            (≤-dec-has-all-paths (≤-dec (2 + m) (3 + n)) (inl (≤-+-l 1 (lteSR $ lteSR $ inr Sm<n))))
            (≤-dec-has-all-paths (≤-dec (3 + m) (3 + n)) (inl (≤-+-l 2 (lteSR (inr Sm<n)))))
        ∙ ap2
            (λ δ₁ δ₂ → Ker/Im δ₂ δ₁
              (CXₙ/Xₙ₋₁-is-abelian (⊙cw-take (≤-+-l 1 (lteSR $ lteSR $ inr Sm<n)) ⊙skel) (ℕ-to-ℤ (2 + m))))
            (coboundary-higher-template-descend-from-far {n = 2 + n} ⊙skel {m = m}   (ltSR (ltSR Sm<n))    (<-+-l 1 (ltSR Sm<n)))
            (coboundary-higher-template-descend-from-far {n = 2 + n} ⊙skel {m = S m} (<-+-l 1 (ltSR Sm<n)) (<-+-l 2 Sm<n))
        ∙ ap3
            (λ Sm≤2+n? SSm≤2+n? SSSm≤2+n? → Ker/Im
              (coboundary-template (⊙cw-init ⊙skel) SSm≤2+n? SSSm≤2+n?)
              (coboundary-template (⊙cw-init ⊙skel) Sm≤2+n? SSm≤2+n?)
              (cochain-is-abelian-template (⊙cw-init ⊙skel) SSm≤2+n?))
            (≤-dec-has-all-paths (inl (lteSR $ lteSR $ inr Sm<n))   (≤-dec (1 + m) (2 + n)))
            (≤-dec-has-all-paths (inl (≤-+-l 1 (lteSR $ inr Sm<n))) (≤-dec (2 + m) (2 + n)))
            (≤-dec-has-all-paths (inl (≤-+-l 2 (inr Sm<n)))         (≤-dec (3 + m) (2 + n)))

      higher-cohomology-group-β : ∀ {n} (⊙skel : ⊙Skeleton {i} (3 + n))
        →  cohomology-group (cochain-complex ⊙skel) (ℕ-to-ℤ (2 + n))
        == Ker/Im
            (HC.cw-co∂-last ⊙skel)
            (HC.cw-co∂-last (⊙cw-init ⊙skel))
            (CXₙ/Xₙ₋₁-is-abelian (⊙cw-init ⊙skel) (ℕ-to-ℤ (2 + n)))
      higher-cohomology-group-β {n} ⊙skel
        = ap3
            (λ 1+n≤3+n? 2+n≤3+n? 3+n≤3+n? → Ker/Im
              (coboundary-template ⊙skel 2+n≤3+n? 3+n≤3+n?)
              (coboundary-template ⊙skel 1+n≤3+n? 2+n≤3+n?)
              (cochain-is-abelian-template ⊙skel 2+n≤3+n?))
            (≤-dec-has-all-paths (≤-dec (1 + n) (3 + n)) (inl (lteSR lteS)))
            (≤-dec-has-all-paths (≤-dec (2 + n) (3 + n)) (inl lteS))
            (≤-dec-has-all-paths (≤-dec (3 + n) (3 + n)) (inl lteE))
        ∙ ap2 (λ δ₁ δ₂ → Ker/Im δ₂ δ₁ (CXₙ/Xₙ₋₁-is-abelian (⊙cw-init ⊙skel) (ℕ-to-ℤ (2 + n))))
            ( coboundary-higher-template-descend-from-one-above ⊙skel
            ∙ coboundary-higher-template-β (⊙cw-init ⊙skel))
            (coboundary-higher-template-β ⊙skel)

      higher-cohomology-group-β-one-below : ∀ {n} (⊙skel : ⊙Skeleton {i} (2 + n))
        →  cohomology-group (cochain-complex ⊙skel) (ℕ-to-ℤ (2 + n))
        == Ker/Im
            (cst-hom {H = Lift-group {j = i} Unit-group})
            (HC.cw-co∂-last ⊙skel)
            (CXₙ/Xₙ₋₁-is-abelian ⊙skel (ℕ-to-ℤ (2 + n)))
      higher-cohomology-group-β-one-below {n} ⊙skel
        = ap3
            (λ 1+n≤2+n? 2+n≤2+n? 3+n≤2+n? → Ker/Im
              (coboundary-template ⊙skel 2+n≤2+n? 3+n≤2+n?)
              (coboundary-template ⊙skel 1+n≤2+n? 2+n≤2+n?)
              (cochain-is-abelian-template ⊙skel 2+n≤2+n?))
            (≤-dec-has-all-paths (≤-dec (1 + n) (2 + n)) (inl lteS))
            (≤-dec-has-all-paths (≤-dec (2 + n) (2 + n)) (inl lteE))
            (≤-dec-has-all-paths (≤-dec (3 + n) (2 + n)) (inr (<-to-≱ ltS)))
        ∙ ap
            (λ δ₁ → Ker/Im
              (cst-hom {H = Lift-group {j = i} Unit-group})
              δ₁ (CXₙ/Xₙ₋₁-is-abelian ⊙skel (ℕ-to-ℤ (2 + n))))
            (coboundary-higher-template-β ⊙skel)

      higher-cohomology-group-β-far-below : ∀ {n} (⊙skel : ⊙Skeleton {i} n) {m} (SSm≰n : ¬ (2 + m ≤ n))
        →  cohomology-group (cochain-complex ⊙skel) (ℕ-to-ℤ (2 + m))
        == Ker/Im
            (cst-hom {H = Lift-group {j = i} Unit-group})
            (cst-hom
              {G = AbGroup.grp (cochain-template ⊙skel (≤-dec (1 + m) n))}
              {H = Lift-group {j = i} Unit-group})
            (snd (Lift-abgroup {j = i} Unit-abgroup))
      higher-cohomology-group-β-far-below {n} ⊙skel {m} SSm≰n
        = ap2
            (λ 2+m≤n? 3+m≤n? → Ker/Im
              (coboundary-template ⊙skel 2+m≤n? 3+m≤n?)
              (coboundary-template ⊙skel (≤-dec (1 + m) n) 2+m≤n?)
              (cochain-is-abelian-template ⊙skel 2+m≤n?))
            (≤-dec-has-all-paths (≤-dec (2 + m) n) (inr SSm≰n))
            (≤-dec-has-all-paths (≤-dec (3 + m) n) (inr (λ SSSm≤n → SSm≰n (≤-trans lteS SSSm≤n))))

  abstract
    higher-cohomology-group : ∀ m {n} (⊙skel : ⊙Skeleton {i} n)
      → ⊙has-cells-with-choice 0 ⊙skel i
      → C (ℕ-to-ℤ (2 + m)) ⊙⟦ ⊙skel ⟧ ≃ᴳ cohomology-group (cochain-complex ⊙skel) (ℕ-to-ℤ (2 + m))
    higher-cohomology-group m {n = 0} ⊙skel ac =
      CGTH.C-cw-iso-ker/im (2 + m) (O<S (S m)) (Lift-group {j = i} Unit-group) ⊙skel ac
    higher-cohomology-group m {n = 1} ⊙skel ac =
      CGTH.C-cw-iso-ker/im (2 + m) (<-+-l 1 (O<S m))
        (AbGroup.grp (cochain-template ⊙skel (≤-dec (1 + m) 1)))
        ⊙skel ac
    higher-cohomology-group m {n = S (S n)} ⊙skel ac with ℕ-trichotomy (S m) (S n)
    ... | inl idp = coe!ᴳ-iso (higher-cohomology-group-β-one-below ⊙skel)
                ∘eᴳ HCGD.C-cw-iso-ker/im ⊙skel ac
    ... | inr (inl ltS) = coe!ᴳ-iso (higher-cohomology-group-β ⊙skel)
                      ∘eᴳ HCG.C-cw-iso-ker/im ⊙skel ac
    ... | inr (inl (ltSR Sm<n)) =
                coe!ᴳ-iso (higher-cohomology-group-descend ⊙skel Sm<n)
            ∘eᴳ higher-cohomology-group m {n = S n} (⊙cw-init ⊙skel) (⊙init-has-cells-with-choice ⊙skel ac)
            ∘eᴳ C-cw-descend-at-lower ⊙skel (<-+-l 1 Sm<n) ac
    ... | inr (inr Sn<Sm) =
                coe!ᴳ-iso (higher-cohomology-group-β-far-below ⊙skel (<-to-≱ (<-+-l 1 Sn<Sm)))
            ∘eᴳ CGTH.C-cw-iso-ker/im (2 + m) (<-+-l 1 Sn<Sm)
                  (AbGroup.grp (cochain-template ⊙skel (≤-dec (1 + m) (2 + n))))
                  ⊙skel ac


{-

  Reflection-based tools for converting between iterated record types, particularly between
  record types and iterated Σ-types.

  See end of file for examples.

-}
{-# OPTIONS --cubical --no-exact-split --safe #-}
module Cubical.Reflection.RecordEquiv where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Data.List as List
open import Cubical.Data.Nat
open import Cubical.Data.Maybe as Maybe
open import Cubical.Data.Sigma

open import Agda.Builtin.String
import Agda.Builtin.Reflection as R
open import Cubical.Reflection.Base

Projections = Maybe (List R.Name)

-- Describes a correspondence between two iterated record types
RecordAssoc = List (Projections × Projections)

-- Describes a correspondence between a record type and an iterated Σ-types;
-- more convenient than RecordAssoc for this special case
data ΣFormat : Type where
  leaf : R.Name → ΣFormat
  _,_ : ΣFormat → ΣFormat → ΣFormat
  unit : ΣFormat

infixr 4 _,_

flipRecordAssoc : RecordAssoc → RecordAssoc
flipRecordAssoc = List.map λ {p .fst → p .snd; p .snd → p .fst}

fstIdRecordAssoc : RecordAssoc → RecordAssoc
fstIdRecordAssoc = List.map λ {p .fst → p .fst; p .snd → p .fst}

List→ΣFormat : List R.Name → ΣFormat
List→ΣFormat [] = unit
List→ΣFormat (x ∷ []) = leaf x
List→ΣFormat (x ∷ y ∷ xs) = leaf x , List→ΣFormat (y ∷ xs)

ΣFormat→RecordAssoc : ΣFormat → RecordAssoc
ΣFormat→RecordAssoc = go []
  where
  go : List R.Name → ΣFormat → RecordAssoc
  go prefix unit = [ just prefix , nothing ]
  go prefix (leaf fieldName) = [ just prefix , just [ fieldName ] ]
  go prefix (sig₁ , sig₂) =
    go (quote fst ∷ prefix) sig₁ ++ go (quote snd ∷ prefix) sig₂

-- Derive the shape of the compound Σ-type
ΣFormat→Ty : ΣFormat → R.Type
ΣFormat→Ty unit = R.def (quote Unit) []
ΣFormat→Ty (leaf _) = R.unknown
ΣFormat→Ty (sig₁ , sig₂) =
  R.def (quote Σ) (ΣFormat→Ty sig₁ v∷ R.lam R.visible (R.abs "_" (ΣFormat→Ty sig₂)) v∷ [])

recordName→isoTy : R.Name → R.Term → R.TC R.Term
recordName→isoTy name σShape =
  R.inferType (R.def name []) >>= R.normalise >>= go []
  where
  go : List R.ArgInfo → R.Type → R.TC R.Term
  go acc (R.pi (R.arg i argTy) (R.abs s ty)) =
    liftTC (λ t → R.pi (R.arg i' argTy) (R.abs s t)) (go (i ∷ acc) ty)
    where
    i' = R.arg-info R.hidden (R.modality R.relevant R.quantity-ω)
  go acc (R.agda-sort _) =
    R.returnTC (R.def (quote Iso) (R.def name (makeArgs 0 [] acc) v∷ σShape v∷ []))
    where
    makeArgs : ℕ → List (R.Arg R.Term) → List R.ArgInfo → List (R.Arg R.Term)
    makeArgs n acc [] = acc
    makeArgs n acc (i ∷ infos) = makeArgs (suc n) (R.arg i (v n) ∷ acc) infos
  go _ _ = R.typeError (R.strErr "Not a record type name: " ∷ R.nameErr name ∷ [])

projNames→Patterns : List R.Name → List (R.Arg R.Pattern)
projNames→Patterns = go []
  where
  go : List (R.Arg R.Pattern) → List R.Name → List (R.Arg R.Pattern)
  go acc [] = acc
  go acc (π ∷ projs) = go (varg (R.proj π) ∷ acc) projs

projNames→Term : R.Term → List R.Name → R.Term
projNames→Term term [] = term
projNames→Term term (π ∷ projs) = R.def π [ varg (projNames→Term term projs) ]

convertClauses : RecordAssoc → R.Term → List R.Clause
convertClauses al term = fixIfEmpty (List.filterMap makeClause al)
  where
  makeClause : Projections × Projections → Maybe R.Clause
  makeClause (projl , just projr) =
    just (R.clause [] (goPat [] projr) (Maybe.rec R.unknown goTm projl))
    where
    goPat : List (R.Arg R.Pattern) → List R.Name → List (R.Arg R.Pattern)
    goPat acc [] = acc
    goPat acc (π ∷ projs) = goPat (varg (R.proj π) ∷ acc) projs

    goTm : List R.Name → R.Term
    goTm [] = term
    goTm (π ∷ projs) = R.def π [ varg (goTm projs) ]
  makeClause (_ , nothing) = nothing

  fixIfEmpty : List R.Clause → List R.Clause
  fixIfEmpty [] = [ R.clause [] [] R.unknown ]
  fixIfEmpty (c ∷ cs) = c ∷ cs

mapClause :
  (List (String × R.Arg R.Type) → List (String × R.Arg R.Type))
  → (List (R.Arg R.Pattern) → List (R.Arg R.Pattern))
  → (R.Clause → R.Clause)
mapClause f g (R.clause tel ps t) = R.clause (f tel) (g ps) t
mapClause f g (R.absurd-clause tel ps) = R.absurd-clause (f tel) (g ps)

recordIsoΣClauses : ΣFormat → List R.Clause
recordIsoΣClauses σ =
  funClauses (quote Iso.fun) Σ↔R ++
  funClauses (quote Iso.inv) R↔Σ ++
  pathClauses (quote Iso.rightInv) R↔Σ ++
  pathClauses (quote Iso.leftInv) Σ↔R
  where
  R↔Σ = ΣFormat→RecordAssoc σ
  Σ↔R = flipRecordAssoc R↔Σ

  funClauses : R.Name → RecordAssoc → List R.Clause
  funClauses name al =
    List.map
      (mapClause
        (("_" , varg R.unknown) ∷_)
        (λ ps → R.proj name v∷ R.var 0 v∷ ps))
      (convertClauses al (v 0))

  pathClauses : R.Name → RecordAssoc → List R.Clause
  pathClauses name al =
    List.map
      (mapClause
        (λ vs → ("_" , varg R.unknown) ∷ ("_" , varg R.unknown) ∷ vs)
        (λ ps → R.proj name v∷ R.var 1 v∷ R.var 0 v∷ ps))
      (convertClauses (fstIdRecordAssoc al) (v 1))

recordIsoΣTerm : ΣFormat → R.Term
recordIsoΣTerm σ = R.pat-lam (recordIsoΣClauses σ) []

-- with a provided ΣFormat for the record
declareRecordIsoΣ' : R.Name → ΣFormat → R.Name → R.TC Unit
declareRecordIsoΣ' idName σ recordName =
  let σTy = ΣFormat→Ty σ in
  recordName→isoTy recordName σTy >>= λ isoTy →
  R.declareDef (varg idName) isoTy >>
  R.defineFun idName (recordIsoΣClauses σ)

-- using the right-associated Σ given by the record fields
declareRecordIsoΣ : R.Name → R.Name → R.TC Unit
declareRecordIsoΣ idName recordName =
  R.getDefinition recordName >>= λ where
  (R.record-type _ fs) →
    let σ = List→ΣFormat (List.map (λ {(R.arg _ n) → n}) fs) in
    declareRecordIsoΣ' idName σ recordName
  _ →
    R.typeError (R.strErr "Not a record type name:" ∷ R.nameErr recordName ∷ [])

private
  module Example where
    variable
      ℓ ℓ' : Level
      A : Type ℓ
      B : A → Type ℓ'

    record Example0 {A : Type ℓ} (B : A → Type ℓ') : Type (ℓ-max ℓ ℓ') where
      no-eta-equality -- works with or without eta equality
      field
        cool : A
        fun : A
        wow : B cool

    -- Declares a function `Example0IsoΣ` that gives an isomorphism between the record type and a
    -- right-associated nested Σ-type (with the parameters to Example0 as implict arguments).
    unquoteDecl Example0IsoΣ = declareRecordIsoΣ Example0IsoΣ (quote Example0)

    -- `Example0IsoΣ` has the type we expect
    test0 : Iso (Example0 B) (Σ[ a ∈ A ] (Σ[ _ ∈ A ] B a))
    test0 = Example0IsoΣ

    -- A record with no fields is isomorphic to Unit

    record Example1 : Type where

    unquoteDecl Example1IsoΣ = declareRecordIsoΣ Example1IsoΣ (quote Example1)

    test1 : Iso Example1 Unit
    test1 = Example1IsoΣ


------------------------------------------------------------------------
-- Decoding types and subtypes into SK terms and equality
------------------------------------------------------------------------

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

module FOmegaInt.Undecidable.Decoding where

open import Data.List using ([]; _∷_)
open import Data.Product using (_,_; proj₁; proj₂; _×_)
open import Data.Fin using (zero; suc)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.ExtraLemmas
open import Function using (_∘_)
open import Relation.Binary.PropositionalEquality hiding ([_])

open import FOmegaInt.Syntax
open import FOmegaInt.Kinding.Canonical as Canonical
open import FOmegaInt.Kinding.Canonical.Reduced as Reduced
open import FOmegaInt.Undecidable.SK
open import FOmegaInt.Undecidable.Encoding

open Syntax
open ElimCtx
open Substitution hiding (subst)
open Canonical.Kinding using (_⊢_<:_; _⊢_<:_⇇_; <:-⇇)
open Reduced.Kinding
open Encoding
open ≡-Reasoning
module V   = VarSubst
module EL  = TermLikeLemmas Substitution.termLikeLemmasElim
module ELV = LiftAppLemmas EL.varLiftAppLemmas

------------------------------------------------------------------------
-- Decoding of types into SK? terms

decode : Elim |Γ| → SK?Term
decode (var x₀ ∙ (a ∷ b ∷ _))     = K · (decode a) · (decode b)
decode (var x₁ ∙ (a ∷ b ∷ c ∷ _)) = S · (decode a) · (decode b) · (decode c)
decode (var x₂ ∙ (a ∷ b ∷ _))     = K · (decode a) · (decode b)
decode (var x₃ ∙ (a ∷ b ∷ c ∷ _)) = S · (decode a) · (decode b) · (decode c)
decode (var x₄ ∙ (a ∷ b ∷ _))     = decode a · decode b
decode (var x₅ ∙ _)               = K
decode (var x₆ ∙ _)               = S
decode (⊥ ∙ _)                    = ⊥
decode (⊤ ∙ _)                    = ⊤
{-# CATCHALL #-}
decode _ = ⊤

-- Decoding is the left-inverse of encoding.
--
-- NOTE. We encode SK terms but decode to SK? terms, so decoding is
-- strictly speaking only left-inverse when restricted to (pure) SK
-- terms.  Hence the use of `inject' here.

decode-encode : ∀ t → decode (encode t) ≡ inject t
decode-encode S       = refl
decode-encode K       = refl
decode-encode (s · t) = cong₂ _·_ (decode-encode s) (decode-encode t)

-- Decoding subsumes squashing

decode-squash : ∀ (a : Elim |Γ|) → decode (squashElim a) ≡ decode a
decode-squash (var x₀ ∙ [])              = refl   -- degenerate case
decode-squash (var x₀ ∙ (_ ∷ []))        = refl   -- degenerate case
decode-squash (var x₀ ∙ (a ∷ b ∷ _))     =
  cong₂ (λ a b → K · a · b) (decode-squash a) (decode-squash b)
decode-squash (var x₁ ∙ [])              = refl   -- degenerate case
decode-squash (var x₁ ∙ (_ ∷ []))        = refl   -- degenerate case
decode-squash (var x₁ ∙ (_ ∷ _ ∷ []))    = refl   -- degenerate case
decode-squash (var x₁ ∙ (a ∷ b ∷ c ∷ _)) =
  trans (cong (S · _ · _ ·_) (decode-squash c))
        (cong₂ (λ a b → S · a · b · _) (decode-squash a) (decode-squash b))
decode-squash (var x₂ ∙ [])              = refl   -- degenerate case
decode-squash (var x₂ ∙ (_ ∷ []))        = refl   -- degenerate case
decode-squash (var x₂ ∙ (a ∷ b ∷ _))     =
  cong₂ (λ a b → K · a · b) (decode-squash a) (decode-squash b)
decode-squash (var x₃ ∙ [])              = refl   -- degenerate case
decode-squash (var x₃ ∙ (_ ∷ []))        = refl   -- degenerate case
decode-squash (var x₃ ∙ (_ ∷ _ ∷ []))    = refl   -- degenerate case
decode-squash (var x₃ ∙ (a ∷ b ∷ c ∷ _)) =
  trans (cong (S · _ · _ ·_) (decode-squash c))
        (cong₂ (λ a b → S · a · b · _) (decode-squash a) (decode-squash b))
decode-squash (var x₄ ∙ [])              = refl   -- degenerate case
decode-squash (var x₄ ∙ (_ ∷ []))        = refl   -- degenerate case
decode-squash (var x₄ ∙ (a ∷ b ∷ _))     =
  cong₂ _·_ (decode-squash a) (decode-squash b)
decode-squash (var x₅ ∙ as) = refl
decode-squash (var x₆ ∙ as) = refl
decode-squash (⊥ ∙ as)      = refl
decode-squash (⊤ ∙ as)      = refl
-- squashed cases
decode-squash (Π k a   ∙ as) = refl
decode-squash ((a ⇒ b) ∙ as) = refl
decode-squash (Λ k a   ∙ as) = refl
decode-squash (ƛ a b   ∙ as) = refl
decode-squash (a ⊡ b   ∙ as) = refl

-- Helper lemmas to be used when decoding kinding derivations.

⌜⌝-⌞⌟-∙∙ : ∀ {n} (a : Elim n) → ⌜ ⌞ a ⌟ ⌝ ∙∙ [] ≡ a
⌜⌝-⌞⌟-∙∙ a = begin
  ⌜ ⌞ a ⌟ ⌝ ∙∙ []  ≡⟨ ∙∙-[] ⌜ ⌞ a ⌟ ⌝ ⟩
  ⌜ ⌞ a ⌟ ⌝        ≡⟨ ⌜⌝∘⌞⌟-id a ⟩
  a                ∎

⌜⌝-⌞⌟-weaken-sub₁ : ∀ {n} (a b : Elim n) →
                    (⌜ weaken ⌞ a ⌟ ⌝ ∙∙ []) Elim[ ⌞ b ⌟ ] ≡ a
⌜⌝-⌞⌟-weaken-sub₁ a b = begin
    (⌜ weaken ⌞ a ⌟ ⌝ ∙∙ []) Elim[ ⌞ b ⌟ ]
  ≡˘⟨ cong (λ a → (⌜ a ⌝ ∙∙ []) Elim[ ⌞ b ⌟ ]) (⌞⌟-/Var a) ⟩
    (⌜ ⌞ weakenElim a ⌟ ⌝ ∙∙ []) Elim[ ⌞ b ⌟ ]
  ≡⟨ cong (_Elim[ ⌞ b ⌟ ]) (⌜⌝-⌞⌟-∙∙ (weakenElim a)) ⟩
    weakenElim a Elim[ ⌞ b ⌟ ]
  ≡⟨ EL.weaken-sub a ⟩
    a
  ∎

⌜⌝-⌞⌟-weaken-sub₂ : ∀ {n} (a b c : Elim n) →
  ((⌜ weaken (weaken ⌞ a ⌟) ⌝ ∙∙ []) Elim/ sub ⌞ b ⌟ ↑) Elim[ ⌞ c ⌟ ] ≡ a
⌜⌝-⌞⌟-weaken-sub₂ a b c = begin
    ((⌜ weaken (weaken ⌞ a ⌟) ⌝ ∙∙ []) Elim/ sub ⌞ b ⌟ ↑) Elim[ ⌞ c ⌟ ]
  ≡˘⟨ cong (λ a → ((⌜ a ⌝ ∙∙ []) Elim/ sub ⌞ b ⌟ ↑) Elim[ ⌞ c ⌟ ])
           (trans (⌞⌟-/Var (weakenElim a)) (cong weaken (⌞⌟-/Var a))) ⟩
    ((⌜ ⌞ weakenElim (weakenElim a) ⌟ ⌝ ∙∙ []) Elim/ sub ⌞ b ⌟ ↑) Elim[ ⌞ c ⌟ ]
  ≡⟨ cong (λ a → (a Elim/ sub ⌞ b ⌟ ↑) Elim[ ⌞ c ⌟ ])
          (⌜⌝-⌞⌟-∙∙ (weakenElim (weakenElim a))) ⟩
    (weakenElim (weakenElim a) Elim/ sub ⌞ b ⌟ ↑) Elim[ ⌞ c ⌟ ]
  ≡⟨ cong (_Elim[ ⌞ c ⌟ ]) (sym (EL.weaken-/ (weakenElim a))) ⟩
    weakenElim (weakenElim a Elim/ sub ⌞ b ⌟) Elim[ ⌞ c ⌟ ]
  ≡⟨ cong ((_Elim[ ⌞ c ⌟ ]) ∘ weakenElim) (EL.weaken-sub a) ⟩
    weakenElim a Elim[ ⌞ c ⌟ ]
  ≡⟨ EL.weaken-sub a ⟩
    a
  ∎

-- Decoding of (reduced) canonical type order into the SK? term order.

mutual

  decode-∼ : ∀ {a b} → Γ-SK? ⊢ a ∼ b → decode a ≤SK? decode b
  decode-∼ (∼-trans a∼b b∼c) = ≤?-trans (decode-∼ a∼b) (decode-∼ b∼c)
  decode-∼ (∼-⊥ b⇉b⋯b)       = ≤?-⊥
  decode-∼ (∼-⊤ a⇉a⋯a)       = ≤?-⊤
  decode-∼ (∼-∙ (⇉-var x₀ _ refl) (∼-∷ _ _ a₁∼b₁ (∼-∷ _ _ a₂∼b₂ ∼-[]))) =
    ≤?-· (≤?-· ≤?-refl (decode-∼ a₁∼b₁)) (decode-∼ a₂∼b₂)
  decode-∼ (∼-∙ (⇉-var x₁ _ refl)
           (∼-∷ _ _ a₁∼b₁ (∼-∷ _ _ a₂∼b₂ (∼-∷ _ _ a₃∼b₃ ∼-[])))) =
    ≤?-· (≤?-· (≤?-· ≤?-refl (decode-∼ a₁∼b₁)) (decode-∼ a₂∼b₂))
         (decode-∼ a₃∼b₃)
  decode-∼ (∼-∙ (⇉-var x₂ _ refl) (∼-∷ _ _ a₁∼b₁ (∼-∷ _ _ a₂∼b₂ ∼-[]))) =
    ≤?-· (≤?-· ≤?-refl (decode-∼ a₁∼b₁)) (decode-∼ a₂∼b₂)
  decode-∼ (∼-∙ (⇉-var x₃ _ refl)
           (∼-∷ _ _ a₁∼b₁ (∼-∷ _ _ a₂∼b₂ (∼-∷ _ _ a₃∼b₃ ∼-[])))) =
    ≤?-· (≤?-· (≤?-· ≤?-refl (decode-∼ a₁∼b₁)) (decode-∼ a₂∼b₂))
         (decode-∼ a₃∼b₃)
  decode-∼ (∼-∙ (⇉-var x₄ _ refl) (∼-∷ _ _ a₁∼b₁ (∼-∷ _ _ a₂∼b₂ ∼-[]))) =
    ≤?-· (decode-∼ a₁∼b₁) (decode-∼ a₂∼b₂)
  decode-∼ (∼-∙ (⇉-var x₅ _ refl) ∼-[]) = ≤?-refl
  decode-∼ (∼-∙ (⇉-var x₆ _ refl) ∼-[]) = ≤?-refl
  decode-∼ (∼-⟨| a⇉b⋯c) = proj₁ (decode-Ne⇉ a⇉b⋯c)
  decode-∼ (∼-|⟩ a⇉b⋯c) = proj₂ (decode-Ne⇉ a⇉b⋯c)

  decode-Ne⇉ : ∀ {a b c} → Γ-SK? ⊢Ne a ⇉ b ⋯ c →
               decode b ≤SK? decode a × decode a ≤SK? decode c
  decode-Ne⇉ (⇉-∙ (⇉-var x₀ _ refl) (⇉-∷ {a} a∈* (⇉-∷ {b} b∈* ⇉-[]))) =
    subst (λ a′ → decode a′ ≤SK? K · (decode a) · (decode b))
          (sym (⌜⌝-⌞⌟-weaken-sub₁ a b)) ≤?-Kexp ,
    subst₂ (λ a′ b′ → K · decode a  · decode b ≤SK?
                      K · decode a′ · decode b′)
           (sym (⌜⌝-⌞⌟-weaken-sub₁ a b)) (sym (⌜⌝-⌞⌟-∙∙ b)) ≤?-refl
  decode-Ne⇉ (⇉-∙ (⇉-var x₁ _ refl)
                  (⇉-∷ {a} a∈* (⇉-∷ {b} b∈* (⇉-∷ {c} c∈* ⇉-[])))) =
    subst₂ (λ a′ b′ → decode a′ · decode c′ · (decode b′ · decode c′) ≤SK?
                      S · decode a · decode b · decode c)
      (sym (⌜⌝-⌞⌟-weaken-sub₂ a b c)) (sym (⌜⌝-⌞⌟-weaken-sub₁ b c))
      (subst (λ c′ → decode a · decode c′ · (decode b · decode c′) ≤SK?
                     S · decode a · decode b · decode c)
        (sym (⌜⌝-⌞⌟-∙∙ c)) ≤?-Sexp) ,
    subst₂ (λ a′ b′ → S · decode a  · decode b  · decode c ≤SK?
                      S · decode a′ · decode b′ · decode c′)
      (sym (⌜⌝-⌞⌟-weaken-sub₂ a b c)) (sym (⌜⌝-⌞⌟-weaken-sub₁ b c))
      (subst (λ c′ → S · decode a · decode b · decode c ≤SK?
                     S · decode a · decode b · decode c′)
        (sym (⌜⌝-⌞⌟-∙∙ c)) ≤?-refl)
    where c′ = ⌜ ⌞ c ⌟ ⌝ ∙∙ []
  decode-Ne⇉ (⇉-∙ (⇉-var x₂ _ refl) (⇉-∷ {a} a∈* (⇉-∷ {b} b∈* ⇉-[]))) =
    subst₂ (λ a′ b′ → K · decode a′ · decode b′ ≤SK?
                      K · decode a  · decode b)
           (sym (⌜⌝-⌞⌟-weaken-sub₁ a b)) (sym (⌜⌝-⌞⌟-∙∙ b)) ≤?-refl ,
    subst (λ a′ → K · decode a · decode b ≤SK? decode a′)
          (sym (⌜⌝-⌞⌟-weaken-sub₁ a b)) ≤?-Kred
  decode-Ne⇉ (⇉-∙ (⇉-var x₃ _ refl)
             (⇉-∷ {a} a∈* (⇉-∷ {b} b∈* (⇉-∷ {c} c∈* ⇉-[])))) =
    subst₂ (λ a′ b′ → S · decode a′ · decode b′ · decode c′ ≤SK?
                      S · decode a  · decode b  · decode c)
      (sym (⌜⌝-⌞⌟-weaken-sub₂ a b c)) (sym (⌜⌝-⌞⌟-weaken-sub₁ b c))
      (subst (λ c′ → S · decode a · decode b · decode c′ ≤SK?
                     S · decode a · decode b · decode c)
        (sym (⌜⌝-⌞⌟-∙∙ c)) ≤?-refl) ,
    subst₂ (λ a′ b′ → S · decode a · decode b · decode c ≤SK?
                      decode a′ · decode c′ · (decode b′ · decode c′))
      (sym (⌜⌝-⌞⌟-weaken-sub₂ a b c)) (sym (⌜⌝-⌞⌟-weaken-sub₁ b c))
      (subst (λ c′ → S · decode a · decode b · decode c ≤SK?
                     decode a · decode c′ · (decode b · decode c′))
        (sym (⌜⌝-⌞⌟-∙∙ c)) ≤?-Sred)
    where c′ = ⌜ ⌞ c ⌟ ⌝ ∙∙ []
  decode-Ne⇉ (⇉-∙ (⇉-var x₄ _ refl) (⇉-∷ a∈* (⇉-∷ b∈* ⇉-[]))) = ≤?-⊥ , ≤?-⊤
  decode-Ne⇉ (⇉-∙ (⇉-var x₅ _ refl) ⇉-[]) = ≤?-⊥ , ≤?-⊤
  decode-Ne⇉ (⇉-∙ (⇉-var x₆ _ refl) ⇉-[]) = ≤?-⊥ , ≤?-⊤

-- The SK? signature is a first-order context.

kdS-fo : FOKind (kdS {0})
kdS-fo = fo-⋯

kdK-fo : FOKind (kdK {0})
kdK-fo = fo-⋯

kdApp-fo : FOKind (kdApp {0})
kdApp-fo = fo-Π (fo-Π fo-⋯)

kdSRed-fo : FOKind (kdSRed {0})
kdSRed-fo = fo-Π (fo-Π (fo-Π fo-⋯))

kdKRed-fo : FOKind (kdKRed {0})
kdKRed-fo = fo-Π (fo-Π fo-⋯)

kdSExp-fo : FOKind (kdSExp {0})
kdSExp-fo = fo-Π (fo-Π (fo-Π fo-⋯))

kdKExp-fo : FOKind (kdKExp {0})
kdKExp-fo = fo-Π (fo-Π fo-⋯)

Γ-SK?-fo : FOCtx Γ-SK?
Γ-SK?-fo =
  fo-∷ (fo-kd kdKExp-fo)
       (fo-∷ (fo-kd kdSExp-fo)
             (fo-∷ (fo-kd kdKRed-fo)
                   (fo-∷ (fo-kd kdSRed-fo)
                         (fo-∷ (fo-kd kdApp-fo)
                               (fo-∷ (fo-kd kdK-fo)
                                     (fo-∷ (fo-kd kdS-fo) fo-[]))))))

-- Decoding of canonical subtyping into the SK? term order.

decode-<: : ∀ {a b} → Γ-SK? ⊢ a <: b → decode a ≤SK? decode b
decode-<: {a} {b} a<:b =
  subst₂ (_≤SK?_) (decode-squash a) (decode-squash b)
         (decode-∼ (reduce-<: Γ-SK?-fo a<:b))

decode-<:⇇ : ∀ {a b} → Γ-SK? ⊢ a <: b ⇇ ⌜*⌝ → decode a ≤SK? decode b
decode-<:⇇ {a} {b} (<:-⇇ _ _ a<:b) = decode-<: a<:b

-- Full decoding to SK term equality.

decode-<:⇇-encode : ∀ {s t} → Γ-SK? ⊢ encode s <: encode t ⇇ ⌜*⌝ → s ≡SK t
decode-<:⇇-encode {s} {t} es<:et⇇* =
  inject-≤SK?-≡SK (subst₂ (_≤SK?_) (decode-encode s) (decode-encode t)
                          (decode-<:⇇ es<:et⇇*))