module CP where

open import Level
open import Data.Nat
open import Relation.Nullary
-- open import Data.String using (String)

TypeVar : Set
TypeVar = ℕ

infixl 10 _⨂_
infixl 10 _⅋_
data Type : Set where
    -- ‵_ : TypeVar → Type
    -- _^ : Type → Type
    _⨂_ : Type → Type → Type
    _⅋_ : Type → Type → Type
    -- _⨁_ : Type → Type → Type
    -- _&_ : Type → Type → Type
    -- !_ : Type → Type
    -- ¿_ : Type → Type
    -- ∃[_] : Type → Type
    -- ∀[_] : Type → Type
    𝟙 : Type
    ⊥ : Type
    𝟘 : Type
    ⊤ : Type

infixl 11 _^
_^ : Type → Type
(A ⨂ B) ^ = A ^ ⅋ B ^
(A ⅋ B) ^ = A ^ ⨂ B ^
𝟙 ^ = ⊥
⊥ ^ = 𝟙
𝟘 ^ = ⊤
⊤ ^ = 𝟘

open import Relation.Binary.PropositionalEquality
open import Relation.Nullary.Decidable
open import Relation.Nullary.Product
open import Data.Product
open import Agda.Builtin.Bool

-- _≟t_ : (A B : Type) → Dec (A ≡ B)
-- (A ⨂ B) ≟t (C ⨂ D) with A ≟t C | B ≟t D
-- ... | yes refl | yes refl = yes refl
-- ... | yes refl | no ¬q    = no λ where refl → ¬q refl
-- ... | no ¬p    | yes q    = no λ where refl → ¬p refl
-- ... | no ¬p    | no ¬q    = no λ where refl → ¬p refl
-- (A ⨂ B) ≟t (C ⅋ D) = no (λ ())
-- (A ⨂ B) ≟t 𝟙 = no (λ ())
-- (A ⨂ B) ≟t ⊥ = no (λ ())
-- (A ⨂ B) ≟t 𝟘 = no (λ ())
-- (A ⨂ B) ≟t ⊤ = no (λ ())
-- (A ⅋ B) ≟t (C ⨂ D) = no (λ ())
-- (A ⅋ B) ≟t (C ⅋ D) with A ≟t C | B ≟t D
-- ... | yes refl | yes refl = yes refl
-- ... | yes refl | no ¬q    = no λ where refl → ¬q refl
-- ... | no ¬p    | yes q    = no λ where refl → ¬p refl
-- ... | no ¬p    | no ¬q    = no λ where refl → ¬p refl
-- (A ⅋ B) ≟t 𝟙 = no (λ ())
-- (A ⅋ B) ≟t ⊥ = no (λ ())
-- (A ⅋ B) ≟t 𝟘 = no (λ ())
-- (A ⅋ B) ≟t ⊤ = no (λ ())
-- 𝟙 ≟t (B ⨂ C) = no (λ ())
-- 𝟙 ≟t (B ⅋ C) = no (λ ())
-- 𝟙 ≟t 𝟙 = yes refl
-- 𝟙 ≟t ⊥ = no (λ ())
-- 𝟙 ≟t 𝟘 = no (λ ())
-- 𝟙 ≟t ⊤ = no (λ ())
-- ⊥ ≟t (B ⨂ C) = no (λ ())
-- ⊥ ≟t (B ⅋ C) = no (λ ())
-- ⊥ ≟t 𝟙 = no (λ ())
-- ⊥ ≟t ⊥ = yes refl
-- ⊥ ≟t 𝟘 = no (λ ())
-- ⊥ ≟t ⊤ = no (λ ())
-- 𝟘 ≟t (B ⨂ C) = no (λ ())
-- 𝟘 ≟t (B ⅋ C) = no (λ ())
-- 𝟘 ≟t 𝟙 = no (λ ())
-- 𝟘 ≟t ⊥ = no (λ ())
-- 𝟘 ≟t 𝟘 = yes refl
-- 𝟘 ≟t ⊤ = no (λ ())
-- ⊤ ≟t (B ⨂ C) = no (λ ())
-- ⊤ ≟t (B ⅋ C) = no (λ ())
-- ⊤ ≟t 𝟙 = no (λ ())
-- ⊤ ≟t ⊥ = no (λ ())
-- ⊤ ≟t 𝟘 = no (λ ())
-- ⊤ ≟t ⊤ = yes refl

Chan : Set
Chan = ℕ

data Proc⁺ : Set
data Proc⁻ : Set

data Proc⁺ where
    _↔_ : Chan → Chan → Proc⁺

    ν_∶_∙_∣_ : Chan → Type → Proc⁻ → Proc⁻ → Proc⁺
-- infixl  7  _⟦⟧0

-- inherited types
data Proc⁻ where

    -- x[y].(P|Q)
    _⟦_⟧_∣_ : Chan → Chan → Proc⁻ → Proc⁻ → Proc⁻

    -- x(y).(P|Q)
    _⦅_⦆_ : Chan → Chan → Proc⁻ → Proc⁻

    -- x[].0
    _⟦⟧0 : Chan → Proc⁻
    -- x().P
    _⦅⦆_ : Chan → Proc⁻ → Proc⁻
    -- x.case()
    _case : Chan → Proc⁻

-- _[_/_] : Type → Type → TypeVar → Type
-- (‵ P) [ T / X ] with P ≟ X
-- ((‵ P) [ T / X ]) | yes p = T
-- ((‵ P) [ T / X ]) | no ¬p = ‵ P
-- (P ^) [ T / X ] = (P [ T / X ]) ^
-- (P ⨂ Q) [ T / X ] = (P [ T / X ]) ⨂ (Q [ T / X ])
-- (P ⅋ Q) [ T / X ] = (P [ T / X ]) ⅋ (Q [ T / X ])
-- (P ⨁ Q) [ T / X ] = (P [ T / X ]) ⨁ (Q [ T / X ])
-- (P & Q) [ T / X ] = (P [ T / X ]) & (Q [ T / X ])
-- (! P) [ T / X ] = ! (P [ T / X ])
-- (¿ P) [ T / X ] = ¿ (P [ T / X ])
-- ∃[ P ] [ T / zero ] = {!   !}
-- ∃[ P ] [ T / suc X ] = {!   !}
-- ∀[ P ] [ T / X ] = {!   !}
-- 𝟙 [ T / X ] = {!   !}
-- ⊥ [ T / X ] = {!   !}
-- 𝟘 [ T / X ] = {!   !}
-- ⊤ [ T / X ] = {!  !}

-- infixl 5 _,_∶_
-- data Session : Set where
--     _,_∶_ : Session → Chan → Type → Session
--     _++_ : Session → Session → Session
--     ∅ : Session

-- infix   4  _⊢↓_
-- infix   4  _⊢↑_
-- data _⊢↑_ : Proc⁺ → Session → Set
-- data _⊢↓_ : Proc⁻ → Session → Set

-- data _⊢↑_ where
--     ⊢↔ : ∀ {w x A}
--         ---------------
--         → w ↔ x ⊢↑ ∅ , w ∶ A ^ , x ∶ A

--     ⊢Cut : ∀ {P Q Γ Δ x A}
--         → P ⊢↓ Γ , x ∶ A
--         → Q ⊢↓ Δ , x ∶ A ^
--         ---------------
--         → ν x ∶ A ∙ P ∣ Q ⊢↑ Γ ++ Δ

-- data _⊢↓_ where

--     ⊢⨂ : ∀ {P Q Γ Δ y x A B}
--         → P ⊢↓ Γ , y ∶ A
--         → Q ⊢↓ Δ , x ∶ B
--         ---------------
--         → x ⟦ y ⟧ P ∣ Q ⊢↓ ∅ , x ∶ 𝟙

--     ⊢⅋ : ∀ {R Θ y x A B}
--         → R ⊢↓ Θ , y ∶ A , x ∶ B
--         ---------------
--         → x ⦅ y ⦆ R ⊢↓ Θ , x ∶ A ⅋ B

--     ⊢𝟏 : ∀ {x}
--         ---------------
--         → x ⟦⟧0 ⊢↓ ∅ , x ∶ 𝟙

--     ⊢⊥ : ∀ {x P Γ}
--         → P ⊢↓ Γ
--         ---------------
--         → x ⦅⦆ P ⊢↓ Γ , x ∶ ⊥

--     ⊢⊤ : ∀ {x Γ}
--         ---------------
--         → x case ⊢↓ Γ , x ∶ ⊤

-- data Result (P : Proc⁺) : Set where
--     known : (Γ : Session) → P ⊢↑ Γ → Result P
--     cntx? : ((Γ : Session) → P ⊢↑ Γ) → Result P
--     type? : ((Γ : Session) → (x : Chan) → (T : Type) → P ⊢↑ Γ , x ∶ T) → Result P
--     wrong : Result P

-- infer : ∀ (P : Proc⁺) → Result P
-- infer (w ↔ x) = type? λ Γ x A → {! ⊢↔ {w} {x} {A} !}
-- infer (ν x ∶ x₁ ∙ x₂ ∣ x₃) = {!   !}

-- open import Data.Bool hiding (_≟_)

open import Relation.Binary using (Decidable; DecSetoid)

ChanSetoid : DecSetoid _ _
ChanSetoid = ≡-decSetoid
    where
        open import Data.Nat.Properties using (≡-decSetoid)

module Example where
    open import CP.Session3 ChanSetoid Type

    Γ : Session
    Γ = (0 ∶ 𝟙) ∷ (1 ∶ 𝟘) ∷ []

    Δ : Session
    Δ = (1 ∶ 𝟘) ∷ (0 ∶ 𝟙) ∷ []

    open import Data.List.Relation.Binary.Subset.DecSetoid typeOfDecSetoid

-- _∋_ : Session → Chan → Bool
-- (Γ , y ∶ A) ∋ x with x ≟ y
-- ... | no ¬p = Γ ∋ x
-- ... | yes p = true
-- (Γ ++ Δ) ∋ x = (Γ ∋ x) ∨ (Δ ∋ x)
-- ∅ ∋ x = false

-- _≈_ : Session → Session → Set
-- Γ ≈ Δ = ∀ x → Γ ∋ x ≡ Δ ∋ x

-- empty : ∀ {Γ x A} → ¬ (∅ ≈ (Γ , x ∶ A))
-- empty {Γ} {x} {A} P with x ≟ x
-- ... | no ¬p = {!   !}
-- ... | yes p = {!   !}

-- lookup : (Γ : Session) (x : Chan) → Dec (∃[ Δ ] ∃[ A ] (Γ ≈ (Δ , x ∶ A)))
-- lookup (Γ , y ∶ A) x = {!   !}
-- lookup (Γ ++ Δ) x with lookup Γ x
-- ... | yes p = {!   !}
-- ... | no ¬p = {!   !}
-- lookup ∅ x = no (λ where (Γ , A , P) → {! P x  !})

-- infer : ∀ (P : Proc⁺) → Dec (∃[ Γ ] (P ⊢↑ Γ))
-- infer' : ∀ (P : Proc⁻) → Dec (∃[ Γ ] (P ⊢↓ Γ))
-- check : ∀ (P : Proc⁻) (Γ : Session) → Dec (P ⊢↓ Γ)

-- infer' (x ⟦ y ⟧ P ∣ Q) = {!   !}
-- infer' (x ⦅ y ⦆ P) with infer' P
-- ... | no ¬p = no λ where ((Γ , x ∶ A ⅋ B) , ⊢⅋ P⊢Γ) → ¬p ((Γ , y ∶ A , x ∶ B) , P⊢Γ)
-- ... | yes (Γ , P⊢Γ) = yes ({!   !} , (⊢⅋ {!   !}))
-- infer' (x ⟦⟧0) = yes (∅ , x ∶ 𝟙 , ⊢𝟏)
-- infer' (x ⦅⦆ P) with infer' P
-- ... | no ¬p = no λ where ((Γ , x ∶ ⊥) , ⊢⊥ P⊢Γ) → ¬p (Γ , P⊢Γ)
-- ... | yes (Γ , P⊢Γ) = yes ((Γ , x ∶ ⊥) , (⊢⊥ P⊢Γ))
-- infer' (x case) = yes (∅ , x ∶ ⊤ , ⊢⊤)

-- infer (w ↔ x) = yes ((∅ , w ∶ {!   !} , x ∶ {!   !}) , ⊢↔)
-- infer (ν x ∶ A ∙ P ∣ Q) with infer' P  | infer' Q
-- ... | no ¬p | no ¬q = {!   !}
-- ... | no ¬p | yes q = no (λ where (.(_ ++ _) , ⊢Cut x _) → ¬p {! x  !})
-- ... | yes p | no ¬q = {!   !}
-- ... | yes (Γ , P⊢Γ) | yes (Δ , Q⊢Δ) = yes ({!   !} , ⊢Cut {! P⊢Γ  !} {!   !})
-- --  = yes ({!   !} , {!   !})

-- check P Γ = {!   !}

-- infixl 4 _∶_∈_
-- data _∶_∈_ : Channel → Type → Session → Set where
--   Z  : ∀ {Γ x A}
--     ------------------
--     → x ∶ A ∈ Γ , x ∶ A

--   S_ : ∀ {Γ x y A B}
--     → x ∶ A ∈ Γ
--     ------------------
--     → x ∶ A ∈ Γ , x ∶ A , y ∶ B

-- ¿[_] : Session → Session
-- ¿[ Γ , x ∶ A ]  = ¿[ Γ ] , x ∶ ¿ A
-- ¿[ Γ ++ Δ ]     = ¿[ Γ ] ++ ¿[ Δ ]
-- ¿[ ∅ ]          = ∅

-- infix 4 ⊢_
-- data ⊢_ : Session → Set where

--     Ax : ∀ {A w x}
--         ---------------------------
--         → ⊢ ∅ , w ∶ A ^ , x ∶ A

--     Cut : ∀ {Γ Δ x A}
--         → (P : ⊢ Γ , x ∶ A)
--         → (Q : ⊢ Δ , x ∶ A ^)
--         ---------------------------
--         → ⊢ Γ ++ Δ

--     Times : ∀ {Γ Δ x y A B}
--         → (P : ⊢ Γ , y ∶ A)
--         → (Q : ⊢ Δ , x ∶ B)
--         ---------------------------
--         → ⊢ Γ ++ Δ , x ∶ A ⨂ B

--     Par : ∀ {Θ x y A B}
--         → (R : ⊢ Θ , y ∶ A , x ∶ B)
--         ---------------------------
--         → ⊢ Θ , x ∶ A ⅋ B

--     PlusL : ∀ {Γ x A B}
--         → (P : ⊢ Γ , x ∶ A)
--         ---------------------------
--         → ⊢ Γ , x ∶ A ⨁ B

--     PlusR : ∀ {Γ x A B}
--         → (P : ⊢ Γ , x ∶ B)
--         ---------------------------
--         → ⊢ Γ , x ∶ A ⨁ B

--     With : ∀ {Δ x A B}
--         → (Q : ⊢ Δ , x ∶ A)
--         → (R : ⊢ Δ , x ∶ B)
--         ---------------------------
--         → ⊢ Δ , x ∶ A & B

--     OfCourse : ∀ {Γ x y A}
--         → (P : ⊢ ¿[ Γ ] , y ∶ A)
--         ---------------------------
--         → ⊢ ¿[ Γ ] , x ∶ ! A

--     WhyNot : ∀ {Δ x y A}
--         → (Q : ⊢ Δ , y ∶ A)
--         ---------------------------
--         → ⊢ Δ , x ∶ ¿ A

--     Weaken : ∀ {Δ x A}
--         → (Q : ⊢ Δ)
--         ---------------------------
--         → ⊢ Δ , x ∶ ¿ A

--     Contract : ∀ {Δ x x' A}
--         → (Q : ⊢ Δ , x ∶ ¿ A , x' ∶ ¿ A)
--         ---------------------------
--         → ⊢ Δ , x ∶ ¿ A

--     -- Exist : ∀ {Γ x x' A}
--     --     → (Q : ⊢ Δ , x ∶ ¿ A , x' ∶ ¿ A)
--     --     ---------------------------
--     --     → ⊢ Δ , x ∶ ¿ A


module UniDB.Morph.Sim where

open import UniDB.Spec
open import UniDB.Subst.Core
open import UniDB.Morph.Depth
import Level
open import Relation.Binary.PropositionalEquality

--------------------------------------------------------------------------------

record Sim (T : STX) (γ₁ γ₂ : Dom) : Set where
  constructor sim
  field
    lk-sim : (i : Ix γ₁) → T γ₂
open Sim public

module _ {T : STX} where
  instance
    iLkSim : {{vrT : Vr T}} → Lk T (Sim T)
    lk {{iLkSim}} = lk-sim

    iWkSim : {{wkT : Wk T}} → {γ₁ : Dom} → Wk (Sim T γ₁)
    wk₁     {{iWkSim}}         (sim f) = sim (wk₁ ∘ f)
    wk      {{iWkSim}} δ       (sim f) = sim (wk δ ∘ f)
    wk-zero {{iWkSim {{wkT}}}} (sim f) = cong sim (ext (wk-zero {T} ∘ f))
      where postulate ext : Extensionality Level.zero Level.zero
    wk-suc {{iWkSim}} δ        (sim f) = cong sim (ext (wk-suc {T} δ ∘ f))
      where postulate ext : Extensionality Level.zero Level.zero

    iSnocSim : Snoc T (Sim T)
    snoc {{iSnocSim}} (sim f) t = sim λ
      { zero    → t
      ; (suc i) → f i
      }

    iUpSim : {{vrT : Vr T}} {{wkT : Wk T}} → Up (Sim T)
    _↑₁ {{iUpSim}} ξ = snoc {T} {Sim T} (wk₁ ξ) (vr {T} zero)
    _↑_ {{iUpSim}} ξ 0 = ξ
    _↑_ {{iUpSim}} ξ (suc δ⁺) = ξ ↑ δ⁺ ↑₁
    ↑-zero {{iUpSim}} ξ = refl
    ↑-suc {{iUpSim}} ξ δ⁺ = refl

    iIdmSim : {{vrT : Vr T}} → Idm (Sim T)
    idm {{iIdmSim}} γ = sim (vr {T})

    iWkmSim : {{vrT : Vr T}} → Wkm (Sim T)
    wkm {{iWkmSim}} δ = sim (vr {T} ∘ wk δ)

    iBetaSim : {{vrT : Vr T}} → Beta T (Sim T)
    beta {{iBetaSim}} t = snoc {T} {Sim T} (idm {Sim T} _) t

    iCompSim : {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}} → Comp (Sim T)
    lk-sim (_⊙_ {{iCompSim}} ξ ζ) i =
      ap {T} {T} {Sim T} ζ (lk {T} {Sim T} ξ i)

    iLkUpSim : {{vrT : Vr T}} {{wkT : Wk T}} → LkUp T (Sim T)
    lk-↑₁-zero {{iLkUpSim}} ξ         = refl
    lk-↑₁-suc  {{iLkUpSim}} ξ zero    = refl
    lk-↑₁-suc  {{iLkUpSim}} ξ (suc i) = refl

    iLkWkmSim : {{vrT : Vr T}} → LkWkm T (Sim T)
    lk-wkm {{iLkWkmSim}} δ i = refl

    iLkIdmSim : {{vrT : Vr T}} → LkIdm T (Sim T)
    lk-idm {{iLkIdmSim}} i = refl

    iLkCompSim : {{vrT : Vr T}} {{wkT : Wk T}} {{apTT : Ap T T}} → LkCompAp T (Sim T)
    lk-⊙-ap {{iLkCompSim}} ξ₁ ξ₂ i = refl

--------------------------------------------------------------------------------

module SimIxInstances (T : STX) where

  instance
    iLkSimIx : {{vrT : Vr T}} → Lk T (Sim Ix)
    lk {{iLkSimIx}} ξ i = vr {T} (lk {Ix} {Sim Ix} ξ i)

    iLkUpSimIx : {{vrT : Vr T}} {{wkT : Wk T}}
      {{wkVrT : WkVr T}} → LkUp T (Sim Ix)
    lk-↑₁-zero {{iLkUpSimIx}} ξ   = refl
    lk-↑₁-suc  {{iLkUpSimIx}} ξ i = sym (wk₁-vr {T} (lk {Ix} {Sim Ix} ξ i))

    iLkWkmSimIx : {{vrT : Vr T}} → LkWkm T (Sim Ix)
    lk-wkm {{iLkWkmSimIx}} δ i = refl

    iLkIdmSimIx : {{vrT : Vr T}} → LkIdm T (Sim Ix)
    lk-idm {{iLkIdmSimIx}} i = refl

    iLkCompSimIx : {{vrT : Vr T}} {{apTT : Ap T T}}
      {{apVrT : ApVr T}} → LkCompAp T (Sim Ix)
    lk-⊙-ap {{iLkCompSimIx}} ξ₁ ξ₂ i =
      sym (ap-vr {T} {Sim Ix} ξ₂ (lk {Ix} {Sim Ix} ξ₁ i))

--------------------------------------------------------------------------------


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

open import Syntax
import Renaming
import Substitution

module Instantiation where

  module _ {𝕊 : Signature} where

    open Expression 𝕊
    open Substitution
    open Renaming
    open Equality

    -- the set of instantiations

    infix 5 _→ⁱ_∥_

    _→ⁱ_∥_ : MShape → MShape → VShape → Set
    𝕄 →ⁱ 𝕂 ∥ γ = ∀ {clᴹ γᴹ} (M : [ clᴹ , γᴹ ]∈ 𝕄) → Arg clᴹ 𝕂 γ γᴹ

    -- equality of instantiations

    infix 4 _≈ⁱ_

    _≈ⁱ_ : ∀ {𝕂 𝕄} {γ} (I J : 𝕂 →ⁱ 𝕄 ∥ γ) → Set
    I ≈ⁱ J = ∀ {clᴹ γᴹ} (M : [ clᴹ , γᴹ ]∈ _) → I M ≈ J M

    -- equality of instaniations is an equivalence relation

    ≈ⁱ-refl : ∀ {𝕂 𝕄} {γ} {I : 𝕂 →ⁱ 𝕄 ∥ γ} → I ≈ⁱ I
    ≈ⁱ-refl M = ≈-refl

    ≈ⁱ-sym : ∀ {𝕂 𝕄} {γ} {I J : 𝕂 →ⁱ 𝕄 ∥ γ} → I ≈ⁱ J → J ≈ⁱ I
    ≈ⁱ-sym ξ M = ≈-sym (ξ M)

    ≈ⁱ-trans : ∀ {𝕂 𝕄} {γ} {I J K : 𝕂 →ⁱ 𝕄 ∥ γ} → I ≈ⁱ J → J ≈ⁱ K → I ≈ⁱ K
    ≈ⁱ-trans ζ ξ M = ≈-trans (ζ M) (ξ M)

    -- identity instantiation

    𝟙ⁱ : ∀ {𝕄 γ} → 𝕄 →ⁱ 𝕄 ∥ γ
    𝟙ⁱ M = expr-meta-generic M

    -- instantiation extension

    ⇑ⁱ : ∀ {𝕂 𝕄 γ δ} → 𝕂 →ⁱ 𝕄 ∥ γ → 𝕂 →ⁱ 𝕄 ∥ γ ⊕ δ
    ⇑ⁱ I M = [ ⇑ʳ var-left ]ʳ (I M)

    -- extension respects equality

    ⇑ⁱ-resp-≈ⁱ : ∀ {𝕂 𝕄} {γ δ} → {I J : 𝕂 →ⁱ 𝕄 ∥ γ} → I ≈ⁱ J → ⇑ⁱ {δ = δ} I ≈ⁱ ⇑ⁱ J
    ⇑ⁱ-resp-≈ⁱ ξ M = []ʳ-resp-≈ (⇑ʳ var-left) (ξ M)

    -- the action of an instantiation on an expression

    infix 6 [_]ⁱ_

    [_]ⁱ_ : ∀ {cl 𝕂 𝕄} {γ} → (𝕂 →ⁱ 𝕄 ∥ γ) → Expr cl 𝕂 γ → Expr cl 𝕄 γ
    [ I ]ⁱ (expr-var x) = expr-var x
    [ I ]ⁱ (expr-symb S es) = expr-symb S (λ i → [ ⇑ⁱ I ]ⁱ es i)
    [ I ]ⁱ (expr-meta M ts) = [ [ 𝟙ˢ , (λ i → [ I ]ⁱ ts i) ]ˢ ]ˢ I M
    [ I ]ⁱ expr-eqty = expr-eqty
    [ I ]ⁱ expr-eqtm = expr-eqtm

    -- instantiations respects equality

    []ⁱ-resp-≈ : ∀ {cl} {𝕂 𝕄} {γ} (I : 𝕂 →ⁱ 𝕄 ∥ γ) {t u : Expr cl 𝕂 γ} → t ≈ u → [ I ]ⁱ t ≈ [ I ]ⁱ u
    []ⁱ-resp-≈ I (≈-≡ ξ) = ≈-≡ (cong [ I ]ⁱ_ ξ)
    []ⁱ-resp-≈ I (≈-symb ξ) = ≈-symb (λ i → []ⁱ-resp-≈ (⇑ⁱ I) (ξ i))
    []ⁱ-resp-≈ I (≈-meta ξ) = []ˢ-resp-≈ˢ ([,]ˢ-resp-≈ˢ ≈ˢ-refl (λ i → []ⁱ-resp-≈ I (ξ i))) (I _)

    []ⁱ-resp-≈ⁱ : ∀ {cl} {𝕂 𝕄} {γ} {I J : 𝕂 →ⁱ 𝕄 ∥ γ} (t : Expr cl 𝕂 γ) → I ≈ⁱ J → [ I ]ⁱ t ≈ [ J ]ⁱ t
    []ⁱ-resp-≈ⁱ (expr-var x) ξ = ≈-refl
    []ⁱ-resp-≈ⁱ (expr-symb S es) ξ = ≈-symb (λ i → []ⁱ-resp-≈ⁱ (es i) (⇑ⁱ-resp-≈ⁱ ξ))
    []ⁱ-resp-≈ⁱ (expr-meta M ts) ξ = []ˢ-resp-≈ˢ-≈ ([,]ˢ-resp-≈ˢ ≈ˢ-refl (λ i → []ⁱ-resp-≈ⁱ (ts i) ξ)) (ξ M)
    []ⁱ-resp-≈ⁱ expr-eqty ξ = ≈-eqty
    []ⁱ-resp-≈ⁱ expr-eqtm ξ = ≈-eqtm

    []ⁱ-resp-≈ⁱ-≈ : ∀ {cl} {𝕂 𝕄} {γ} {I J : 𝕂 →ⁱ 𝕄 ∥ γ} {t u : Expr cl 𝕂 γ} →
                    I ≈ⁱ J → t ≈ u → [ I ]ⁱ t ≈ [ J ]ⁱ u
    []ⁱ-resp-≈ⁱ-≈ {J = J} {t = t} ζ ξ = ≈-trans ([]ⁱ-resp-≈ⁱ t ζ) ([]ⁱ-resp-≈ J ξ)

    -- composition of instantiations

    infixl 7 _∘ⁱ_

    _∘ⁱ_ : ∀ {𝕂 𝕃 𝕄} {γ} → 𝕃 →ⁱ 𝕄 ∥ γ → 𝕂 →ⁱ 𝕃 ∥ γ → 𝕂 →ⁱ 𝕄 ∥ γ
    (J ∘ⁱ I) M = [ ⇑ⁱ J ]ⁱ I M

    -- composition of a renaming and instantiation

    infixr 7 _ʳ∘ⁱ_

    _ʳ∘ⁱ_ : ∀ {𝕂 𝕄} {γ δ} → (ρ : γ →ʳ δ) → (I : 𝕂 →ⁱ 𝕄 ∥ γ) → 𝕂 →ⁱ 𝕄 ∥ δ
    (ρ ʳ∘ⁱ I) M =  [ ⇑ʳ ρ ]ʳ I M

    ⇑ⁱ-resp-ʳ∘ⁱ : ∀ {𝕂 𝕄} {γ δ η} {ρ : γ →ʳ δ} → {I : 𝕂 →ⁱ 𝕄 ∥ γ} →
                  ⇑ⁱ {δ = η} (ρ ʳ∘ⁱ I) ≈ⁱ ⇑ʳ ρ ʳ∘ⁱ ⇑ⁱ I
    ⇑ⁱ-resp-ʳ∘ⁱ {I = I} M =
      ≈-trans
        (≈-trans
          (≈-sym ([∘ʳ] (I M)))
          ([]ʳ-resp-≡ʳ (I M) (λ {(var-left x) → refl ; (var-right y) → refl})))
        ([∘ʳ] (I M))

    [ʳ∘ⁱ] : ∀ {cl 𝕂 𝕄} {γ δ} {ρ : γ →ʳ δ} {I : 𝕂 →ⁱ 𝕄 ∥ γ} (t : Expr cl 𝕂 γ) →
             [ ρ ]ʳ ([ I ]ⁱ t) ≈ [ ρ ʳ∘ⁱ I ]ⁱ [ ρ ]ʳ t
    [ʳ∘ⁱ] (expr-var x) = ≈-refl
    [ʳ∘ⁱ] {ρ = ρ} {I = I} (expr-symb S es) =
      ≈-symb (λ i → ≈-trans
                     ([ʳ∘ⁱ] (es i))
                     ([]ⁱ-resp-≈ⁱ
                        ([ ⇑ʳ ρ ]ʳ es i)
                        (≈ⁱ-sym (⇑ⁱ-resp-ʳ∘ⁱ {I = I}))))
    [ʳ∘ⁱ] {ρ = ρ} {I = I} (expr-meta M ts) =
      ≈-trans
        (≈-sym ([ʳ∘ˢ] (I M)))
        (≈-trans
          ([]ˢ-resp-≈ˢ (λ { (var-left x) → ≈-refl ; (var-right y) →  [ʳ∘ⁱ] (ts y)}) (I M))
          ([ˢ∘ʳ] (I M)))
    [ʳ∘ⁱ] expr-eqty = ≈-eqty
    [ʳ∘ⁱ] expr-eqtm = ≈-eqtm

    -- composition of an instantiation and substitution

    infixr 7 _ⁱ∘ˢ_

    _ⁱ∘ˢ_ : ∀ {𝕂 𝕄} {γ δ} (I : 𝕂 →ⁱ 𝕄 ∥ δ) (σ : 𝕂 ∥ γ →ˢ δ) → (𝕄 ∥ γ →ˢ δ)
    (I ⁱ∘ˢ σ) x = [ I ]ⁱ σ x

    -- extension respects identity

    ⇑ⁱ-resp-𝟙ⁱ : ∀ {𝕄 γ δ} → ⇑ⁱ {δ = δ} 𝟙ⁱ ≈ⁱ 𝟙ⁱ {𝕄 = 𝕄} {γ = γ ⊕ δ}
    ⇑ⁱ-resp-𝟙ⁱ {clᴹ = obj _} M = ≈-refl
    ⇑ⁱ-resp-𝟙ⁱ {clᴹ = EqTy} M = ≈-eqty
    ⇑ⁱ-resp-𝟙ⁱ {clᴹ = EqTm} M = ≈-eqtm

    -- extension respects composition

    ⇑ⁱ-resp-∘ⁱ : ∀ {𝕂 𝕃 𝕄 γ δ} {I : 𝕂 →ⁱ 𝕃 ∥ γ} {J : 𝕃 →ⁱ 𝕄 ∥ γ} → ⇑ⁱ {δ = δ} (J ∘ⁱ I) ≈ⁱ ⇑ⁱ J ∘ⁱ ⇑ⁱ I
    ⇑ⁱ-resp-∘ⁱ {I = I} {J = J} M =
      ≈-trans
        ([ʳ∘ⁱ] (I M))
        ([]ⁱ-resp-≈ⁱ ([ ⇑ʳ var-left ]ʳ I M)
          λ N → ≈-trans (≈-sym ([∘ʳ] (J N)))
                  (≈-trans ([]ʳ-resp-≡ʳ (J N)  λ {(var-left x) → refl ; (var-right y) → refl}) ([∘ʳ] (J N))))

    ⇑ⁱ-resp-ⁱ∘ˢ : ∀ {𝕂 𝕄} {γ δ η} {I : 𝕂 →ⁱ 𝕄 ∥ δ} {σ : 𝕂 ∥ γ →ˢ δ} →
                  ⇑ˢ {η = η} (I ⁱ∘ˢ σ ) ≈ˢ ⇑ⁱ I ⁱ∘ˢ ⇑ˢ σ
    ⇑ⁱ-resp-ⁱ∘ˢ {σ = σ}(var-left x) = ≈-trans ([ʳ∘ⁱ] (σ x)) ([]ⁱ-resp-≈ⁱ ([ var-left ]ʳ σ x) (λ _ → ≈-refl))
    ⇑ⁱ-resp-ⁱ∘ˢ (var-right y) = ≈-refl

    -- the action of an instantiation on a generic metavariable application

    []ⁱ-meta-generic : ∀ {𝕂 𝕄} {γ} {I : 𝕂 →ⁱ 𝕄 ∥ γ} {clᴹ γᴹ} {M : [ clᴹ , γᴹ ]∈ 𝕂} → [ ⇑ⁱ I ]ⁱ (expr-meta-generic {γ = γ} M) ≈ I M
    []ⁱ-meta-generic {I = I} {clᴹ = obj _} {M = M} =
      ≈-trans (≈-sym ([ˢ∘ʳ] (I M))) ([]ˢ-id (λ { (var-left _) → ≈-refl ; (var-right _) → ≈-refl}))
    []ⁱ-meta-generic {clᴹ = EqTy} = ≈-eqty
    []ⁱ-meta-generic {clᴹ = EqTm} = ≈-eqtm

    -- action of the identity

    [𝟙ⁱ] : ∀ {cl 𝕄 γ} (t : Expr cl 𝕄 γ) → [ 𝟙ⁱ ]ⁱ t ≈ t
    [𝟙ⁱ] (expr-var x) = ≈-refl
    [𝟙ⁱ] (expr-symb S es) = ≈-symb (λ i → ≈-trans ([]ⁱ-resp-≈ⁱ (es i) ⇑ⁱ-resp-𝟙ⁱ) ([𝟙ⁱ] (es i)))
    [𝟙ⁱ] (expr-meta M ts) = ≈-meta (λ i → [𝟙ⁱ] (ts i))
    [𝟙ⁱ] (expr-eqty) = ≈-eqty
    [𝟙ⁱ] (expr-eqtm) = ≈-eqtm

    -- interaction of instantiation, substitution and renaming

    []ⁱ-[]ˢ : ∀ {cl 𝕂 𝕄 γ δ} {I : 𝕂 →ⁱ 𝕄 ∥ δ} {σ : 𝕂 ∥ γ →ˢ δ} {ρ : δ →ʳ γ} (t : Expr cl 𝕂 γ) →
          σ ˢ∘ʳ ρ ≈ˢ 𝟙ˢ → ([ I ]ⁱ ([ σ ]ˢ t)) ≈ ([ I ⁱ∘ˢ σ ]ˢ [ ρ ʳ∘ⁱ I ]ⁱ t)
    []ⁱ-[]ˢ (expr-var x) ξ = ≈-refl
    []ⁱ-[]ˢ {I = I} {σ = σ} {ρ = ρ} (expr-symb S es) ξ =
      ≈-symb (λ i →
        ≈-trans
          ([]ⁱ-[]ˢ {ρ = ⇑ʳ ρ} (es i)
            λ where
              (var-left x) → []ʳ-resp-≈ var-left (ξ x)
              (var-right x) → ≈-refl)
          (≈-sym ([]ˢ-resp-≈ˢ-≈ (⇑ⁱ-resp-ⁱ∘ˢ) (([]ⁱ-resp-≈ⁱ (es i) (⇑ⁱ-resp-ʳ∘ⁱ {I = I}))))))
    []ⁱ-[]ˢ {I = I} {σ = σ} (expr-meta M ts) ξ =
      ≈-trans
        (≈-trans
          ([]ˢ-resp-≈ˢ (λ where
                          (var-left x) → ≈-sym ([]ⁱ-resp-≈ I (ξ x))
                          (var-right x) → []ⁱ-[]ˢ (ts x) ξ)
                       (I M))
          ([∘ˢ] (I M)))
        (≈-sym ([]ˢ-resp-≈ (I ⁱ∘ˢ σ) (≈-sym ([ˢ∘ʳ] (I M))) ))
    []ⁱ-[]ˢ expr-eqty _ = ≈-eqty
    []ⁱ-[]ˢ expr-eqtm _ = ≈-eqtm

    _ˢ∘ⁱ_ : ∀ {𝕂 𝕄 γ δ} → 𝕊 % 𝕄 ∥ γ →ˢ δ → 𝕂 →ⁱ 𝕄 ∥ γ → 𝕂 →ⁱ 𝕄 ∥ δ
    (σ ˢ∘ⁱ I) M =  [ ⇑ˢ σ ]ˢ I M

    ⇑ⁱ-resp-ˢ∘ⁱ : ∀ {𝕂 𝕄 γ δ η} {σ : 𝕊 % 𝕄 ∥ γ →ˢ δ} {I : 𝕂 →ⁱ 𝕄 ∥ γ} →
                  ⇑ⁱ {δ = η} (σ ˢ∘ⁱ I) ≈ⁱ ⇑ˢ σ ˢ∘ⁱ ⇑ⁱ I
    ⇑ⁱ-resp-ˢ∘ⁱ {I = I} M =
      ≈-trans
        (≈-sym ([ʳ∘ˢ] (I M)))
        (≈-trans
           ([]ˢ-resp-≈ˢ (≈ˢ-sym ⇑ˢ-resp-ʳ∘ˢ) (I M))
           (≈-trans
              ([]ˢ-resp-≈ˢ
                 (λ { (var-left x) → ≈-refl ; (var-right y) → ≈-refl})
                 (I M))
              ([ˢ∘ʳ] (I M))))

    []ˢ-[]ⁱ : ∀ {cl 𝕂 𝕄 γ δ η} {I : 𝕂 →ⁱ 𝕄 ∥ γ} {σ : 𝕊 % 𝕄 ∥ γ →ˢ δ} {ρ : η →ʳ δ} {τ : η →ʳ γ} (t : Expr cl 𝕂 η) →
              (∀ x → σ (τ x) ≈ expr-var (ρ x)) → [ σ ]ˢ ([ I ]ⁱ ([ τ ]ʳ t)) ≈ [ σ ˢ∘ⁱ I ]ⁱ ([ ρ ]ʳ t)
    []ˢ-[]ⁱ (expr-var x) ξ = ξ x
    []ˢ-[]ⁱ {I = I} {σ = σ} {ρ = ρ} (expr-symb S es) ξ =
      ≈-symb
        (λ i →
           ≈-trans
             ([]ˢ-[]ⁱ {I = ⇑ⁱ I} {σ = ⇑ˢ σ} {ρ = ⇑ʳ ρ} (es i)
                (λ { (var-left x) → []ʳ-resp-≈ var-left (ξ x)
                   ; (var-right x) → ≈-refl}))
             ([]ⁱ-resp-≈ⁱ ([ ⇑ʳ ρ ]ʳ es i) (≈ⁱ-sym (⇑ⁱ-resp-ˢ∘ⁱ {σ = σ} {I = I}))))
    []ˢ-[]ⁱ {I = I} {σ = σ} (expr-meta M ts) ξ =
      ≈-trans
        (≈-sym ([∘ˢ] (I M)))
        (≈-trans
           ([]ˢ-resp-≈ˢ
              (λ { (var-left x) → ≈-trans (≈-sym ([𝟙ˢ] (σ x))) ([ˢ∘ʳ] (σ x))
                 ; (var-right j) → []ˢ-[]ⁱ (ts j) ξ}) (I M))
           ([∘ˢ] (I M)))
    []ˢ-[]ⁱ expr-eqty ξ = ≈-eqty
    []ˢ-[]ⁱ expr-eqtm ξ = ≈-eqtm

    -- action of composition

    [∘ⁱ] : ∀ {cl 𝕂 𝕃 𝕄 γ} {I : 𝕂 →ⁱ 𝕃 ∥ γ} {J : 𝕃 →ⁱ 𝕄 ∥ γ} (t : Expr cl 𝕂 γ) → [ J ∘ⁱ I ]ⁱ t ≈ [ J ]ⁱ [ I ]ⁱ t
    [∘ⁱ] (expr-var _) = ≈-refl
    [∘ⁱ] {I = I} {J = J} (expr-symb S es) =
      ≈-symb (λ i → ≈-trans ([]ⁱ-resp-≈ⁱ (es i) (⇑ⁱ-resp-∘ⁱ {I = I})) ([∘ⁱ] (es i)))
    [∘ⁱ] {I = I} {J = J} (expr-meta M ts) =
      ≈-sym (≈-trans
               ([]ⁱ-[]ˢ {I = J} {ρ = var-left} (I M) λ _ → ≈-refl)
               ([]ˢ-resp-≈ˢ
                 (λ { (var-left x) → ≈-refl ; (var-right x) → ≈-sym ([∘ⁱ] (ts x))})
                 ([ ⇑ⁱ J ]ⁱ (I M))))
    [∘ⁱ] expr-eqty = ≈-eqty
    [∘ⁱ] expr-eqtm = ≈-eqtm

  -- Notation for working with multiple signatures

  infix 5 _%_→ⁱ_∥_
  _%_→ⁱ_∥_ : ∀ (𝕊 : Signature) → MShape → MShape → VShape → Set
  _%_→ⁱ_∥_ 𝕊 = _→ⁱ_∥_ {𝕊 = 𝕊}


{-# OPTIONS --allow-unsolved-metas #-}
open import Level
open import Ordinals
module OD {n : Level } (O : Ordinals {n} ) where

open import zf
open import Data.Nat renaming ( zero to Zero ; suc to Suc ;  ℕ to Nat ; _⊔_ to _n⊔_ )
open import  Relation.Binary.PropositionalEquality hiding ( [_] )
open import Data.Nat.Properties
open import Data.Empty
open import Relation.Nullary
open import Relation.Binary  hiding (_⇔_)
open import Relation.Binary.Core hiding (_⇔_)

open import logic
import OrdUtil
open import nat

open Ordinals.Ordinals  O
open Ordinals.IsOrdinals isOrdinal
open Ordinals.IsNext isNext
open OrdUtil O

-- Ordinal Definable Set

record OD : Set (suc n ) where
  field
    def : (x : Ordinal  ) → Set n

open OD

open _∧_
open _∨_
open Bool

record _==_  ( a b :  OD  ) : Set n where
  field
     eq→ : ∀ { x : Ordinal  } → def a x → def b x
     eq← : ∀ { x : Ordinal  } → def b x → def a x

==-refl :  {  x :  OD  } → x == x
==-refl  {x} = record { eq→ = λ x → x ; eq← = λ x → x }

open  _==_

==-trans : { x y z : OD } →  x == y →  y == z →  x ==  z
==-trans x=y y=z  = record { eq→ = λ {m} t → eq→ y=z (eq→ x=y t) ; eq← =  λ {m} t → eq← x=y (eq← y=z t) }

==-sym : { x y  : OD } →  x == y →  y == x
==-sym x=y = record { eq→ = λ {m} t → eq← x=y t ; eq← =  λ {m} t → eq→ x=y t }

⇔→== :  {  x y :  OD  } → ( {z : Ordinal } → (def x z ⇔  def y z)) → x == y
eq→ ( ⇔→==  {x} {y}  eq ) {z} m = proj1 eq m
eq← ( ⇔→==  {x} {y}  eq ) {z} m = proj2 eq m

-- next assumptions are our axiom
--
--  OD is an equation on Ordinals, so it contains Ordinals. If these Ordinals have one-to-one
--  correspondence to the OD then the OD looks like a ZF Set.
--
--  If all ZF Set have supreme upper bound, the solutions of OD have to be bounded, i.e.
--  bbounded ODs are ZF Set. Unbounded ODs are classes.
--
--  In classical Set Theory, HOD is used, as a subset of OD,
--     HOD = { x | TC x ⊆ OD }
--  where TC x is a transitive clusure of x, i.e. Union of all elemnts of all subset of x.
--  This is not possible because we don't have V yet. So we assumes HODs are bounded OD.
--
--  We also assumes HODs are isomorphic to Ordinals, which is ususally proved by Goedel number tricks.
--  There two contraints on the HOD order, one is ∋, the other one is ⊂.
--  ODs have an ovbious maximum, but Ordinals are not, but HOD has no maximum because there is an aribtrary
--  bound on each HOD.
--
--  In classical Set Theory, sup is defined by Uion, since we are working on constructive logic,
--  we need explict assumption on sup.
--
--  ==→o≡ is necessary to prove axiom of extensionality.

-- Ordinals in OD , the maximum
Ords : OD
Ords = record { def = λ x → One }

record HOD : Set (suc n) where
  field
    od : OD
    odmax : Ordinal
    <odmax : {y : Ordinal} → def od y → y o< odmax

open HOD

record ODAxiom : Set (suc n) where
 field
  -- HOD is isomorphic to Ordinal (by means of Goedel number)
  & : HOD  → Ordinal
  * : Ordinal  → HOD
  c<→o<  :  {x y : HOD  }   → def (od y) ( & x ) → & x o< & y
  ⊆→o≤   :  {y z : HOD  }   → ({x : Ordinal} → def (od y) x → def (od z) x ) → & y o< osuc (& z)
  *iso   :  {x : HOD }      → * ( & x ) ≡ x
  &iso   :  {x : Ordinal }  → & ( * x ) ≡ x
  ==→o≡  :  {x y : HOD  }   → (od x == od y) → x ≡ y
  sup-o  :  (A : HOD) → (     ( x : Ordinal ) → def (od A) x →  Ordinal ) →  Ordinal
  sup-o< :  (A : HOD) → { ψ : ( x : Ordinal ) → def (od A) x →  Ordinal } → ∀ {x : Ordinal } → (lt : def (od A) x ) → ψ x lt o<  sup-o A ψ
-- possible order restriction
  ho< : {x : HOD} → & x o< next (odmax x)

postulate  odAxiom : ODAxiom
open ODAxiom odAxiom

-- odmax minimality
--
-- since we have ==→o≡ , so odmax have to be unique. We should have odmaxmin in HOD.
-- We can calculate the minimum using sup but it is tedius.
-- Only Select has non minimum odmax.
-- We have the same problem on 'def' itself, but we leave it.

odmaxmin : Set (suc n)
odmaxmin = (y : HOD) (z : Ordinal) → ((x : Ordinal)→ def (od y) x → x o< z) → odmax y o< osuc z

-- OD ⇔ Ordinal leads a contradiction, so we need bounded HOD
¬OD-order : ( & : OD  → Ordinal ) → ( * : Ordinal  → OD ) → ( { x y : OD  }  → def y ( & x ) → & x o< & y) → ⊥
¬OD-order & * c<→o< = osuc-< <-osuc (c<→o< {Ords} OneObj )

-- Open supreme upper bound leads a contradition, so we use domain restriction on sup
¬open-sup : ( sup-o : (Ordinal →  Ordinal ) → Ordinal) → ((ψ : Ordinal →  Ordinal ) → (x : Ordinal) → ψ x  o<  sup-o ψ ) → ⊥
¬open-sup sup-o sup-o< = o<> <-osuc (sup-o< next-ord (sup-o next-ord)) where
   next-ord : Ordinal → Ordinal
   next-ord x = osuc x

-- Ordinal in OD ( and ZFSet ) Transitive Set
Ord : ( a : Ordinal  ) → HOD
Ord  a = record { od = record { def = λ y → y o< a } ; odmax = a ; <odmax = lemma } where
   lemma :  {x : Ordinal} → x o< a → x o< a
   lemma {x} lt = lt

od∅ : HOD
od∅  = Ord o∅

odef : HOD → Ordinal → Set n
odef A x = def ( od A ) x

_∋_ : ( a x : HOD  ) → Set n
_∋_  a x  = odef a ( & x )

-- _c<_ : ( x a : HOD  ) → Set n
-- x c< a = a ∋ x

d→∋ : ( a : HOD  ) { x : Ordinal} → odef a x → a ∋ (* x)
d→∋ a lt = subst (λ k → odef a k ) (sym &iso) lt

-- odef-subst :  {Z : HOD } {X : Ordinal  }{z : HOD } {x : Ordinal  }→ odef Z X → Z ≡ z  →  X ≡ x  →  odef z x
-- odef-subst df refl refl = df

otrans : {a x y : Ordinal  } → odef (Ord a) x → odef (Ord x) y → odef (Ord a) y
otrans x<a y<x = ordtrans y<x x<a

-- If we have reverse of c<→o<, everything becomes Ordinal
∈→c<→HOD=Ord : ( o<→c< : {x y : Ordinal  } → x o< y → odef (* y) x ) → {x : HOD } →  x ≡ Ord (& x)
∈→c<→HOD=Ord o<→c< {x} = ==→o≡ ( record { eq→ = lemma1 ; eq← = lemma2 } ) where
   lemma1 : {y : Ordinal} → odef x y → odef (Ord (& x)) y
   lemma1 {y} lt = subst ( λ k → k o< & x ) &iso (c<→o< {* y} {x} (d→∋ x lt))
   lemma2 : {y : Ordinal} → odef (Ord (& x)) y → odef x y
   lemma2 {y} lt = subst (λ k → odef k y ) *iso (o<→c< {y} {& x} lt )

-- avoiding lv != Zero error
orefl : { x : HOD  } → { y : Ordinal  } → & x ≡ y → & x ≡ y
orefl refl = refl

==-iso : { x y : HOD  } → od (* (& x)) == od (* (& y))  →  od x == od y
==-iso  {x} {y} eq = record {
      eq→ = λ {z} d →  lemma ( eq→  eq (subst (λ k → odef k z ) (sym *iso) d )) ;
      eq← = λ {z} d →  lemma ( eq←  eq (subst (λ k → odef k z ) (sym *iso) d )) }
        where
           lemma : {x : HOD  } {z : Ordinal } → odef (* (& x)) z → odef x z
           lemma {x} {z} d = subst (λ k → odef k z) (*iso) d

=-iso :  {x y : HOD  } → (od x == od y) ≡ (od (* (& x)) == od y)
=-iso  {_} {y} = cong ( λ k → od k == od y ) (sym *iso)

ord→== : { x y : HOD  } → & x ≡  & y →  od x == od y
ord→==  {x} {y} eq = ==-iso (lemma (& x) (& y) (orefl eq)) where
   lemma : ( ox oy : Ordinal  ) → ox ≡ oy →  od (* ox) == od (* oy)
   lemma ox ox  refl = ==-refl

o≡→== : { x y : Ordinal  } → x ≡  y →  od (* x) == od (* y)
o≡→==  {x} {.x} refl = ==-refl

o∅≡od∅ : * (o∅ ) ≡ od∅
o∅≡od∅  = ==→o≡ lemma where
     lemma0 :  {x : Ordinal} → odef (* o∅) x → odef od∅ x
     lemma0 {x} lt with c<→o< {* x} {* o∅} (subst (λ k → odef (* o∅) k ) (sym &iso) lt)
     ... | t = subst₂ (λ j k → j o< k ) &iso &iso t
     lemma1 :  {x : Ordinal} → odef od∅ x → odef (* o∅) x
     lemma1 {x} lt = ⊥-elim (¬x<0 lt)
     lemma : od (* o∅) == od od∅
     lemma = record { eq→ = lemma0 ; eq← = lemma1 }

ord-od∅ : & (od∅ ) ≡ o∅
ord-od∅  = sym ( subst (λ k → k ≡  & (od∅ ) ) &iso (cong ( λ k → & k ) o∅≡od∅ ) )

≡o∅→=od∅  : {x : HOD} → & x ≡ o∅ → od x == od od∅
≡o∅→=od∅ {x} eq = record { eq→ = λ {y} lt → ⊥-elim ( ¬x<0 {y} (subst₂ (λ j k → j o< k ) &iso eq ( c<→o< {* y} {x} (d→∋ x lt))))
    ; eq← = λ {y} lt → ⊥-elim ( ¬x<0 lt )}

=od∅→≡o∅  : {x : HOD} → od x == od od∅ → & x ≡ o∅
=od∅→≡o∅ {x} eq = trans (cong (λ k → & k ) (==→o≡ {x} {od∅} eq)) ord-od∅

∅0 : record { def = λ x →  Lift n ⊥ } == od od∅
eq→ ∅0 {w} (lift ())
eq← ∅0 {w} lt = lift (¬x<0 lt)

∅< : { x y : HOD  } → odef x (& y ) → ¬ (  od x  == od od∅  )
∅<  {x} {y} d eq with eq→ (==-trans eq (==-sym ∅0) ) d
∅<  {x} {y} d eq | lift ()

∅6 : { x : HOD  }  → ¬ ( x ∋ x )    --  no Russel paradox
∅6  {x} x∋x = o<¬≡ refl ( c<→o<  {x} {x} x∋x )

odef-iso : {A B : HOD } {x y : Ordinal } → x ≡ y  → (odef A y → odef B y)  → odef A x → odef B x
odef-iso refl t = t

is-o∅ : ( x : Ordinal  ) → Dec ( x ≡ o∅  )
is-o∅ x with trio< x o∅
is-o∅ x | tri< a ¬b ¬c = no ¬b
is-o∅ x | tri≈ ¬a b ¬c = yes b
is-o∅ x | tri> ¬a ¬b c = no ¬b

-- the pair
_,_ : HOD  → HOD  → HOD
x , y = record { od = record { def = λ t → (t ≡ & x ) ∨ ( t ≡ & y ) } ; odmax = omax (& x)  (& y) ; <odmax = lemma }  where
    lemma : {t : Ordinal} → (t ≡ & x) ∨ (t ≡ & y) → t o< omax (& x) (& y)
    lemma {t} (case1 refl) = omax-x  _ _
    lemma {t} (case2 refl) = omax-y  _ _

pair<y : {x y : HOD } → y ∋ x  → & (x , x) o< osuc (& y)
pair<y {x} {y} y∋x = ⊆→o≤ lemma where
   lemma : {z : Ordinal} → def (od (x , x)) z → def (od y) z
   lemma (case1 refl) = y∋x
   lemma (case2 refl) = y∋x

-- another possible restriction. We reqest no minimality on odmax, so it may arbitrary larger.
odmax<&  : { x y : HOD } → x ∋ y →  Set n
odmax<& {x} {y} x∋y = odmax x o< & x

in-codomain : (X : HOD  ) → ( ψ : HOD  → HOD  ) → OD
in-codomain  X ψ = record { def = λ x → ¬ ( (y : Ordinal ) → ¬ ( odef X y ∧  ( x ≡ & (ψ (* y )))))  }

_∩_ : ( A B : HOD ) → HOD
A ∩ B = record { od = record { def = λ x → odef A x ∧ odef B x }
        ; odmax = omin (odmax A) (odmax B) ; <odmax = λ y → min1 (<odmax A (proj1 y)) (<odmax B (proj2 y))}

record _⊆_ ( A B : HOD   ) : Set (suc n) where
  field
     incl : { x : HOD } → A ∋ x →  B ∋ x

open _⊆_
infixr  220 _⊆_

-- if we have & (x , x) ≡ osuc (& x),  ⊆→o≤ → c<→o<
⊆→o≤→c<→o< : ({x : HOD} → & (x , x) ≡ osuc (& x) )
   →  ({y z : HOD  }   → ({x : Ordinal} → def (od y) x → def (od z) x ) → & y o< osuc (& z) )
   →   {x y : HOD  }   → def (od y) ( & x ) → & x o< & y
⊆→o≤→c<→o< peq ⊆→o≤ {x} {y} y∋x with trio< (& x) (& y)
⊆→o≤→c<→o< peq ⊆→o≤ {x} {y} y∋x | tri< a ¬b ¬c = a
⊆→o≤→c<→o< peq ⊆→o≤ {x} {y} y∋x | tri≈ ¬a b ¬c = ⊥-elim ( o<¬≡ (peq {x}) (pair<y (subst (λ k → k ∋ x) (sym ( ==→o≡ {x} {y} (ord→== b))) y∋x )))
⊆→o≤→c<→o< peq ⊆→o≤ {x} {y} y∋x | tri> ¬a ¬b c =
  ⊥-elim ( o<> (⊆→o≤ {x , x} {y} y⊆x,x ) lemma1 ) where
    lemma : {z : Ordinal} → (z ≡ & x) ∨ (z ≡ & x) → & x ≡ z
    lemma (case1 refl) = refl
    lemma (case2 refl) = refl
    y⊆x,x : {z : Ordinal} → def (od (x , x)) z → def (od y) z
    y⊆x,x {z} lt = subst (λ k → def (od y) k ) (lemma lt) y∋x
    lemma1 : osuc (& y) o< & (x , x)
    lemma1 = subst (λ k → osuc (& y) o< k ) (sym (peq {x})) (osucc c )

ε-induction : { ψ : HOD  → Set (suc n)}
   → ( {x : HOD } → ({ y : HOD } →  x ∋ y → ψ y ) → ψ x )
   → (x : HOD ) → ψ x
ε-induction {ψ} ind x = subst (λ k → ψ k ) *iso (ε-induction-ord (osuc (& x)) <-osuc )  where
     induction : (ox : Ordinal) → ((oy : Ordinal) → oy o< ox → ψ (* oy)) → ψ (* ox)
     induction ox prev = ind  ( λ {y} lt → subst (λ k → ψ k ) *iso (prev (& y) (o<-subst (c<→o< lt) refl &iso )))
     ε-induction-ord : (ox : Ordinal) { oy : Ordinal } → oy o< ox → ψ (* oy)
     ε-induction-ord ox {oy} lt = TransFinite {λ oy → ψ (* oy)} induction oy

Select : (X : HOD  ) → ((x : HOD  ) → Set n ) → HOD
Select X ψ = record { od = record { def = λ x →  ( odef X x ∧ ψ ( * x )) } ; odmax = odmax X ; <odmax = λ y → <odmax X (proj1 y) }

Replace : HOD  → (HOD  → HOD) → HOD
Replace X ψ = record { od = record { def = λ x → (x o< sup-o X (λ y X∋y → & (ψ (* y)))) ∧ def (in-codomain X ψ) x }
    ; odmax = rmax ; <odmax = rmax<} where
        rmax : Ordinal
        rmax = sup-o X (λ y X∋y → & (ψ (* y)))
        rmax< :  {y : Ordinal} → (y o< rmax) ∧ def (in-codomain X ψ) y → y o< rmax
        rmax< lt = proj1 lt

--
-- If we have LEM, Replace' is equivalent to Replace
--
in-codomain' : (X : HOD  ) → ((x : HOD) → X ∋ x → HOD) → OD
in-codomain'  X ψ = record { def = λ x → ¬ ( (y : Ordinal ) → ¬ ( odef X y ∧  ((lt : odef X y) →  x ≡ & (ψ (* y ) (d→∋ X lt) ))))  }
Replace' : (X : HOD)  → ((x : HOD) → X ∋ x → HOD) → HOD
Replace' X ψ = record { od = record { def = λ x → (x o< sup-o X (λ y X∋y → & (ψ (* y) (d→∋ X X∋y) ))) ∧ def (in-codomain' X ψ) x }
      ; odmax = rmax ; <odmax = rmax< } where
        rmax : Ordinal
        rmax = sup-o X (λ y X∋y → & (ψ (* y) (d→∋ X X∋y)))
        rmax< :  {y : Ordinal} → (y o< rmax) ∧ def (in-codomain' X ψ) y → y o< rmax
        rmax< lt = proj1 lt

Union : HOD  → HOD
Union U = record { od = record { def = λ x → ¬ (∀ (u : Ordinal ) → ¬ ((odef U u) ∧ (odef (* u) x)))  }
    ; odmax = osuc (& U) ; <odmax = umax< } where
        umax< :  {y : Ordinal} → ¬ ((u : Ordinal) → ¬ def (od U) u ∧ def (od (* u)) y) → y o< osuc (& U)
        umax< {y} not = lemma (FExists _ lemma1 not ) where
            lemma0 : {x : Ordinal} → def (od (* x)) y → y o< x
            lemma0 {x} x<y = subst₂ (λ j k → j o< k ) &iso  &iso (c<→o< (d→∋ (* x) x<y ))
            lemma2 : {x : Ordinal} → def (od U) x → x o< & U
            lemma2 {x} x<U = subst (λ k → k o< & U ) &iso (c<→o< (d→∋ U x<U))
            lemma1 : {x : Ordinal} → def (od U) x ∧ def (od (* x)) y → ¬ (& U o< y)
            lemma1 {x} lt u<y = o<> u<y (ordtrans (lemma0 (proj2 lt)) (lemma2 (proj1 lt)) )
            lemma : ¬ ((& U) o< y ) → y o< osuc (& U)
            lemma not with trio< y (& U)
            lemma not | tri< a ¬b ¬c = ordtrans a <-osuc
            lemma not | tri≈ ¬a refl ¬c = <-osuc
            lemma not | tri> ¬a ¬b c = ⊥-elim (not c)
_∈_ : ( A B : HOD  ) → Set n
A ∈ B = B ∋ A

OPwr :  (A :  HOD ) → HOD
OPwr  A = Ord ( sup-o (Ord (osuc (& A))) ( λ x A∋x → & ( A ∩ (* x)) ) )

Power : HOD  → HOD
Power A = Replace (OPwr (Ord (& A))) ( λ x → A ∩ x )
-- ｛_｝ : ZFSet → ZFSet
-- ｛ x ｝ = ( x ,  x )     -- better to use (x , x) directly

union→ :  (X z u : HOD) → (X ∋ u) ∧ (u ∋ z) → Union X ∋ z
union→ X z u xx not = ⊥-elim ( not (& u) ( ⟪ proj1 xx
    , subst ( λ k → odef k (& z)) (sym *iso) (proj2 xx) ⟫ ))
union← :  (X z : HOD) (X∋z : Union X ∋ z) →  ¬  ( (u : HOD ) → ¬ ((X ∋  u) ∧ (u ∋ z )))
union← X z UX∋z =  FExists _ lemma UX∋z where
    lemma : {y : Ordinal} → odef X y ∧ odef (* y) (& z) → ¬ ((u : HOD) → ¬ (X ∋ u) ∧ (u ∋ z))
    lemma {y} xx not = not (* y) ⟪ d→∋ X (proj1 xx) , proj2 xx ⟫

data infinite-d  : ( x : Ordinal  ) → Set n where
    iφ :  infinite-d o∅
    isuc : {x : Ordinal  } →   infinite-d  x  →
            infinite-d  (& ( Union (* x , (* x , * x ) ) ))

-- ω can be diverged in our case, since we have no restriction on the corresponding ordinal of a pair.
-- We simply assumes infinite-d y has a maximum.
--
-- This means that many of OD may not be HODs because of the & mapping divergence.
-- We should have some axioms to prevent this such as & x o< next (odmax x).
--
-- postulate
--     ωmax : Ordinal
--     <ωmax : {y : Ordinal} → infinite-d y → y o< ωmax
--
-- infinite : HOD
-- infinite = record { od = record { def = λ x → infinite-d x } ; odmax = ωmax ; <odmax = <ωmax }

infinite : HOD
infinite = record { od = record { def = λ x → infinite-d x } ; odmax = next o∅ ; <odmax = lemma }  where
    u : (y : Ordinal ) → HOD
    u y = Union (* y , (* y , * y))
    --   next< : {x y z : Ordinal} → x o< next z  → y o< next x → y o< next z
    lemma8 : {y : Ordinal} → & (* y , * y) o< next (odmax (* y , * y))
    lemma8 = ho<
    ---           (x,y) < next (omax x y) < next (osuc y) = next y
    lemmaa : {x y : HOD} → & x o< & y → & (x , y) o< next (& y)
    lemmaa {x} {y} x<y = subst (λ k → & (x , y) o< k ) (sym nexto≡) (subst (λ k → & (x , y) o< next k ) (sym (omax< _ _ x<y)) ho< )
    lemma81 : {y : Ordinal} → & (* y , * y) o< next (& (* y))
    lemma81 {y} = nexto=n (subst (λ k →  & (* y , * y) o< k ) (cong (λ k → next k) (omxx _)) lemma8)
    lemma9 : {y : Ordinal} → & (* y , (* y , * y)) o< next (& (* y , * y))
    lemma9 = lemmaa (c<→o< (case1 refl))
    lemma71 : {y : Ordinal} → & (* y , (* y , * y)) o< next (& (* y))
    lemma71 = next< lemma81 lemma9
    lemma1 : {y : Ordinal} → & (u y) o< next (osuc (& (* y , (* y , * y))))
    lemma1 = ho<
    --- main recursion
    lemma : {y : Ordinal} → infinite-d y → y o< next o∅
    lemma {o∅} iφ = x<nx
    lemma (isuc {y} x) = next< (lemma x) (next< (subst (λ k → & (* y , (* y , * y)) o< next k) &iso lemma71 ) (nexto=n lemma1))

empty : (x : HOD  ) → ¬  (od∅ ∋ x)
empty x = ¬x<0

_=h=_ : (x y : HOD) → Set n
x =h= y  = od x == od y

pair→ : ( x y t : HOD  ) →  (x , y)  ∋ t  → ( t =h= x ) ∨ ( t =h= y )
pair→ x y t (case1 t≡x ) = case1 (subst₂ (λ j k → j =h= k ) *iso *iso (o≡→== t≡x ))
pair→ x y t (case2 t≡y ) = case2 (subst₂ (λ j k → j =h= k ) *iso *iso (o≡→== t≡y ))

pair← : ( x y t : HOD  ) → ( t =h= x ) ∨ ( t =h= y ) →  (x , y)  ∋ t
pair← x y t (case1 t=h=x) = case1 (cong (λ k → & k ) (==→o≡ t=h=x))
pair← x y t (case2 t=h=y) = case2 (cong (λ k → & k ) (==→o≡ t=h=y))

o<→c< :  {x y : Ordinal } → x o< y → (Ord x) ⊆ (Ord y)
o<→c< lt = record { incl = λ z → ordtrans z lt }

⊆→o< :  {x y : Ordinal } → (Ord x) ⊆ (Ord y) →  x o< osuc y
⊆→o< {x} {y}  lt with trio< x y
⊆→o< {x} {y}  lt | tri< a ¬b ¬c = ordtrans a <-osuc
⊆→o< {x} {y}  lt | tri≈ ¬a b ¬c = subst ( λ k → k o< osuc y) (sym b) <-osuc
⊆→o< {x} {y}  lt | tri> ¬a ¬b c with (incl lt)  (o<-subst c (sym &iso) refl )
... | ttt = ⊥-elim ( o<¬≡ refl (o<-subst ttt &iso refl ))

ψiso :  {ψ : HOD  → Set n} {x y : HOD } → ψ x → x ≡ y   → ψ y
ψiso {ψ} t refl = t
selection : {ψ : HOD → Set n} {X y : HOD} → ((X ∋ y) ∧ ψ y) ⇔ (Select X ψ ∋ y)
selection {ψ} {X} {y} = ⟪
     ( λ cond → ⟪ proj1 cond , ψiso {ψ} (proj2 cond) (sym *iso)  ⟫ )
  ,  ( λ select → ⟪ proj1 select  , ψiso {ψ} (proj2 select) *iso  ⟫ )
  ⟫

selection-in-domain : {ψ : HOD → Set n} {X y : HOD} → Select X ψ ∋ y → X ∋ y
selection-in-domain {ψ} {X} {y} lt = proj1 ((proj2 (selection {ψ} {X} )) lt)

sup-c< :  (ψ : HOD → HOD) → {X x : HOD} → X ∋ x  → & (ψ x) o< (sup-o X (λ y X∋y → & (ψ (* y))))
sup-c< ψ {X} {x} lt = subst (λ k → & (ψ k) o< _ ) *iso (sup-o< X lt )
replacement← : {ψ : HOD → HOD} (X x : HOD) →  X ∋ x → Replace X ψ ∋ ψ x
replacement← {ψ} X x lt = ⟪ sup-c< ψ {X} {x} lt , lemma ⟫ where
    lemma : def (in-codomain X ψ) (& (ψ x))
    lemma not = ⊥-elim ( not ( & x ) ⟪ lt , cong (λ k → & (ψ k)) (sym *iso)⟫ )
replacement→ : {ψ : HOD → HOD} (X x : HOD) → (lt : Replace X ψ ∋ x) → ¬ ( (y : HOD) → ¬ (x =h= ψ y))
replacement→ {ψ} X x lt = contra-position lemma (lemma2 (proj2 lt)) where
    lemma2 :  ¬ ((y : Ordinal) → ¬ odef X y ∧ ((& x) ≡ & (ψ (* y))))
            → ¬ ((y : Ordinal) → ¬ odef X y ∧ (* (& x) =h= ψ (* y)))
    lemma2 not not2  = not ( λ y d →  not2 y ⟪ proj1 d , lemma3 (proj2 d)⟫) where
        lemma3 : {y : Ordinal } → (& x ≡ & (ψ (*  y))) → (* (& x) =h= ψ (* y))
        lemma3 {y} eq = subst (λ k  → * (& x) =h= k ) *iso (o≡→== eq )
    lemma :  ( (y : HOD) → ¬ (x =h= ψ y)) → ( (y : Ordinal) → ¬ odef X y ∧ (* (& x) =h= ψ (* y)) )
    lemma not y not2 = not (* y) (subst (λ k → k =h= ψ (* y)) *iso  ( proj2 not2 ))

---
--- Power Set
---
---    First consider ordinals in HOD
---
--- A ∩ x =  record { def = λ y → odef A y ∧  odef x y }                   subset of A
--
--
∩-≡ :  { a b : HOD  } → ({x : HOD  } → (a ∋ x → b ∋ x)) → a =h= ( b ∩ a )
∩-≡ {a} {b} inc = record {
   eq→ = λ {x} x<a → ⟪ (subst (λ k → odef b k ) &iso (inc (d→∋ a x<a))) , x<a  ⟫ ;
   eq← = λ {x} x<a∩b → proj2 x<a∩b }
--
-- Transitive Set case
-- we have t ∋ x → Ord a ∋ x means t is a subset of Ord a, that is (Ord a) ∩ t =h= t
-- OPwr (Ord a) is a sup of (Ord a) ∩ t, so OPwr (Ord a) ∋ t
-- OPwr  A = Ord ( sup-o ( λ x → & ( A ∩ (* x )) ) )
--
ord-power← : (a : Ordinal ) (t : HOD) → ({x : HOD} → (t ∋ x → (Ord a) ∋ x)) → OPwr (Ord a) ∋ t
ord-power← a t t→A  = subst (λ k → odef (OPwr (Ord a)) k ) (lemma1 lemma-eq) lemma where
    lemma-eq :  ((Ord a) ∩ t) =h= t
    eq→ lemma-eq {z} w = proj2 w
    eq← lemma-eq {z} w = ⟪ subst (λ k → odef (Ord a) k ) &iso ( t→A (d→∋ t w)) , w ⟫
    lemma1 :  {a : Ordinal } { t : HOD }
        → (eq : ((Ord a) ∩ t) =h= t)  → & ((Ord a) ∩ (* (& t))) ≡ & t
    lemma1  {a} {t} eq = subst (λ k → & ((Ord a) ∩ k) ≡ & t ) (sym *iso) (cong (λ k → & k ) (==→o≡ eq ))
    lemma2 : (& t) o< (osuc (& (Ord a)))
    lemma2 = ⊆→o≤  {t} {Ord a} (λ {x} x<t → subst (λ k → def (od (Ord a)) k) &iso (t→A (d→∋ t x<t)))
    lemma :  & ((Ord a) ∩ (* (& t)) ) o< sup-o (Ord (osuc (& (Ord a)))) (λ x lt → & ((Ord a) ∩ (* x)))
    lemma = sup-o< _ lemma2

--
-- Every set in HOD is a subset of Ordinals, so make OPwr (Ord (& A)) first
-- then replace of all elements of the Power set by A ∩ y
--
-- Power A = Replace (OPwr (Ord (& A))) ( λ y → A ∩ y )

-- we have oly double negation form because of the replacement axiom
--
power→ :  ( A t : HOD) → Power A ∋ t → {x : HOD} → t ∋ x → ¬ ¬ (A ∋ x)
power→ A t P∋t {x} t∋x = FExists _ lemma5 lemma4 where
    a = & A
    lemma2 : ¬ ( (y : HOD) → ¬ (t =h=  (A ∩ y)))
    lemma2 = replacement→ {λ x → A ∩ x} (OPwr (Ord (& A))) t P∋t
    lemma3 : (y : HOD) → t =h= ( A ∩ y ) → ¬ ¬ (A ∋ x)
    lemma3 y eq not = not (proj1 (eq→ eq t∋x))
    lemma4 : ¬ ((y : Ordinal) → ¬ (t =h= (A ∩ * y)))
    lemma4 not = lemma2 ( λ y not1 → not (& y) (subst (λ k → t =h= ( A ∩ k )) (sym *iso) not1 ))
    lemma5 : {y : Ordinal} → t =h= (A ∩ * y) →  ¬ ¬  (odef A (& x))
    lemma5 {y} eq not = (lemma3 (* y) eq) not

power← :  (A t : HOD) → ({x : HOD} → (t ∋ x → A ∋ x)) → Power A ∋ t
power← A t t→A = ⟪ lemma1 , lemma2 ⟫ where
    a = & A
    lemma0 : {x : HOD} → t ∋ x → Ord a ∋ x
    lemma0 {x} t∋x = c<→o< (t→A t∋x)
    lemma3 : OPwr (Ord a) ∋ t
    lemma3 = ord-power← a t lemma0
    lemma4 :  (A ∩ * (& t)) ≡ t
    lemma4 = let open ≡-Reasoning in begin
        A ∩ * (& t)
        ≡⟨ cong (λ k → A ∩ k) *iso ⟩
        A ∩ t
        ≡⟨ sym (==→o≡ ( ∩-≡ {t} {A} t→A )) ⟩
        t
        ∎
    sup1 : Ordinal
    sup1 =  sup-o (Ord (osuc (& (Ord (& A))))) (λ x A∋x → & ((Ord (& A)) ∩ (* x)))
    lemma9 : def (od (Ord (Ordinals.osuc O (& (Ord (& A)))))) (& (Ord (& A)))
    lemma9 = <-osuc
    lemmab : & ((Ord (& A)) ∩ (* (& (Ord (& A) )))) o< sup1
    lemmab = sup-o< (Ord (osuc (& (Ord (& A))))) lemma9
    lemmad : Ord (osuc (& A)) ∋ t
    lemmad = ⊆→o≤ (λ {x} lt → subst (λ k → def (od A) k ) &iso (t→A (d→∋ t lt)))
    lemmac : ((Ord (& A)) ∩  (* (& (Ord (& A) )))) =h= Ord (& A)
    lemmac = record { eq→ = lemmaf ; eq← = lemmag } where
        lemmaf : {x : Ordinal} → def (od ((Ord (& A)) ∩  (* (& (Ord (& A)))))) x → def (od (Ord (& A))) x
        lemmaf {x} lt = proj1 lt
        lemmag :  {x : Ordinal} → def (od (Ord (& A))) x → def (od ((Ord (& A)) ∩ (* (& (Ord (& A)))))) x
        lemmag {x} lt = ⟪ lt , subst (λ k → def (od k) x) (sym *iso) lt ⟫
    lemmae : & ((Ord (& A)) ∩ (* (& (Ord (& A))))) ≡ & (Ord (& A))
    lemmae = cong (λ k → & k ) ( ==→o≡ lemmac)
    lemma7 : def (od (OPwr (Ord (& A)))) (& t)
    lemma7 with osuc-≡< lemmad
    lemma7 | case2 lt = ordtrans (c<→o< lt) (subst (λ k → k o< sup1) lemmae lemmab )
    lemma7 | case1 eq with osuc-≡< (⊆→o≤ {* (& t)} {* (& (Ord (& t)))} (λ {x} lt → lemmah lt )) where
        lemmah : {x : Ordinal } → def (od (* (& t))) x → def (od (* (& (Ord (& t))))) x
        lemmah {x} lt = subst (λ k → def (od k) x ) (sym *iso) (subst (λ k → k o< (& t))
            &iso
            (c<→o< (subst₂ (λ j k → def (od j)  k) *iso (sym &iso) lt )))
    lemma7 | case1 eq | case1 eq1 = subst (λ k → k o< sup1) (trans lemmae lemmai) lemmab where
        lemmai : & (Ord (& A)) ≡ & t
        lemmai = let open ≡-Reasoning in begin
                & (Ord (& A))
            ≡⟨ sym (cong (λ k → & (Ord k)) eq) ⟩
                & (Ord (& t))
            ≡⟨ sym &iso ⟩
                & (* (& (Ord (& t))))
            ≡⟨ sym eq1 ⟩
                & (* (& t))
            ≡⟨ &iso ⟩
                & t
            ∎
    lemma7 | case1 eq | case2 lt = ordtrans lemmaj (subst (λ k → k o< sup1) lemmae lemmab ) where
        lemmak : & (* (& (Ord (& t)))) ≡ & (Ord (& A))
        lemmak = let open ≡-Reasoning in begin
                & (* (& (Ord (& t))))
            ≡⟨ &iso ⟩
                & (Ord (& t))
            ≡⟨ cong (λ k → & (Ord k)) eq ⟩
                & (Ord (& A))
            ∎
        lemmaj : & t o< & (Ord (& A))
        lemmaj = subst₂ (λ j k → j o< k ) &iso lemmak lt
    lemma1 : & t o< sup-o (OPwr (Ord (& A))) (λ x lt → & (A ∩ (* x)))
    lemma1 = subst (λ k → & k o< sup-o (OPwr (Ord (& A)))  (λ x lt → & (A ∩ (* x))))
        lemma4 (sup-o< (OPwr (Ord (& A))) lemma7 )
    lemma2 :  def (in-codomain (OPwr (Ord (& A))) (_∩_ A)) (& t)
    lemma2 not = ⊥-elim ( not (& t) ⟪ lemma3 , lemma6 ⟫ ) where
        lemma6 : & t ≡ & (A ∩ * (& t))
        lemma6 = cong ( λ k → & k ) (==→o≡ (subst (λ k → t =h= (A ∩ k)) (sym *iso) ( ∩-≡ {t} {A} t→A  )))

extensionality0 : {A B : HOD } → ((z : HOD) → (A ∋ z) ⇔ (B ∋ z)) → A =h= B
eq→ (extensionality0 {A} {B} eq ) {x} d = odef-iso  {A} {B} (sym &iso) (proj1 (eq (* x))) d
eq← (extensionality0 {A} {B} eq ) {x} d = odef-iso  {B} {A} (sym &iso) (proj2 (eq (* x))) d

extensionality : {A B w : HOD  } → ((z : HOD ) → (A ∋ z) ⇔ (B ∋ z)) → (w ∋ A) ⇔ (w ∋ B)
proj1 (extensionality {A} {B} {w} eq ) d = subst (λ k → w ∋ k) ( ==→o≡ (extensionality0 {A} {B} eq) ) d
proj2 (extensionality {A} {B} {w} eq ) d = subst (λ k → w ∋ k) (sym ( ==→o≡ (extensionality0 {A} {B} eq) )) d

infinity∅ : infinite  ∋ od∅
infinity∅ = subst (λ k → odef infinite k ) lemma iφ where
    lemma : o∅ ≡ & od∅
    lemma =  let open ≡-Reasoning in begin
        o∅
        ≡⟨ sym &iso ⟩
        & ( * o∅ )
        ≡⟨ cong ( λ k → & k ) o∅≡od∅ ⟩
        & od∅
        ∎
infinity : (x : HOD) → infinite ∋ x → infinite ∋ Union (x , (x , x ))
infinity x lt = subst (λ k → odef infinite k ) lemma (isuc {& x} lt) where
    lemma :  & (Union (* (& x) , (* (& x) , * (& x))))
        ≡ & (Union (x , (x , x)))
    lemma = cong (λ k → & (Union ( k , ( k , k ) ))) *iso

isZF : IsZF (HOD )  _∋_  _=h=_ od∅ _,_ Union Power Select Replace infinite
isZF = record {
        isEquivalence  = record { refl = ==-refl ; sym = ==-sym; trans = ==-trans }
    ;   pair→  = pair→
    ;   pair←  = pair←
    ;   union→ = union→
    ;   union← = union←
    ;   empty = empty
    ;   power→ = power→
    ;   power← = power←
    ;   extensionality = λ {A} {B} {w} → extensionality {A} {B} {w}
    ;   ε-induction = ε-induction
    ;   infinity∅ = infinity∅
    ;   infinity = infinity
    ;   selection = λ {X} {ψ} {y} → selection {X} {ψ} {y}
    ;   replacement← = replacement←
    ;   replacement→ = λ {ψ} → replacement→ {ψ}
    }

HOD→ZF : ZF
HOD→ZF   = record {
    ZFSet = HOD
    ; _∋_ = _∋_
    ; _≈_ = _=h=_
    ; ∅  = od∅
    ; _,_ = _,_
    ; Union = Union
    ; Power = Power
    ; Select = Select
    ; Replace = Replace
    ; infinite = infinite
    ; isZF = isZF
 }


-- Andreas, 2017-07-26, issue #2331.
-- Andrea found a counterexample against the "usable decrease" feature:

open import Agda.Builtin.Size

data D (i : Size) : Set where
  c : (j : Size< i) → D i

mutual
  test : (i : Size) → D i → Set
  test  i (c j) = (k : Size< j) (l : Size< k) → helper i j k (c l)

  helper : (i : Size) (j : Size< i) (k : Size< j) → D k → Set
  helper i j k d = test k d

-- Should not termination check.


module Test where

open import LF
open import IID

-- r←→gArgs-subst eliminates the identity proof stored in the rArgs. If this proof is
-- by reflexivity r←→gArgs-subst is a definitional identity. This is the case
-- when a = g→rArgs a'
r←→gArgs-subst-identity :
  {I  : Set}(γ : OPg I)(U : I -> Set)
  (C  : (i : I) -> rArgs (ε γ) U i -> Set)
  (a' : Args γ U) ->
  let a = g→rArgs γ U a'
      i = index γ U a' in
  (h : C (index γ U (r→gArgs γ U i a))
	 (g→rArgs γ U (r→gArgs γ U i a))
  ) -> r←→gArgs-subst γ U C i a h ≡ h
r←→gArgs-subst-identity (ι i)	  U C _   h = refl-≡
r←→gArgs-subst-identity (σ A γ)	  U C arg h = r←→gArgs-subst-identity (γ (π₀ arg)) U C' (π₁ arg) h
  where C' = \i c -> C i (π₀ arg , c)
r←→gArgs-subst-identity (δ H i γ) U C arg h = r←→gArgs-subst-identity γ U C' (π₁ arg) h
  where C' = \i c -> C i (π₀ arg , c)


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Substitution.Weakening {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Substitution

open import Tools.Product
import Tools.PropositionalEquality as PE

-- Weakening of valid types by one.
wk1ᵛ : ∀ {A F Γ l}
      ([Γ] : ⊩ᵛ Γ)
      ([F] : Γ ⊩ᵛ⟨ l ⟩ F / [Γ])
    → Γ ⊩ᵛ⟨ l ⟩ A / [Γ]
    → Γ ∙ F ⊩ᵛ⟨ l ⟩ wk1 A / (VPack _ _ (V∙ [Γ] [F]))
wk1ᵛ {A} [Γ] [F] [A] ⊢Δ [σ] =
  let [σA] = proj₁ ([A] ⊢Δ (proj₁ [σ]))
      [σA]′ = irrelevance′ (PE.sym (subst-wk A)) [σA]
  in  [σA]′
  ,   (λ [σ′] [σ≡σ′] →
         irrelevanceEq″ (PE.sym (subst-wk A))
                        (PE.sym (subst-wk A))
                        [σA] [σA]′
                        (proj₂ ([A] ⊢Δ (proj₁ [σ])) (proj₁ [σ′]) (proj₁ [σ≡σ′])))

-- Weakening of valid type equality by one.
wk1Eqᵛ : ∀ {A B F Γ l}
         ([Γ] : ⊩ᵛ Γ)
         ([F] : Γ ⊩ᵛ⟨ l ⟩ F / [Γ])
         ([A] : Γ ⊩ᵛ⟨ l ⟩ A / [Γ])
         ([A≡B] : Γ ⊩ᵛ⟨ l ⟩ A ≡ B / [Γ] / [A])
       → Γ ∙ F ⊩ᵛ⟨ l ⟩ wk1 A ≡ wk1 B / (VPack _ _ (V∙ [Γ] [F])) / wk1ᵛ {A} {F} [Γ] [F] [A]
wk1Eqᵛ {A} {B} [Γ] [F] [A] [A≡B] ⊢Δ [σ] =
  let [σA] = proj₁ ([A] ⊢Δ (proj₁ [σ]))
      [σA]′ = irrelevance′ (PE.sym (subst-wk A)) [σA]
  in  irrelevanceEq″ (PE.sym (subst-wk A))
                     (PE.sym (subst-wk B))
                     [σA] [σA]′
                     ([A≡B] ⊢Δ (proj₁ [σ]))


open import SOAS.Metatheory.Syntax

-- Metasubstitution operation
module SOAS.Metatheory.SecondOrder.Metasubstitution {T : Set}(Syn : Syntax {T}) where

open Syntax Syn

open import SOAS.Metatheory.FreeMonoid Syn

open import SOAS.Common
open import SOAS.Families.Core {T}
open import SOAS.Families.Build
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Construction.Structure as Structure
open import SOAS.ContextMaps.Combinators
open import SOAS.ContextMaps.CategoryOfRenamings

open import SOAS.Abstract.Hom
open import SOAS.Abstract.ExpStrength
import SOAS.Abstract.Coalgebra as →□
open →□.Sorted
import SOAS.Abstract.Box as □ ; open □.Sorted

open import Categories.Monad

open import SOAS.Coalgebraic.Monoid

open import SOAS.Metatheory Syn

private
  variable
    Γ Δ Π : Ctx
    α β τ : T
    𝔛 𝔜 ℨ : Familyₛ
    𝔐 𝔑 : MCtx

open Theory

-- Ground metasubstitution from the monad structure
msub₀ : (𝔛 ⇾̣ 𝕋 𝔜) → 𝕋 𝔛 ⇾̣ 𝕋 𝔜
msub₀ {𝔛}{𝔜} κ t = μ.η 𝔜 (F.₁ κ t) where open Monad ΣMon:Monad

-- Meta-algebra structure on the exponential [ 𝔛 ⊸ 𝒫 ] ⇨ ℳ
[_⊸_]⇨_ᵃ : (𝔛 {𝒫}{ℳ} : Familyₛ) → Coalg 𝒫 → ΣMon ℳ → (𝒫 ⇾̣ ℳ)
         → MetaAlg 𝔛 ([ 𝔛 ⊸ 𝒫 ] ⇨ ℳ)
[_⊸_]⇨_ᵃ 𝔛 {𝒫}{ℳ} 𝒫ᵇ Σℳᵐ ψ = record
  { 𝑎𝑙𝑔 = λ t ζ → ℳ.𝑎𝑙𝑔 (estr [ 𝔛 ⊸ 𝒫ᵇ ]ᵇ ℳ t ζ)
  ; 𝑣𝑎𝑟 = λ v ζ → ℳ.η v
  ; 𝑚𝑣𝑎𝑟 = λ 𝔪 ε ζ → ℳ.μ (ψ (ζ 𝔪)) (copair ℳ (λ x → ε x ζ) ℳ.η)
  } where module ℳ = ΣMon Σℳᵐ

□[_⊸_]⇨_ᵃ : (𝔛 {𝒫}{ℳ} : Familyₛ) → Coalg 𝒫 → ΣMon ℳ → (𝒫 ⇾̣ ℳ)
           → MetaAlg 𝔛 ([ 𝔛 ⊸ 𝒫 ] ➡ ℳ)
□[ 𝔛 ⊸ 𝒫ᵇ ]⇨ Σℳᵐ ᵃ ψ = □ᵃ 𝔛 ([ 𝔛 ⊸ 𝒫ᵇ ]⇨ Σℳᵐ ᵃ ψ)

-- Derived meta-algebra instance for [ 𝔛 ⊸ 𝕋 𝔜 ] ⇨ 𝕋 𝔜
⟅_⇨_⟆ᵃ : (𝔛 𝔜 : Familyₛ) → MetaAlg 𝔛 ⟅ 𝔛 ⇨ 𝕋 𝔜 ⟆
⟅ 𝔛 ⇨ 𝔜 ⟆ᵃ = [ 𝔛 ⊸ 𝕋ᵇ 𝔜 ]⇨ Σ𝕋ᵐ 𝔜 ᵃ id

module MS {𝔛 𝔜 : Familyₛ} =  Semantics 𝔛 ⟅ 𝔛 ⇨ 𝔜 ⟆ᵃ
module □MS {𝔛 𝔜 : Familyₛ} = □Traversal 𝔛 ⟅ 𝔛 ⇨ 𝔜 ⟆ᵃ

-- Metasubstitution operations
-- Base
msub : 𝕋 𝔛 ⇾̣ ⟅ 𝔛 ⇨ 𝕋 𝔜 ⟆
msub = MS.𝕤𝕖𝕞
-- Parametrised
□msub : 𝕋 𝔛 ⇾̣ ⟅ 𝔛 ➡ 𝕋 𝔜 ⟆
□msub = □MS.𝕥𝕣𝕒𝕧
-- Linear
○msub : 𝕋 𝔛 ⇾̣ ⟅ 𝔛 ⊸ 𝕋 𝔜 ⟆
○msub {𝔜 = 𝔜}{Γ = Γ} t ζ = □msub t (inl Γ) λ {_}{Π} 𝔪 → 𝕣𝕖𝕟 𝔜 (ζ 𝔪) (Π ∔∣ inr Γ)

-- Unit parametrisation
□msub-id : (t : 𝕋 𝔛 α Γ)(κ : [ 𝔛 ⊸ 𝕋 𝔜 ] Γ) → □msub t id κ ≡ msub t κ
□msub-id {𝔛}{𝔜 = 𝔜} t κ = cong (λ - → - κ) (□𝕥𝕣𝕒𝕧-id≈𝕤𝕖𝕞 𝔛 ⟅ 𝔛 ⇨ 𝔜 ⟆ᵃ)

-- Unit metasubstitution mapping
ms-unit : [ 𝔛 ⊸ 𝕋 𝔛 ] Γ
ms-unit {𝔛}{Δ = Δ} 𝔪 = 𝕞𝕧𝕒𝕣 𝔛 𝔪 (𝕧𝕒𝕣 𝔛 ∘ inl Δ)

-- | Inductive metasubstitution

-- List of terms in an extended (object variable) context mapped to every element of a metavariable context
data MSub (Γ : Ctx) : MCtx → MCtx → Set₁ where
  ◦   : MSub Γ ⁅⁆ 𝔑
  _◃_ : (𝔑 ▷ 𝕋) α (Π ∔ Γ) → MSub Γ 𝔐 𝔑 → MSub Γ (⁅ Π ⊩ₙ α ⁆ 𝔐) 𝔑

infixr 15 _◃_ _▹_

-- Add term to the end of a metasubstitution map
_▹_ : MSub Γ 𝔐 𝔑 → (𝔑 ▷ 𝕋) τ (Π ∔ Γ) → MSub Γ (𝔐 ⁅ Π ⊩ₙ τ ⁆) 𝔑
◦ ▹ t = t ◃ ◦
(s ◃ ζ) ▹ t = s ◃ (ζ ▹ t)

-- Application of a metasubstitution to a metavariable
ix≀ : MSub Γ 𝔐 𝔑 → [ ∥ 𝔐 ∥ ⊸ 𝔑 ▷ 𝕋 ] Γ
ix≀ (t ◃ ζ) ↓ = t
ix≀ (t ◃ ζ) (↑ 𝔪) = ix≀ ζ 𝔪

-- Term corresponding to the topmost distinguished metavariable of an extended mvar context
_⊩◌ : (Π : Ctx) → (⁅ Π ⊩ₙ β ⁆ 𝔐 ▷ 𝕋) β (Π ∔ Γ)
_⊩◌ {β}{𝔐} Π = ms-unit ↓

◌ : (⁅ β ⁆ 𝔐 ▷ 𝕋) β Γ
◌ = ∅ ⊩◌

-- Weakening of metavariable context
wk≀ : (𝔐 ▷ 𝕋) α Γ →  (⁅ Π ⊩ₙ τ ⁆ 𝔐 ▷ 𝕋) α Γ
wk≀ t = 𝕋₁ ↑_ t

-- Extension of the codomain of a metasubstitution
ext≀ : (Π : Ctx)(τ : T) → MSub Γ 𝔐 𝔑 → MSub Γ 𝔐 (⁅ Π ⊩ₙ τ ⁆ 𝔑)
ext≀ Π τ ◦ = ◦
ext≀ Π τ (t ◃ κ) = wk≀ t ◃ (ext≀ Π τ κ)

-- Lifting of a metasubstitution
lift≀ : (Π : Ctx)(τ : T) → MSub Γ 𝔐 𝔑 → MSub Γ (⁅ Π ⊩ₙ τ ⁆ 𝔐) (⁅ Π ⊩ₙ τ ⁆ 𝔑)
lift≀ Π τ κ = (Π ⊩◌) ◃ (ext≀ Π τ κ)

-- Identity metasubstitution
id≀ : (Γ : Ctx) →  MSub Γ 𝔐 𝔐
id≀ {⁅⁆} Γ = ◦
id≀ {⁅ Π ⊩ₙ τ ⁆ 𝔐} Γ = lift≀ Π τ (id≀ Γ)

-- Left and right weakening of object context of a metasubstitution
inl≀ : MSub Γ 𝔐 𝔑 → MSub (Γ ∔ Δ) 𝔐 𝔑
inl≀ ◦ = ◦
inl≀ {𝔑 = 𝔑} (_◃_ {Π = Π} t κ) = 𝕣𝕖𝕟 ∥ 𝔑 ∥ t (Π ∔∣ inl _) ◃ (inl≀ κ)

inr≀ : (Γ : Ctx) → MSub Δ 𝔐 𝔑 → MSub (Γ ∔ Δ) 𝔐 𝔑
inr≀ _ ◦ = ◦
inr≀ {Δ}{𝔑 = 𝔑} Γ (_◃_ {Π = Π} t κ) = (𝕣𝕖𝕟 ∥ 𝔑 ∥ t (Π ∔∣ inr Γ)) ◃ (inr≀ Γ κ)

-- Application of weakened metasubstitution corresponds to centre weakening
ix-inr≀ : (κ : MSub Δ 𝔐 𝔑)(𝔪 : Π ⊩ τ ∈ 𝔐)
        → ix≀ (inr≀ Γ κ) 𝔪 ≡ (𝕣𝕖𝕟 ∥ 𝔑 ∥ (ix≀ κ 𝔪) (Π ∔∣ inr Γ))
ix-inr≀ (x ◃ κ) ↓ = refl
ix-inr≀ (x ◃ κ) (↑ 𝔪) = ix-inr≀ κ 𝔪

-- Correctness lemmas of weakening, lifting, identity
ext≀≈𝕋₁pop : (κ : MSub Γ 𝔐 𝔑)(𝔪 : Π ⊩ τ ∈ 𝔐) → ix≀ (ext≀ Δ β κ) 𝔪 ≡ wk≀ (ix≀ κ 𝔪)
ext≀≈𝕋₁pop (x ◃ κ) ↓ = refl
ext≀≈𝕋₁pop (x ◃ κ) (↑ 𝔪) = ext≀≈𝕋₁pop κ 𝔪

lift≀≈𝕋₁pop : (κ : MSub Γ 𝔐 𝔑)(𝔪 : Γ ⊩ α ∈ 𝔐) → ix≀ (lift≀ Γ α κ) (↑ 𝔪) ≡ wk≀ (ix≀ κ 𝔪)
lift≀≈𝕋₁pop (x ◃ κ) ↓ = refl
lift≀≈𝕋₁pop (x ◃ κ) (↑ 𝔪) = lift≀≈𝕋₁pop κ 𝔪

id≀≈ms-unit : (Γ : Ctx)(𝔪 : Π ⊩ τ ∈ 𝔐) → ix≀ (id≀ Γ) 𝔪 ≡ ms-unit 𝔪
id≀≈ms-unit {𝔐 = ⁅ Π ⊩ₙ τ ⁆ 𝔐} Γ ↓ = refl
id≀≈ms-unit {𝔐 = ⁅ Π ⊩ₙ τ ⁆ 𝔐} Γ (↑_ {Δ}{β}{Γ = .Π}{.τ} 𝔪) = begin
      ix≀ (ext≀ Π τ (id≀ Γ)) 𝔪
  ≡⟨ ext≀≈𝕋₁pop (id≀ Γ) 𝔪 ⟩
      wk≀ (ix≀ (id≀ Γ) 𝔪)
  ≡⟨ cong (wk≀) (id≀≈ms-unit Γ 𝔪) ⟩
      wk≀ (ms-unit 𝔪)
  ≡⟨⟩
      wk≀ (𝕞𝕧𝕒𝕣 ∥ 𝔐 ∥ 𝔪 (𝕧𝕒𝕣 ∥ 𝔐 ∥ ∘ ∔.i₁))
  ≡⟨ 𝕋₁∘𝕞𝕧𝕒𝕣[𝕧𝕒𝕣] ↑_ 𝔪 (∔.i₁) ⟩
      𝕞𝕧𝕒𝕣 ∥ ⁅ Π ⊩ₙ τ ⁆ 𝔐 ∥ (↑ 𝔪) (𝕧𝕒𝕣 ∥ ⁅ Π ⊩ₙ τ ⁆ 𝔐 ∥ ∘ ∔.i₁)
  ∎ where open ≡-Reasoning

-- Inductive metasubstitution operations
-- Base
msub≀ : (𝔐 ▷ 𝕋) α Γ → MSub Γ 𝔐 𝔑 → (𝔑 ▷ 𝕋) α Γ
msub≀ t ζ = msub t (ix≀ ζ)
-- Parametrised
□msub≀ : (𝔐 ▷ 𝕋) α Γ → (Γ ↝ Δ) → MSub Δ 𝔐 𝔑 → (𝔑 ▷ 𝕋) α Δ
□msub≀ t ρ ζ = □msub t ρ (ix≀ ζ)
-- Linear
○msub≀ : (𝔐 ▷ 𝕋) α Γ → MSub Δ 𝔐 𝔑 → (𝔑 ▷ 𝕋) α (Γ ∔ Δ)
○msub≀ {Γ = Γ} t ζ = □msub≀ t (inl Γ) (inr≀ Γ ζ)

-- Syntactic sugar for metasubstitution application
_》 : (𝔑 ▷ 𝕋) α (Π ∔ Γ) → MSub Γ (⁅ Π ⊩ₙ α ⁆̣) 𝔑
t 》  = t ◃ ◦
_《_ : (𝔐 ▷ 𝕋) α Γ → MSub Γ 𝔐 𝔑 → (𝔑 ▷ 𝕋) α Γ
_《_ = msub≀

infixr 25 _》
infix 15 _《_

-- Instantiation of a term extended at the start of the context
instₛ : (⁅ Π ⊩ₙ α ⁆ 𝔐 ▷ 𝕋) β Γ → (𝔐 ▷ 𝕋) α (Π ∔ Γ) → (𝔐 ▷ 𝕋) β Γ
instₛ {Γ = Γ} h s = msub≀ h (s ◃ id≀ Γ)

-- Instantiation of a term extended at the end of the context
instₑ : (𝔐 ⁅ Π ⊩ₙ α ⁆ ▷ 𝕋) β Γ → (𝔐 ▷ 𝕋) α (Π ∔ Γ) → (𝔐 ▷ 𝕋) β Γ
instₑ {Γ = Γ} h s = msub≀ h ((id≀ Γ) ▹ s)

-- Instantiation of a term extended twice at the end of the context
instₑ₂ : {Π₁ Π₂ : Ctx}{α₁ α₂ : T}
       → ((𝔐 ⁅ Π₁ ⊩ₙ α₁ ⁆) ⁅ Π₂ ⊩ₙ α₂ ⁆ ▷ 𝕋) β Γ
       → (𝔐 ▷ 𝕋) α₁ (Π₁ ∔ Γ) → (𝔐 ▷ 𝕋) α₂ (Π₂ ∔ Γ) → (𝔐 ▷ 𝕋) β Γ
instₑ₂ {Γ = Γ} h s t = msub≀ h ((id≀ Γ ▹ s) ▹ t)


-- Laws for weakenings and substitutions.

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

module Definition.Untyped.Properties where

open import Definition.Untyped

open import Tools.Fin
open import Tools.Nat
open import Tools.List
open import Tools.PropositionalEquality hiding (subst)

private
  variable
    ℓ m n : Nat
    ρ ρ′ : Wk m n
    η : Wk n ℓ
    σ σ′ : Subst m n

-- Weakening properties

-- Two weakenings ρ and ρ′ are extensionally equal if they agree on
-- all arguments when interpreted as functions mapping variables to
-- variables.  Formally, they are considered equal iff
--
--   (∀ x → wkVar ρ x ≡ wkVar ρ′ x)
--
-- Intensional (propositional) equality would be too fine.  For
-- instance,
--
--   lift id : Γ∙A ≤ Γ∙A
--
-- is extensionally equal to
--
--   id : Γ∙A ≤ Γ∙A
--
-- but syntactically different.

-- "lift" preserves equality of weakenings.  Or:
-- If two weakenings are equal under wkVar, then they are equal when lifted.

wkVar-lift : (∀ x → wkVar ρ x ≡ wkVar ρ′ x)
           → (∀ x → wkVar (lift ρ) x ≡ wkVar (lift ρ′) x)

wkVar-lift eq x0     = refl
wkVar-lift eq (x +1) = cong _+1 (eq x)

wkVar-lifts : (∀ x → wkVar ρ x ≡ wkVar ρ′ x)
            → (∀ n x → wkVar (liftn ρ n) x ≡ wkVar (liftn ρ′ n) x)
wkVar-lifts eq 0 x      = eq x
wkVar-lifts eq (1+ n) x = wkVar-lift (wkVar-lifts eq n) x

-- Extensionally equal weakenings, if applied to a term,
-- yield the same weakened term.  Or:
-- If two weakenings are equal under wkVar, then they are equal under wk.

mutual
  wkVar-to-wk : (∀ x → wkVar ρ x ≡ wkVar ρ′ x)
              → ∀ t → wk ρ t ≡ wk ρ′ t
  wkVar-to-wk eq (var x)   = cong var (eq x)
  wkVar-to-wk eq (gen k c) = cong (gen k) (wkVar-to-wkGen eq c)

  wkVar-to-wkGen : (∀ x → wkVar ρ x ≡ wkVar ρ′ x)
                 → ∀ {bs} c → wkGen {bs = bs} ρ c ≡ wkGen {bs = bs} ρ′ c
  wkVar-to-wkGen eq [] = refl
  wkVar-to-wkGen eq (_∷_ {b = b} t ts) =
    cong₂ _∷_ (wkVar-to-wk (wkVar-lifts eq b) t) (wkVar-to-wkGen eq ts)

-- lift id  is extensionally equal to  id.

wkVar-lift-id : (x : Fin (1+ n)) → wkVar (lift id) x ≡ wkVar id x
wkVar-lift-id x0     = refl
wkVar-lift-id (x +1) = refl

wkVar-lifts-id : (n : Nat) (x : Fin (n + m)) → wkVar (liftn id n) x ≡ wkVar id x
wkVar-lifts-id 0 x           = refl
wkVar-lifts-id (1+ n) x0     = refl
wkVar-lifts-id (1+ n) (x +1) = cong _+1 (wkVar-lifts-id n x)

-- id is the identity renaming.

wkVar-id : (x : Fin n) → wkVar id x ≡ x
wkVar-id x = refl

mutual
  wk-id : (t : Term n) → wk id t ≡ t
  wk-id (var x)   = refl
  wk-id (gen k ts) = cong (gen k) (wkGen-id ts)

  wkGen-id : ∀ {bs} x → wkGen {n} {n} {bs} id x ≡ x
  wkGen-id [] = refl
  wkGen-id (_∷_ {b = b} t ts) =
    cong₂ _∷_ (trans (wkVar-to-wk (wkVar-lifts-id b) t) ( wk-id t)) (wkGen-id ts)

-- lift id  is also the identity renaming.

wk-lift-id : (t : Term (1+ n)) → wk (lift id) t ≡ t
wk-lift-id t = trans (wkVar-to-wk wkVar-lift-id t) (wk-id t)

-- The composition of weakenings is correct...
--
-- ...as action on variables.

wkVar-comp : (ρ : Wk m ℓ) (ρ′ : Wk ℓ n) (x : Fin n) → wkVar ρ (wkVar ρ′ x) ≡ wkVar (ρ • ρ′) x
wkVar-comp id       ρ′        x      = refl
wkVar-comp (step ρ) ρ′        x      = cong _+1 (wkVar-comp ρ ρ′ x)
wkVar-comp (lift ρ) id        x      = refl
wkVar-comp (lift ρ) (step ρ′) x      = cong _+1 (wkVar-comp ρ ρ′ x)
wkVar-comp (lift ρ) (lift ρ′) x0     = refl
wkVar-comp (lift ρ) (lift ρ′) (x +1) = cong _+1 (wkVar-comp ρ ρ′ x)

wkVar-comps : ∀ k → (ρ : Wk m ℓ) (ρ′ : Wk ℓ n) (x : Fin (k + n))
            → wkVar (liftn ρ k • liftn ρ′ k) x
            ≡ wkVar (liftn (ρ • ρ′) k) x
wkVar-comps 0      ρ ρ′ x      = refl
wkVar-comps (1+ n) ρ ρ′ x0     = refl
wkVar-comps (1+ n) ρ ρ′ (x +1) = cong _+1 (wkVar-comps n ρ ρ′ x)

-- ... as action on terms.

mutual
  wk-comp : (ρ : Wk m ℓ) (ρ′ : Wk ℓ n) (t : Term n) → wk ρ (wk ρ′ t) ≡ wk (ρ • ρ′) t
  wk-comp ρ ρ′ (var x) = cong var (wkVar-comp ρ ρ′ x)
  wk-comp ρ ρ′ (gen k ts) = cong (gen k) (wkGen-comp ρ ρ′ ts)

  wkGen-comp : (ρ : Wk m ℓ) (ρ′ : Wk ℓ n) → ∀ {bs} g
             → wkGen ρ (wkGen ρ′ g) ≡ wkGen {bs = bs} (ρ • ρ′) g
  wkGen-comp ρ ρ′ [] = refl
  wkGen-comp ρ ρ′ (_∷_ {b = b} t ts) =
    cong₂ _∷_ (trans (wk-comp (liftn ρ b) (liftn ρ′ b) t)
                     (wkVar-to-wk (wkVar-comps b ρ ρ′) t))
      (wkGen-comp ρ ρ′ ts)

-- The following lemmata are variations on the equality
--
--   wk1 ∘ ρ = lift ρ ∘ wk1.
--
-- Typing:  Γ∙A ≤ Γ ≤ Δ  <==>  Γ∙A ≤ Δ∙A ≤ Δ.

lift-step-comp : (ρ : Wk m n) → step id • ρ ≡ lift ρ • step id
lift-step-comp id       = refl
lift-step-comp (step ρ) = cong step (lift-step-comp ρ)
lift-step-comp (lift ρ) = refl

wk1-wk : (ρ : Wk m n) (t : Term n) → wk1 (wk ρ t) ≡ wk (step ρ) t
wk1-wk ρ t = wk-comp (step id) ρ t

lift-wk1 : (ρ : Wk m n) (t : Term n) → wk (lift ρ) (wk1 t) ≡ wk (step ρ) t
lift-wk1 pr A = trans (wk-comp (lift pr) (step id) A)
                      (sym (cong (λ x → wk x A) (lift-step-comp pr)))

wk1-wk≡lift-wk1 : (ρ : Wk m n) (t : Term n) → wk1 (wk ρ t) ≡ wk (lift ρ) (wk1 t)
wk1-wk≡lift-wk1 ρ t = trans (wk1-wk ρ t) (sym (lift-wk1 ρ t))

-- Substitution properties.

-- Two substitutions σ and σ′ are equal if they are pointwise equal,
-- i.e., agree on all variables.
--
--   ∀ x →  σ x ≡ σ′ x

-- If  σ = σ′  then  lift σ = lift σ′.

substVar-lift : (∀ x → σ x ≡ σ′ x) → ∀ x → liftSubst σ x ≡ liftSubst σ′ x

substVar-lift eq x0     = refl
substVar-lift eq (x +1) = cong wk1 (eq x)

substVar-lifts : (∀ x → σ x ≡ σ′ x) → ∀ n x → liftSubstn σ n x ≡ liftSubstn σ′ n x

substVar-lifts eq 0 x           = eq x
substVar-lifts eq (1+ n) x0     = refl
substVar-lifts eq (1+ n) (x +1) = cong wk1 (substVar-lifts eq n x)

-- If  σ = σ′  then  subst σ t = subst σ′ t.

mutual
  substVar-to-subst : ((x : Fin n) → σ x ≡ σ′ x)
                    → (t : Term n) → subst σ t ≡ subst σ′ t
  substVar-to-subst eq (var x)    = eq x
  substVar-to-subst eq (gen k ts) = cong (gen k) (substVar-to-substGen eq ts)

  substVar-to-substGen : ∀ {bs} → ((x : Fin n) → σ x ≡ σ′ x)
                       → ∀ g → substGen {bs = bs} σ g ≡ substGen {bs = bs} σ′ g
  substVar-to-substGen eq [] = refl
  substVar-to-substGen eq (_∷_ {b = b} t ts) =
    cong₂ _∷_ (substVar-to-subst (substVar-lifts eq b) t)
              (substVar-to-substGen eq ts)

-- lift id = id  (as substitutions)

subst-lift-id : (x : Fin (1+ n)) → (liftSubst idSubst) x ≡ idSubst x
subst-lift-id x0     = refl
subst-lift-id (x +1) = refl

subst-lifts-id : (n : Nat) → (x : Fin (n + m)) → (liftSubstn idSubst n) x ≡ idSubst x
subst-lifts-id 0 x = refl
subst-lifts-id (1+ n) x0 = refl
subst-lifts-id (1+ n) (x +1) = cong wk1 (subst-lifts-id n x)

-- Identity substitution.

mutual
  subst-id : (t : Term n) → subst idSubst t ≡ t
  subst-id (var x) = refl
  subst-id (gen k ts) = cong (gen k) (substGen-id ts)

  substGen-id : ∀ {bs} g → substGen {n} {n} {bs} idSubst g ≡ g
  substGen-id [] = refl
  substGen-id (_∷_ {b = b} t ts) =
    cong₂ _∷_ (trans (substVar-to-subst (subst-lifts-id b) t )
                     (subst-id t))
              (substGen-id ts)

-- Correctness of composition of weakening and substitution.

-- Composition of liftings is lifting of the composition.
-- lift ρ •ₛ lift σ = lift (ρ •ₛ σ)

subst-lift-•ₛ : ∀ t
              → subst (lift ρ •ₛ liftSubst σ) t
              ≡ subst (liftSubst (ρ •ₛ σ)) t
subst-lift-•ₛ =
  substVar-to-subst (λ { x0 → refl ; (x +1) → sym (wk1-wk≡lift-wk1 _ _)})

helper1 : (n : Nat) (x : Fin (1+ n + m)) →
      (lift (liftn ρ n) •ₛ liftSubst (liftSubstn σ n)) x ≡
      liftSubst (liftSubstn (ρ •ₛ σ) n) x
helper1 0      x0     = refl
helper1 0      (x +1) = sym (wk1-wk≡lift-wk1 _ _)
helper1 (1+ n) x0     = refl
helper1 (1+ n) (x +1) = trans (sym (wk1-wk≡lift-wk1 _ _)) (cong wk1 (helper1 n x))

subst-lifts-•ₛ : ∀ n t
              → subst (liftn ρ n •ₛ liftSubstn σ n) t
              ≡ subst (liftSubstn (ρ •ₛ σ) n) t
subst-lifts-•ₛ 0 t = refl
subst-lifts-•ₛ (1+ n) t = substVar-to-subst (helper1 n) t

-- lift σ ₛ• lift ρ = lift (σ ₛ• ρ)

subst-lift-ₛ• : ∀ t
              → subst (liftSubst σ ₛ• lift ρ) t
              ≡ subst (liftSubst (σ ₛ• ρ)) t
subst-lift-ₛ• = substVar-to-subst (λ { x0 → refl ; (x +1) → refl})

helper2 : (n : Nat) → (x : Fin (1+ n + m))
        → liftSubst (liftSubstn σ n) (wkVar (lift (liftn ρ n)) x) ≡
          liftSubst (liftSubstn (λ x₁ → σ (wkVar ρ x₁)) n) x
helper2 0 x0          = refl
helper2 0 (x +1)      = refl
helper2 (1+ n) x0     = refl
helper2 (1+ n) (x +1) = cong wk1 (helper2 n x)

subst-lifts-ₛ• : ∀ n t
              → subst (liftSubstn σ n ₛ• liftn ρ n) t
              ≡ subst (liftSubstn (σ ₛ• ρ) n) t
subst-lifts-ₛ• 0 t = refl
subst-lifts-ₛ• (1+ n) t = substVar-to-subst (helper2 n) t

-- wk ρ ∘ subst σ = subst (ρ •ₛ σ)

mutual
  wk-subst : ∀ t → wk ρ (subst σ t) ≡ subst (ρ •ₛ σ) t
  wk-subst (var x) = refl
  wk-subst (gen k ts) = cong (gen k) (wkGen-substGen ts)

  wkGen-substGen : ∀ {bs} t → wkGen ρ (substGen σ t) ≡ substGen {bs = bs} (ρ •ₛ σ) t
  wkGen-substGen [] = refl
  wkGen-substGen (_∷_ {b = b} t ts) =
    cong₂ _∷_ (trans (wk-subst t) ( subst-lifts-•ₛ b t)) (wkGen-substGen ts)

-- subst σ ∘ wk ρ = subst (σ •ₛ ρ)

mutual
  subst-wk : ∀ t → subst σ (wk ρ t) ≡ subst (σ ₛ• ρ) t
  subst-wk (var x) = refl
  subst-wk (gen k ts) = cong (gen k) (substGen-wkGen ts)

  substGen-wkGen : ∀ {bs} t → substGen σ (wkGen ρ t) ≡ substGen {bs = bs} (σ ₛ• ρ) t
  substGen-wkGen [] = refl
  substGen-wkGen (_∷_ {b = b} t ts) =
    cong₂ _∷_ (trans (subst-wk t) (subst-lifts-ₛ• b t)) (substGen-wkGen ts)

-- Composition of liftings is lifting of the composition.

wk-subst-lift : (G : Term (1+ n))
              → wk (lift ρ) (subst (liftSubst σ) G)
              ≡ subst (liftSubst (ρ •ₛ σ)) G
wk-subst-lift G = trans (wk-subst G) (subst-lift-•ₛ G)

-- Renaming with ρ is the same as substituting with ρ turned into a substitution.

wk≡subst : (ρ : Wk m n) (t : Term n) → wk ρ t ≡ subst (toSubst ρ) t
wk≡subst ρ t = trans (cong (wk ρ) (sym (subst-id t))) (wk-subst t)

-- Composition of substitutions.

-- Composition of liftings is lifting of the composition.

substCompLift : ∀ x
              → (liftSubst σ ₛ•ₛ liftSubst σ′) x
              ≡ (liftSubst (σ ₛ•ₛ σ′)) x
substCompLift                    x0    = refl
substCompLift {σ = σ} {σ′ = σ′} (x +1) = trans (subst-wk (σ′ x)) (sym (wk-subst (σ′ x)))

substCompLifts : ∀ n x
              → (liftSubstn σ n ₛ•ₛ liftSubstn σ′ n) x
              ≡ (liftSubstn (σ ₛ•ₛ σ′) n) x
substCompLifts                   0       x     = refl
substCompLifts                   (1+ n)  x0    = refl
substCompLifts {σ = σ} {σ′ = σ′} (1+ n) (x +1) =
  trans (substCompLift {σ = liftSubstn σ n} {σ′ = liftSubstn σ′ n} (x +1))
        (cong wk1 (substCompLifts n x))
-- Soundness of the composition of substitutions.

mutual
  substCompEq : ∀ (t : Term n)
              → subst σ (subst σ′ t) ≡ subst (σ ₛ•ₛ σ′) t
  substCompEq (var x) = refl
  substCompEq (gen k ts) = cong (gen k) (substGenCompEq ts)

  substGenCompEq : ∀ {bs} t
              → substGen σ (substGen σ′ t) ≡ substGen {bs = bs} (σ ₛ•ₛ σ′) t
  substGenCompEq [] = refl
  substGenCompEq (_∷_ {b = b} t ts) =
    cong₂ _∷_ (trans (substCompEq t) (substVar-to-subst (substCompLifts b) t))
              (substGenCompEq ts)

-- Weakening single substitutions.

-- Pulling apart a weakening composition in specific context _[a].

wk-comp-subst : ∀ {a} (ρ : Wk m ℓ) (ρ′ : Wk ℓ n) G
  → wk (lift (ρ • ρ′)) G [ a ] ≡ wk (lift ρ) (wk (lift ρ′) G) [ a ]

wk-comp-subst {a = a} ρ ρ′ G =
  cong (λ x → x [ a ]) (sym (wk-comp (lift ρ) (lift ρ′) G))

-- Pushing a weakening into a single substitution.
-- ρ (t[a]) = ((lift ρ) t)[ρ a]

wk-β : ∀ {a} t → wk ρ (t [ a ]) ≡ wk (lift ρ) t [ wk ρ a ]
wk-β t = trans (wk-subst t) (sym (trans (subst-wk t)
               (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) t)))

-- Pushing a weakening into a single shifting substitution.
-- If  ρ′ = lift ρ  then  ρ′(t[a]↑) = ρ′(t) [ρ′(a)]↑

wk-β↑ : ∀ {a} t → wk (lift ρ) (t [ a ]↑) ≡ wk (lift ρ) t [ wk (lift ρ) a ]↑
wk-β↑ t = trans (wk-subst t) (sym (trans (subst-wk t)
                (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) t)))

-- A specific equation on weakenings used for the reduction of natrec.

wk-β-natrec : ∀ (ρ : Wk m n )G
  → Π ℕ ▹ (Π wk (lift ρ) G ▹ wk (lift (lift ρ)) (wk1 (G [ suc (var x0) ]↑)))
  ≡ Π ℕ ▹ (wk (lift ρ) G ▹▹ wk (lift ρ) G [ suc (var x0) ]↑)
wk-β-natrec ρ G =
  cong₂ Π_▹_ refl (cong₂ Π_▹_ refl
    (trans (wk-comp (lift (lift ρ)) (step id)
                    (subst (consSubst (wk1Subst var) (suc (var x0))) G))
       (trans (wk-subst G) (sym (trans (wk-subst (wk (lift ρ) G))
         (trans (subst-wk G)
                (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) G)))))))

-- Composing a singleton substitution and a lifted substitution.
-- sg u ∘ lift σ = cons id u ∘ lift σ = cons σ u

substVarSingletonComp : ∀ {u} (x : Fin (1+ n))
  → (sgSubst u ₛ•ₛ liftSubst σ) x ≡ (consSubst σ u) x
substVarSingletonComp x0 = refl
substVarSingletonComp {σ = σ} (x +1) = trans (subst-wk (σ x)) (subst-id (σ x))

-- The same again, as action on a term t.

substSingletonComp : ∀ {a} t
  → subst (sgSubst a ₛ•ₛ liftSubst σ) t ≡ subst (consSubst σ a) t
substSingletonComp = substVar-to-subst substVarSingletonComp

-- A single substitution after a lifted substitution.
-- ((lift σ) G)[t] = (cons σ t)(G)

singleSubstComp : ∀ t (σ : Subst m n) G
                 → (subst (liftSubst σ) G) [ t ]
                 ≡ subst (consSubst σ t) G
singleSubstComp t σ G = trans (substCompEq G) (substSingletonComp G)

-- A single substitution after a lifted substitution (with weakening).
-- ((lift (ρ ∘ σ)) G)[t] = (cons (ρ ∘ σ) t)(G)

singleSubstWkComp : ∀ t (σ : Subst m n) G
               → wk (lift ρ) (subst (liftSubst σ) G) [ t ]
               ≡ subst (consSubst (ρ •ₛ σ) t) G
singleSubstWkComp t σ G =
  trans (cong (subst (consSubst var t))
              (trans (wk-subst G) (subst-lift-•ₛ G)))
        (trans (substCompEq G) (substSingletonComp G))

-- Pushing a substitution into a single substitution.

singleSubstLift : ∀ G t
                → subst σ (G [ t ])
                ≡ subst (liftSubst σ) G [ subst σ t ]
singleSubstLift G t =
  trans (substCompEq G)
        (trans (trans (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) G)
                      (sym (substSingletonComp G)))
               (sym (substCompEq G)))

-- More specific laws.

idWkLiftSubstLemma : ∀ (σ : Subst m n) G
  → wk (lift (step id)) (subst (liftSubst σ) G) [ var x0 ]
  ≡ subst (liftSubst σ) G
idWkLiftSubstLemma σ G =
  trans (singleSubstWkComp (var x0) σ G)
        (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) G)

substVarComp↑ : ∀ {t} (σ : Subst m n) x
  → (consSubst (wk1Subst idSubst) (subst (liftSubst σ) t) ₛ•ₛ liftSubst σ) x
  ≡ (liftSubst σ ₛ•ₛ consSubst (wk1Subst idSubst) t) x
substVarComp↑ σ x0 = refl
substVarComp↑ σ (x +1) = trans (subst-wk (σ x)) (sym (wk≡subst (step id) (σ x)))

singleSubstLift↑ : ∀ (σ : Subst m n) G t
                 → subst (liftSubst σ) (G [ t ]↑)
                 ≡ subst (liftSubst σ) G [ subst (liftSubst σ) t ]↑
singleSubstLift↑ σ G t =
  trans (substCompEq G)
        (sym (trans (substCompEq G) (substVar-to-subst (substVarComp↑ σ) G)))

substConsComp : ∀ {t G}
       → subst (consSubst (λ x → σ (x +1)) (subst (tail σ) t)) G
       ≡ subst σ (subst (consSubst (λ x → var (x +1)) (wk1 t)) G)
substConsComp {t = t} {G = G} =
  trans (substVar-to-subst (λ { x0 → sym (subst-wk t) ; (x +1) → refl }) G)
        (sym (substCompEq G))

wkSingleSubstId : (F : Term (1+ n)) → (wk (lift (step id)) F) [ var x0 ] ≡ F
wkSingleSubstId F =
  trans (subst-wk F)
        (trans (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) F)
               (subst-id F))

cons-wk-subst : ∀ (ρ : Wk m n) (σ : Subst n ℓ) a t
       → subst (sgSubst a ₛ• lift ρ ₛ•ₛ liftSubst σ) t
       ≡ subst (consSubst (ρ •ₛ σ) a) t
cons-wk-subst ρ σ a = substVar-to-subst
  (λ { x0 → refl
     ; (x +1) → trans (subst-wk (σ x)) (sym (wk≡subst ρ (σ x))) })

natrecSucCaseLemma : (x : Fin (1+ n))
  → (step id •ₛ consSubst (wk1Subst idSubst) (suc (var x0)) ₛ•ₛ liftSubst σ) x
  ≡ (liftSubst (liftSubst σ) ₛ• step id ₛ•ₛ consSubst (wk1Subst idSubst) (suc (var x0))) x
natrecSucCaseLemma x0 = refl
natrecSucCaseLemma {σ = σ} (x +1) =
  trans (subst-wk (σ x))
           (sym (trans (wk1-wk (step id) _)
                             (wk≡subst (step (step id)) (σ x))))

natrecSucCase : ∀ (σ : Subst m n) F
  → Π ℕ ▹ (Π subst (liftSubst σ) F
                ▹ subst (liftSubst (liftSubst σ)) (wk1 (F [ suc (var x0) ]↑)))
  ≡ Π ℕ ▹ (subst (liftSubst σ) F ▹▹ subst (liftSubst σ) F [ suc (var x0) ]↑)
natrecSucCase σ F =
  cong₂ Π_▹_ refl
    (cong₂ Π_▹_ refl
       (trans (trans (subst-wk (F [ suc (var x0) ]↑))
                           (substCompEq F))
                 (sym (trans (wk-subst (subst (liftSubst σ) F))
                                   (trans (substCompEq F)
                                             (substVar-to-subst natrecSucCaseLemma F))))))

natrecIrrelevantSubstLemma : ∀ F z s m (σ : Subst ℓ n) (x : Fin (1+ n))
  → (sgSubst (natrec (subst (liftSubst σ) F) (subst σ z) (subst σ s) m)
     ₛ•ₛ liftSubst (sgSubst m)
     ₛ•ₛ liftSubst (liftSubst σ)
     ₛ•  step id
     ₛ•ₛ consSubst (tail idSubst) (suc (var x0))) x
  ≡ (consSubst σ (suc m)) x
natrecIrrelevantSubstLemma F z s m σ x0 =
  cong suc (trans (subst-wk m) (subst-id m))
natrecIrrelevantSubstLemma F z s m σ (x +1) =
  trans (subst-wk (wk (step id) (σ x)))
           (trans (subst-wk (σ x))
                     (subst-id (σ x)))

natrecIrrelevantSubst : ∀ F z s m (σ : Subst ℓ n)
  → subst (consSubst σ (suc m)) F
  ≡ subst (liftSubst (sgSubst m))
          (subst (liftSubst (liftSubst σ))
                 (wk1 (F [ suc (var x0) ]↑)))
                   [ natrec (subst (liftSubst σ) F) (subst σ z) (subst σ s) m ]
natrecIrrelevantSubst F z s m σ =
  sym (trans (substCompEq (subst (liftSubst (liftSubst σ))
        (wk (step id)
         (subst (consSubst (tail idSubst) (suc (var x0))) F))))
         (trans (substCompEq (wk (step id)
        (subst (consSubst (tail idSubst) (suc (var x0))) F)))
        (trans
           (subst-wk (subst (consSubst (tail idSubst) (suc (var x0))) F))
           (trans (substCompEq F)
                     (substVar-to-subst (natrecIrrelevantSubstLemma F z s m σ) F)))))

natrecIrrelevantSubstLemma′ : ∀ F z s n (x : Fin (1+ m))
  → (sgSubst (natrec F z s n)
     ₛ•ₛ liftSubst (sgSubst n)
     ₛ•  step id
     ₛ•ₛ consSubst (tail idSubst) (suc (var x0))) x
  ≡ (consSubst var (suc n)) x
natrecIrrelevantSubstLemma′ F z s n x0 =
  cong suc (trans (subst-wk n) (subst-id n))
natrecIrrelevantSubstLemma′ F z s n (x +1) = refl

natrecIrrelevantSubst′ : ∀ (F : Term (1+ m)) z s n
  → subst (liftSubst (sgSubst n))
      (wk1 (F [ suc (var x0) ]↑))
      [ natrec F z s n ]
  ≡ F [ suc n ]
natrecIrrelevantSubst′ F z s n =
  trans (substCompEq (wk (step id)
                         (subst (consSubst (tail idSubst) (suc (var x0))) F)))
        (trans (subst-wk (subst (consSubst (tail idSubst) (suc (var x0))) F))
               (trans (substCompEq F)
                      (substVar-to-subst (natrecIrrelevantSubstLemma′ F z s n) F)))

cons0wkLift1-id : ∀ (σ : Subst m n) G
    → subst (sgSubst (var x0))
            (wk (lift (step id)) (subst (liftSubst σ) G))
    ≡ subst (liftSubst σ) G
cons0wkLift1-id σ G =
  trans (subst-wk (subst (liftSubst σ) G))
        (trans (substVar-to-subst (λ { x0 → refl ; (x +1) → refl })
                                  (subst (liftSubst σ) G))
               (subst-id (subst (liftSubst σ) G)))

substConsId : ∀ {t} G
        → subst (consSubst σ (subst σ t)) G
        ≡ subst σ (subst (sgSubst t) G)
substConsId G =
  sym (trans (substCompEq G)
             (substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) G))

substConsTailId : ∀ {G t}
                → subst (consSubst (tail σ) (subst σ t)) G
                ≡ subst σ (subst (consSubst (tail idSubst) t) G)
substConsTailId {G = G} =
  trans (substVar-to-subst (λ { x0 → refl
                            ; (x +1) → refl }) G)
        (sym (substCompEq G))

substConcatSingleton′ : ∀ {a} t
                      → subst (σ ₛ•ₛ sgSubst a) t
                      ≡ subst (consSubst σ (subst σ a)) t
substConcatSingleton′ t = substVar-to-subst (λ { x0 → refl ; (x +1) → refl}) t

wk1-tailId : (t : Term n) → wk1 t ≡ subst (tail idSubst) t
wk1-tailId t = trans (sym (subst-id (wk1 t))) (subst-wk t)

wk1-sgSubst : ∀ (t : Term n) t' → (wk1 t) [ t' ] ≡ t
wk1-sgSubst t t' rewrite wk1-tailId t =
  let substVar-sgSubst-tail : ∀ a n → (sgSubst a ₛ•ₛ tail idSubst) n ≡ idSubst n
      substVar-sgSubst-tail a n = refl
  in  trans (trans
        (substCompEq t)
        (substVar-to-subst (substVar-sgSubst-tail t') t))
      (subst-id t)