------------------------------------------------------------------------
-- Typing and reduction relations
------------------------------------------------------------------------

open import Definition.Typed.Restrictions
open import Graded.Modality

module Definition.Typed
  {ā„“} {M : Set ā„“}
  {š•„ : Modality M}
  (R : Type-restrictions š•„)
  where

open Type-restrictions R

open import Definition.Typed.Variant

open import Definition.Untyped M
import Definition.Untyped.Erased š•„ as Erased
open import Definition.Untyped.Whnf M type-variant

open import Tools.Fin
open import Tools.Nat
open import Tools.Product
import Tools.PropositionalEquality as PE
open import Tools.Relation
import Tools.Vec as Vec

infix 24 āˆ™_

private
  variable
    m n l l₁ lā‚‚ α : Nat
    āˆ‡ āˆ‡ā€² : DCon (Term 0) _
    φ φ′ : Unfolding _
    ω : Opacity _
    Ī“ : Con Term _
    A A₁ Aā‚‚ A′ B B₁ Bā‚‚ C E F F′ G H : Term _
    a f g n′ s s′ t t₁ tā‚‚ t′ u u₁ uā‚‚ u′ v v₁ vā‚‚ v′ w w₁ wā‚‚ w′ z z′ :
      Term _
    σ Ļƒā€² : Subst _ _
    x : Fin _
    p p′ q q′ r : M
    b : BinderMode
    k : Strength

-- Well-typed variables
data _∷_∈_ : (x : Fin n) (A : Term n) (Ī“ : Con Term n) → Set ā„“ where
  here  :                 x0 ∷ wk1 A ∈ Ī“ āˆ™ A
  there : x ∷ A ∈ Ī“ → (x +1) ∷ wk1 A ∈ Ī“ āˆ™ B

opaque
  unfolding _āŠ”įµ’_

  infixl 5 _āŠ”įµ’įµ—_

  -- Definition context unfolding.

  _āŠ”įµ’įµ—_ : Unfolding n → Unfolding n → Unfolding n
  _āŠ”įµ’įµ—_ with unfolding-mode
  … | explicit   = Ī» φ _ → φ
  … | transitive = _āŠ”įµ’_

opaque

  -- Transparentisation.

  Trans : Unfolding n → DCon (Term 0) n → DCon (Term 0) n
  Trans _ ε =
    ε
  Trans φ (āˆ‡ āˆ™āŸØ tra ⟩[ t ∷ A ]) =
    Trans (Vec.tail φ) āˆ‡ āˆ™āŸØ tra ⟩[ t ∷ A ]
  Trans (φ ⁰) (āˆ‡ āˆ™āŸØ ω ⟩[ t ∷ A ]) =
    Trans φ āˆ‡ āˆ™āŸØ ω ⟩[ t ∷ A ]
  Trans (φ ¹) (āˆ‡ āˆ™āŸØ opa φ′ ⟩[ t ∷ A ]) =
    Trans (φ āŠ”įµ’įµ— φ′) āˆ‡ āˆ™āŸØ tra ⟩[ t ∷ A ]

mutual

  -- Well-formed definition contexts.

  infix 4 »_

  data Ā»_ : DCon (Term 0) m → Set ā„“ where
    ε          : » ε
    āˆ™įµ’āŸØ_⟩[_∷_] : Opacity-allowed
               → Trans φ āˆ‡ Ā» ε ⊢ t ∷ A
               → āˆ‡ Ā» ε ⊢ A
               → Ā» āˆ‡ āˆ™āŸØ opa φ ⟩[ t ∷ A ]
    āˆ™įµ—[_]      : āˆ‡ Ā» ε ⊢ t ∷ A
               → Ā» āˆ‡ āˆ™āŸØ tra ⟩[ t ∷ A ]

  -- Well-formed contexts.

  infix 4 _»⊢_

  data _»⊢_ (āˆ‡ : DCon (Term 0) m) : Con Term n → Set ā„“ where
    ε  : Ā» āˆ‡       → āˆ‡ »⊢ ε
    āˆ™_ : āˆ‡ Ā» Ī“ ⊢ A → āˆ‡ »⊢ Ī“ āˆ™ A

  pattern εε = ε ε

  -- A variant of _»⊢_.

  infix 4 ⊢_

  ⊢_ : Cons m n → Set ā„“
  ⊢ āˆ‡ Ā» Ī“ = āˆ‡ »⊢ Ī“

  -- Well-formed types.

  infix 4 _⊢_

  data _⊢_ (Ī“ : Cons m n) : Term n → Set ā„“ where
    Uā±¼     : ⊢ Ī“ → Ī“ ⊢ U l
    univ   : Ī“ ⊢ A ∷ U l
           → Ī“ ⊢ A
    Emptyā±¼ : ⊢ Ī“ → Ī“ ⊢ Empty
    Unitā±¼  : ⊢ Ī“ → Unit-allowed k → Ī“ ⊢ Unit k l
    ΠΣⱼ    : Ī“ Ā»āˆ™ A ⊢ B
           → ΠΣ-allowed b p q
           → Ī“ ⊢ ΠΣ⟨ b ⟩ p , q ā–· A ā–¹ B
    ā„•ā±¼     : ⊢ Ī“ → Ī“ ⊢ ā„•
    Idā±¼    : Ī“ ⊢ A
           → Ī“ ⊢ t ∷ A
           → Ī“ ⊢ u ∷ A
           → Ī“ ⊢ Id A t u

  -- Well-typed terms.

  infix 4 _⊢_∷_

  data _⊢_∷_ (Ī“ : Cons m n) : Term n → Term n → Set ā„“ where
    conv      : Ī“ ⊢ t ∷ A
              → Ī“ ⊢ A ≔ B
              → Ī“ ⊢ t ∷ B

    var       : ⊢ Ī“
              → x ∷ A ∈ Ī“ .vars
              → Ī“ ⊢ var x ∷ A
    defn      : ⊢ Ī“
              → α ā†¦āˆ· A′ ∈ Ī“ .defs
              → A PE.≔ wk wkā‚€ A′
              → Ī“ ⊢ defn α ∷ A

    Uā±¼        : ⊢ Ī“ → Ī“ ⊢ U l ∷ U (1+ l)

    Emptyā±¼    : ⊢ Ī“ → Ī“ ⊢ Empty ∷ U 0
    emptyrecā±¼ : Ī“ ⊢ A → Ī“ ⊢ t ∷ Empty → Ī“ ⊢ emptyrec p A t ∷ A

    Unitā±¼     : ⊢ Ī“ → Unit-allowed k → Ī“ ⊢ Unit k l ∷ U l
    starā±¼     : ⊢ Ī“ → Unit-allowed k → Ī“ ⊢ star k l ∷ Unit k l
    unitrecā±¼  : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A
              → Ī“ ⊢ t ∷ UnitŹ· l
              → Ī“ ⊢ u ∷ A [ starŹ· l ]ā‚€
              → UnitŹ·-allowed
              → Ī“ ⊢ unitrec l p q A t u ∷ A [ t ]ā‚€

    ΠΣⱼ       : Ī“ ⊢ F ∷ U l₁
              → Ī“ Ā»āˆ™ F ⊢ G ∷ U lā‚‚
              → ΠΣ-allowed b p q
              → Ī“ ⊢ ΠΣ⟨ b ⟩ p , q ā–· F ā–¹ G ∷ U (l₁ āŠ”įµ˜ lā‚‚)

    lamā±¼      : Ī“ Ā»āˆ™ F ⊢ G
              → Ī“ Ā»āˆ™ F ⊢ t ∷ G
              → Ī -allowed p q
              → Ī“ ⊢ lam p t ∷ Ī  p , q ā–· F ā–¹ G
    _∘ⱼ_      : Ī“ ⊢ t ∷ Ī  p , q ā–· F ā–¹ G
              → Ī“ ⊢ u ∷ F
              → Ī“ ⊢ t ∘⟨ p ⟩ u ∷ G [ u ]ā‚€

    prodā±¼     : Ī“ Ā»āˆ™ F ⊢ G
              → Ī“ ⊢ t ∷ F
              → Ī“ ⊢ u ∷ G [ t ]ā‚€
              → Ī£-allowed k p q
              → Ī“ ⊢ prod k p t u ∷ Σ⟨ k ⟩ p , q ā–· F ā–¹ G
    fstā±¼      : Ī“ Ā»āˆ™ F ⊢ G
              → Ī“ ⊢ t ∷ Σˢ p , q ā–· F ā–¹ G
              → Ī“ ⊢ fst p t ∷ F
    sndā±¼      : Ī“ Ā»āˆ™ F ⊢ G
              → Ī“ ⊢ t ∷ Σˢ p , q ā–· F ā–¹ G
              → Ī“ ⊢ snd p t ∷ G [ fst p t ]ā‚€
    prodrecā±¼  : Ī“ Ā»āˆ™ (Σʷ p , q′ ā–· F ā–¹ G) ⊢ A
              → Ī“ ⊢ t ∷ Σʷ p , q′ ā–· F ā–¹ G
              → Ī“ Ā»āˆ™ F Ā»āˆ™ G ⊢ u ∷ A [ prodŹ· p (var x1) (var x0) ]↑²
              → Σʷ-allowed p q′
              → Ī“ ⊢ prodrec r p q A t u ∷ A [ t ]ā‚€

    ā„•ā±¼        : ⊢ Ī“ → Ī“ ⊢ ā„• ∷ U 0
    zeroā±¼     : ⊢ Ī“
              → Ī“ ⊢ zero ∷ ā„•
    sucā±¼      : āˆ€ {n}
              → Ī“ ⊢     n ∷ ā„•
              → Ī“ ⊢ suc n ∷ ā„•
    natrecā±¼   : āˆ€ {n}
              → Ī“ ⊢ z ∷ A [ zero ]ā‚€
              → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ∷ A [ suc (var x1) ]↑²
              → Ī“ ⊢ n ∷ ā„•
              → Ī“ ⊢ natrec p q r A z s n ∷ A [ n ]ā‚€

    Idā±¼       : Ī“ ⊢ A ∷ U l
              → Ī“ ⊢ t ∷ A
              → Ī“ ⊢ u ∷ A
              → Ī“ ⊢ Id A t u ∷ U l
    rflā±¼      : Ī“ ⊢ t ∷ A
              → Ī“ ⊢ rfl ∷ Id A t t
    Jā±¼        : Ī“ ⊢ t ∷ A
              → Ī“ Ā»āˆ™ A Ā»āˆ™ Id (wk1 A) (wk1 t) (var x0) ⊢ B
              → Ī“ ⊢ u ∷ B [ t , rfl ]₁₀
              → Ī“ ⊢ v ∷ A
              → Ī“ ⊢ w ∷ Id A t v
              → Ī“ ⊢ J p q A t B u v w ∷ B [ v , w ]₁₀
    Kā±¼        : Ī“ Ā»āˆ™ Id A t t ⊢ B
              → Ī“ ⊢ u ∷ B [ rfl ]ā‚€
              → Ī“ ⊢ v ∷ Id A t t
              → K-allowed
              → Ī“ ⊢ K p A t B u v ∷ B [ v ]ā‚€
    []-congā±¼  : Ī“ ⊢ A
              → Ī“ ⊢ t ∷ A
              → Ī“ ⊢ u ∷ A
              → Ī“ ⊢ v ∷ Id A t u
              → []-cong-allowed k
              → let open Erased k in
                Ī“ ⊢ []-cong k A t u v ∷
                  Id (Erased A) ([ t ]) ([ u ])

  -- Type equality.

  infix 4 _⊢_≔_

  data _⊢_≔_ (Ī“ : Cons m n) : Term n → Term n → Set ā„“ where
    refl   : Ī“ ⊢ A
           → Ī“ ⊢ A ≔ A
    sym    : Ī“ ⊢ A ≔ B
           → Ī“ ⊢ B ≔ A
    trans  : Ī“ ⊢ A ≔ B
           → Ī“ ⊢ B ≔ C
           → Ī“ ⊢ A ≔ C
    univ   : Ī“ ⊢ A ≔ B ∷ U l
           → Ī“ ⊢ A ≔ B
    ΠΣ-cong
           : Ī“ ⊢ F ≔ H
           → Ī“ Ā»āˆ™ F ⊢ G ≔ E
           → ΠΣ-allowed b p q
           → Ī“ ⊢ ΠΣ⟨ b ⟩ p , q ā–· F ā–¹ G ≔ ΠΣ⟨ b ⟩ p , q ā–· H ā–¹ E
    Id-cong
           : Ī“ ⊢ A₁ ≔ Aā‚‚
           → Ī“ ⊢ t₁ ≔ tā‚‚ ∷ A₁
           → Ī“ ⊢ u₁ ≔ uā‚‚ ∷ A₁
           → Ī“ ⊢ Id A₁ t₁ u₁ ≔ Id Aā‚‚ tā‚‚ uā‚‚

  -- Term equality.

  infix 4 _⊢_≔_∷_

  data _⊢_≔_∷_ (Ī“ : Cons m n) : Term n → Term n → Term n → Set ā„“ where
    conv          : Ī“ ⊢ t ≔ u ∷ A
                  → Ī“ ⊢ A ≔ B
                  → Ī“ ⊢ t ≔ u ∷ B

    refl          : Ī“ ⊢ t ∷ A
                  → Ī“ ⊢ t ≔ t ∷ A
    sym           : Ī“ ⊢ A
                  → Ī“ ⊢ t ≔ u ∷ A
                  → Ī“ ⊢ u ≔ t ∷ A
    trans         : Ī“ ⊢ t ≔ u ∷ A
                  → Ī“ ⊢ u ≔ v ∷ A
                  → Ī“ ⊢ t ≔ v ∷ A

    Ī“-red         : ⊢ Ī“
                  → α ↦ t′ ∷ A′ ∈ Ī“ .defs
                  → A PE.≔ wk wkā‚€ A′
                  → t PE.≔ wk wkā‚€ t′
                  → Ī“ ⊢ defn α ≔ t ∷ A

    emptyrec-cong : Ī“ ⊢ A ≔ B
                  → Ī“ ⊢ t ≔ u ∷ Empty
                  → Ī“ ⊢ emptyrec p A t ≔ emptyrec p B u ∷ A

    Ī·-unit        : Ī“ ⊢ t ∷ Unit k l
                  → Ī“ ⊢ t′ ∷ Unit k l
                  → Unit-with-Ī· k
                  → Ī“ ⊢ t ≔ t′ ∷ Unit k l

    unitrec-cong  : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A ≔ A′
                  → Ī“ ⊢ t ≔ t′ ∷ UnitŹ· l
                  → Ī“ ⊢ u ≔ u′ ∷ A [ starŹ· l ]ā‚€
                  → UnitŹ·-allowed
                  → ¬ UnitŹ·-Ī·
                  → Ī“ ⊢ unitrec l p q A t u ≔ unitrec l p q A′ t′ u′ ∷
                      A [ t ]ā‚€
    unitrec-β     : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A
                  → Ī“ ⊢ u ∷ A [ starŹ· l ]ā‚€
                  → UnitŹ·-allowed
                  → ¬ UnitŹ·-Ī·
                  → Ī“ ⊢ unitrec l p q A (starŹ· l) u ≔ u ∷ A [ starŹ· l ]ā‚€
    unitrec-β-Ī·   : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A
                  → Ī“ ⊢ t ∷ UnitŹ· l
                  → Ī“ ⊢ u ∷ A [ starŹ· l ]ā‚€
                  → UnitŹ·-allowed
                  → UnitŹ·-Ī·
                  → Ī“ ⊢ unitrec l p q A t u ≔ u ∷ A [ t ]ā‚€

    ΠΣ-cong       : Ī“ ⊢ F ≔ H ∷ U l₁
                  → Ī“ Ā»āˆ™ F ⊢ G ≔ E ∷ U lā‚‚
                  → ΠΣ-allowed b p q
                  → Ī“ ⊢ ΠΣ⟨ b ⟩ p , q ā–· F ā–¹ G ≔ ΠΣ⟨ b ⟩ p , q ā–· H ā–¹ E ∷
                      U (l₁ āŠ”įµ˜ lā‚‚)

    app-cong      : āˆ€ {b}
                  → Ī“ ⊢ f ≔ g ∷ Ī  p , q ā–· F ā–¹ G
                  → Ī“ ⊢ a ≔ b ∷ F
                  → Ī“ ⊢ f ∘⟨ p ⟩ a ≔ g ∘⟨ p ⟩ b ∷ G [ a ]ā‚€
    β-red         : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ Ā»āˆ™ F ⊢ t ∷ G
                  → Ī“ ⊢ a ∷ F
                  → p PE.≔ p′
                  → -- Note that q can be chosen arbitrarily.
                    Ī -allowed p q
                  → Ī“ ⊢ lam p t ∘⟨ p′ ⟩ a ≔ t [ a ]ā‚€ ∷ G [ a ]ā‚€
    Ī·-eq          : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ f ∷ Ī  p , q ā–· F ā–¹ G
                  → Ī“ ⊢ g ∷ Ī  p , q ā–· F ā–¹ G
                  → Ī“ Ā»āˆ™ F ⊢ wk1 f ∘⟨ p ⟩ var x0 ≔ wk1 g ∘⟨ p ⟩ var x0 ∷ G
                  → Ī -allowed p q
                  → Ī“ ⊢ f ≔ g ∷ Ī  p , q ā–· F ā–¹ G

    prod-cong     : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ t ≔ t′ ∷ F
                  → Ī“ ⊢ u ≔ u′ ∷ G [ t ]ā‚€
                  → Ī£-allowed k p q
                  → Ī“ ⊢ prod k p t u ≔ prod k p t′ u′ ∷ Σ⟨ k ⟩ p , q ā–· F ā–¹ G

    fst-cong      : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ t ≔ t′ ∷ Σˢ p , q ā–· F ā–¹ G
                  → Ī“ ⊢ fst p t ≔ fst p t′ ∷ F
    Ī£-β₁          : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ t ∷ F
                  → Ī“ ⊢ u ∷ G [ t ]ā‚€
                  → p PE.≔ p′
                  → -- Note that q can be chosen arbitrarily.
                    Σˢ-allowed p q
                  → Ī“ ⊢ fst p (prodĖ¢ p′ t u) ≔ t ∷ F
    snd-cong      : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ t ≔ u ∷ Σˢ p , q ā–· F ā–¹ G
                  → Ī“ ⊢ snd p t ≔ snd p u ∷ G [ fst p t ]ā‚€
    Ī£-β₂          : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ t ∷ F
                  → Ī“ ⊢ u ∷ G [ t ]ā‚€
                  → p PE.≔ p′
                  → -- Note that q can be chosen arbitrarily.
                    Σˢ-allowed p q
                  → Ī“ ⊢ snd p (prodĖ¢ p′ t u) ≔ u ∷ G [ fst p (prodĖ¢ p′ t u) ]ā‚€
    Ī£-Ī·           : Ī“ Ā»āˆ™ F ⊢ G
                  → Ī“ ⊢ t ∷ Σˢ p , q ā–· F ā–¹ G
                  → Ī“ ⊢ u ∷ Σˢ p , q ā–· F ā–¹ G
                  → Ī“ ⊢ fst p t ≔ fst p u ∷ F
                  → Ī“ ⊢ snd p t ≔ snd p u ∷ G [ fst p t ]ā‚€
                  → Σˢ-allowed p q
                  → Ī“ ⊢ t ≔ u ∷ Σˢ p , q ā–· F ā–¹ G

    prodrec-cong  : Ī“ Ā»āˆ™ Σʷ p , q′ ā–· F ā–¹ G ⊢ A ≔ A′
                  → Ī“ ⊢ t ≔ t′ ∷ Σʷ p , q′ ā–· F ā–¹ G
                  → Ī“ Ā»āˆ™ F Ā»āˆ™ G ⊢ u ≔ u′ ∷
                      A [ prodŹ· p (var x1) (var x0) ]↑²
                  → Σʷ-allowed p q′
                  → Ī“ ⊢ prodrec r p q A t u ≔ prodrec r p q A′ t′ u′ ∷ A [ t ]ā‚€
    prodrec-β     : Ī“ Ā»āˆ™ Σʷ p , q′ ā–· F ā–¹ G ⊢ A
                  → Ī“ ⊢ t ∷ F
                  → Ī“ ⊢ t′ ∷ G [ t ]ā‚€
                  → Ī“ Ā»āˆ™ F Ā»āˆ™ G ⊢ u ∷ A [ prodŹ· p (var x1) (var x0) ]↑²
                  → p PE.≔ p′
                  → Σʷ-allowed p q′
                  → Ī“ ⊢ prodrec r p q A (prodŹ· p′ t t′) u ≔
                        u [ t , t′ ]₁₀ ∷ A [ prodŹ· p′ t t′ ]ā‚€

    suc-cong      : āˆ€ {n}
                  → Ī“ ⊢ t ≔ n ∷ ā„•
                  → Ī“ ⊢ suc t ≔ suc n ∷ ā„•
    natrec-cong   : āˆ€ {n}
                  → Ī“ Ā»āˆ™ ā„• ⊢ A ≔ A′
                  → Ī“ ⊢ z ≔ z′ ∷ A [ zero ]ā‚€
                  → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ≔ s′ ∷ A [ suc (var x1) ]↑²
                  → Ī“ ⊢ n ≔ n′ ∷ ā„•
                  → Ī“ ⊢ natrec p q r A z s n ≔
                      natrec p q r A′ z′ s′ n′ ∷ A [ n ]ā‚€
    natrec-zero   : Ī“ ⊢ z ∷ A [ zero ]ā‚€
                  → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ∷ A [ suc (var x1) ]↑²
                  → Ī“ ⊢ natrec p q r A z s zero ≔ z ∷ A [ zero ]ā‚€
    natrec-suc    : āˆ€ {n}
                  → Ī“ ⊢ z ∷ A [ zero ]ā‚€
                  → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ∷ A [ suc (var x1) ]↑²
                  → Ī“ ⊢ n ∷ ā„•
                  → Ī“ ⊢ natrec p q r A z s (suc n) ≔
                      s [ n , natrec p q r A z s n ]₁₀ ∷ A [ suc n ]ā‚€

    Id-cong       : Ī“ ⊢ A₁ ≔ Aā‚‚ ∷ U l
                  → Ī“ ⊢ t₁ ≔ tā‚‚ ∷ A₁
                  → Ī“ ⊢ u₁ ≔ uā‚‚ ∷ A₁
                  → Ī“ ⊢ Id A₁ t₁ u₁ ≔ Id Aā‚‚ tā‚‚ uā‚‚ ∷ U l
    J-cong        : Ī“ ⊢ A₁ ≔ Aā‚‚
                  → Ī“ ⊢ t₁ ∷ A₁
                  → Ī“ ⊢ t₁ ≔ tā‚‚ ∷ A₁
                  → Ī“ Ā»āˆ™ A₁ Ā»āˆ™ Id (wk1 A₁) (wk1 t₁) (var x0) ⊢ B₁ ≔ Bā‚‚
                  → Ī“ ⊢ u₁ ≔ uā‚‚ ∷ B₁ [ t₁ , rfl ]₁₀
                  → Ī“ ⊢ v₁ ≔ vā‚‚ ∷ A₁
                  → Ī“ ⊢ w₁ ≔ wā‚‚ ∷ Id A₁ t₁ v₁
                  → Ī“ ⊢ J p q A₁ t₁ B₁ u₁ v₁ w₁ ≔
                        J p q Aā‚‚ tā‚‚ Bā‚‚ uā‚‚ vā‚‚ wā‚‚ ∷ B₁ [ v₁ , w₁ ]₁₀
    J-β           : Ī“ ⊢ t ∷ A
                  → Ī“ Ā»āˆ™ A Ā»āˆ™ Id (wk1 A) (wk1 t) (var x0) ⊢ B
                  → Ī“ ⊢ u ∷ B [ t , rfl ]₁₀
                  → t PE.≔ t′
                  → Ī“ ⊢ J p q A t B u t′ rfl ≔ u ∷ B [ t , rfl ]₁₀
    K-cong        : Ī“ ⊢ A₁ ≔ Aā‚‚
                  → Ī“ ⊢ t₁ ≔ tā‚‚ ∷ A₁
                  → Ī“ Ā»āˆ™ Id A₁ t₁ t₁ ⊢ B₁ ≔ Bā‚‚
                  → Ī“ ⊢ u₁ ≔ uā‚‚ ∷ B₁ [ rfl ]ā‚€
                  → Ī“ ⊢ v₁ ≔ vā‚‚ ∷ Id A₁ t₁ t₁
                  → K-allowed
                  → Ī“ ⊢ K p A₁ t₁ B₁ u₁ v₁ ≔ K p Aā‚‚ tā‚‚ Bā‚‚ uā‚‚ vā‚‚ ∷
                      B₁ [ v₁ ]ā‚€
    K-β           : Ī“ Ā»āˆ™ Id A t t ⊢ B
                  → Ī“ ⊢ u ∷ B [ rfl ]ā‚€
                  → K-allowed
                  → Ī“ ⊢ K p A t B u rfl ≔ u ∷ B [ rfl ]ā‚€
    []-cong-cong  : Ī“ ⊢ A₁ ≔ Aā‚‚
                  → Ī“ ⊢ t₁ ≔ tā‚‚ ∷ A₁
                  → Ī“ ⊢ u₁ ≔ uā‚‚ ∷ A₁
                  → Ī“ ⊢ v₁ ≔ vā‚‚ ∷ Id A₁ t₁ u₁
                  → []-cong-allowed k
                  → let open Erased k in
                    Ī“ ⊢ []-cong k A₁ t₁ u₁ v₁ ≔ []-cong k Aā‚‚ tā‚‚ uā‚‚ vā‚‚ ∷
                      Id (Erased A₁) ([ t₁ ]) ([ u₁ ])
    []-cong-β     : Ī“ ⊢ t ∷ A
                  → t PE.≔ t′
                  → []-cong-allowed k
                  → let open Erased k in
                    Ī“ ⊢ []-cong k A t t′ rfl ≔ rfl ∷
                      Id (Erased A) ([ t ]) ([ t′ ])
    equality-reflection
                  : Equality-reflection
                  → Ī“ ⊢ Id A t u
                  → Ī“ ⊢ v ∷ Id A t u
                  → Ī“ ⊢ t ≔ u ∷ A

-- Term reduction.

infix 4 _⊢_⇒_∷_

data _⊢_⇒_∷_ (Ī“ : Cons m n) : Term n → Term n → Term n → Set ā„“ where
  conv           : Ī“ ⊢ t ⇒ u ∷ A
                 → Ī“ ⊢ A ≔ B
                 → Ī“ ⊢ t ⇒ u ∷ B

  Ī“-red          : ⊢ Ī“
                 → α ↦ t′ ∷ A′ ∈ Ī“ .defs
                 → A PE.≔ wk wkā‚€ A′
                 → t PE.≔ wk wkā‚€ t′
                 → Ī“ ⊢ defn α ⇒ t ∷ A

  emptyrec-subst : āˆ€ {n}
                 → Ī“ ⊢ A
                 → Ī“     ⊢ n ⇒ n′ ∷ Empty
                 → Ī“     ⊢ emptyrec p A n ⇒ emptyrec p A n′ ∷ A

  unitrec-subst : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A
                → Ī“ ⊢ u ∷ A [ starŹ· l ]ā‚€
                → Ī“ ⊢ t ⇒ t′ ∷ UnitŹ· l
                → UnitŹ·-allowed
                → ¬ UnitŹ·-Ī·
                → Ī“ ⊢ unitrec l p q A t u ⇒ unitrec l p q A t′ u ∷
                    A [ t ]ā‚€
  unitrec-β     : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A
                → Ī“ ⊢ u ∷ A [ starŹ· l ]ā‚€
                → UnitŹ·-allowed
                → ¬ UnitŹ·-Ī·
                → Ī“ ⊢ unitrec l p q A (starŹ· l) u ⇒ u ∷ A [ starŹ· l ]ā‚€
  unitrec-β-Ī·   : Ī“ Ā»āˆ™ UnitŹ· l ⊢ A
                → Ī“ ⊢ t ∷ UnitŹ· l
                → Ī“ ⊢ u ∷ A [ starŹ· l ]ā‚€
                → UnitŹ·-allowed
                → UnitŹ·-Ī·
                → Ī“ ⊢ unitrec l p q A t u ⇒ u ∷ A [ t ]ā‚€

  app-subst      : Ī“ ⊢ t ⇒ u ∷ Ī  p , q ā–· F ā–¹ G
                 → Ī“ ⊢ a ∷ F
                 → Ī“ ⊢ t ∘⟨ p ⟩ a ⇒ u ∘⟨ p ⟩ a ∷ G [ a ]ā‚€
  β-red          : Ī“ Ā»āˆ™ F ⊢ G
                 → Ī“ Ā»āˆ™ F ⊢ t ∷ G
                 → Ī“ ⊢ a ∷ F
                 → p PE.≔ p′
                 → -- Note that q can be chosen arbitrarily.
                   Ī -allowed p q
                 → Ī“ ⊢ lam p t ∘⟨ p′ ⟩ a ⇒ t [ a ]ā‚€ ∷ G [ a ]ā‚€

  fst-subst      : Ī“ Ā»āˆ™ F ⊢ G
                 → Ī“ ⊢ t ⇒ u ∷ Σˢ p , q ā–· F ā–¹ G
                 → Ī“ ⊢ fst p t ⇒ fst p u ∷ F
  Ī£-β₁           : Ī“ Ā»āˆ™ F ⊢ G
                 → Ī“ ⊢ t ∷ F
                 → Ī“ ⊢ u ∷ G [ t ]ā‚€
                 → p PE.≔ p′
                 → -- Note that q can be chosen arbitrarily.
                   Σˢ-allowed p q
                 → Ī“ ⊢ fst p (prodĖ¢ p′ t u) ⇒ t ∷ F
  snd-subst      : Ī“ Ā»āˆ™ F ⊢ G
                 → Ī“ ⊢ t ⇒ u ∷ Σˢ p , q ā–· F ā–¹ G
                 → Ī“ ⊢ snd p t ⇒ snd p u ∷ G [ fst p t ]ā‚€
  Ī£-β₂           : Ī“ Ā»āˆ™ F ⊢ G
                 → Ī“ ⊢ t ∷ F
                 → Ī“ ⊢ u ∷ G [ t ]ā‚€
                 → p PE.≔ p′
                 → -- Note that q can be chosen arbitrarily.
                   Σˢ-allowed p q
                 → Ī“ ⊢ snd p (prodĖ¢ p′ t u) ⇒ u ∷ G [ fst p (prodĖ¢ p′ t u) ]ā‚€

  prodrec-subst  : Ī“ Ā»āˆ™ Σʷ p , q′ ā–· F ā–¹ G ⊢ A
                 → Ī“ Ā»āˆ™ F Ā»āˆ™ G ⊢ u ∷ A [ prodŹ· p (var x1) (var x0) ]↑²
                 → Ī“ ⊢ t ⇒ t′ ∷ Σʷ p , q′ ā–· F ā–¹ G
                 → Σʷ-allowed p q′
                 → Ī“ ⊢ prodrec r p q A t u ⇒ prodrec r p q A t′ u ∷ A [ t ]ā‚€
  prodrec-β      : Ī“ Ā»āˆ™ Σʷ p , q′ ā–· F ā–¹ G ⊢ A
                 → Ī“ ⊢ t ∷ F
                 → Ī“ ⊢ t′ ∷ G [ t ]ā‚€
                 → Ī“ Ā»āˆ™ F Ā»āˆ™ G ⊢ u ∷ A [ prodŹ· p (var x1) (var x0) ]↑²
                 → p PE.≔ p′
                 → Σʷ-allowed p q′
                 → Ī“ ⊢ prodrec r p q A (prodŹ· p′ t t′) u ⇒
                       u [ t , t′ ]₁₀ ∷ A [ prodŹ· p′ t t′ ]ā‚€

  natrec-subst   : āˆ€ {n}
                 → Ī“ ⊢ z ∷ A [ zero ]ā‚€
                 → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ∷ A [ suc (var x1) ]↑²
                 → Ī“ ⊢ n ⇒ n′ ∷ ā„•
                 → Ī“ ⊢ natrec p q r A z s n ⇒ natrec p q r A z s n′ ∷
                     A [ n ]ā‚€
  natrec-zero    : Ī“ ⊢ z ∷ A [ zero ]ā‚€
                 → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ∷ A [ suc (var x1) ]↑²
                 → Ī“ ⊢ natrec p q r A z s zero ⇒ z ∷ A [ zero ]ā‚€
  natrec-suc     : āˆ€ {n}
                 → Ī“ ⊢ z ∷ A [ zero ]ā‚€
                 → Ī“ Ā»āˆ™ ā„• Ā»āˆ™ A ⊢ s ∷ A [ suc (var x1) ]↑²
                 → Ī“ ⊢ n ∷ ā„•
                 → Ī“ ⊢ natrec p q r A z s (suc n) ⇒
                     s [ n , natrec p q r A z s n ]₁₀ ∷ A [ suc n ]ā‚€

  J-subst        : Ī“ ⊢ t ∷ A
                 → Ī“ Ā»āˆ™ A Ā»āˆ™ Id (wk1 A) (wk1 t) (var x0) ⊢ B
                 → Ī“ ⊢ u ∷ B [ t , rfl ]₁₀
                 → Ī“ ⊢ v ∷ A
                 → Ī“ ⊢ w₁ ⇒ wā‚‚ ∷ Id A t v
                 → Ī“ ⊢ J p q A t B u v w₁ ⇒ J p q A t B u v wā‚‚ ∷
                     B [ v , w₁ ]₁₀
  J-β            : Ī“ ⊢ t ∷ A
                 → Ī“ ⊢ t′ ∷ A
                 → Ī“ ⊢ t ≔ t′ ∷ A
                 → Ī“ Ā»āˆ™ A Ā»āˆ™ Id (wk1 A) (wk1 t) (var x0) ⊢ B
                 → Ī“ ⊢ B [ t , rfl ]₁₀ ≔ B [ t′ , rfl ]₁₀
                 → Ī“ ⊢ u ∷ B [ t , rfl ]₁₀
                 → Ī“ ⊢ J p q A t B u t′ rfl ⇒ u ∷ B [ t , rfl ]₁₀
  K-subst        : Ī“ Ā»āˆ™ Id A t t ⊢ B
                 → Ī“ ⊢ u ∷ B [ rfl ]ā‚€
                 → Ī“ ⊢ v₁ ⇒ vā‚‚ ∷ Id A t t
                 → K-allowed
                 → Ī“ ⊢ K p A t B u v₁ ⇒ K p A t B u vā‚‚ ∷ B [ v₁ ]ā‚€
  K-β            : Ī“ Ā»āˆ™ Id A t t ⊢ B
                 → Ī“ ⊢ u ∷ B [ rfl ]ā‚€
                 → K-allowed
                 → Ī“ ⊢ K p A t B u rfl ⇒ u ∷ B [ rfl ]ā‚€
  []-cong-subst  : Ī“ ⊢ A
                 → Ī“ ⊢ t ∷ A
                 → Ī“ ⊢ u ∷ A
                 → Ī“ ⊢ v₁ ⇒ vā‚‚ ∷ Id A t u
                 → []-cong-allowed k
                 → let open Erased k in
                   Ī“ ⊢ []-cong k A t u v₁ ⇒ []-cong k A t u vā‚‚ ∷
                     Id (Erased A) ([ t ]) ([ u ])
  []-cong-β      : Ī“ ⊢ A
                 → Ī“ ⊢ t ∷ A
                 → Ī“ ⊢ t′ ∷ A
                 → Ī“ ⊢ t ≔ t′ ∷ A
                 → []-cong-allowed k
                 → let open Erased k in
                   Ī“ ⊢ []-cong k A t t′ rfl ⇒ rfl ∷
                     Id (Erased A) ([ t ]) ([ t′ ])

-- Type reduction.

infix 4 _⊢_⇒_

data _⊢_⇒_ (Ī“ : Cons m n) : Term n → Term n → Set ā„“ where
  univ   : Ī“ ⊢ A ⇒ B ∷ U l
         → Ī“ ⊢ A ⇒ B

-- A kind of reflexive transitive closure for _⊢_⇒_∷_.

infix 4 _⊢_⇒*_∷_

data _⊢_⇒*_∷_ (Ī“ : Cons m n) : Term n → Term n → Term n → Set ā„“ where
  id  : Ī“ ⊢ t ∷ A
      → Ī“ ⊢ t ⇒* t ∷ A
  _⇨_ : Ī“ ⊢ t  ⇒  t′ ∷ A
      → Ī“ ⊢ t′ ⇒* u  ∷ A
      → Ī“ ⊢ t  ⇒* u  ∷ A

-- A kind of reflexive transitive closure for _⊢_⇒_.

infix 4 _⊢_⇒*_

data _⊢_⇒*_ (Ī“ : Cons m n) : Term n → Term n → Set ā„“ where
  id  : Ī“ ⊢ A
      → Ī“ ⊢ A ⇒* A
  _⇨_ : Ī“ ⊢ A  ⇒  A′
      → Ī“ ⊢ A′ ⇒* B
      → Ī“ ⊢ A  ⇒* B

-- Reduction of types to WHNF.

infix 4 _⊢_ā†˜_

_⊢_ā†˜_ : Cons m n → Term n → Term n → Set ā„“
Ī“ ⊢ A ā†˜ B = Ī“ ⊢ A ⇒* B Ɨ Whnf (Ī“ .defs) B

-- Reduction of terms to WHNF.

infix 4 _⊢_ā†˜_∷_

_⊢_ā†˜_∷_ : Cons m n → Term n → Term n → Term n → Set ā„“
Ī“ ⊢ t ā†˜ u ∷ A = Ī“ ⊢ t ⇒* u ∷ A Ɨ Whnf (Ī“ .defs) u

-- A context pair Ī“ is consistent if the empty type is not inhabited
-- inĀ Ī“.

Consistent : Cons m n → Set ā„“
Consistent Ī“ = āˆ€ t → ¬ Ī“ ⊢ t ∷ Empty