------------------------------------------------------------------------
-- Typing and kinding of Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Typing where

open import Data.Context.WellFormed
open import Data.Fin using (Fin; zero)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Fin.Substitution.Typed
open import Data.Nat using (ℕ)
open import Data.Product using (_,_; _×_)
open import Level using () renaming (zero to lzero)
open import Relation.Binary.PropositionalEquality as PropEq hiding ([_])

open import FOmegaInt.Syntax

------------------------------------------------------------------------
-- Typing derivations.

module Typing where
  open TermCtx
  open Syntax
  open Substitution using (_[_]; _Kind[_]; weaken)

  infix 4 _ctx _⊢_kd _⊢_wf
  infix 4 _⊢Tp_∈_ _⊢Tm_∈_ _⊢_∈_
  infix 4 _⊢_<:_∈_ _⊢_<∷_ _⊢_≤_
  infix 4 _⊢_≃_∈_ _⊢_≅_ _⊢_≃_wf _≃_ctx
  mutual

    -- Well-formed typing contexts.

    _ctx : ∀ {n} → Ctx n → Set
    _ctx = ContextFormation._wf _⊢_wf

    -- Well-formed type/kind ascriptions in typing contexts.

    data _⊢_wf {n} (Γ : Ctx n) : TermAsc n → Set where
      wf-kd : ∀ {a} → Γ ⊢ a kd    → Γ ⊢ (kd a) wf
      wf-tp : ∀ {a} → Γ ⊢Tp a ∈ * → Γ ⊢ (tp a) wf

    -- Well-formed kinds.

    data _⊢_kd {n} (Γ : Ctx n) : Kind Term n → Set where
      kd-⋯ : ∀ {a b} → Γ ⊢Tp a ∈ * → Γ ⊢Tp b ∈ * → Γ ⊢ a ⋯ b kd
      kd-Π : ∀ {j k} → Γ ⊢ j kd → kd j ∷ Γ ⊢ k kd → Γ ⊢ Π j k kd

    -- Kinding derivations.

    data _⊢Tp_∈_ {n} (Γ : Ctx n) : Term n → Kind Term n → Set where
      ∈-var : ∀ {k} x → Γ ctx → lookup Γ x ≡ kd k → Γ ⊢Tp var x ∈ k
      ∈-⊥-f : Γ ctx → Γ ⊢Tp ⊥ ∈ *
      ∈-⊤-f : Γ ctx → Γ ⊢Tp ⊤ ∈ *
      ∈-∀-f : ∀ {k a} → Γ ⊢ k kd → kd k ∷ Γ ⊢Tp a ∈ * → Γ ⊢Tp Π k a ∈ *
      ∈-→-f : ∀ {a b} → Γ ⊢Tp a ∈ * → Γ ⊢Tp b ∈ * → Γ ⊢Tp a ⇒ b ∈ *
      ∈-Π-i : ∀ {j a k} → Γ ⊢ j kd → kd j ∷ Γ ⊢Tp a ∈ k →
              Γ ⊢Tp Λ j a ∈ Π j k
      ∈-Π-e : ∀ {a b j k} → Γ ⊢Tp a ∈ Π j k → Γ ⊢Tp b ∈ j →
              Γ ⊢Tp a · b ∈ k Kind[ b ]
      ∈-s-i : ∀ {a b c} → Γ ⊢Tp a ∈ b ⋯ c → Γ ⊢Tp a ∈ a ⋯ a
      ∈-⇑   : ∀ {a j k} → Γ ⊢Tp a ∈ j → Γ ⊢ j <∷ k → Γ ⊢Tp a ∈ k

    -- Subkinding derivations.

    data _⊢_<∷_ {n} (Γ : Ctx n) : Kind Term n → Kind Term n → Set where
      <∷-⋯ : ∀ {a₁ a₂ b₁ b₂} →
             Γ ⊢ a₂ <: a₁ ∈ * → Γ ⊢ b₁ <: b₂ ∈ * → Γ ⊢ a₁ ⋯ b₁ <∷ a₂ ⋯ b₂
      <∷-Π : ∀ {j₁ j₂ k₁ k₂} →
             Γ ⊢ j₂ <∷ j₁ → kd j₂ ∷ Γ ⊢ k₁ <∷ k₂ → Γ ⊢ Π j₁ k₁ kd →
             Γ ⊢ Π j₁ k₁ <∷ Π j₂ k₂

    -- Subtyping derivations.

    data _⊢_<:_∈_ {n} (Γ : Ctx n) : Term n → Term n → Kind Term n → Set where
      <:-refl  : ∀ {a k}     → Γ ⊢Tp a ∈ k → Γ ⊢ a <: a ∈ k
      <:-trans : ∀ {a b c k} → Γ ⊢ a <: b ∈ k → Γ ⊢ b <: c ∈ k → Γ ⊢ a <: c ∈ k
      <:-β₁    : ∀ {j a k b} → kd j ∷ Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ j →
                 Γ ⊢ (Λ j a) · b <: a [ b ] ∈ k Kind[ b ]
      <:-β₂    : ∀ {j a k b} → kd j ∷ Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ j →
                 Γ ⊢ a [ b ] <: (Λ j a) · b ∈ k Kind[ b ]
      <:-η₁    : ∀ {a j k} → Γ ⊢Tp a ∈ Π j k →
                 Γ ⊢ Λ j (weaken a · var zero) <: a ∈ Π j k
      <:-η₂    : ∀ {a j k} → Γ ⊢Tp a ∈ Π j k →
                 Γ ⊢ a <: Λ j (weaken a · var zero) ∈ Π j k
      <:-⊥     : ∀ {a b c} → Γ ⊢Tp a ∈ b ⋯ c → Γ ⊢ ⊥ <: a ∈ *
      <:-⊤     : ∀ {a b c} → Γ ⊢Tp a ∈ b ⋯ c → Γ ⊢ a <: ⊤ ∈ *
      <:-∀     : ∀ {k₁ k₂ a₁ a₂} →
                 Γ ⊢ k₂ <∷ k₁ → kd k₂ ∷ Γ ⊢ a₁ <: a₂ ∈ * → Γ ⊢Tp Π k₁ a₁ ∈ * →
                 Γ ⊢ Π k₁ a₁ <: Π k₂ a₂ ∈ *
      <:-→     : ∀ {a₁ a₂ b₁ b₂} → Γ ⊢ a₂ <: a₁ ∈ * → Γ ⊢ b₁ <: b₂ ∈ * →
                 Γ ⊢ a₁ ⇒ b₁ <: a₂ ⇒ b₂ ∈ *
      <:-λ     : ∀ {j₁ j₂ a₁ a₂ j k} → kd j ∷ Γ ⊢ a₁ <: a₂ ∈ k →
                 Γ ⊢Tp Λ j₁ a₁ ∈ Π j k → Γ ⊢Tp Λ j₂ a₂ ∈ Π j k →
                 Γ ⊢ Λ j₁ a₁ <: Λ j₂ a₂ ∈ Π j k
      <:-·     : ∀ {a₁ a₂ b₁ b₂ j k} → Γ ⊢ a₁ <: a₂ ∈ Π j k → Γ ⊢ b₁ ≃ b₂ ∈ j →
                 Γ ⊢ a₁ · b₁ <: a₂ · b₂ ∈ k Kind[ b₁ ]
      <:-⟨|    : ∀ {a b c} → Γ ⊢Tp a ∈ b ⋯ c → Γ ⊢ b <: a ∈ *
      <:-|⟩    : ∀ {a b c} → Γ ⊢Tp a ∈ b ⋯ c → Γ ⊢ a <: c ∈ *
      <:-⋯-i   : ∀ {a b c d} → Γ ⊢ a <: b ∈ c ⋯ d → Γ ⊢ a <: b ∈ a ⋯ b
      <:-⇑     : ∀ {a b j k} → Γ ⊢ a <: b ∈ j → Γ ⊢ j <∷ k → Γ ⊢ a <: b ∈ k

    -- Type equality.

    data _⊢_≃_∈_ {n} (Γ : Ctx n) : Term n → Term n → Kind Term n → Set where
      <:-antisym : ∀ {a b k} → Γ ⊢ a <: b ∈ k → Γ ⊢ b <: a ∈ k → Γ ⊢ a ≃ b ∈ k

  -- Kind equality.

  data _⊢_≅_ {n} (Γ : Ctx n) : Kind Term n → Kind Term n → Set where
    <∷-antisym : ∀ {j k} → Γ ⊢ j <∷ k → Γ ⊢ k <∷ j → Γ ⊢ j ≅ k

  -- Typing derivations.

  data _⊢Tm_∈_ {n} (Γ : Ctx n) : Term n → Term n → Set where
    ∈-var : ∀ {a} x → Γ ctx → lookup Γ x ≡ tp a → Γ ⊢Tm var x ∈ a
    ∈-∀-i : ∀ {k a b} → Γ ⊢ k kd → kd k ∷ Γ ⊢Tm a ∈ b →
            Γ ⊢Tm Λ k a ∈ Π k b
    ∈-→-i : ∀ {a b c} → Γ ⊢Tp a ∈ * → tp a ∷ Γ ⊢Tm b ∈ weaken c →
            -- NOTE. The following premise is a redundant validity
            -- condition that could be avoided by proving a context
            -- strengthening lemma (showing that unused variables,
            -- i.e. variables not appearing freely to the right of the
            -- turnstyle, can be eliminated from the context).
            Γ ⊢Tp c ∈ * →
            Γ ⊢Tm ƛ a b ∈ a ⇒ c
    ∈-∀-e : ∀ {a b k c} → Γ ⊢Tm a ∈ Π k c → Γ ⊢Tp b ∈ k →
            Γ ⊢Tm a ⊡ b ∈ c [ b ]
    ∈-→-e : ∀ {a b c d} → Γ ⊢Tm a ∈ c ⇒ d → Γ ⊢Tm b ∈ c →
            Γ ⊢Tm a · b ∈ d
    ∈-⇑   : ∀ {a b c} → Γ ⊢Tm a ∈ b → Γ ⊢ b <: c ∈ * → Γ ⊢Tm a ∈ c

  -- Combined typing and kinding of terms and types.

  data _⊢_∈_ {n} (Γ : Ctx n) : Term n → TermAsc n → Set where
    ∈-tp : ∀ {a k} → Γ ⊢Tp a ∈ k → Γ ⊢ a ∈ kd k
    ∈-tm : ∀ {a b} → Γ ⊢Tm a ∈ b → Γ ⊢ a ∈ tp b

  -- Combined subtyping and subkinding.

  data _⊢_≤_ {n} (Γ : Ctx n) : TermAsc n → TermAsc n → Set where
    ≤-<∷ : ∀ {j k} → Γ ⊢ j <∷ k     → Γ ⊢ kd j ≤ kd k
    ≤-<: : ∀ {a b} → Γ ⊢ a <: b ∈ * → Γ ⊢ tp a ≤ tp b

  mutual

    -- Combined kind and type equality, i.e. equality of well-formed
    -- ascriptions.

    data _⊢_≃_wf {n} (Γ : Ctx n) : TermAsc n → TermAsc n → Set where
      ≃wf-≅ : ∀ {j k} → Γ ⊢ j ≅ k     → Γ ⊢ kd j ≃ kd k wf
      ≃wf-≃ : ∀ {a b} → Γ ⊢ a ≃ b ∈ * → Γ ⊢ tp a ≃ tp b wf

    -- Equality of well-formed contexts.

    data _≃_ctx : ∀ {n} → Ctx n → Ctx n → Set where
      ≃-[] : [] ≃ [] ctx
      ≃-∷  : ∀ {n a b} {Γ Δ : Ctx n} →
             Γ ⊢ a ≃ b wf → Γ ≃ Δ ctx → a ∷ Γ ≃ b ∷ Δ ctx

  open PropEq using ([_])

  -- A derived variable rule.

  ∈-var′ : ∀ {n} {Γ : Ctx n} x → Γ ctx → Γ ⊢ var x ∈ lookup Γ x
  ∈-var′ {Γ = Γ} x Γ-ctx with lookup Γ x | inspect (lookup Γ) x
  ∈-var′ x Γ-ctx | kd k | [ Γ[x]≡kd-k ] = ∈-tp (∈-var x Γ-ctx Γ[x]≡kd-k)
  ∈-var′ x Γ-ctx | tp a | [ Γ[x]≡tp-a ] = ∈-tm (∈-var x Γ-ctx Γ[x]≡tp-a)

  -- A derived subsumption rule.

  ∈-⇑′ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢ a ∈ b → Γ ⊢ b ≤ c → Γ ⊢ a ∈ c
  ∈-⇑′ (∈-tp a∈b) (≤-<∷ b<∷c) = ∈-tp (∈-⇑ a∈b b<∷c)
  ∈-⇑′ (∈-tm a∈b) (≤-<: b<:c) = ∈-tm (∈-⇑ a∈b b<:c)

  open ContextFormation _⊢_wf public
    hiding (_wf) renaming (_⊢_wfExt to _⊢_ext)

------------------------------------------------------------------------
-- Properties of typings

open Syntax
open TermCtx
open Typing

-- Inversion lemmas for _⊢_wf.

wf-kd-inv : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ kd k wf → Γ ⊢ k kd
wf-kd-inv (wf-kd k-kd) = k-kd

wf-tp-inv : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢ tp a wf → Γ ⊢Tp a ∈ *
wf-tp-inv (wf-tp a∈*) = a∈*

-- An inversion lemma for _⊢_≃_wf.

≃wf-kd-inv : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ kd j ≃ kd k wf → Γ ⊢ j ≅ k
≃wf-kd-inv (≃wf-≅ j≅k) = j≅k

-- Kind and type equality imply subkinding and subtyping, respectively.

≅⇒<∷ : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → Γ ⊢ j <∷ k
≅⇒<∷ (<∷-antisym j<∷k k<∷j) = j<∷k

≃⇒<: : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a ≃ b ∈ k → Γ ⊢ a <: b ∈ k
≃⇒<: (<:-antisym a<:b b<:a) = a<:b

-- Reflexivity of subkinding.
<∷-refl : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢ k <∷ k
<∷-refl (kd-⋯ a∈* b∈*)   = <∷-⋯ (<:-refl a∈*) (<:-refl b∈*)
<∷-refl (kd-Π j-kd k-kd) = <∷-Π (<∷-refl j-kd) (<∷-refl k-kd) (kd-Π j-kd k-kd)

-- Reflexivity of kind equality.
≅-refl : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ⊢ k ≅ k
≅-refl k-kd = <∷-antisym (<∷-refl k-kd) (<∷-refl k-kd)

-- Symmetry of kind equality.
≅-sym : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → Γ ⊢ k ≅ j
≅-sym (<∷-antisym j<∷k k<∷j) = <∷-antisym k<∷j j<∷k

-- An admissible kind equality rule for interval kinds.
≅-⋯ : ∀ {n} {Γ : Ctx n} {a₁ a₂ b₁ b₂} →
      Γ ⊢ a₁ ≃ a₂ ∈ * → Γ ⊢ b₁ ≃ b₂ ∈ * → Γ ⊢ a₁ ⋯ b₁ ≅ a₂ ⋯ b₂
≅-⋯ (<:-antisym a₁<:a₂ a₂<:a₁) (<:-antisym b₁<:b₂ b₂<:b₁) =
  <∷-antisym (<∷-⋯ a₂<:a₁ b₁<:b₂) (<∷-⋯ a₁<:a₂ b₂<:b₁)

-- Type equality is reflexive.
≃-refl : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Tp a ∈ k → Γ ⊢ a ≃ a ∈ k
≃-refl a∈k = <:-antisym (<:-refl a∈k) (<:-refl a∈k)

-- Type equality is transitive.
≃-trans : ∀ {n} {Γ : Ctx n} {a b c k} →
          Γ ⊢ a ≃ b ∈ k → Γ ⊢ b ≃ c ∈ k → Γ ⊢ a ≃ c ∈ k
≃-trans (<:-antisym a<:b b<:a) (<:-antisym b<:c c<:b) =
  <:-antisym (<:-trans a<:b b<:c) (<:-trans c<:b b<:a)

-- Type equality is symmetric.
≃-sym : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a ≃ b ∈ k → Γ ⊢ b ≃ a ∈ k
≃-sym (<:-antisym a<:b b<:a) = <:-antisym b<:a a<:b

-- Types inhabiting interval kinds are proper Types.
Tp∈-⋯-* : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Tp a ∈ b ⋯ c → Γ ⊢Tp a ∈ *
Tp∈-⋯-* a∈b⋯c = ∈-⇑ (∈-s-i a∈b⋯c) (<∷-⋯ (<:-⊥ a∈b⋯c) (<:-⊤ a∈b⋯c))

-- Well-formedness of the * kind.
*-kd : ∀ {n} {Γ : Ctx n} → Γ ctx → Γ ⊢ * kd
*-kd Γ-ctx = kd-⋯ (∈-⊥-f Γ-ctx) (∈-⊤-f Γ-ctx)

module _ where
  open Substitution

  -- An admissible β-rule for type equality.
  ≃-β : ∀ {n} {Γ : Ctx n} {j a k b} → kd j ∷ Γ ⊢Tp a ∈ k → Γ ⊢Tp b ∈ j →
        Γ ⊢ (Λ j a) · b ≃ a [ b ] ∈ k Kind[ b ]
  ≃-β a∈k b∈j = <:-antisym (<:-β₁ a∈k b∈j) (<:-β₂ a∈k b∈j)

  -- An admissible η-rule for type equality.
  ≃-η : ∀ {n} {Γ : Ctx n} {a j k} →
        Γ ⊢Tp a ∈ Π j k → Γ ⊢ Λ j (weaken a · var zero) ≃ a ∈ Π j k
  ≃-η a∈Πjk = <:-antisym (<:-η₁ a∈Πjk) (<:-η₂ a∈Πjk)

-- An admissible congruence rule for type equality w.r.t. formation of
-- arrow types.
≃-→ : ∀ {n} {Γ : Ctx n} {a₁ a₂ b₁ b₂} →
      Γ ⊢ a₁ ≃ a₂ ∈ * → Γ ⊢ b₁ ≃ b₂ ∈ * → Γ ⊢ a₁ ⇒ b₁ ≃ a₂ ⇒ b₂ ∈ *
≃-→ (<:-antisym a₁<:a₂∈* a₂<:a₁∈*) (<:-antisym b₁<:b₂∈* b₂<:b₁∈*) =
  <:-antisym (<:-→ a₂<:a₁∈* b₁<:b₂∈*) (<:-→ a₁<:a₂∈* b₂<:b₁∈*)

-- An admissible congruence rule for type equality w.r.t. operator
-- abstraction.
≃-λ : ∀ {n} {Γ : Ctx n} {j₁ j₂ a₁ a₂ j k} →
      kd j ∷ Γ ⊢ a₁ ≃ a₂ ∈ k → Γ ⊢Tp Λ j₁ a₁ ∈ Π j k → Γ ⊢Tp Λ j₂ a₂ ∈ Π j k →
      Γ ⊢ Λ j₁ a₁ ≃ Λ j₂ a₂ ∈ Π j k
≃-λ (<:-antisym a₁<:a₂∈k a₂<:a₁∈k) Λj₁a₁∈Πjk Λj₂a₂∈Πjk =
  <:-antisym (<:-λ a₁<:a₂∈k Λj₁a₁∈Πjk Λj₂a₂∈Πjk)
             (<:-λ a₂<:a₁∈k Λj₂a₂∈Πjk Λj₁a₁∈Πjk)

-- An admissible subsumption rule for type equality.
≃-⇑ : ∀ {n} {Γ : Ctx n} {a b j k} → Γ ⊢ a ≃ b ∈ j → Γ ⊢ j <∷ k → Γ ⊢ a ≃ b ∈ k
≃-⇑ (<:-antisym a<:b b<:a) j<∷k = <:-antisym (<:-⇑ a<:b j<∷k) (<:-⇑ b<:a j<∷k)

-- NOTE.  There are more admissible rules one might want to prove
-- here, such as congruence lemmas for type and kind equality
-- w.r.t. the remaining type constructors (e.g. Π and _·_) or
-- transitivity of subkinding and kind equality.  But the proofs of
-- these lemmas require context narrowing and/or validity lemmas which
-- we have not yet established.  We will prove these lemmas once we
-- have established validity of the declarative judgments (see the
-- Typing.Validity module).

-- The contexts of all the above judgments are well-formed.

mutual

  kd-ctx : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → Γ ctx
  kd-ctx (kd-⋯ a∈* b∈*)    = Tp∈-ctx a∈*
  kd-ctx (kd-Π j-kd  k-kd) = kd-ctx j-kd

  Tp∈-ctx : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Tp a ∈ k → Γ ctx
  Tp∈-ctx (∈-var x Γ-ctx Γ[x]≡kd-k) = Γ-ctx
  Tp∈-ctx (∈-⊥-f Γ-ctx)     = Γ-ctx
  Tp∈-ctx (∈-⊤-f Γ-ctx)     = Γ-ctx
  Tp∈-ctx (∈-∀-f k-kd a∈*)  = kd-ctx k-kd
  Tp∈-ctx (∈-→-f a∈* b∈*)   = Tp∈-ctx a∈*
  Tp∈-ctx (∈-Π-i j-kd a∈k)  = kd-ctx j-kd
  Tp∈-ctx (∈-Π-e a∈Πjk b∈j) = Tp∈-ctx a∈Πjk
  Tp∈-ctx (∈-s-i a∈b⋯c)     = Tp∈-ctx a∈b⋯c
  Tp∈-ctx (∈-⇑ a∈j j<∷k)    = Tp∈-ctx a∈j

wf-ctx : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢ a wf → Γ ctx
wf-ctx (wf-kd k-kd) = kd-ctx k-kd
wf-ctx (wf-tp a∈*)  = Tp∈-ctx a∈*

<:-ctx : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a <: b ∈ k → Γ ctx
<:-ctx (<:-refl a∈k)            = Tp∈-ctx a∈k
<:-ctx (<:-trans a<:b∈k b<:c∈k) = <:-ctx a<:b∈k
<:-ctx (<:-β₁ a∈j b∈k) = Tp∈-ctx b∈k
<:-ctx (<:-β₂ a∈j b∈k) = Tp∈-ctx b∈k
<:-ctx (<:-η₁ a∈Πjk)   = Tp∈-ctx a∈Πjk
<:-ctx (<:-η₂ a∈Πjk)   = Tp∈-ctx a∈Πjk
<:-ctx (<:-⊥ a∈b⋯c)    = Tp∈-ctx a∈b⋯c
<:-ctx (<:-⊤ a∈b⋯c)    = Tp∈-ctx a∈b⋯c
<:-ctx (<:-∀ k₂<∷k₁ a₁<:a₂ ∀k₁a₁∈*) = Tp∈-ctx ∀k₁a₁∈*
<:-ctx (<:-→ a₂<:a₁ b₁<:b₂)         = <:-ctx a₂<:a₁
<:-ctx (<:-λ a₂<:a₁∈Πjk Λa₁k₁∈Πjk Λa₂k₂∈Πjk) = Tp∈-ctx Λa₁k₁∈Πjk
<:-ctx (<:-· a₂<:a₁∈Πjk b₂≃b₁∈j)             = <:-ctx a₂<:a₁∈Πjk
<:-ctx (<:-⟨| a∈b⋯c)      = Tp∈-ctx a∈b⋯c
<:-ctx (<:-|⟩ a∈b⋯c)      = Tp∈-ctx a∈b⋯c
<:-ctx (<:-⋯-i a<:b∈c⋯d)  = <:-ctx a<:b∈c⋯d
<:-ctx (<:-⇑ a<:b∈j j<∷k) = <:-ctx a<:b∈j

<∷-ctx : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j <∷ k → Γ ctx
<∷-ctx (<∷-⋯ a₂<:a₁ b₁<:b₂)          = <:-ctx a₂<:a₁
<∷-ctx (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd) = <∷-ctx j₂<∷j₁

≅-ctx : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → Γ ctx
≅-ctx (<∷-antisym j<∷k k<∷j) = <∷-ctx j<∷k

≃-ctx : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a ≃ b ∈ k → Γ ctx
≃-ctx (<:-antisym a<:b∈k b<:a∈k) = <:-ctx a<:b∈k

Tm∈-ctx : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢Tm a ∈ b → Γ ctx
Tm∈-ctx (∈-var x Γ-ctx Γ[x]≡tp-a) = Γ-ctx
Tm∈-ctx (∈-∀-i k-kd a∈b)    = kd-ctx k-kd
Tm∈-ctx (∈-→-i a∈* b∈c c∈*) = Tp∈-ctx a∈*
Tm∈-ctx (∈-∀-e a∈∀kc b∈k)   = Tm∈-ctx a∈∀kc
Tm∈-ctx (∈-→-e a∈c⇒d b∈c)   = Tm∈-ctx a∈c⇒d
Tm∈-ctx (∈-⇑ a∈b b<:c)      = Tm∈-ctx a∈b

∈-ctx : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a ∈ b → Γ ctx
∈-ctx (∈-tp a∈k) = Tp∈-ctx a∈k
∈-ctx (∈-tm a∈b) = Tm∈-ctx a∈b

----------------------------------------------------------------------
-- Well-typed substitutions (i.e. substitution lemmas)

-- A shorthand for kindings and typings of Ts by kind or type
-- ascriptions.
TermAscTyping : (ℕ → Set) → Set₁
TermAscTyping T = Typing TermAsc T TermAsc lzero

-- Liftings from well-typed Ts to well-typed/kinded terms/types.
LiftTo-∈ : ∀ {T} → TermAscTyping T → Set₁
LiftTo-∈ _⊢T_∈_ = LiftTyped Substitution.termAscTermSubst _⊢_wf _⊢T_∈_ _⊢_∈_

-- Helper lemmas about untyped T-substitutions in raw terms and kinds.

record TypedSubstAppHelpers {T} (rawLift : Lift T Term) : Set where
  open Substitution using (weaken; _[_]; _Kind[_])
  module A = SubstApp rawLift
  module L = Lift     rawLift

  field
    -- Substitutions in kinds and types commute.

    Kind/-sub-↑ : ∀ {m n} k a (σ : Sub T m n) →
                  k Kind[ a ] A.Kind/ σ ≡ (k A.Kind/ σ L.↑) Kind[ a A./ σ ]

    /-sub-↑ : ∀ {m n} b a (σ : Sub T m n) →
              b [ a ] A./ σ ≡ (b A./ σ L.↑) [ a A./ σ ]

    -- Weakening of terms commutes with substitution in terms.

    weaken-/ : ∀ {m n} {σ : Sub T m n} a →
               weaken (a A./ σ) ≡ weaken a A./ σ L.↑

-- Application of generic well-typed T-substitutions to all the judgments.

module TypedSubstApp {T : ℕ → Set} (_⊢T_∈_ : TermAscTyping T)
                     (liftTyped : LiftTo-∈ _⊢T_∈_)
                     (helpers : TypedSubstAppHelpers
                                  (LiftTyped.rawLift liftTyped))
                     where

  open LiftTyped liftTyped renaming (lookup to /∈-lookup)
  open TypedSubstAppHelpers helpers

  -- Lift well-kinded Ts to well-kinded types.
  liftTp : ∀ {n} {Γ : Ctx n} {a k kd-k} →
           kd-k ≡ kd k → Γ ⊢T a ∈ kd-k → Γ ⊢Tp L.lift a ∈ k
  liftTp refl a∈kd-k with ∈-lift a∈kd-k
  liftTp refl a∈kd-k | ∈-tp a∈k = a∈k

  -- Lift well-typed Ts to well-typed terms.
  liftTm : ∀ {n} {Γ : Ctx n} {a b tp-b} →
           tp-b ≡ tp b → Γ ⊢T a ∈ tp-b → Γ ⊢Tm L.lift a ∈ b
  liftTm refl a∈tp-b with ∈-lift a∈tp-b
  liftTm refl a∈tp-b | ∈-tm a∈b = a∈b

  mutual

    -- Substitutions preserve well-formedness of kinds and
    -- well-kindedness of types.

    kd-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {k σ} →
           Γ ⊢ k kd → Δ ⊢/ σ ∈ Γ → Δ ⊢ k A.Kind/ σ kd
    kd-/ (kd-⋯ a∈* b∈*)   σ∈Γ = kd-⋯ (Tp∈-/ a∈* σ∈Γ) (Tp∈-/ b∈* σ∈Γ)
    kd-/ (kd-Π j-kd k-kd) σ∈Γ =
      kd-Π j/σ-kd (kd-/ k-kd (∈-↑ (wf-kd j/σ-kd) σ∈Γ))
      where j/σ-kd = kd-/ j-kd σ∈Γ

    Tp∈-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a k σ} →
            Γ ⊢Tp a ∈ k → Δ ⊢/ σ ∈ Γ → Δ ⊢Tp a A./ σ ∈ k A.Kind/ σ
    Tp∈-/ (∈-var x Γ-ctx Γ[x]≡kd-k) σ∈Γ =
      liftTp (cong (_/ _) Γ[x]≡kd-k) (/∈-lookup σ∈Γ x)
    Tp∈-/ (∈-⊥-f Γ-ctx)    σ∈Γ = ∈-⊥-f (/∈-wf σ∈Γ)
    Tp∈-/ (∈-⊤-f Γ-ctx)    σ∈Γ = ∈-⊤-f (/∈-wf σ∈Γ)
    Tp∈-/ (∈-∀-f k-kd a∈*) σ∈Γ =
      ∈-∀-f k/σ-kd (Tp∈-/ a∈* (∈-↑ (wf-kd k/σ-kd) σ∈Γ))
      where k/σ-kd = kd-/ k-kd σ∈Γ
    Tp∈-/ (∈-→-f a∈* b∈*)  σ∈Γ = ∈-→-f (Tp∈-/ a∈* σ∈Γ) (Tp∈-/ b∈* σ∈Γ)
    Tp∈-/ (∈-Π-i j-kd a∈k) σ∈Γ =
      ∈-Π-i j/σ-kd (Tp∈-/ a∈k (∈-↑ (wf-kd j/σ-kd) σ∈Γ))
      where j/σ-kd = kd-/ j-kd σ∈Γ
    Tp∈-/ (∈-Π-e {_} {b} {_} {k} a∈Πjk b∈j) σ∈Γ =
      subst (_ ⊢Tp _ ∈_) (sym (Kind/-sub-↑ k b _))
            (∈-Π-e (Tp∈-/ a∈Πjk σ∈Γ) (Tp∈-/ b∈j σ∈Γ))
    Tp∈-/ (∈-s-i a∈b⋯c)  σ∈Γ = ∈-s-i (Tp∈-/ a∈b⋯c σ∈Γ)
    Tp∈-/ (∈-⇑ a∈j j<∷k) σ∈Γ = ∈-⇑ (Tp∈-/ a∈j σ∈Γ) (<∷-/ j<∷k σ∈Γ)

    -- Substitutions commute with subkinding, subtyping and type
    -- equality.

    <∷-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k σ} →
           Γ ⊢ j <∷ k → Δ ⊢/ σ ∈ Γ → Δ ⊢ j A.Kind/ σ <∷ k A.Kind/ σ
    <∷-/ (<∷-⋯ a₂<:a₁ b₁<:b₂) σ∈Γ = <∷-⋯ (<:-/ a₂<:a₁ σ∈Γ) (<:-/ b₁<:b₂ σ∈Γ)
    <∷-/ (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd) σ∈Γ =
      <∷-Π (<∷-/ j₂<∷j₁ σ∈Γ) (<∷-/ k₁<∷k₂ (∈-↑ (<∷-/-wf k₁<∷k₂ σ∈Γ) σ∈Γ))
           (kd-/ Πj₁k₁-kd σ∈Γ)

    <:-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b k σ} →
           Γ ⊢ a <: b ∈ k → Δ ⊢/ σ ∈ Γ → Δ ⊢ a A./ σ <: b A./ σ ∈ k A.Kind/ σ
    <:-/ (<:-refl a∈k)            σ∈Γ = <:-refl (Tp∈-/ a∈k σ∈Γ)
    <:-/ (<:-trans a<:b∈k b<:c∈k) σ∈Γ =
      <:-trans (<:-/ a<:b∈k σ∈Γ) (<:-/ b<:c∈k σ∈Γ)
    <:-/ (<:-β₁ {j} {a} {k} {b} a∈k b∈j) σ∈Γ =
      subst₂ (_ ⊢ (Λ j a) · b A./ _ <:_∈_)
             (sym (/-sub-↑ a b _)) (sym (Kind/-sub-↑ k b _))
             (<:-β₁ (Tp∈-/ a∈k (∈-↑ (Tp∈-/-wf a∈k σ∈Γ) σ∈Γ)) (Tp∈-/ b∈j σ∈Γ))
    <:-/ (<:-β₂ {j} {a} {k} {b} a∈k b∈j) σ∈Γ =
      subst₂ (_ ⊢_<: (Λ j a) · b A./ _ ∈_)
             (sym (/-sub-↑ a b _)) (sym (Kind/-sub-↑ k b _))
             (<:-β₂ (Tp∈-/ a∈k (∈-↑ (Tp∈-/-wf a∈k σ∈Γ) σ∈Γ)) (Tp∈-/ b∈j σ∈Γ))
    <:-/ {Δ = Δ} {σ = σ} (<:-η₁ {a} {j} {k} a∈Πjk) σ∈Γ =
      subst (Δ ⊢_<: a A./ σ ∈ Π j k A.Kind/ σ)
            (cong (Λ _) (cong₂ _·_ (weaken-/ a) (sym (lift-var zero))))
            (<:-η₁ (Tp∈-/ a∈Πjk σ∈Γ))
    <:-/ {Δ = Δ} {σ = σ} (<:-η₂  {a} {j} {k} a∈Πjk) σ∈Γ =
      subst (Δ ⊢ a A./ σ <:_∈ Π j k A.Kind/ σ)
            (cong (Λ _) (cong₂ _·_ (weaken-/ a) (sym (lift-var zero))))
            (<:-η₂ (Tp∈-/ a∈Πjk σ∈Γ))
    <:-/ (<:-⊥ a∈b⋯c) σ∈Γ = <:-⊥ (Tp∈-/ a∈b⋯c σ∈Γ)
    <:-/ (<:-⊤ a∈b⋯c) σ∈Γ = <:-⊤ (Tp∈-/ a∈b⋯c σ∈Γ)
    <:-/ (<:-∀ k₂<∷k₁ a₁<:a₂∈* ∀j₁k₁∈*) σ∈Γ =
      <:-∀ (<∷-/ k₂<∷k₁ σ∈Γ) (<:-/ a₁<:a₂∈* (∈-↑ (<:-/-wf a₁<:a₂∈* σ∈Γ) σ∈Γ))
           (Tp∈-/ ∀j₁k₁∈* σ∈Γ)
    <:-/ (<:-→ a₂<:a₁∈* b₁<:b₂∈*) σ∈Γ =
      <:-→ (<:-/ a₂<:a₁∈* σ∈Γ) (<:-/ b₁<:b₂∈* σ∈Γ)
    <:-/ (<:-λ a₁<:a₂∈k Λj₁a₁∈Πjk Λj₂a₂∈Πjk) σ∈Γ =
      <:-λ (<:-/ a₁<:a₂∈k (∈-↑ (<:-/-wf a₁<:a₂∈k σ∈Γ) σ∈Γ))
           (Tp∈-/ Λj₁a₁∈Πjk σ∈Γ) (Tp∈-/ Λj₂a₂∈Πjk σ∈Γ)
    <:-/ (<:-· {k = k} a₁<:a₂∈Πjk b₁≃b₂∈j) σ∈Γ =
      subst (_ ⊢ _ <: _ ∈_) (sym (Kind/-sub-↑ k _ _))
            (<:-· (<:-/ a₁<:a₂∈Πjk σ∈Γ) (≃-/ b₁≃b₂∈j σ∈Γ))
    <:-/ (<:-⟨| a∈b⋯c)      σ∈Γ = <:-⟨| (Tp∈-/ a∈b⋯c σ∈Γ)
    <:-/ (<:-|⟩ a∈b⋯c)      σ∈Γ = <:-|⟩ (Tp∈-/ a∈b⋯c σ∈Γ)
    <:-/ (<:-⋯-i a<:b∈c⋯d)  σ∈Γ = <:-⋯-i (<:-/ a<:b∈c⋯d σ∈Γ)
    <:-/ (<:-⇑ a<:b∈j j<∷k) σ∈Γ = <:-⇑ (<:-/ a<:b∈j σ∈Γ) (<∷-/ j<∷k σ∈Γ)

    ≃-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b k σ} →
          Γ ⊢ a ≃ b ∈ k → Δ ⊢/ σ ∈ Γ → Δ ⊢ a A./ σ ≃ b A./ σ ∈ k A.Kind/ σ
    ≃-/ (<:-antisym a<:b∈k b<:a∈k) σ∈Γ =
      <:-antisym (<:-/ a<:b∈k σ∈Γ) (<:-/ b<:a∈k σ∈Γ)

    -- Helpers (to satisfy the termination checker).

    kd-/-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k σ} →
              kd j ∷ Γ ⊢ k kd → Δ ⊢/ σ ∈ Γ → Δ ⊢ kd (j A.Kind/ σ) wf
    kd-/-wf (kd-⋯ a∈* _)  σ∈Γ = Tp∈-/-wf a∈* σ∈Γ
    kd-/-wf (kd-Π j-kd _) σ∈Γ = kd-/-wf j-kd σ∈Γ

    Tp∈-/-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a j k σ} →
               kd j ∷ Γ ⊢Tp a ∈ k → Δ ⊢/ σ ∈ Γ → Δ ⊢ kd (j A.Kind/ σ) wf
    Tp∈-/-wf (∈-var x (wf-kd k-kd ∷ Γ-ctx) _) σ∈Γ = wf-kd (kd-/ k-kd σ∈Γ)
    Tp∈-/-wf (∈-⊥-f (wf-kd j-kd ∷ Γ-ctx)) σ∈Γ = wf-kd (kd-/ j-kd σ∈Γ)
    Tp∈-/-wf (∈-⊤-f (wf-kd j-kd ∷ Γ-ctx)) σ∈Γ = wf-kd (kd-/ j-kd σ∈Γ)
    Tp∈-/-wf (∈-∀-f k-kd _)      σ∈Γ = kd-/-wf k-kd σ∈Γ
    Tp∈-/-wf (∈-→-f a∈* _)       σ∈Γ = Tp∈-/-wf a∈* σ∈Γ
    Tp∈-/-wf (∈-Π-i j-kd _)      σ∈Γ = kd-/-wf j-kd σ∈Γ
    Tp∈-/-wf (∈-Π-e a∈Πjk _)     σ∈Γ = Tp∈-/-wf a∈Πjk σ∈Γ
    Tp∈-/-wf (∈-s-i a∈b⋯c)       σ∈Γ = Tp∈-/-wf a∈b⋯c σ∈Γ
    Tp∈-/-wf (∈-⇑ a∈b _)         σ∈Γ = Tp∈-/-wf a∈b σ∈Γ

    <:-/-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b j k σ} →
              kd j ∷ Γ ⊢ a <: b ∈ k → Δ ⊢/ σ ∈ Γ → Δ ⊢ kd (j A.Kind/ σ) wf
    <:-/-wf (<:-refl a∈k)        σ∈Γ = Tp∈-/-wf a∈k σ∈Γ
    <:-/-wf (<:-trans a<:b _)    σ∈Γ = <:-/-wf a<:b σ∈Γ
    <:-/-wf (<:-β₁ _ b∈j)        σ∈Γ = Tp∈-/-wf b∈j σ∈Γ
    <:-/-wf (<:-β₂ _ b∈j)        σ∈Γ = Tp∈-/-wf b∈j σ∈Γ
    <:-/-wf (<:-η₁ a∈Πjk)        σ∈Γ = Tp∈-/-wf a∈Πjk σ∈Γ
    <:-/-wf (<:-η₂ a∈Πjk)        σ∈Γ = Tp∈-/-wf a∈Πjk σ∈Γ
    <:-/-wf (<:-⊥ a∈b⋯c)         σ∈Γ = Tp∈-/-wf a∈b⋯c σ∈Γ
    <:-/-wf (<:-⊤ a∈b⋯c)         σ∈Γ = Tp∈-/-wf a∈b⋯c σ∈Γ
    <:-/-wf (<:-∀ j₂<∷j₁ _ _)    σ∈Γ = <∷-/-wf j₂<∷j₁ σ∈Γ
    <:-/-wf (<:-→ a₂<:a₁∈* _)    σ∈Γ = <:-/-wf a₂<:a₁∈* σ∈Γ
    <:-/-wf (<:-λ _ Λj₁a₂∈Πjk _) σ∈Γ = Tp∈-/-wf Λj₁a₂∈Πjk σ∈Γ
    <:-/-wf (<:-· a₁<:a₂∈Πjk _)  σ∈Γ = <:-/-wf a₁<:a₂∈Πjk σ∈Γ
    <:-/-wf (<:-⟨| a∈b⋯c)        σ∈Γ = Tp∈-/-wf a∈b⋯c σ∈Γ
    <:-/-wf (<:-|⟩ a∈b⋯c)        σ∈Γ = Tp∈-/-wf a∈b⋯c σ∈Γ
    <:-/-wf (<:-⋯-i a<:b∈c⋯d)    σ∈Γ = <:-/-wf a<:b∈c⋯d σ∈Γ
    <:-/-wf (<:-⇑ a<:b∈k _)      σ∈Γ = <:-/-wf a<:b∈k σ∈Γ

    <∷-/-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k l σ} →
              kd j ∷ Γ ⊢ k <∷ l → Δ ⊢/ σ ∈ Γ → Δ ⊢ kd (j A.Kind/ σ) wf
    <∷-/-wf (<∷-⋯ j₂<:j₁ _)   σ∈Γ = <:-/-wf j₂<:j₁ σ∈Γ
    <∷-/-wf (<∷-Π j₂<∷j₁ _ _) σ∈Γ = <∷-/-wf j₂<∷j₁ σ∈Γ

  -- Substitutions preserve well-formedness of ascriptions.
  wf-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a σ} →
         Γ ⊢ a wf → Δ ⊢/ σ ∈ Γ → Δ ⊢ a A.TermAsc/ σ wf
  wf-/ (wf-kd k-kd) σ∈Γ = wf-kd (kd-/ k-kd σ∈Γ)
  wf-/ (wf-tp a∈b)  σ∈Γ = wf-tp (Tp∈-/ a∈b σ∈Γ)

  -- Substitutions commute with kind equality.
  ≅-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k σ} →
        Γ ⊢ j ≅ k → Δ ⊢/ σ ∈ Γ → Δ ⊢ j A.Kind/ σ ≅ k A.Kind/ σ
  ≅-/ (<∷-antisym j<∷k k<∷j) σ∈Γ = <∷-antisym (<∷-/ j<∷k σ∈Γ) (<∷-/ k<∷j σ∈Γ)

  -- Substitutions preserve well-typedness of terms.
  Tm∈-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b σ} →
          Γ ⊢Tm a ∈ b → Δ ⊢/ σ ∈ Γ → Δ ⊢Tm a A./ σ ∈ b A./ σ
  Tm∈-/ (∈-var x Γ-ctx Γ[x]=tp-k) σ∈Γ =
    liftTm (cong (_/ _) Γ[x]=tp-k) (/∈-lookup σ∈Γ x)
  Tm∈-/ (∈-∀-i k-kd a∈*)          σ∈Γ =
    ∈-∀-i k/σ-kd (Tm∈-/ a∈* (∈-↑ (wf-kd k/σ-kd) σ∈Γ))
    where k/σ-kd = kd-/ k-kd σ∈Γ
  Tm∈-/ (∈-→-i {c = c} a∈* b∈c c∈*) σ∈Γ =
    ∈-→-i a/σ∈* (subst (_ ⊢Tm _ ∈_) (sym (weaken-/ c) ) b/σ↑∈c)
          (Tp∈-/ c∈* σ∈Γ)
    where
      a/σ∈*  = Tp∈-/ a∈* σ∈Γ
      b/σ↑∈c = Tm∈-/ b∈c (∈-↑ (wf-tp a/σ∈*) σ∈Γ)
  Tm∈-/ (∈-∀-e {c = c} a∈∀kc b∈k) σ∈Γ =
    subst (_ ⊢Tm _ ∈_) (sym (/-sub-↑ c _ _))
          (∈-∀-e (Tm∈-/ a∈∀kc σ∈Γ) (Tp∈-/ b∈k σ∈Γ))
  Tm∈-/ (∈-→-e a∈c→d b∈c)         σ∈Γ = ∈-→-e (Tm∈-/ a∈c→d σ∈Γ) (Tm∈-/ b∈c σ∈Γ)
  Tm∈-/ (∈-⇑ a∈b b<:c)            σ∈Γ = ∈-⇑ (Tm∈-/ a∈b σ∈Γ) (<:-/ b<:c σ∈Γ)

  -- Substitutions preserve well-kindedness and well-typedness.
  ∈-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b σ} →
        Γ ⊢ a ∈ b → Δ ⊢/ σ ∈ Γ → Δ ⊢ a A./ σ ∈ b A.TermAsc/ σ
  ∈-/ (∈-tp a∈b) σ∈Γ = ∈-tp (Tp∈-/ a∈b σ∈Γ)
  ∈-/ (∈-tm a∈b) σ∈Γ = ∈-tm (Tm∈-/ a∈b σ∈Γ)

  -- Substitutions preserve subkinding and subtyping.
  ≤-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b σ} →
        Γ ⊢ a ≤ b → Δ ⊢/ σ ∈ Γ → Δ ⊢ a A.TermAsc/ σ ≤ b A.TermAsc/ σ
  ≤-/ (≤-<∷ a<∷b)   σ∈Γ = ≤-<∷ (<∷-/ a<∷b σ∈Γ)
  ≤-/ (≤-<: a<:b∈k) σ∈Γ = ≤-<: (<:-/ a<:b∈k σ∈Γ)

  -- Substitutions preserve equality of kind and type ascriptions.
  ≃wf-/ : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b σ} →
          Γ ⊢ a ≃ b wf → Δ ⊢/ σ ∈ Γ → Δ ⊢ a A.TermAsc/ σ ≃ b A.TermAsc/ σ wf
  ≃wf-/ (≃wf-≅ j≅k) σ∈Γ = ≃wf-≅ (≅-/ j≅k σ∈Γ)
  ≃wf-/ (≃wf-≃ a≃b) σ∈Γ = ≃wf-≃ (≃-/ a≃b σ∈Γ)

-- Well-kinded/typed type and term substitutions.
module TypedSubstitution where
  open Substitution           using (simple; termSubst)
  open SimpleExt    simple    using (extension)
  open TermSubst    termSubst using (varLift; termLift)

  private
    module S  = Substitution
    module KL = TermLikeLemmas S.termLikeLemmasKind

  -- Helper lemmas about untyped renamings and substitutions in terms
  -- and kinds.

  varHelpers : TypedSubstAppHelpers varLift
  varHelpers = record
    { Kind/-sub-↑ = KL./-sub-↑
    ; /-sub-↑     = LiftSubLemmas./-sub-↑ S.varLiftSubLemmas
    ; weaken-/    = LiftAppLemmas.wk-commutes S.varLiftAppLemmas
    }

  termHelpers : TypedSubstAppHelpers termLift
  termHelpers = record
    { Kind/-sub-↑ = λ k _ _ → KL.sub-commutes k
    ; /-sub-↑     = λ a _ _ → S.sub-commutes a
    ; weaken-/    = S.weaken-/
    }

  -- Typed term substitutions.

  typedTermSubst : TypedTermSubst TermAsc Term lzero TypedSubstAppHelpers
  typedTermSubst = record
    { _⊢_wf = _⊢_wf
    ; _⊢_∈_ = _⊢_∈_
    ; termLikeLemmas = S.termLikeLemmasTermAsc
    ; varHelpers     = varHelpers
    ; termHelpers    = termHelpers
    ; wf-wf    = wf-ctx
    ; ∈-wf     = ∈-ctx
    ; ∈-var    = ∈-var′
    ; typedApp = TypedSubstApp.∈-/
    }
  open TypedTermSubst typedTermSubst public
    hiding (_⊢_wf; _⊢_∈_; varHelpers; termHelpers; ∈-var; ∈-/Var; ∈-/)
    renaming (lookup to /∈-lookup)
  open TypedSubstApp _⊢Var_∈_ varLiftTyped varHelpers public using () renaming
    ( wf-/  to wf-/Var
    ; kd-/  to kd-/Var
    ; Tp∈-/ to Tp∈-/Var
    ; <∷-/  to <∷-/Var
    ; <:-/  to <:-/Var
    ; ∈-/   to ∈-/Var
    ; ≤-/   to ≤-/Var
    ; ≃wf-/ to ≃wf-/Var
    )
  open Substitution using (weaken; weakenKind; weakenTermAsc)

  -- Weakening preserves the various judgments.

  wf-weaken : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a wf → Γ ⊢ b wf →
              (a ∷ Γ) ⊢ weakenTermAsc b wf
  wf-weaken a-wf b-wf = wf-/Var b-wf (Var∈-wk a-wf)

  kd-weaken : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢ a wf → Γ ⊢ k kd →
              (a ∷ Γ) ⊢ weakenKind k kd
  kd-weaken a-wf k-kd = kd-/Var k-kd (Var∈-wk a-wf)

  Tp∈-weaken : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a wf → Γ ⊢Tp b ∈ k →
               (a ∷ Γ) ⊢Tp weaken b ∈ weakenKind k
  Tp∈-weaken a-wf b∈k = Tp∈-/Var b∈k (Var∈-wk a-wf)

  <∷-weaken : ∀ {n} {Γ : Ctx n} {a j k} → Γ ⊢ a wf → Γ ⊢ j <∷ k →
              (a ∷ Γ) ⊢ weakenKind j <∷ weakenKind k
  <∷-weaken a-wf j<∷k = <∷-/Var j<∷k (Var∈-wk a-wf)

  <:-weaken : ∀ {n} {Γ : Ctx n} {a b c k} → Γ ⊢ a wf → Γ ⊢ b <: c ∈ k →
              (a ∷ Γ) ⊢ weaken b <: weaken c ∈ weakenKind k
  <:-weaken a-wf b<:c∈k = <:-/Var b<:c∈k (Var∈-wk a-wf)

  ∈-weaken : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢ a wf → Γ ⊢ b ∈ c →
             (a ∷ Γ) ⊢ weaken b ∈ weakenTermAsc c
  ∈-weaken a-wf b∈c = ∈-/Var b∈c (Var∈-wk a-wf)

  ≤-weaken : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢ a wf → Γ ⊢ b ≤ c →
             (a ∷ Γ) ⊢ weakenTermAsc b ≤ weakenTermAsc c
  ≤-weaken a-wf b≤c = ≤-/Var b≤c (Var∈-wk a-wf)

  ≃wf-weaken : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢ a wf → Γ ⊢ b ≃ c wf →
               (a ∷ Γ) ⊢ weakenTermAsc b ≃ weakenTermAsc c wf
  ≃wf-weaken a-wf b≃c = ≃wf-/Var b≃c (Var∈-wk a-wf)

  open TypedSubstApp _⊢_∈_ termLiftTyped termHelpers public
  open Substitution using (_/_; _Kind/_; id; sub; _↑⋆_; _[_]; _Kind[_])

  -- Substitution of a single well-typed term or well-kinded type
  -- preserves the various judgments.

  kd-[] : ∀ {n} {Γ : Ctx n} {a b k} →
          b ∷ Γ ⊢ k kd → Γ ⊢ a ∈ b → Γ ⊢ k Kind[ a ] kd
  kd-[] k-kd a∈b = kd-/ k-kd (∈-sub a∈b)

  Tp∈-[] : ∀ {n} {Γ : Ctx n} {a b k c} →
           c ∷ Γ ⊢Tp a ∈ k → Γ ⊢ b ∈ c → Γ ⊢Tp a [ b ] ∈ k Kind[ b ]
  Tp∈-[] a∈k b∈c = Tp∈-/ a∈k (∈-sub b∈c)

  Tm∈-[] : ∀ {n} {Γ : Ctx n} {a b c d} →
           d ∷ Γ ⊢Tm a ∈ c → Γ ⊢ b ∈ d → Γ ⊢Tm a [ b ] ∈ c [ b ]
  Tm∈-[] a∈c b∈d = Tm∈-/ a∈c (∈-sub b∈d)

  <:-[] : ∀ {n} {Γ : Ctx n} {a b c d k} →
          d ∷ Γ ⊢ a <: b ∈ k → Γ ⊢ c ∈ d → Γ ⊢ a [ c ] <: b [ c ] ∈ k Kind[ c ]
  <:-[] a<:b c∈d = <:-/ a<:b (∈-sub c∈d)

-- Operations on well-formed contexts that require weakening of
-- well-formedness judgments.

module WfCtxOps where
  wfWeakenOps : WellFormedWeakenOps weakenOps
  wfWeakenOps = record { wf-weaken = TypedSubstitution.wf-weaken }

  open WellFormedWeakenOps wfWeakenOps public renaming (lookup to lookup-wf)

  -- Lookup the kind of a type variable in a well-formed context.
  lookup-kd : ∀ {m} {Γ : Ctx m} {k} x →
              Γ ctx → TermCtx.lookup Γ x ≡ kd k → Γ ⊢ k kd
  lookup-kd x Γ-ctx Γ[x]≡kd-k =
    wf-kd-inv (subst (_ ⊢_wf) Γ[x]≡kd-k (lookup-wf Γ-ctx x))

  -- Lookup the type of a term variable in a well-formed context.
  lookup-tp : ∀ {m} {Γ : Ctx m} {a} x →
              Γ ctx → TermCtx.lookup Γ x ≡ tp a → Γ ⊢Tp a ∈ *
  lookup-tp x Γ-ctx Γ[x]≡tp-a =
    wf-tp-inv (subst (_ ⊢_wf) Γ[x]≡tp-a (lookup-wf Γ-ctx x))

open TypedSubstitution
open WfCtxOps

----------------------------------------------------------------------
-- Generation lemmas for kinding

-- A generation lemma for kinding of universals.

Tp∈-∀-inv : ∀ {n} {Γ : Ctx n} {a j k} → Γ ⊢Tp Π j a ∈ k →
            Γ ⊢ j kd × kd j ∷ Γ ⊢Tp a ∈ *
Tp∈-∀-inv (∈-∀-f j-kd a∈*) = j-kd , a∈*
Tp∈-∀-inv (∈-s-i ∀ka∈b⋯c)  = Tp∈-∀-inv ∀ka∈b⋯c
Tp∈-∀-inv (∈-⇑ ∀ja∈k k<∷l) = Tp∈-∀-inv ∀ja∈k

-- A generation lemma for kinding of arrows.

Tp∈-→-inv : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢Tp a ⇒ b ∈ k →
            Γ ⊢Tp a ∈ * × Γ ⊢Tp b ∈ *
Tp∈-→-inv (∈-→-f a∈* b∈*)  = a∈* , b∈*
Tp∈-→-inv (∈-s-i a⇒b∈c⋯d)  = Tp∈-→-inv a⇒b∈c⋯d
Tp∈-→-inv (∈-⇑ a⇒b∈j j<∷k) = Tp∈-→-inv a⇒b∈j


open import dynamics-core

module focus-formation where
  -- every ε is an evaluation context -- trivially, here, since we don't
  -- include any of the premises in red brackets about finality
  focus-formation : ∀{d d' ε} → d == ε ⟦ d' ⟧ → ε evalctx
  focus-formation (FHPlus1 sub) = ECPlus1 (focus-formation sub)
  focus-formation (FHPlus2 sub) = ECPlus2 (focus-formation sub)
  focus-formation FHOuter = ECDot
  focus-formation (FHAp1 sub) = ECAp1 (focus-formation sub)
  focus-formation (FHAp2 sub) = ECAp2 (focus-formation sub)
  focus-formation (FHInl sub) = ECInl (focus-formation sub)
  focus-formation (FHInr sub) = ECInr (focus-formation sub)
  focus-formation (FHCase sub) = ECCase (focus-formation sub)
  focus-formation (FHFst sub) = ECFst
  focus-formation (FHSnd sub) = ECSnd
  focus-formation (FHPair1 sub) = ECPair1 (focus-formation sub)
  focus-formation (FHPair2 sub) = ECPair2 (focus-formation sub)
  focus-formation (FHNEHole sub) = ECNEHole (focus-formation sub)
  focus-formation (FHCast sub) = ECCast (focus-formation sub)
  focus-formation (FHFailedCast x) = ECFailedCast (focus-formation x)


{- Byzantine Fault Tolerant Consensus Verification in Agda, version 0.9.

   Copyright (c) 2020, 2021, Oracle and/or its affiliates.
   Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.oracle.com/licenses/upl
-}
open import LibraBFT.Prelude
open import LibraBFT.Lemmas
open import LibraBFT.Abstract.Types
open import LibraBFT.Abstract.Types.EpochConfig
open WithAbsVote

-- This module defines the notion of one Record r "extending" another
-- Record r' (denoted r' ← r), ensuring rules about rounds and that r
-- correctly identifies r'

module LibraBFT.Abstract.Records.Extends
    (UID    : Set)
    (_≟UID_ : (u₀ u₁ : UID) → Dec (u₀ ≡ u₁))
    (NodeId : Set)
    (𝓔      : EpochConfig UID NodeId)
    (𝓥     : VoteEvidence UID NodeId 𝓔)
  where

  open import LibraBFT.Abstract.Records UID _≟UID_ NodeId 𝓔 𝓥

  -- Most of the conditions in section 4.2 of the paper (see
  -- LibraBFT.Abstract.RecordChain.Properties) would be checked
  -- by the implementation to validate data received.
  --
  -- In the Abstract model, however, we are only concerned with
  -- proving the properties; only round numbers and identifiers
  -- for previous records are actually critical to thm S5!
  data _←_ : Record → Record → Set where
    I←B : {b : Block}
        → 0 < getRound b
        → bPrevQC b ≡ nothing
        → I ← B b
    Q←B : {q : QC} {b : Block}
        → getRound q < getRound b
        → just (qCertBlockId q) ≡ bPrevQC b
        → Q q ← B b
    B←Q : {b : Block} {q : QC}
        → getRound q ≡ getRound b
        → bId b ≡ qCertBlockId q
        → B b ← Q q

  -- Equivalent records extend equivalent records (modulo injectivity
  -- failure of bId).
  ←-≈Rec : ∀{r₀ r₁ s₀ s₁} → s₀ ← r₀ → s₁ ← r₁
           → r₀ ≈Rec r₁
           → NonInjective-≡ bId ⊎ (s₀ ≈Rec s₁)
  ←-≈Rec (I←B x x₁) (I←B x₂ x₃) hyp = inj₂ eq-I
  ←-≈Rec (I←B x x₁) (Q←B x₂ x₃) (eq-B refl)
    = ⊥-elim (maybe-⊥ (sym x₃) x₁)
  ←-≈Rec (Q←B x x₁) (I←B x₂ x₃) (eq-B refl)
    = ⊥-elim (maybe-⊥ (sym x₁) x₃)
  ←-≈Rec (Q←B {q₀} x refl) (Q←B {q₁} x₂ refl) (eq-B refl)
    = inj₂ (eq-Q refl) -- Here is where we wouldn't be able to
                       -- complete the proof if we required round equality
                       -- in eq-Q
  ←-≈Rec (B←Q {b₀} x refl) (B←Q {b₁} w refl) (eq-Q refl)
    with b₀ ≟Block b₁
  ...| no  hb  = inj₁ ((b₀ , b₁) , (λ x → hb x) , refl)
  ...| yes prf = inj₂ (eq-B prf)

  ←-irrelevant : Irrelevant _←_
  ←-irrelevant (I←B r₁ h₁) (I←B r₂ h₂)
    = cong₂ I←B (≤-irrelevant r₁ r₂) (≡-irrelevant h₁ h₂)
  ←-irrelevant (Q←B r₁ h₁) (Q←B r₂ h₂)
    = cong₂ Q←B (≤-irrelevant r₁ r₂) (≡-irrelevant h₁ h₂)
  ←-irrelevant (B←Q r₁ h₁) (B←Q r₂ h₂)
    = cong₂ B←Q (≡-irrelevant r₁ r₂) (≡-irrelevant h₁ h₂)

  ←-round-≤ : ∀{r₀ r₁} → r₀ ← r₁ → round r₀ ≤ round r₁
  ←-round-≤ (I←B r h)    = z≤n
  ←-round-≤ (Q←B r h)    = <⇒≤ r
  ←-round-≤ (B←Q refl h) = ≤-refl

  ←←-round-< : ∀{r r₀ r₁} → r ← r₀ → r₀ ← r₁
             → round r < round r₁
  ←←-round-< (I←B r h)     (B←Q refl _) = r
  ←←-round-< (Q←B r h)     rr           = ≤-trans r (←-round-≤ rr)
  ←←-round-< (B←Q refl h)  (Q←B prf _)  = prf

  -- LemmaS1, clause 2: injectivity of _←_
  lemmaS1-2 : ∀{r₀ r₁ r₂ r₂'}
            → r₂ ≈Rec r₂'
            → r₀ ← r₂ → r₁ ← r₂'
            → uid r₀ ≡ uid r₁
  lemmaS1-2 {i₀} {i₁} {b} hyp (I←B _ i₀←b) (I←B _ i₁←b) = refl
  lemmaS1-2 {q}  {i}  {b} (eq-B refl) (Q←B _ ())   (I←B _ refl)
  lemmaS1-2 {i}  {q}  {b} (eq-B refl) (I←B _ refl) (Q←B _ ())
  lemmaS1-2 {q₀} {q₁} {b} (eq-B refl) (Q←B _ refl) (Q←B _ refl) = refl
  lemmaS1-2 {b₀} {b₁} {q} (eq-Q refl) (B←Q _ refl) (B←Q _ refl) = refl

  -- A better name for lemmaS1-2
  ←-inj : ∀{r₀ r₁ r₂}
        → r₀ ← r₂ → r₁ ← r₂
        → uid r₀ ≡ uid r₁
  ←-inj = lemmaS1-2 ≈Rec-refl


------------------------------------------------------------------------------
-- The alternating bit protocol (ABP)
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

-- This module define the ABP following the presentation in Dybjer and
-- Sander (1989).

module FOTC.Program.ABP.ABP where

open import FOTC.Base
open import FOTC.Base.Loop
open import FOTC.Base.List
open import FOTC.Data.Bool
open import FOTC.Data.Stream.Type
open import FOTC.Program.ABP.Fair.Type
open import FOTC.Program.ABP.Terms

------------------------------------------------------------------------------
-- ABP equations

postulate
  send ack out corrupt : D → D
  await                : D → D → D → D → D

postulate send-eq : ∀ b i is ds →
                    send b · (i ∷ is) · ds ≡ < i , b > ∷ await b i is ds
{-# ATP axioms send-eq #-}

postulate
  await-ok≡ : ∀ b b' i is ds →
              b ≡ b' →
              await b i is (ok b' ∷ ds) ≡ send (not b) · is · ds

  await-ok≢ : ∀ b b' i is ds →
              b ≢ b' →
              await b i is (ok b' ∷ ds) ≡ < i , b > ∷ await b i is ds

  await-error : ∀ b i is ds →
                await b i is (error ∷ ds) ≡ < i , b > ∷ await b i is ds
{-# ATP axioms await-ok≡ await-ok≢ await-error #-}

postulate
  ack-ok≡ : ∀ b b' i bs →
            b ≡ b' →
            ack b · (ok < i , b' > ∷ bs) ≡ b ∷ ack (not b) · bs

  ack-ok≢ : ∀ b b' i bs →
            b ≢ b' →
            ack b · (ok < i , b' > ∷ bs) ≡ not b ∷ ack b · bs

  ack-error : ∀ b bs → ack b · (error ∷ bs) ≡ not b ∷ ack b · bs
{-# ATP axioms ack-ok≡ ack-ok≢ ack-error #-}

postulate
  out-ok≡ : ∀ b b' i bs →
            b ≡ b' →
            out b · (ok < i , b' > ∷ bs) ≡ i ∷ out (not b) · bs

  out-ok≢ : ∀ b b' i bs →
            b ≢ b' →
            out b · (ok < i , b' > ∷ bs) ≡ out b · bs

  out-error : ∀ b bs → out b · (error ∷ bs) ≡ out b · bs
{-# ATP axioms out-ok≡ out-ok≢ out-error #-}

postulate
  corrupt-T : ∀ os x xs →
              corrupt (T ∷ os) · (x ∷ xs) ≡ ok x ∷ corrupt os · xs
  corrupt-F : ∀ os x xs →
              corrupt (F ∷ os) · (x ∷ xs) ≡ error ∷ corrupt os · xs
{-# ATP axioms corrupt-T corrupt-F #-}

postulate has hbs hcs hds : D → D → D → D → D → D → D

postulate has-eq : ∀ f₁ f₂ f₃ g₁ g₂ is →
                   has f₁ f₂ f₃ g₁ g₂ is ≡ f₁ · is · (hds f₁ f₂ f₃ g₁ g₂ is)
{-# ATP axioms has-eq #-}

postulate hbs-eq : ∀ f₁ f₂ f₃ g₁ g₂ is →
                   hbs f₁ f₂ f₃ g₁ g₂ is ≡ g₁ · (has f₁ f₂ f₃ g₁ g₂ is)
{-# ATP axioms hbs-eq #-}

postulate hcs-eq : ∀ f₁ f₂ f₃ g₁ g₂ is →
                   hcs f₁ f₂ f₃ g₁ g₂ is ≡ f₂ · (hbs f₁ f₂ f₃ g₁ g₂ is)
{-# ATP axioms hcs-eq #-}

postulate hds-eq : ∀ f₁ f₂ f₃ g₁ g₂ is →
                   hds f₁ f₂ f₃ g₁ g₂ is ≡ g₂ · (hcs f₁ f₂ f₃ g₁ g₂ is)
{-# ATP axioms hds-eq #-}

postulate
  transfer    : D → D → D → D → D → D → D
  transfer-eq : ∀ f₁ f₂ f₃ g₁ g₂ is →
                transfer f₁ f₂ f₃ g₁ g₂ is ≡ f₃ · (hbs f₁ f₂ f₃ g₁ g₂ is)
{-# ATP axioms transfer-eq #-}

postulate
  abpTransfer : D → D → D → D → D
  abpTransfer-eq :
    ∀ b os₁ os₂ is →
      abpTransfer b os₁ os₂ is ≡
        transfer (send b) (ack b) (out b) (corrupt os₁) (corrupt os₂) is
{-# ATP axiom abpTransfer-eq #-}

------------------------------------------------------------------------------
-- ABP relations

-- Start state for the ABP.
S : D → D → D → D → D → D → D → D → D → Set
S b is os₁ os₂ as bs cs ds js =
  as ≡ send b · is · ds
  ∧ bs ≡ corrupt os₁ · as
  ∧ cs ≡ ack b · bs
  ∧ ds ≡ corrupt os₂ · cs
  ∧ js ≡ out b · bs
{-# ATP definition S #-}

-- Auxiliary state for the ABP.
S' : D → D → D → D → D → D → D → D → D → D → Set
S' b i' is' os₁' os₂' as' bs' cs' ds' js' =
  as' ≡ await b i' is' ds'  -- Typo in ds'.
  ∧ bs' ≡ corrupt os₁' · as'
  ∧ cs' ≡ ack (not b) · bs'
  ∧ ds' ≡ corrupt os₂' · (b ∷ cs')
  ∧ js' ≡ out (not b) · bs'
{-# ATP definition S' #-}

-- Auxiliary bisimulation.
B : D → D → Set
B is js = ∃[ b ] ∃[ os₁ ] ∃[ os₂ ] ∃[ as ] ∃[ bs ] ∃[ cs ] ∃[ ds ]
            Stream is
            ∧ Bit b
            ∧ Fair os₁
            ∧ Fair os₂
            ∧ S b is os₁ os₂ as bs cs ds js
{-# ATP definition B #-}

------------------------------------------------------------------------------
-- References
--
-- Dybjer, Peter and Sander, Herbert P. (1989). A Functional
-- Programming Approach to the Specification and Verification of
-- Concurrent Systems. Formal Aspects of Computing 1, pp. 303–319.


open import Common.Reflection
open import Common.Unit
open import Common.Prelude

macro

  t1 : (A : Set) -> Term -> Term -> Tactic
  t1 x y z = give y

  t2 : Term -> (A : Set) -> Term -> Tactic
  t2 x y z = give x

  t3 : Term -> Term -> (A : Set) -> QName -> Tactic
  t3 x y z n = give x

  t4 : Term -> Term -> (A : Set) -> A -> Tactic
  t4 x y z a = give x

f1 : {A : Set} -> A -> A
f1 x = t1 Unit x x

f2 : {A : Set} -> A -> A
f2 x = t2 x Unit x

y : Set
y = Unit

f3 : {A : Set} -> A -> A
f3 x = (t3 x x Unit y)

f4 : {A : Set} -> A -> A
f4 x = t4 x x Unit unit


open import Prelude

module Implicits.Resolution.Infinite.Algorithm.Soundness where

open import Data.Vec hiding (_∈_)
open import Data.List hiding (map)
open import Data.List.Any hiding (tail)
open Membership-≡
open import Data.Bool
open import Data.Unit.Base
open import Data.Maybe as Maybe hiding (All)
open import Coinduction
open import Data.Fin.Substitution
open import Implicits.Syntax
open import Implicits.Substitutions
open import Implicits.Substitutions.MetaType
open import Implicits.Substitutions.Lemmas
open import Implicits.Syntax.Type.Unification
open import Implicits.Syntax.Type.Unification.Lemmas renaming (sound to mgu-sound)
open import Implicits.Resolution.Infinite.Resolution
open import Implicits.Resolution.Infinite.Algorithm
open import Implicits.Resolution.Termination
open Inductive

open import Induction.WellFounded
open import Category.Functor
open import Category.Monad.Partiality as P
open import Category.Monad.Partiality.All using (All; module Alternative)
open Alternative renaming (sound to AllP-sound)

private module MaybeFunctor {f} = RawFunctor (functor {f})

open import Extensions.Bool as B hiding (All)
open import Relation.Binary.PropositionalEquality as PEq using (_≡_)
module PR = P.Reasoning (PEq.isEquivalence {A = Bool})

private
  module M = MetaTypeMetaSubst

  postulate lem₄ : ∀ {m ν} (a : MetaType m (suc ν)) u us →
                    from-meta (((M.open-meta a) M./ (us M.↑)) M./ (M.sub u))
                      ≡ (from-meta (a M./ (us M.↑tp))) tp[/tp from-meta u ]

open-↓-∀ : ∀ {ν m} {Δ : ICtx ν} (a : MetaType m (suc ν)) τ u us →
      Δ ⊢ (from-meta ((open-meta a) M./ (u ∷ us))) ↓ τ →
      Δ ⊢ (from-meta (∀' a M./ us)) ↓ τ
open-↓-∀ {Δ = Δ} a τ u us p = (i-tabs (from-meta u) (subst (λ v → Δ ⊢ v ↓ τ) (begin
      from-meta (M._/_ (open-meta a) (u ∷ us))
        ≡⟨ cong (λ v → from-meta (M._/_ (open-meta a) v)) (sym $ us↑-⊙-sub-u≡u∷us u us) ⟩
      from-meta ((open-meta a) M./ (us M.↑ M.⊙ (M.sub u)))
        ≡⟨ cong from-meta (/-⊙ (open-meta a)) ⟩
      from-meta ((open-meta a) M./ us M.↑ M./ (M.sub u))
        ≡⟨ lem₄ a u us ⟩
      from-meta (M._/_ a (us M.↑tp)) tp[/tp from-meta u ] ∎) p))
    where open MetaTypeMetaLemmas hiding (subst)

mutual

  delayed-resolve-sound : ∀ {ν} (Δ : ICtx ν) (a : Type ν) →
                          AllP (B.All (Δ ⊢ᵣ a)) (delayed-resolve Δ a)
  delayed-resolve-sound Δ a = later (♯ (resolve'-sound Δ a))

  resolve-context-sound : ∀ {ν m} (Δ : ICtx ν) (a : MetaType m ν) b {τ v} →
                          Maybe.All (λ u → Δ ⊢ from-meta (b M./ u) ↓ τ) v →
                          AllP (Maybe.All (λ u →
                            (Δ ⊢ from-meta (a M./ u) ⇒ from-meta (b M./ u) ↓ τ))
                          ) (resolve-context Δ a v)
  resolve-context-sound Δ a b {τ = τ} (just {x = u} px) = _
    ≅⟨ resolve-context-comp Δ a u ⟩P delayed-resolve-sound Δ (from-meta (M._/_ a u)) >>=-congP lem
    where
      lem : ∀ {v} → B.All (Δ ⊢ᵣ from-meta (a M./ u)) v →
            AllP (Maybe.All (λ u → Δ ⊢ from-meta (a M./ u) ⇒ from-meta (b M./ u) ↓ τ)) (map-bool u v)
      lem (true x) = now (just (i-iabs x px))
      lem (false) = now nothing
  resolve-context-sound Δ a b nothing = now nothing

  match-u-sound : ∀ {ν m} (Δ : ICtx ν) τ (r : MetaType m ν) → (r-acc : m<-Acc r) →
                  AllP (Maybe.All (λ u → (Δ ⊢ from-meta (r M./ u) ↓ τ))) (match-u Δ τ r r-acc)
  match-u-sound Δ τ (a ⇒ b) (acc rs) = _ ≅⟨ match-u-iabs-comp Δ τ a b rs ⟩P
    match-u-sound Δ τ b (rs _ (b-m<-a⇒b a b)) >>=-congP resolve-context-sound Δ a b
  match-u-sound Δ τ (∀' r) (acc rs) = _ ≅⟨ match-u-tabs-comp Δ τ r rs ⟩P
    match-u-sound Δ τ (open-meta r) _ >>=-congP lem
    where
      lem : ∀ {v} → Maybe.All (λ u → Δ ⊢ from-meta (open-meta r M./ u) ↓ τ) v →
            AllP (Maybe.All (λ u → Δ ⊢ ∀' (from-meta (r M./ u M.↑tp)) ↓ τ ))
                ((now ∘ (MaybeFunctor._<$>_ tail)) v)
      lem (just {x = u ∷ us} px) = now (just (open-↓-∀ r τ u us px))
      lem nothing = now nothing
  match-u-sound Δ τ (simpl x) (acc rs) with mgu (simpl x) τ | mgu-sound (simpl x) τ
  match-u-sound Δ τ (simpl x) (acc rs) | just us | just x/us≡τ =
    now (just (subst (λ z → Δ ⊢ z ↓ τ) (sym x/us≡τ) (i-simp τ)))
  match-u-sound Δ τ (simpl x) (acc rs) | nothing | nothing = now nothing

  match-sound : ∀ {ν} (Δ : ICtx ν) τ r →
                AllP (B.All (Δ ⊢ r ↓ τ)) (match Δ τ r)
  match-sound Δ τ r = _
    ≅⟨ match-comp Δ τ r ⟩P
    match-u-sound Δ τ (to-meta {zero} r) (m<-well-founded _) >>=-congP lem
    where
      eq : ∀ {ν} {a : Type ν} {s} → from-meta (to-meta {zero} a M./ s) ≡ a
      eq {a = a} {s = []} = begin
          from-meta (M._/_ (to-meta {zero} a) [])
        ≡⟨ cong (λ q → from-meta q) (MetaTypeMetaLemmas.id-vanishes (to-meta {zero} a)) ⟩
          from-meta (to-meta {zero} a)
        ≡⟨ to-meta-zero-vanishes ⟩
          a ∎

      lem : ∀ {v} → Maybe.All (λ u → (Δ ⊢ from-meta ((to-meta {zero} r) M./ u) ↓ τ)) v →
            AllP (B.All (Δ ⊢ r ↓ τ)) ((now ∘ is-just) v)
      lem (just px) = now (true (subst (λ z → Δ ⊢ z ↓ τ) eq px))
      lem nothing = now false

  match1st-recover-sound : ∀ {ν b} x (Δ ρs : ICtx ν) τ → B.All (Δ ⊢ x ↓ τ) b →
                          AllP (B.All (∃₂ λ r (r∈Δ : r ∈ (x ∷ ρs)) → Δ ⊢ r ↓ τ))
                                (match1st-recover Δ ρs τ b)
  match1st-recover-sound x Δ ρs τ (true p) = now (true (x , (here refl) , p))
  match1st-recover-sound x Δ ρs τ false = _
    ≅⟨ PR.sym (right-identity refl (match1st Δ ρs τ)) ⟩P
    match1st'-sound Δ ρs τ >>=-congP lem
    where
      lem : ∀ {v} → B.All (∃₂ λ r (r∈Δ : r ∈ ρs)→ Δ ⊢ r ↓ τ) v →
            AllP (B.All (∃₂ λ r (r∈Δ : r ∈ x ∷ ρs) → Δ ⊢ r ↓ τ)) (now v)
      lem (true (r , r∈ρs , p)) = now (true (r , (there r∈ρs) , p))
      lem false = now false

    -- {!match1st'-sound Δ ρs τ!}

  match1st'-sound : ∀ {ν} (Δ ρs : ICtx ν) τ →
                    AllP (B.All (∃₂ λ r (r∈Δ : r ∈ ρs) → Δ ⊢ r ↓ τ)) (match1st Δ ρs τ)
  match1st'-sound Δ [] τ = now false
  match1st'-sound Δ (x ∷ ρs) τ = _
    ≅⟨ match1st-comp Δ x ρs τ ⟩P
    match-sound Δ τ x >>=-congP match1st-recover-sound x Δ ρs τ

  resolve'-sound : ∀ {ν} (Δ : ICtx ν) r → AllP (B.All (Δ ⊢ᵣ r)) (resolve Δ r)
  resolve'-sound Δ (simpl x) = _
    ≅⟨ PR.sym (right-identity refl (match1st Δ Δ x)) ⟩P
    match1st'-sound Δ Δ x >>=-congP (λ x → now (B.all-map x (λ{ (r , r∈Δ , p) → r-simp r∈Δ p })))
  resolve'-sound Δ (a ⇒ b) = _
    ≅⟨ PR.sym (right-identity refl (resolve (a ∷ Δ) b)) ⟩P
    resolve'-sound (a ∷ Δ) b >>=-congP (λ x → now (B.all-map x r-iabs))
  resolve'-sound Δ (∀' r) = _
    ≅⟨ PR.sym (right-identity refl (resolve (ictx-weaken Δ) r)) ⟩P
    resolve'-sound (ictx-weaken Δ) r >>=-congP (λ x → now (B.all-map x r-tabs))

  -- Soundness means:
  -- for all terminating runs of the algorithm we have a finite resolution proof.
  sound : ∀ {ν} (Δ : ICtx ν) r → All (B.All (Δ ⊢ᵣ r)) (resolve Δ r)
  sound Δ r = AllP-sound (resolve'-sound Δ r)


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

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

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Application
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Introductions.SingleSubst
open import Definition.LogicalRelation.Substitution.Introductions.Pi
open import Definition.LogicalRelation.Substitution.Weakening
import Definition.LogicalRelation.Substitution.Irrelevance as S

open import Tools.Product
import Tools.PropositionalEquality as PE

import Data.Nat as Nat

-- Application of valid terms.
appᵛ : ∀ {F G rF lF lG rΠ lΠ t u Γ l}
       ([Γ] : ⊩ᵛ Γ)
       ([F] : Γ ⊩ᵛ⟨ l ⟩ F ^ [ rF , ι lF ] / [Γ])
       ([ΠFG] : Γ ⊩ᵛ⟨ l ⟩ Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ [ rΠ , ι lΠ ] / [Γ])
       ([t] : Γ ⊩ᵛ⟨ l ⟩ t ∷ Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ [ rΠ , ι lΠ ] / [Γ] / [ΠFG])
       ([u] : Γ ⊩ᵛ⟨ l ⟩ u ∷ F ^ [ rF , ι lF ] / [Γ] / [F])
     → Γ ⊩ᵛ⟨ l ⟩ t ∘ u ^ lΠ ∷ G [ u ] ^ [ rΠ , ι lG ] / [Γ] / substSΠ {F} {G} {u} [Γ] [F] [ΠFG] [u]
appᵛ {F} {G} {rF} {lF} {lG} {rΠ} {lΠ} {t} {u} [Γ] [F] [ΠFG] [t] [u] {σ = σ} ⊢Δ [σ] =
  let [G[u]] = substSΠ {F} {G} {u} [Γ] [F] [ΠFG] [u]
      [σF] = proj₁ ([F] ⊢Δ [σ])
      [σΠFG] = proj₁ ([ΠFG] ⊢Δ [σ])
      [σt] = proj₁ ([t] ⊢Δ [σ])
      [σu] = proj₁ ([u] ⊢Δ [σ])
      [σG[u]]  = proj₁ ([G[u]] ⊢Δ [σ])
      [σG[u]]′ = irrelevance′ (singleSubstLift G u) [σG[u]]
  in  irrelevanceTerm′ (PE.sym (singleSubstLift G u)) PE.refl PE.refl
                       [σG[u]]′ [σG[u]]
                       (appTerm PE.refl [σF] [σG[u]]′ [σΠFG] [σt] [σu])
  ,   (λ [σ′] [σ≡σ′] →
         let [σu′] = convTerm₂ [σF] (proj₁ ([F] ⊢Δ [σ′]))
                               (proj₂ ([F] ⊢Δ [σ]) [σ′] [σ≡σ′])
                               (proj₁ ([u] ⊢Δ [σ′]))
         in  irrelevanceEqTerm′ (PE.sym (singleSubstLift G u)) PE.refl PE.refl
                                [σG[u]]′ [σG[u]]
                                (app-congTerm [σF] [σG[u]]′ [σΠFG]
                                              (proj₂ ([t] ⊢Δ [σ]) [σ′] [σ≡σ′])
                                              [σu] [σu′]
                                              (proj₂ ([u] ⊢Δ [σ]) [σ′] [σ≡σ′])))

-- Application congurence of valid terms.
app-congᵛ : ∀ {F G rF lF lG rΠ lΠ t u a b Γ l}
            ([Γ] : ⊩ᵛ Γ)
            ([F] : Γ ⊩ᵛ⟨ l ⟩ F ^ [ rF , ι lF ] / [Γ])
            ([ΠFG] : Γ ⊩ᵛ⟨ l ⟩ Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ [ rΠ , ι lΠ ] / [Γ])
            ([t≡u] : Γ ⊩ᵛ⟨ l ⟩ t ≡ u ∷ Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ [ rΠ , ι lΠ ] / [Γ] / [ΠFG])
            ([a] : Γ ⊩ᵛ⟨ l ⟩ a ∷ F ^ [ rF , ι lF ] / [Γ] / [F])
            ([b] : Γ ⊩ᵛ⟨ l ⟩ b ∷ F ^ [ rF , ι lF ] / [Γ] / [F])
            ([a≡b] : Γ ⊩ᵛ⟨ l ⟩ a ≡ b ∷ F ^ [ rF , ι lF ] / [Γ] / [F])
          → Γ ⊩ᵛ⟨ l ⟩ t ∘ a ^ lΠ ≡ u ∘ b ^ lΠ ∷ G [ a ] ^ [ rΠ , ι lG ] / [Γ]
              / substSΠ {F} {G} {a} [Γ] [F] [ΠFG] [a]
app-congᵛ {F} {G} {rF} {lF} {lG} {rΠ} {a = a} [Γ] [F] [ΠFG] [t≡u] [a] [b] [a≡b] ⊢Δ [σ] =
  let [σF] = proj₁ ([F] ⊢Δ [σ])
      [G[a]]  = proj₁ (substSΠ {F} {G} {a} [Γ] [F] [ΠFG] [a] ⊢Δ [σ])
      [G[a]]′ = irrelevance′ (singleSubstLift G a) [G[a]]
      [σΠFG] = proj₁ ([ΠFG] ⊢Δ [σ])
      [σa] = proj₁ ([a] ⊢Δ [σ])
      [σb] = proj₁ ([b] ⊢Δ [σ])
  in  irrelevanceEqTerm′ (PE.sym (singleSubstLift G a)) PE.refl PE.refl [G[a]]′ [G[a]]
                         (app-congTerm [σF] [G[a]]′ [σΠFG] ([t≡u] ⊢Δ [σ])
                                       [σa] [σb] ([a≡b] ⊢Δ [σ]))

appᵛ↑ : ∀ {F F' G rF rF' lF lF' lG rΠ lΠ t u Γ l}
       (lF≤ : lF ≤ lΠ)
       (lG≤ : lG ≤ lΠ)
       ([Γ] : ⊩ᵛ Γ)
       ([F] : Γ ⊩ᵛ⟨ l ⟩ F ^ [ rF , ι lF ] / [Γ])
       ([F'] : Γ ⊩ᵛ⟨ l ⟩ F' ^ [ rF' , ι lF' ] / [Γ])
       ([G] : Γ ∙ F ^ [ rF , ι lF ] ⊩ᵛ⟨ l ⟩ G ^ [ rΠ , ι lG ] / [Γ] ∙ [F])
       ([ΠFG] : Γ ⊩ᵛ⟨ l ⟩ Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ [ rΠ , ι lΠ ] / [Γ])
       ([t] : Γ ∙ F' ^ [ rF' , ι lF' ] ⊩ᵛ⟨ l ⟩ t ∷ wk1 (Π F ^ rF ° lF ▹ G ° lG ° lΠ) ^ [ rΠ , ι lΠ ] / [Γ] ∙ [F'] / wk1ᵛ {A = Π F ^ rF ° lF ▹ G ° lG ° lΠ} {F = F'} [Γ] [F'] [ΠFG])
       ([u] : Γ ∙ F' ^ [ rF' , ι lF' ] ⊩ᵛ⟨ l ⟩ u ∷ wk1 F ^ [ rF , ι lF ] / [Γ] ∙ [F'] / wk1ᵛ {A = F} {F = F'} [Γ] [F'] [F])
         → Γ ∙ F' ^ [ rF' , ι lF' ] ⊩ᵛ⟨ l ⟩ t ∘ u ^ lΠ ∷ G [ u ]↑ ^ [ rΠ , ι lG ] / [Γ] ∙ [F'] / subst↑S {F'} {G} {u} {F' = F} [Γ] [F'] [F] [G] [u]
appᵛ↑ {F} {F'} {G} {rF} {rF'} {lF} {lF'} {lG} {rΠ} {lΠ} {t} {u} lF≤ lG≤ [Γ] [F] [F'] [G] [ΠFG] [t] [u] =
  let [G[u]] = subst↑S {F'} {G} {u} {F' = F} [Γ] [F'] [F] [G] [u]
      [wF] = wk1ᵛ {A = F} {F = F'} [Γ] [F'] [F]
      [wΠFG] = wk1ᵛ {A = Π F ^ rF ° lF ▹ G ° lG ° lΠ} {F = F'} [Γ] [F'] [ΠFG]
      [app] = appᵛ {F = wk1 F} {G = wk1d G} {t = t} {u = u} (_∙_ {A = F'} [Γ] [F']) [wF] [wΠFG] [t] [u]
  in S.irrelevanceTerm′ {A = wk1d G [ u ]} {A′ =  G [ u ]↑} {t = t ∘ u ^ lΠ} (PE.sym (wk1d[]-[]↑ G u)) PE.refl (_∙_ {A = F'} [Γ] [F']) (_∙_ {A = F'} [Γ] [F'])
                        (substSΠ {wk1 F} {wk1d G} {u} (_∙_ {A = F'} [Γ] [F']) [wF] [wΠFG] [u]) [G[u]] [app]


------------------------------------------------------------------------
-- The Agda standard library
--
-- Support for reflection
------------------------------------------------------------------------

module Reflection where

open import Data.Bool as Bool using (Bool); open Bool.Bool
open import Data.List using (List); open Data.List.List
open import Data.Nat using (ℕ) renaming (_≟_ to _≟-ℕ_)
open import Data.Nat.Show renaming (show to showNat)
open import Data.Float using (Float) renaming (_≟_ to _≟f_; show to showFloat)
open import Data.Char using (Char) renaming (_≟_ to _≟c_; show to showChar)
open import Data.String using (String) renaming (_≟_ to _≟s_; show to showString)
open import Data.Product
open import Function
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Binary.PropositionalEquality.TrustMe
open import Relation.Nullary
open import Relation.Nullary.Decidable as Dec
open import Relation.Nullary.Product

------------------------------------------------------------------------
-- Names

-- Names.

postulate Name : Set

{-# BUILTIN QNAME Name #-}

private
  primitive
    primQNameEquality : Name → Name → Bool

-- Equality of names is decidable.

infix 4 _==_ _≟-Name_

private

  _==_ : Name → Name → Bool
  _==_ = primQNameEquality

_≟-Name_ : Decidable {A = Name} _≡_
s₁ ≟-Name s₂ with s₁ == s₂
... | true  = yes trustMe
... | false = no whatever
  where postulate whatever : _

-- Names can be shown.

private
  primitive
    primShowQName : Name → String

showName : Name → String
showName = primShowQName

------------------------------------------------------------------------
-- Terms

-- Is the argument visible (explicit), hidden (implicit), or an
-- instance argument?

data Visibility : Set where
  visible hidden instance′ : Visibility

{-# BUILTIN HIDING   Visibility #-}
{-# BUILTIN VISIBLE  visible    #-}
{-# BUILTIN HIDDEN   hidden     #-}
{-# BUILTIN INSTANCE instance′  #-}

-- Arguments can be relevant or irrelevant.

data Relevance : Set where
  relevant irrelevant : Relevance

{-# BUILTIN RELEVANCE  Relevance  #-}
{-# BUILTIN RELEVANT   relevant   #-}
{-# BUILTIN IRRELEVANT irrelevant #-}

-- Arguments.

data Arg-info : Set where
  arg-info : (v : Visibility) (r : Relevance) → Arg-info

visibility : Arg-info → Visibility
visibility (arg-info v _) = v

relevance : Arg-info → Relevance
relevance (arg-info _ r) = r

data Arg (A : Set) : Set where
  arg : (i : Arg-info) (x : A) → Arg A

{-# BUILTIN ARGINFO    Arg-info #-}
{-# BUILTIN ARGARGINFO arg-info #-}
{-# BUILTIN ARG        Arg      #-}
{-# BUILTIN ARGARG     arg      #-}

-- Literals.

data Literal : Set where
  nat    : ℕ → Literal
  float  : Float → Literal
  char   : Char → Literal
  string : String → Literal
  name   : Name → Literal

{-# BUILTIN AGDALITERAL   Literal #-}
{-# BUILTIN AGDALITNAT    nat     #-}
{-# BUILTIN AGDALITFLOAT  float   #-}
{-# BUILTIN AGDALITCHAR   char    #-}
{-# BUILTIN AGDALITSTRING string  #-}
{-# BUILTIN AGDALITQNAME  name    #-}

-- Terms.

mutual
  data Term : Set where
    -- Variable applied to arguments.
    var     : (x : ℕ) (args : List (Arg Term)) → Term
    -- Constructor applied to arguments.
    con     : (c : Name) (args : List (Arg Term)) → Term
    -- Identifier applied to arguments.
    def     : (f : Name) (args : List (Arg Term)) → Term
    -- Different kinds of λ-abstraction.
    lam     : (v : Visibility) (t : Term) → Term
    -- Pattern matching λ-abstraction.
    pat-lam : (cs : List Clause) (args : List (Arg Term)) → Term
    -- Pi-type.
    pi      : (t₁ : Arg Type) (t₂ : Type) → Term
    -- A sort.
    sort    : Sort → Term
    -- A literal.
    lit     : Literal → Term
    -- Anything else.
    unknown : Term

  data Type : Set where
    el : (s : Sort) (t : Term) → Type

  data Sort : Set where
    -- A Set of a given (possibly neutral) level.
    set     : (t : Term) → Sort
    -- A Set of a given concrete level.
    lit     : (n : ℕ) → Sort
    -- Anything else.
    unknown : Sort

  data Pattern : Set where
    con    : Name → List (Arg Pattern) → Pattern
    dot    : Pattern
    var    : Pattern
    lit    : Literal → Pattern
    proj   : Name → Pattern
    absurd : Pattern

  data Clause : Set where
    clause        : List (Arg Pattern) → Term → Clause
    absurd-clause : List (Arg Pattern) → Clause

{-# BUILTIN AGDASORT    Sort    #-}
{-# BUILTIN AGDATYPE    Type    #-}
{-# BUILTIN AGDATERM    Term    #-}
{-# BUILTIN AGDAPATTERN Pattern #-}
{-# BUILTIN AGDACLAUSE  Clause  #-}

{-# BUILTIN AGDATERMVAR         var     #-}
{-# BUILTIN AGDATERMCON         con     #-}
{-# BUILTIN AGDATERMDEF         def     #-}
{-# BUILTIN AGDATERMLAM         lam     #-}
{-# BUILTIN AGDATERMEXTLAM      pat-lam #-}
{-# BUILTIN AGDATERMPI          pi      #-}
{-# BUILTIN AGDATERMSORT        sort    #-}
{-# BUILTIN AGDATERMLIT         lit     #-}
{-# BUILTIN AGDATERMUNSUPPORTED unknown #-}
{-# BUILTIN AGDATYPEEL          el      #-}
{-# BUILTIN AGDASORTSET         set     #-}
{-# BUILTIN AGDASORTLIT         lit     #-}
{-# BUILTIN AGDASORTUNSUPPORTED unknown #-}

{-# BUILTIN AGDAPATCON    con    #-}
{-# BUILTIN AGDAPATDOT    dot    #-}
{-# BUILTIN AGDAPATVAR    var    #-}
{-# BUILTIN AGDAPATLIT    lit    #-}
{-# BUILTIN AGDAPATPROJ   proj   #-}
{-# BUILTIN AGDAPATABSURD absurd #-}

{-# BUILTIN AGDACLAUSECLAUSE clause        #-}
{-# BUILTIN AGDACLAUSEABSURD absurd-clause #-}

------------------------------------------------------------------------
-- Definitions

-- Function definition.
data FunctionDef : Set where
  fun-def : Type → List Clause → FunctionDef

{-# BUILTIN AGDAFUNDEF    FunctionDef #-}
{-# BUILTIN AGDAFUNDEFCON fun-def     #-}

postulate
  -- Data type definition.
  Data-type : Set
  -- Record type definition.
  Record    : Set

{-# BUILTIN AGDADATADEF   Data-type #-}
{-# BUILTIN AGDARECORDDEF Record    #-}

-- Definitions.

data Definition : Set where
  function     : FunctionDef  → Definition
  data-type    : Data-type → Definition
  record′      : Record    → Definition
  constructor′ : Definition
  axiom        : Definition
  primitive′   : Definition

{-# BUILTIN AGDADEFINITION                Definition   #-}
{-# BUILTIN AGDADEFINITIONFUNDEF          function     #-}
{-# BUILTIN AGDADEFINITIONDATADEF         data-type    #-}
{-# BUILTIN AGDADEFINITIONRECORDDEF       record′      #-}
{-# BUILTIN AGDADEFINITIONDATACONSTRUCTOR constructor′ #-}
{-# BUILTIN AGDADEFINITIONPOSTULATE       axiom        #-}
{-# BUILTIN AGDADEFINITIONPRIMITIVE       primitive′   #-}

showLiteral : Literal → String
showLiteral (nat x)    = showNat x
showLiteral (float x)  = showFloat x
showLiteral (char x)   = showChar x
showLiteral (string x) = showString x
showLiteral (name x)   = showName x

private
  primitive
    primQNameType        : Name → Type
    primQNameDefinition  : Name → Definition
    primDataConstructors : Data-type → List Name

-- The type of the thing with the given name.

type : Name → Type
type = primQNameType

-- The definition of the thing with the given name.

definition : Name → Definition
definition = primQNameDefinition

-- The constructors of the given data type.

constructors : Data-type → List Name
constructors = primDataConstructors

------------------------------------------------------------------------
-- Term equality is decidable

-- Boring helper functions.

private

  cong₂′ : ∀ {A B C : Set} (f : A → B → C) {x y u v} →
          x ≡ y × u ≡ v → f x u ≡ f y v
  cong₂′ f = uncurry (cong₂ f)

  cong₃′ : ∀ {A B C D : Set} (f : A → B → C → D) {x y u v r s} →
           x ≡ y × u ≡ v × r ≡ s → f x u r ≡ f y v s
  cong₃′ f (refl , refl , refl) = refl

  arg₁ : ∀ {A i i′} {x x′ : A} → arg i x ≡ arg i′ x′ → i ≡ i′
  arg₁ refl = refl

  arg₂ : ∀ {A i i′} {x x′ : A} → arg i x ≡ arg i′ x′ → x ≡ x′
  arg₂ refl = refl

  arg-info₁ : ∀ {v v′ r r′} → arg-info v r ≡ arg-info v′ r′ → v ≡ v′
  arg-info₁ refl = refl

  arg-info₂ : ∀ {v v′ r r′} → arg-info v r ≡ arg-info v′ r′ → r ≡ r′
  arg-info₂ refl = refl

  cons₁ : ∀ {A : Set} {x y} {xs ys : List A} → x ∷ xs ≡ y ∷ ys → x ≡ y
  cons₁ refl = refl

  cons₂ : ∀ {A : Set} {x y} {xs ys : List A} → x ∷ xs ≡ y ∷ ys → xs ≡ ys
  cons₂ refl = refl

  var₁ : ∀ {x x′ args args′} → Term.var x args ≡ var x′ args′ → x ≡ x′
  var₁ refl = refl

  var₂ : ∀ {x x′ args args′} → Term.var x args ≡ var x′ args′ → args ≡ args′
  var₂ refl = refl

  con₁ : ∀ {c c′ args args′} → Term.con c args ≡ con c′ args′ → c ≡ c′
  con₁ refl = refl

  con₂ : ∀ {c c′ args args′} → Term.con c args ≡ con c′ args′ → args ≡ args′
  con₂ refl = refl

  def₁ : ∀ {f f′ args args′} → def f args ≡ def f′ args′ → f ≡ f′
  def₁ refl = refl

  def₂ : ∀ {f f′ args args′} → def f args ≡ def f′ args′ → args ≡ args′
  def₂ refl = refl

  lam₁ : ∀ {v v′ t t′} → lam v t ≡ lam v′ t′ → v ≡ v′
  lam₁ refl = refl

  lam₂ : ∀ {v v′ t t′} → lam v t ≡ lam v′ t′ → t ≡ t′
  lam₂ refl = refl

  pat-lam₁ : ∀ {cs cs′ args args′} → pat-lam cs args ≡ pat-lam cs′ args′ → cs ≡ cs′
  pat-lam₁ refl = refl

  pat-lam₂ : ∀ {cs cs′ args args′} → pat-lam cs args ≡ pat-lam cs′ args′ → args ≡ args′
  pat-lam₂ refl = refl

  pi₁ : ∀ {t₁ t₁′ t₂ t₂′} → pi t₁ t₂ ≡ pi t₁′ t₂′ → t₁ ≡ t₁′
  pi₁ refl = refl

  pi₂ : ∀ {t₁ t₁′ t₂ t₂′} → pi t₁ t₂ ≡ pi t₁′ t₂′ → t₂ ≡ t₂′
  pi₂ refl = refl

  sort₁ : ∀ {x y} → sort x ≡ sort y → x ≡ y
  sort₁ refl = refl

  lit₁ : ∀ {x y} → Term.lit x ≡ lit y → x ≡ y
  lit₁ refl = refl

  pcon₁ : ∀ {c c′ args args′} → Pattern.con c args ≡ con c′ args′ → c ≡ c′
  pcon₁ refl = refl

  pcon₂ : ∀ {c c′ args args′} → Pattern.con c args ≡ con c′ args′ → args ≡ args′
  pcon₂ refl = refl

  plit₁ : ∀ {x y} → Pattern.lit x ≡ lit y → x ≡ y
  plit₁ refl = refl

  pproj₁ : ∀ {x y} → proj x ≡ proj y → x ≡ y
  pproj₁ refl = refl

  set₁ : ∀ {x y} → set x ≡ set y → x ≡ y
  set₁ refl = refl

  slit₁ : ∀ {x y} → Sort.lit x ≡ lit y → x ≡ y
  slit₁ refl = refl

  el₁ : ∀ {s s′ t t′} → el s t ≡ el s′ t′ → s ≡ s′
  el₁ refl = refl

  el₂ : ∀ {s s′ t t′} → el s t ≡ el s′ t′ → t ≡ t′
  el₂ refl = refl

  nat₁ : ∀ {x y} → nat x ≡ nat y → x ≡ y
  nat₁ refl = refl

  float₁ : ∀ {x y} → float x ≡ float y → x ≡ y
  float₁ refl = refl

  char₁ : ∀ {x y} → char x ≡ char y → x ≡ y
  char₁ refl = refl

  string₁ : ∀ {x y} → string x ≡ string y → x ≡ y
  string₁ refl = refl

  name₁ : ∀ {x y} → name x ≡ name y → x ≡ y
  name₁ refl = refl

  clause₁ : ∀ {ps ps′ b b′} → clause ps b ≡ clause ps′ b′ → ps ≡ ps′
  clause₁ refl = refl

  clause₂ : ∀ {ps ps′ b b′} → clause ps b ≡ clause ps′ b′ → b ≡ b′
  clause₂ refl = refl

  absurd-clause₁ : ∀ {ps ps′} → absurd-clause ps ≡ absurd-clause ps′ → ps ≡ ps′
  absurd-clause₁ refl = refl

_≟-Visibility_ : Decidable (_≡_ {A = Visibility})
visible   ≟-Visibility visible   = yes refl
hidden    ≟-Visibility hidden    = yes refl
instance′ ≟-Visibility instance′ = yes refl
visible   ≟-Visibility hidden    = no λ()
visible   ≟-Visibility instance′ = no λ()
hidden    ≟-Visibility visible   = no λ()
hidden    ≟-Visibility instance′ = no λ()
instance′ ≟-Visibility visible   = no λ()
instance′ ≟-Visibility hidden    = no λ()

_≟-Relevance_ : Decidable (_≡_ {A = Relevance})
relevant   ≟-Relevance relevant   = yes refl
irrelevant ≟-Relevance irrelevant = yes refl
relevant   ≟-Relevance irrelevant = no λ()
irrelevant ≟-Relevance relevant   = no λ()

_≟-Arg-info_ : Decidable (_≡_ {A = Arg-info})
arg-info v r ≟-Arg-info arg-info v′ r′ =
  Dec.map′ (cong₂′ arg-info)
           < arg-info₁ , arg-info₂ >
           (v ≟-Visibility v′ ×-dec r ≟-Relevance r′)

_≟-Lit_ : Decidable (_≡_ {A = Literal})
nat x ≟-Lit nat x₁ = Dec.map′ (cong nat) nat₁ (x ≟-ℕ x₁)
nat x ≟-Lit float x₁ = no (λ ())
nat x ≟-Lit char x₁ = no (λ ())
nat x ≟-Lit string x₁ = no (λ ())
nat x ≟-Lit name x₁ = no (λ ())
float x ≟-Lit nat x₁ = no (λ ())
float x ≟-Lit float x₁ = Dec.map′ (cong float) float₁ (x ≟f x₁)
float x ≟-Lit char x₁ = no (λ ())
float x ≟-Lit string x₁ = no (λ ())
float x ≟-Lit name x₁ = no (λ ())
char x ≟-Lit nat x₁ = no (λ ())
char x ≟-Lit float x₁ = no (λ ())
char x ≟-Lit char x₁ = Dec.map′ (cong char) char₁ (x ≟c x₁)
char x ≟-Lit string x₁ = no (λ ())
char x ≟-Lit name x₁ = no (λ ())
string x ≟-Lit nat x₁ = no (λ ())
string x ≟-Lit float x₁ = no (λ ())
string x ≟-Lit char x₁ = no (λ ())
string x ≟-Lit string x₁ = Dec.map′ (cong string) string₁ (x ≟s x₁)
string x ≟-Lit name x₁ = no (λ ())
name x ≟-Lit nat x₁ = no (λ ())
name x ≟-Lit float x₁ = no (λ ())
name x ≟-Lit char x₁ = no (λ ())
name x ≟-Lit string x₁ = no (λ ())
name x ≟-Lit name x₁ = Dec.map′ (cong name) name₁ (x ≟-Name x₁)

mutual
  infix 4 _≟_ _≟-Args_ _≟-ArgType_

  _≟-ArgTerm_ : Decidable (_≡_ {A = Arg Term})
  arg i a ≟-ArgTerm arg i′ a′ =
    Dec.map′ (cong₂′ arg)
             < arg₁ , arg₂ >
             (i ≟-Arg-info i′ ×-dec a ≟ a′)

  _≟-ArgType_ : Decidable (_≡_ {A = Arg Type})
  arg i a ≟-ArgType arg i′ a′ =
    Dec.map′ (cong₂′ arg)
             < arg₁ , arg₂ >
             (i ≟-Arg-info i′ ×-dec a ≟-Type a′)

  _≟-ArgPattern_ : Decidable (_≡_ {A = Arg Pattern})
  arg i a ≟-ArgPattern arg i′ a′ =
    Dec.map′ (cong₂′ arg)
             < arg₁ , arg₂ >
             (i ≟-Arg-info i′ ×-dec a ≟-Pattern a′)

  _≟-Args_ : Decidable (_≡_ {A = List (Arg Term)})
  []       ≟-Args []       = yes refl
  (x ∷ xs) ≟-Args (y ∷ ys) = Dec.map′ (cong₂′ _∷_) < cons₁ , cons₂ > (x ≟-ArgTerm y ×-dec xs ≟-Args ys)
  []       ≟-Args (_ ∷ _)  = no λ()
  (_ ∷ _)  ≟-Args []       = no λ()

  _≟-Clause_ : Decidable (_≡_ {A = Clause})
  clause ps b ≟-Clause clause ps′ b′ = Dec.map′ (cong₂′ clause) < clause₁ , clause₂ > (ps ≟-ArgPatterns ps′ ×-dec b ≟ b′)
  absurd-clause ps ≟-Clause absurd-clause ps′ = Dec.map′ (cong absurd-clause) absurd-clause₁ (ps ≟-ArgPatterns ps′)
  clause _ _      ≟-Clause absurd-clause _ = no λ()
  absurd-clause _ ≟-Clause clause _ _      = no λ()

  _≟-Clauses_ : Decidable (_≡_ {A = List Clause})
  []       ≟-Clauses []       = yes refl
  (x ∷ xs) ≟-Clauses (y ∷ ys) = Dec.map′ (cong₂′ _∷_) < cons₁ , cons₂ > (x ≟-Clause y ×-dec xs ≟-Clauses ys)
  []       ≟-Clauses (_ ∷ _)  = no λ()
  (_ ∷ _)  ≟-Clauses []       = no λ()

  _≟-Pattern_ : Decidable (_≡_ {A = Pattern})
  con c ps ≟-Pattern con c′ ps′ = Dec.map′ (cong₂′ con) < pcon₁ , pcon₂ > (c ≟-Name c′ ×-dec ps ≟-ArgPatterns ps′)
  con x x₁ ≟-Pattern dot = no (λ ())
  con x x₁ ≟-Pattern var = no (λ ())
  con x x₁ ≟-Pattern lit x₂ = no (λ ())
  con x x₁ ≟-Pattern proj x₂ = no (λ ())
  con x x₁ ≟-Pattern absurd = no (λ ())
  dot ≟-Pattern con x x₁ = no (λ ())
  dot ≟-Pattern dot = yes refl
  dot ≟-Pattern var = no (λ ())
  dot ≟-Pattern lit x = no (λ ())
  dot ≟-Pattern proj x = no (λ ())
  dot ≟-Pattern absurd = no (λ ())
  var ≟-Pattern con x x₁ = no (λ ())
  var ≟-Pattern dot = no (λ ())
  var ≟-Pattern var = yes refl
  var ≟-Pattern lit x = no (λ ())
  var ≟-Pattern proj x = no (λ ())
  var ≟-Pattern absurd = no (λ ())
  lit x ≟-Pattern con x₁ x₂ = no (λ ())
  lit x ≟-Pattern dot = no (λ ())
  lit x ≟-Pattern var = no (λ ())
  lit l ≟-Pattern lit l′ = Dec.map′ (cong lit) plit₁ (l ≟-Lit l′)
  lit x ≟-Pattern proj x₁ = no (λ ())
  lit x ≟-Pattern absurd = no (λ ())
  proj x ≟-Pattern con x₁ x₂ = no (λ ())
  proj x ≟-Pattern dot = no (λ ())
  proj x ≟-Pattern var = no (λ ())
  proj x ≟-Pattern lit x₁ = no (λ ())
  proj x ≟-Pattern proj x₁ = Dec.map′ (cong proj) pproj₁ (x ≟-Name x₁)
  proj x ≟-Pattern absurd = no (λ ())
  absurd ≟-Pattern con x x₁ = no (λ ())
  absurd ≟-Pattern dot = no (λ ())
  absurd ≟-Pattern var = no (λ ())
  absurd ≟-Pattern lit x = no (λ ())
  absurd ≟-Pattern proj x = no (λ ())
  absurd ≟-Pattern absurd = yes refl

  _≟-ArgPatterns_ : Decidable (_≡_ {A = List (Arg Pattern)})
  []       ≟-ArgPatterns []       = yes refl
  (x ∷ xs) ≟-ArgPatterns (y ∷ ys) = Dec.map′ (cong₂′ _∷_) < cons₁ , cons₂ > (x ≟-ArgPattern y ×-dec xs ≟-ArgPatterns ys)
  []       ≟-ArgPatterns (_ ∷ _)  = no λ()
  (_ ∷ _)  ≟-ArgPatterns []       = no λ()

  _≟_ : Decidable (_≡_ {A = Term})
  var x args ≟ var x′ args′ = Dec.map′ (cong₂′ var) < var₁ , var₂ > (x ≟-ℕ x′          ×-dec args ≟-Args args′)
  con c args ≟ con c′ args′ = Dec.map′ (cong₂′ con) < con₁ , con₂ > (c ≟-Name c′       ×-dec args ≟-Args args′)
  def f args ≟ def f′ args′ = Dec.map′ (cong₂′ def) < def₁ , def₂ > (f ≟-Name f′       ×-dec args ≟-Args args′)
  lam v t    ≟ lam v′ t′    = Dec.map′ (cong₂′ lam) < lam₁ , lam₂ > (v ≟-Visibility v′ ×-dec t ≟ t′)
  pat-lam cs args ≟ pat-lam cs′ args′ =
                              Dec.map′ (cong₂′ pat-lam) < pat-lam₁ , pat-lam₂ > (cs ≟-Clauses cs′ ×-dec args ≟-Args args′)
  pi t₁ t₂   ≟ pi t₁′ t₂′   = Dec.map′ (cong₂′ pi)  < pi₁  , pi₂  > (t₁ ≟-ArgType t₁′  ×-dec t₂ ≟-Type t₂′)
  sort s     ≟ sort s′      = Dec.map′ (cong sort)  sort₁           (s ≟-Sort s′)
  lit l      ≟ lit l′       = Dec.map′ (cong lit)   lit₁           (l ≟-Lit l′)
  unknown    ≟ unknown      = yes refl

  var x args ≟ con c args′ = no λ()
  var x args ≟ def f args′ = no λ()
  var x args ≟ lam v t     = no λ()
  var x args ≟ pi t₁ t₂    = no λ()
  var x args ≟ sort _      = no λ()
  var x args ≟ lit _      = no λ()
  var x args ≟ unknown     = no λ()
  con c args ≟ var x args′ = no λ()
  con c args ≟ def f args′ = no λ()
  con c args ≟ lam v t     = no λ()
  con c args ≟ pi t₁ t₂    = no λ()
  con c args ≟ sort _      = no λ()
  con c args ≟ lit _      = no λ()
  con c args ≟ unknown     = no λ()
  def f args ≟ var x args′ = no λ()
  def f args ≟ con c args′ = no λ()
  def f args ≟ lam v t     = no λ()
  def f args ≟ pi t₁ t₂    = no λ()
  def f args ≟ sort _      = no λ()
  def f args ≟ lit _      = no λ()
  def f args ≟ unknown     = no λ()
  lam v t    ≟ var x args  = no λ()
  lam v t    ≟ con c args  = no λ()
  lam v t    ≟ def f args  = no λ()
  lam v t    ≟ pi t₁ t₂    = no λ()
  lam v t    ≟ sort _      = no λ()
  lam v t    ≟ lit _      = no λ()
  lam v t    ≟ unknown     = no λ()
  pi t₁ t₂   ≟ var x args  = no λ()
  pi t₁ t₂   ≟ con c args  = no λ()
  pi t₁ t₂   ≟ def f args  = no λ()
  pi t₁ t₂   ≟ lam v t     = no λ()
  pi t₁ t₂   ≟ sort _      = no λ()
  pi t₁ t₂   ≟ lit _      = no λ()
  pi t₁ t₂   ≟ unknown     = no λ()
  sort _     ≟ var x args  = no λ()
  sort _     ≟ con c args  = no λ()
  sort _     ≟ def f args  = no λ()
  sort _     ≟ lam v t     = no λ()
  sort _     ≟ pi t₁ t₂    = no λ()
  sort _     ≟ lit _       = no λ()
  sort _     ≟ unknown     = no λ()
  lit _     ≟ var x args  = no λ()
  lit _     ≟ con c args  = no λ()
  lit _     ≟ def f args  = no λ()
  lit _     ≟ lam v t     = no λ()
  lit _     ≟ pi t₁ t₂    = no λ()
  lit _     ≟ sort _      = no λ()
  lit _     ≟ unknown     = no λ()
  unknown    ≟ var x args  = no λ()
  unknown    ≟ con c args  = no λ()
  unknown    ≟ def f args  = no λ()
  unknown    ≟ lam v t     = no λ()
  unknown    ≟ pi t₁ t₂    = no λ()
  unknown    ≟ sort _      = no λ()
  unknown    ≟ lit _       = no λ()
  pat-lam _ _ ≟ var x args  = no λ()
  pat-lam _ _ ≟ con c args  = no λ()
  pat-lam _ _ ≟ def f args  = no λ()
  pat-lam _ _ ≟ lam v t     = no λ()
  pat-lam _ _ ≟ pi t₁ t₂    = no λ()
  pat-lam _ _ ≟ sort _      = no λ()
  pat-lam _ _ ≟ lit _       = no λ()
  pat-lam _ _ ≟ unknown     = no λ()
  var x args  ≟ pat-lam _ _ = no λ()
  con c args  ≟ pat-lam _ _ = no λ()
  def f args  ≟ pat-lam _ _ = no λ()
  lam v t     ≟ pat-lam _ _ = no λ()
  pi t₁ t₂    ≟ pat-lam _ _ = no λ()
  sort _      ≟ pat-lam _ _ = no λ()
  lit _       ≟ pat-lam _ _ = no λ()
  unknown     ≟ pat-lam _ _ = no λ()

  _≟-Type_ : Decidable (_≡_ {A = Type})
  el s t ≟-Type el s′ t′ = Dec.map′ (cong₂′ el) < el₁ , el₂ > (s ≟-Sort s′ ×-dec t ≟ t′)

  _≟-Sort_ : Decidable (_≡_ {A = Sort})
  set t   ≟-Sort set t′  = Dec.map′ (cong set) set₁ (t ≟ t′)
  lit n   ≟-Sort lit n′  = Dec.map′ (cong lit) slit₁ (n ≟-ℕ n′)
  unknown ≟-Sort unknown = yes refl
  set _   ≟-Sort lit _   = no λ()
  set _   ≟-Sort unknown = no λ()
  lit _   ≟-Sort set _   = no λ()
  lit _   ≟-Sort unknown = no λ()
  unknown ≟-Sort set _   = no λ()
  unknown ≟-Sort lit _   = no λ()


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Properties.Symmetry {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Properties
import Definition.Typed.Weakening as Wk
open import Definition.LogicalRelation
open import Definition.LogicalRelation.ShapeView
open import Definition.LogicalRelation.Irrelevance
open import Definition.LogicalRelation.Properties.Conversion

open import Tools.Product
import Tools.PropositionalEquality as PE

mutual
  -- Helper function for symmetry of type equality using shape views.
  symEqT : ∀ {Γ A B l l′} {[A] : Γ ⊩⟨ l ⟩ A} {[B] : Γ ⊩⟨ l′ ⟩ B}
         → ShapeView Γ l l′ A B [A] [B]
         → Γ ⊩⟨ l  ⟩ A ≡ B / [A]
         → Γ ⊩⟨ l′ ⟩ B ≡ A / [B]
  symEqT (ℕᵥ D D′) A≡B = red D
  symEqT (Emptyᵥ D D′) A≡B = red D
  symEqT (Unitᵥ D D′) A≡B = red D
  symEqT (ne (ne K D neK K≡K) (ne K₁ D₁ neK₁ K≡K₁)) (ne₌ M D′ neM K≡M)
         rewrite whrDet* (red D′ , ne neM) (red D₁ , ne neK₁) =
    ne₌ _ D neK
        (~-sym K≡M)
  symEqT {Γ = Γ} (Bᵥ W (Bᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                       (Bᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁))
         (B₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]) =
    let ΠF₁G₁≡ΠF′G′   = whrDet* (red D₁ , ⟦ W ⟧ₙ) (D′ , ⟦ W ⟧ₙ)
        F₁≡F′ , G₁≡G′ = B-PE-injectivity W ΠF₁G₁≡ΠF′G′
        [F₁≡F] : ∀ {Δ} {ρ} [ρ] ⊢Δ → _
        [F₁≡F] {Δ} {ρ} [ρ] ⊢Δ =
          let ρF′≡ρF₁ ρ = PE.cong (wk ρ) (PE.sym F₁≡F′)
              [ρF′] {ρ} [ρ] ⊢Δ = PE.subst (λ x → Δ ⊩⟨ _ ⟩ wk ρ x) F₁≡F′ ([F]₁ [ρ] ⊢Δ)
          in  irrelevanceEq′ {Δ} (ρF′≡ρF₁ ρ)
                             ([ρF′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ)
                             (symEq ([F] [ρ] ⊢Δ) ([ρF′] [ρ] ⊢Δ)
                                    ([F≡F′] [ρ] ⊢Δ))
    in  B₌ _ _ (red D) (≅-sym (PE.subst (λ x → Γ ⊢ ⟦ W ⟧ F ▹ G ≅ x) (PE.sym ΠF₁G₁≡ΠF′G′) A≡B))
          [F₁≡F]
          (λ {ρ} [ρ] ⊢Δ [a] →
               let ρG′a≡ρG₁′a = PE.cong (λ x → wk (lift ρ) x [ _ ]) (PE.sym G₁≡G′)
                   [ρG′a] = PE.subst (λ x → _ ⊩⟨ _ ⟩ wk (lift ρ) x [ _ ]) G₁≡G′
                                     ([G]₁ [ρ] ⊢Δ [a])
                   [a]₁ = convTerm₁ ([F]₁ [ρ] ⊢Δ) ([F] [ρ] ⊢Δ) ([F₁≡F] [ρ] ⊢Δ) [a]
               in  irrelevanceEq′ ρG′a≡ρG₁′a
                                  [ρG′a]
                                  ([G]₁ [ρ] ⊢Δ [a])
                                  (symEq ([G] [ρ] ⊢Δ [a]₁) [ρG′a]
                                         ([G≡G′] [ρ] ⊢Δ [a]₁)))
  symEqT (Uᵥ (Uᵣ _ _ _) (Uᵣ _ _ _)) A≡B = PE.refl
  symEqT (emb⁰¹ x) A≡B = symEqT x A≡B
  symEqT (emb¹⁰ x) A≡B = symEqT x A≡B

  -- Symmetry of type equality.
  symEq : ∀ {Γ A B l l′} ([A] : Γ ⊩⟨ l ⟩ A) ([B] : Γ ⊩⟨ l′ ⟩ B)
        → Γ ⊩⟨ l ⟩ A ≡ B / [A]
        → Γ ⊩⟨ l′ ⟩ B ≡ A / [B]
  symEq [A] [B] A≡B = symEqT (goodCases [A] [B] A≡B) A≡B

symNeutralTerm : ∀ {t u A Γ}
               → Γ ⊩neNf t ≡ u ∷ A
               → Γ ⊩neNf u ≡ t ∷ A
symNeutralTerm (neNfₜ₌ neK neM k≡m) = neNfₜ₌ neM neK (~-sym k≡m)

symNatural-prop : ∀ {Γ k k′}
                → [Natural]-prop Γ k k′
                → [Natural]-prop Γ k′ k
symNatural-prop (sucᵣ (ℕₜ₌ k k′ d d′ t≡u prop)) =
  sucᵣ (ℕₜ₌ k′ k d′ d (≅ₜ-sym t≡u) (symNatural-prop prop))
symNatural-prop zeroᵣ = zeroᵣ
symNatural-prop (ne prop) = ne (symNeutralTerm prop)

symEmpty-prop : ∀ {Γ k k′}
              → [Empty]-prop Γ k k′
              → [Empty]-prop Γ k′ k
symEmpty-prop (ne prop) = ne (symNeutralTerm prop)

-- Symmetry of term equality.
symEqTerm : ∀ {l Γ A t u} ([A] : Γ ⊩⟨ l ⟩ A)
          → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / [A]
          → Γ ⊩⟨ l ⟩ u ≡ t ∷ A / [A]
symEqTerm (Uᵣ′ .⁰ 0<1 ⊢Γ) (Uₜ₌ A B d d′ typeA typeB A≡B [A] [B] [A≡B]) =
  Uₜ₌ B A d′ d typeB typeA (≅ₜ-sym A≡B) [B] [A] (symEq [A] [B] [A≡B])
symEqTerm (ℕᵣ D) (ℕₜ₌ k k′ d d′ t≡u prop) =
  ℕₜ₌ k′ k d′ d (≅ₜ-sym t≡u) (symNatural-prop prop)
symEqTerm (Emptyᵣ D) (Emptyₜ₌ k k′ d d′ t≡u prop) =
  Emptyₜ₌ k′ k d′ d (≅ₜ-sym t≡u) (symEmpty-prop prop)
symEqTerm (Unitᵣ D) (Unitₜ₌ ⊢t ⊢u) =
  Unitₜ₌ ⊢u ⊢t
symEqTerm (ne′ K D neK K≡K) (neₜ₌ k m d d′ nf) =
  neₜ₌ m k d′ d (symNeutralTerm nf)
symEqTerm (Bᵣ′ BΠ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
          (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g]) =
  Πₜ₌ g f d′ d funcG funcF (≅ₜ-sym f≡g) [g] [f]
      (λ ρ ⊢Δ [a] → symEqTerm ([G] ρ ⊢Δ [a]) ([f≡g] ρ ⊢Δ [a]))
symEqTerm (Bᵣ′ BΣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
          (Σₜ₌ p r d d′ pProd rProd p≅r [t] [u] [fstp] [fstr] [fst≡] [snd≡]) =
  let ⊢Γ = wf ⊢F
      [Gfstp≡Gfstr] = G-ext Wk.id ⊢Γ [fstp] [fstr] [fst≡]
  in  Σₜ₌ r p d′ d rProd pProd (≅ₜ-sym p≅r) [u] [t] [fstr] [fstp]
          (symEqTerm ([F] Wk.id ⊢Γ) [fst≡])
          (convEqTerm₁
            ([G] Wk.id ⊢Γ [fstp]) ([G] Wk.id ⊢Γ [fstr])
            [Gfstp≡Gfstr]
            (symEqTerm ([G] Wk.id ⊢Γ [fstp]) [snd≡]))
symEqTerm (emb 0<1 x) t≡u = symEqTerm x t≡u


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

open import Base

module Homotopy.VanKampen.Guide where

open import Homotopy.Truncation
open import Homotopy.Connected

record legend i (city : Set i) : Set (suc i) where
  constructor leg_,_,_
  field
    name : Set i
    loc : name → city
    all-listed : ∀ c → [ hfiber loc c ]

id-legend : ∀ {i} (A : Set i) → legend i A
id-legend A = leg A , id A , λ x → proj $ x , refl

private
  module Book {i} {city : Set i} {l : legend i city} where
    open legend l

    private
      data #guide : Set i where
        #point : name → #guide

    guide : Set i
    guide = #guide

    point : name → guide
    point = #point

    postulate  -- HIT
      route : ∀ n₁ n₂ → loc n₁ ≡ loc n₂ → point n₁ ≡ point n₂

    guide-rec : ∀ {l} (P : guide → Set l)
      (point* : (n : name) → P (point n))
      (route* : ∀ n₁ n₂ (p : loc n₁ ≡ loc n₂)
        → transport P (route n₁ n₂ p) (point* n₁) ≡ point* n₂)
      → (x : guide) → P x
    guide-rec P point* route* (#point n) = point* n

    postulate  -- HIT
      guide-β-route : ∀ {l} (P : guide → Set l)
        (point* : (n : name) → P (point n))
        (route* : ∀ n₁ n₂ (p : loc n₁ ≡ loc n₂)
          → transport P (route n₁ n₂ p) (point* n₁) ≡ point* n₂)
        n₁ n₂ (p : loc n₁ ≡ loc n₂)
        → apd (guide-rec {l} P point* route*) (route n₁ n₂ p)
        ≡ route* n₁ n₂ p

    guide-rec-nondep : ∀ {l} (P : Set l) (point* : name → P)
      (route* : ∀ n₁ n₂ → loc n₁ ≡ loc n₂ → point* n₁ ≡ point* n₂)
      → (guide → P)
    guide-rec-nondep P point* route* (#point n) = point* n

    postulate  -- HIT
      guide-β-route-nondep : ∀ {l} (P : Set l) (point* : name → P)
        (route* : ∀ n₁ n₂ → loc n₁ ≡ loc n₂ → point* n₁ ≡ point* n₂)
        n₁ n₂ (p : loc n₁ ≡ loc n₂)
        → ap (guide-rec-nondep P point* route*) (route n₁ n₂ p)
        ≡ route* n₁ n₂ p

open Book public hiding (guide)

module _ {i} {city : Set i} (l : legend i city) where
  open legend l

  guide : Set i
  guide = Book.guide {i} {city} {l}

  visit : guide → city
  visit = guide-rec-nondep city loc (λ _ _ p → p)

  visit-β-route : ∀ n₁ n₂ (p : loc n₁ ≡ loc n₂) → ap visit (route n₁ n₂ p) ≡ p
  visit-β-route = guide-β-route-nondep city loc (λ _ _ p → p)

  private
    drawn-as-one : ∀ c → is-contr (π₀ $ hfiber visit c)
    drawn-as-one c = []-extend-nondep
      ⦃ is-connected-is-prop ⟨0⟩ ⦄
      ( λ {(n₁ , p₁) → proj (point n₁ , p₁) , π₀-extend
        ⦃ λ _ → ≡-is-set $ π₀-is-set $ hfiber visit c ⦄
        ( uncurry $ guide-rec
          (λ x → ∀ p₂ → proj (x , p₂) ≡ proj (point n₁ , p₁))
          (λ n₂ p₂ → ap proj let p₃ = p₂ ∘ ! p₁ in
            Σ-eq (route n₂ n₁ p₃) $
              transport (λ x → visit x ≡ c) (route n₂ n₁ p₃) p₂
                ≡⟨ trans-app≡cst visit c (route n₂ n₁ p₃) p₂ ⟩
              ! (ap visit (route n₂ n₁ p₃)) ∘ p₂
                ≡⟨ ap (λ x → ! x ∘ p₂) $ visit-β-route n₂ n₁ p₃ ⟩
              ! (p₂ ∘ ! p₁) ∘ p₂
                ≡⟨ ap (λ x → x ∘ p₂) $ opposite-concat p₂ (! p₁) ⟩
              (! (! p₁) ∘ ! p₂) ∘ p₂
                ≡⟨ concat-assoc (! (! p₁)) (! p₂) p₂ ⟩
              ! (! p₁) ∘ (! p₂ ∘ p₂)
                ≡⟨ ap (λ x → ! (! p₁) ∘ x) $ opposite-left-inverse p₂ ⟩
              ! (! p₁) ∘ refl
                ≡⟨ refl-right-unit $ ! (! p₁) ⟩
              ! (! p₁)
                ≡⟨ opposite-opposite p₁ ⟩∎
              p₁
                ∎)
          (λ _ _ _ → funext λ _ → prop-has-all-paths (π₀-is-set _ _ _) _ _))})
      (all-listed c)

  private
    visit-fiber-rec′ : ∀ {j} (P : city → Set j)
      (h₀-point : ∀ n → P (loc n))
      (h₁-route : ∀ n₁ n₂ r → transport P r (h₀-point n₁) ≡ h₀-point n₂)
      → (∀ c → hfiber visit c → P c)
    visit-fiber-rec′ P h₀-p h₁-r c (pt , p₁) =
      transport P p₁ $ guide-rec (P ◯ visit) h₀-p
        (λ n₁ n₂ r →
            transport (P ◯ visit) (route n₁ n₂ r) (h₀-p n₁)
              ≡⟨ ! $ trans-ap P visit (route n₁ n₂ r) $ h₀-p n₁ ⟩
            transport P (ap visit $ route n₁ n₂ r) (h₀-p n₁)
              ≡⟨ ap (λ x → transport P x $ h₀-p n₁) $ visit-β-route n₁ n₂ r ⟩
            transport P r (h₀-p n₁)
              ≡⟨ h₁-r n₁ n₂ r ⟩∎
            h₀-p n₂
              ∎) pt

  visit-fiber-rec : ∀ {j} (P : city → Set j)
    ⦃ _ : ∀ (c : city) → is-set $ P c ⦄
    (h₀-point : ∀ n → P (loc n))
    (h₁-route : ∀ n₁ n₂ r → transport P r (h₀-point n₁) ≡ h₀-point n₂)
    → (∀ c → P c)
  visit-fiber-rec P ⦃ P-is-set ⦄ h₀-p h₁-r c =
    π₀-extend-nondep ⦃ P-is-set c ⦄
    (visit-fiber-rec′ P h₀-p h₁-r c)
    (π₁ $ drawn-as-one c)

  visit-fiber-β-loc : ∀ {j} (P : city → Set j)
    ⦃ P-is-set : ∀ (c : city) → is-set $ P c ⦄
    (h₀-p : ∀ n → P (loc n))
    (h₁-r : ∀ n₁ n₂ r → transport P r (h₀-p n₁) ≡ h₀-p n₂)
    → ∀ n → visit-fiber-rec P ⦃ P-is-set ⦄ h₀-p h₁-r (loc n) ≡ h₀-p n
  visit-fiber-β-loc P ⦃ P-is-set ⦄ h₀-p h₁-r n =
    visit-fiber-rec P ⦃ P-is-set ⦄ h₀-p h₁-r (loc n)
      ≡⟨ ap (π₀-extend-nondep ⦃ P-is-set $ loc n ⦄ (visit-fiber-rec′ P h₀-p h₁-r $ loc n))
            $ ! $ π₂ (drawn-as-one $ loc n) (proj $ point n , refl) ⟩∎
    h₀-p n
      ∎

  private
    loc-fiber-rec′ : ∀ {j} (P : city → Set j)
      (h₀-point : ∀ n → P (loc n))
      → (∀ c → hfiber loc c → P c)
    loc-fiber-rec′ P h₀-p c (n , p) = transport P p (h₀-p n)

  loc-fiber-rec : ∀ {j} (P : city → Set j)
    ⦃ _ : ∀ c → is-prop $ P c ⦄
    (h₀-point : ∀ n → P (loc n))
    → (∀ c → P c)
  loc-fiber-rec P ⦃ P-is-prop ⦄ h₀-p c = []-extend-nondep ⦃ P-is-prop c ⦄
    (loc-fiber-rec′ P h₀-p c) (all-listed c)

  loc-fiber-β-loc : ∀ {j} (P : city → Set j)
    ⦃ P-is-prop : ∀ (c : city) → is-prop $ P c ⦄
    (h₀-p : ∀ n → P (loc n))
    → ∀ n → loc-fiber-rec P ⦃ P-is-prop ⦄ h₀-p (loc n) ≡ h₀-p n
  loc-fiber-β-loc P ⦃ P-is-prop ⦄ h₀-p n =
    loc-fiber-rec P ⦃ P-is-prop ⦄ h₀-p (loc n)
      ≡⟨ ap ([]-extend-nondep ⦃ P-is-prop $ loc n ⦄ (loc-fiber-rec′ P h₀-p $ loc n))
            $ prop-has-all-paths []-is-prop (all-listed $ loc n) $ proj $ n , refl ⟩∎
    h₀-p n
      ∎

-- Did I preserve the computational contents for identity legend?
private
  module IdTest {i} {city : Set i} where
    open legend (id-legend city)

    visit-fiber-β-id : ∀ {j} (P : city → Set j)
      ⦃ P-is-set : ∀ (c : city) → is-set $ P c ⦄
      (h₀-p : ∀ n → P n)
      (h₁-r : ∀ n₁ n₂ r → transport P r (h₀-p n₁) ≡ h₀-p n₂)
      → ∀ n → visit-fiber-rec (id-legend city) P ⦃ P-is-set ⦄ h₀-p h₁-r n ≡ h₀-p n
    visit-fiber-β-id P ⦃ P-is-set ⦄ h₀-p h₁-r n = refl

    loc-fiber-β-id : ∀ {j} (P : city → Set j)
      ⦃ P-is-prop : ∀ (c : city) → is-prop $ P c ⦄
      (h₀-p : ∀ n → P n)
      → ∀ n → loc-fiber-rec (id-legend city) P ⦃ P-is-prop ⦄ h₀-p n ≡ h₀-p n
    loc-fiber-β-id P ⦃ P-is-prop ⦄ h₀-p n = refl


-- Soundness of term equality
module Semantics.Soundness where

open import Syntax.Types
open import Syntax.Context renaming (_,_ to _,,_)
open import Syntax.Terms
open import Syntax.Equality
open import Syntax.Substitution

open import Semantics.Types
open import Semantics.Context
open import Semantics.Terms
open import Semantics.Substitution
open import Semantics.Bind

open import CategoryTheory.Categories using (ext)
open import CategoryTheory.BCCCs.Cartesian using (Product)
open import CategoryTheory.BCCCs
open import CategoryTheory.Instances.Reactive renaming (top to Top)
open import CategoryTheory.NatTrans
open import CategoryTheory.Monad
open import TemporalOps.Diamond
open import TemporalOps.OtherOps

open import Data.Sum
open import Data.Product using (_,_ ; proj₁ ; proj₂)
open import Relation.Binary.PropositionalEquality as ≡
    using (_≡_ ; refl ; sym ; trans ; cong ; cong₂ ; subst)

open ≡.≡-Reasoning
open Monad M-◇
open import Holes.Term using (⌞_⌟)
open import Holes.Cong.Propositional

open ⟦Kit⟧ ⟦𝒯erm⟧
open Kit 𝒯erm
open ⟦K⟧ ⟦𝒯erm⟧
open K 𝒯erm

mutual
    -- Soundness of term equality: equal terms have equal denotations
    sound : ∀{A Γ} {M₁ M₂ : Γ ⊢ A}
             -> Γ ⊢ M₁ ≡ M₂ ∷ A
             -> ⟦ M₁ ⟧ₘ ≈ ⟦ M₂ ⟧ₘ
    sound (refl M) = refl
    sound (Eq.sym eq) = ≡.sym (sound eq)
    sound (Eq.trans eq₁ eq₂) = ≡.trans (sound eq₁) (sound eq₂)

    sound (β-lam N M) {n} {⟦Γ⟧} rewrite subst-sound M N {n} {⟦Γ⟧} = refl
    sound (β-fst M N) = refl
    sound (β-snd M N) = refl
    sound (β-inl M N₁ N₂) {n} {⟦Γ⟧} rewrite subst-sound M N₁ {n} {⟦Γ⟧} = refl
    sound (β-inr M N₁ N₂) {n} {⟦Γ⟧} rewrite subst-sound M N₂ {n} {⟦Γ⟧} = refl
    sound (β-sig N M) {n} {⟦Γ⟧} rewrite subst-sound M N {n} {⟦Γ⟧} = refl

    sound (η-lam {A} M) {n} {⟦Γ⟧} = ext λ ⟦A⟧ →
                        cong (λ x → x ⟦A⟧) (≡.sym (⟦𝓌⟧ (A now) M {n} {⟦Γ⟧ , ⟦A⟧}))
    sound (η-pair M) = ≡.sym (⊗-η-exp {m = ⟦ M ⟧ₘ})
    sound (η-unit M) = refl
    sound (η-sum M) {n} {⟦Γ⟧} with ⟦ M ⟧ₘ n ⟦Γ⟧
    sound (η-sum M) {n} {a} | inj₁ _ = refl
    sound (η-sum M) {n} {a} | inj₂ _ = refl
    sound (η-sig M) = refl
    sound {Γ = Γ} (η-evt {A} M) {n} {a}
        rewrite bind-to->>= Γ (⟦ M ⟧ₘ) (η.at ⟦ A ⟧ₜ ∘ π₂) n a
              = ≡.sym (>>=-unit-right (⟦ M ⟧ₘ n a))

    sound (cong-pair eq₁ eq₂) {n} {a} rewrite sound eq₁ {n} {a}
                                            | sound eq₂ {n} {a} = refl
    sound (cong-fst eq) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-snd eq) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-lam eq) {n} {a} = ext λ ⟦A⟧ → sound eq
    sound (cong-app eq₁ eq₂) {n} {a} rewrite sound eq₁ {n} {a}
                                           | sound eq₂ {n} {a} = refl
    sound (cong-inl eq) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-inr eq) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-case eq N₁ N₂) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-sig eq) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-letSig eq N) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-sample eq) {n} {a} rewrite sound eq {n} {a} = refl
    sound (cong-stable eq) = ext λ k → sound eq
    sound (cong-event eq) {n} {a} rewrite sound′ eq {n} {a} = refl

    -- Soundness of computational term equality: equal terms have equal denotations
    sound′ : ∀{A Γ} {M₁ M₂ : Γ ⊨ A}
             -> Γ ⊨ M₁ ≡ M₂ ∷ A
             -> ⟦ M₁ ⟧ᵐ ≈ ⟦ M₂ ⟧ᵐ
    sound′ (refl M) = refl
    sound′ (Eq′.sym eq) = ≡.sym (sound′ eq)
    sound′ (Eq′.trans eq₁ eq₂) = ≡.trans (sound′ eq₁) (sound′ eq₂)
    sound′ (β-sig′ C M) {n} {⟦Γ⟧} rewrite subst′-sound M C {n} {⟦Γ⟧} = refl
    sound′ (β-evt′ C D) {n} {⟦Γ⟧} rewrite subst″-sound D C {n} {⟦Γ⟧} = refl
    sound′ {_}{Γ} (β-selectₚ {A}{B}{C} C₁ C₂ C₃ M₁ M₂) {n} {⟦Γ⟧} =
        begin
            ⟦ select event (pure M₁) ↦ C₁ || event (pure M₂) ↦ C₂ ||both↦ C₃ ⟧ᵐ n ⟦Γ⟧
        ≡⟨⟩
            ⟦ C₃ ⟧ᵐ n ((⌞ ⟦ Γ ˢ⟧□ n ⟦Γ⟧ n ⌟ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , ⟦ M₂ ⟧ₘ n ⟦Γ⟧)
        ≡⟨ cong! (⟦ˢ⟧-factor Γ {n} {⟦Γ⟧}) ⟩
            ⟦ C₃ ⟧ᵐ n ((⌞ ⟦ Γ ˢ⟧ n ⟦Γ⟧ ⌟ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , ⟦ M₂ ⟧ₘ n ⟦Γ⟧)
        ≡⟨ cong! (⟦subst⟧-Γˢ⊆Γ Γ {n} {⟦Γ⟧}) ⟩
            ⟦ C₃ ⟧ᵐ n (⌞ (⟦subst⟧ (Γˢ⊆Γ Γ ⊆ₛ 𝒯erm) n ⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) ⌟ , ⟦ M₂ ⟧ₘ n ⟦Γ⟧ )
        ≡⟨ cong! (⟦↑⟧ (A now) (Γˢ⊆Γ Γ ⊆ₛ 𝒯erm) {n} {⟦Γ⟧ , (⟦ M₁ ⟧ₘ n ⟦Γ⟧)}) ⟩
            ⟦ C₃ ⟧ᵐ n ⌞ ((⟦subst⟧ (_↑_ {A now} (Γˢ⊆Γ Γ ⊆ₛ 𝒯erm) 𝒯erm) n (⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧)) , ⟦ M₂ ⟧ₘ n ⟦Γ⟧) ⌟
        ≡⟨ cong! (⟦↑⟧ (B now) (_↑_ {A now} (Γˢ⊆Γ Γ ⊆ₛ 𝒯erm) 𝒯erm) {n} {(⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , (⟦ M₂ ⟧ₘ n ⟦Γ⟧)}) ⟩
            ⟦ C₃ ⟧ᵐ n (⟦subst⟧ (_↑_ {B now} (_↑_ {A now} (Γˢ⊆Γ Γ ⊆ₛ 𝒯erm) 𝒯erm) 𝒯erm) n ((⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , ⌞ ⟦ M₂ ⟧ₘ n ⟦Γ⟧ ⌟))
        ≡⟨ cong! (⟦𝓌⟧ (A now) M₂ {n} {⟦Γ⟧ , (⟦ M₁ ⟧ₘ n ⟦Γ⟧)}) ⟩
            ⟦ C₃ ⟧ᵐ n (⟦subst⟧ (weakₛ 𝒯ermₛ s) n ((⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , ⟦ 𝓌 M₂ ⟧ₘ n (⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧)))
        ≡⟨ ≡.sym (traverse′-sound ⟦𝒯erm⟧ (weakₛ 𝒯ermₛ s) C₃ {n} {(⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , ⟦ 𝓌 M₂ ⟧ₘ n (⟦Γ⟧ , (⟦ M₁ ⟧ₘ n ⟦Γ⟧))})  ⟩
            ⟦ traverse′ (weakₛ 𝒯ermₛ s) C₃ ⟧ᵐ n ((⟦Γ⟧ , ⟦ M₁ ⟧ₘ n ⟦Γ⟧) , ⟦ 𝓌 M₂ ⟧ₘ n (⟦Γ⟧ , (⟦ M₁ ⟧ₘ n ⟦Γ⟧)))
        ≡⟨ ≡.sym (subst′-sound (𝓌 M₂) (weakening′ s C₃) {n} {⟦Γ⟧ , (⟦ M₁ ⟧ₘ n ⟦Γ⟧)}) ⟩
            ⟦ [ 𝓌 M₂ /′] (weakening′ s C₃) ⟧ᵐ n (⟦Γ⟧ , (⟦ M₁ ⟧ₘ n ⟦Γ⟧))
        ≡⟨ ≡.sym (subst′-sound M₁ ([ 𝓌 M₂ /′] (weakening′ s C₃)) {n} {⟦Γ⟧} ) ⟩
            ⟦ [ M₁ /′] ([ 𝓌 M₂ /′] (weakening′ s C₃)) ⟧ᵐ n ⟦Γ⟧
        ∎
        where
        s : Γ ˢ ,, A now ,, B now ⊆ Γ ,, A now ,, B now
        s = keep (keep (Γˢ⊆Γ Γ))
    sound′ (η-sig′ M) = refl
    sound′ (cong-pure′ eq) {n} {⟦Γ⟧} rewrite sound eq {n} {⟦Γ⟧} = refl
    sound′ (cong-letSig′ eq B) {n} {⟦Γ⟧} rewrite sound eq {n} {⟦Γ⟧} = refl
    sound′ (cong-letEvt′ eq D) {n} {⟦Γ⟧} rewrite sound eq {n} {⟦Γ⟧} = refl


-- Data: init commit

--{-# OPTIONS --show-implicit #-}
--{-# OPTIONS --rewriting #-}
module Oscar.Data0 where

data ⟦⟧ : Set where
  ∅ : ⟦⟧
  ! : ⟦⟧ → ⟦⟧

data ⟦_⟧ {a} (A : Set a) : Set a where
  ∅ : ⟦ A ⟧
  _∷_ : A → ⟦ A ⟧ → ⟦ A ⟧

data ⟦⟧[_] : ⟦⟧ → Set where
  ∅ : ∀ {n} → ⟦⟧[ ! n ]
  ! : ∀ {n} → ⟦⟧[ n ] → ⟦⟧[ ! n ]

data ⟦_⟧[_] {a} (A : ⟦⟧ → Set a) : ⟦⟧ → Set a where
  ∅ : ∀ {n} → ⟦ A ⟧[ ! n ]
  _∷_ : ∀ {n} → A n → ⟦ A ⟧[ n ] → ⟦ A ⟧[ ! n ]

data ⟦⟧[_≤↓_] (m : ⟦⟧) : ⟦⟧ → Set where
  ∅ : ⟦⟧[ m ≤↓ m ]
  ! : ∀ {n} → ⟦⟧[ m ≤↓ n ] → ⟦⟧[ m ≤↓ ! n ]

data ⟦_⟧[_≤↓_] {a} (A : ⟦⟧ → Set a) (m : ⟦⟧) : ⟦⟧ → Set a where
  ∅ : ⟦ A ⟧[ m ≤↓ m ]
  _∷_ : ∀ {n} → A n → ⟦ A ⟧[ m ≤↓ n ] → ⟦ A ⟧[ m ≤↓ ! n ]

data ⟦⟧[_↑≤_] (m : ⟦⟧) : ⟦⟧ → Set where
  ∅ : ⟦⟧[ m ↑≤ m ]
  ! : ∀ {n} → ⟦⟧[ ! m ↑≤ n ] → ⟦⟧[ m ↑≤ n ]

data ⟦_⟧[_↑≤_] {a} (A : ⟦⟧ → Set a) (m : ⟦⟧) : ⟦⟧ → Set a where
  ∅ : ⟦ A ⟧[ m ↑≤ m ]
  _∷_ : ∀ {n} → A m → ⟦ A ⟧[ ! m ↑≤ n ] → ⟦ A ⟧[ m ↑≤ n ]

data ⟦⟧[_↓≤↓_] : ⟦⟧ → ⟦⟧ → Set where
  ∅ : ∀ {n} → ⟦⟧[ ∅ ↓≤↓ n ]
  ! : ∀ {m n} → ⟦⟧[ m ↓≤↓ n ] → ⟦⟧[ ! m ↓≤↓ ! n ]

data ⟦_⟧[_↓≤↓_] {a} (A : ⟦⟧ → ⟦⟧ → Set a) : ⟦⟧ → ⟦⟧ → Set a where
  ∅ : ∀ {n} → ⟦ A ⟧[ ∅ ↓≤↓ n ]
  ! : ∀ {m n} → A m n → ⟦ A ⟧[ m ↓≤↓ n ] → ⟦ A ⟧[ ! m ↓≤↓ ! n ]

-- open import Oscar.Data.Unit
-- open import Oscar.Level
-- open import Data.Empty

-- 𝕋 : ∀ {a} {A : Set a} → A → Set
-- 𝕋 = λ _ → ⊤

-- postulate
--   ℓ : Level
--   Term : Set ℓ

-- -- module _ where -- second constructor is recursive

-- --   -- Nat
-- --   data ⟦⟧ : Set where
-- --     ∅ : ⟦⟧
-- --     ! : ⟦⟧ → ⟦⟧

-- --   -- List
-- --   data ⟦_⟧ {a} (A : Set a) : Set a where
-- --     ∅ : ⟦ A ⟧
-- --     _∷_ : A → ⟦ A ⟧ → ⟦ A ⟧

-- --   -- ⋆⋆ m, size: m
-- --   -- [ 0 ] = nothing
-- --   -- [ 1 ] = ∅0
-- --   -- [ 4 ] = ∅3 or !2∅3 or !1!2∅3 or !0!1!2∅3
-- --   data ⟦⟧[_] : ⟦⟧ → Set where
-- --     ∅ : ∀ {n} → ⟦⟧[ ! n ]
-- --     ! : ∀ {n} → ⟦⟧[ n ] → ⟦⟧[ ! n ]

-- --   data ⟦_⟧[_] {a} (A : ⟦⟧ → Set a) : ⟦⟧ → Set a where
-- --     ∅ : ∀ {n} → ⟦ A ⟧[ ! n ]
-- --     _∷_ : ∀ {n} → A n → ⟦ A ⟧[ n ] → ⟦ A ⟧[ ! n ]

-- --   -- 0 : ∅ (only)
-- --   -- 1 : ∅ or ! ∅
-- --   -- 2 : ∅ or ! ∅
-- --   -- 5 : ∅ or !
-- --   -- size: m + 1
-- --   -- ⋆⋆'' 0 = ∅0
-- --   -- ⋆⋆'' 4 = ∅(m=4) or !3∅4 or !2!3∅4 or !1!2!3∅4 or !0!1!2!3∅4
-- --   data ⋆⋆'' (m : ⟦⟧) : Set where
-- --     ∅ : ⋆⋆'' m
-- --     ! : ⋆⋆'' (! m) → ⋆⋆'' m

-- --   -- ⋆⋆'' ⊂ ⋆⋆

-- --   -- m ≤ n == n - 1 ⋱ m
-- --   -- m ↙ n
-- --   -- size = n - m
-- --   -- AList
-- --   -- 4≤↓2 = nothing
-- --   -- 2≤↓4 = !23!22∅22
-- --   data ⟦⟧[_≤↓_] (m : ⟦⟧) : ⟦⟧ → Set where
-- --     ∅ : ⟦⟧[ m ≤↓ m ]
-- --     ! : ∀ {n} → ⟦⟧[ m ≤↓ n ] → ⟦⟧[ m ≤↓ ! n ]
-- --     -- A n

-- --   （_⋱_］ : ⟦⟧ → ⟦⟧ → Set
-- --   （ n ⋱ m ］ = ⟦⟧[ m ≤↓ n ]

-- --   data ⟦_⟧[_≤↓_] {a} (A : ⟦⟧ → Set a) (m : ⟦⟧) : ⟦⟧ → Set where
-- --     ∅ : ⟦ A ⟧[ m ≤↓ m ]
-- --     ! : ∀ {n} → A n → ⟦ A ⟧[ m ≤↓ n ] → ⟦⟧[ m ≤↓ ! n ]

-- --   -- m ≤ n = m ⋰ n - 1
-- --   -- m ↗ n
-- --   -- size = n - m (+1?)
-- --   -- 2↑≤4 = !24!34∅44
-- --   data _↑≤_ (m : ⟦⟧) : ⟦⟧ → Set where
-- --     ∅ : m ↑≤ m
-- --     ! : ∀ {n} → ! m ↑≤ n → m ↑≤ n
-- --     -- A m

-- --   ［_⋰_） : ⟦⟧ → ⟦⟧ → Set
-- --   ［ m ⋰ n ） = m ↑≤ n

-- --   -- m ≤ n == n - 1 ⋱ n - m && m - 1 ⋱ 0
-- --   -- size = m (+1?)
-- --   -- Inj
-- --   -- 2↓≤↓4 = !13!02∅02
-- --   data _↓≤↓_ : ⟦⟧ → ⟦⟧ → Set where
-- --     ∅ : ∀ {n} → ∅ ↓≤↓ n
-- --     ! : ∀ {m n} → m ↓≤↓ n → ! m ↓≤↓ ! n -- A m n

-- --   -- Fin
-- --   -- P 0 = ∅0 or !0∅1 or !0!1∅2 or ...
-- --   -- P 3 = ∅3 or !3∅4 or !3!4∅5 or ... (infinite)
-- --   data P (n : ⋆) : Set where
-- --     ∅ : P n
-- --     ! : P (! n) → P n

-- --   {-
-- --     3 <= 5 = ! (4 <= 5) = ! ! (5 <= 5) = ! ! ∅
-- --     3≤5 witnesses
-- --     n=3,m=5
-- --       4   5
-- --   -}
-- --   -- m ≤ n == m ... n - 1 (this is the same as the two-indexed version)
-- --   data _≤_ (n : ⋆) : ⋆ → Set where
-- --     ∅ : n ≤ n
-- --     ! : ∀ {m} → ! n ≤ m → n ≤ m
-- --   -- or
-- --   -- m ≤ n == n - 1 ... m

-- --   three : ⋆
-- --   three = ! (! (! ∅))

-- --   five : ⋆
-- --   five = ! (! three)

-- --   foo : three ≤ five
-- --   foo = ! {n = {!!}} (! {n = {!!}} ∅)

-- -- --   {-
-- -- --     3 <= 5 = 3 <= ! 4 = ! (3 <= 4) = ! (! (3 <= 3)) = ! ! ∅
-- -- --     3 ≤ 5 witnesses
-- -- --     n=3,m=4
-- -- --     n=3,m=3
-- -- --
-- -- --   -}

-- -- --   data _≤_ (n : ⋆) : ⋆ → Set where
-- -- --     ∅ : n ≤ n
-- -- --     ! : ∀ {m} → n ≤ m → n ≤ ! m -- A m

-- -- --   {-
-- -- --     3 <= 5
-- -- --     m=2,n=4
-- -- --     m=1,n=3
-- -- --     m=0,n=2
-- -- --   -}
-- -- --   data _≤_ : ⋆ → ⋆ → Set where
-- -- --     ∅ : ∀ {n} → ∅ ≤ n
-- -- --     ! : ∀ {m n} → m ≤ n → ! m ≤ ! n

-- -- -- --   data # (n : ⋆) : Set where
-- -- -- --     ∅ : # n
-- -- -- --     ! : # (

-- -- -- --   data # : ⋆ → Set where
-- -- -- --     ∅ : # n
-- -- -- --     ! : ⋆

-- -- -- --   data ⋆[_] {a} (A : Set a) : Set a where
-- -- -- --     ∅ : ⋆[ A ]
-- -- -- --     _∷_ : A → ⋆[ A ] → ⋆[ A ]

-- -- -- --   data _≥_ (n : ⋆) : ⋆ → Set where
-- -- -- --     ∅ : n ≥ n
-- -- -- --     ! : ∀ {m} → n ≥ ¹⁺ m → n ≥ m

-- -- -- -- {-
-- -- -- --   data _≤_ (n : ⋆) : ⋆ → Set where
-- -- -- --     ∅ : n ≤ n
-- -- -- --     ! : ∀ {m} → n ≤ m → n ≤ ¹⁺ m
-- -- -- -- -}
-- -- -- --   data _≥_ : ⋆ → ⋆ → Set where
-- -- -- --     ∅ : ∀ {n} → n ≥ ∅
-- -- -- --     ! : ∀ {m n} → n ≥ m → ¹⁺ m ≤ ¹⁺ n

-- -- -- --   _≱_ : ⋆ → ⋆ → Set
-- -- -- --   n ≱ m = n ≥ m → ⊥

-- -- -- --   ε : ∀ {m} → m ≥ m
-- -- -- --   ε = ≥∅

-- -- -- --   ¹⁺≥ : ∀ {n m} → n ≥ m → ¹⁺ n ≥ m
-- -- -- --   ¹⁺≥ ≥∅ = ≥⁻¹ ≥∅
-- -- -- --   ¹⁺≥ (≥⁻¹ n≥m) = ≥⁻¹ (¹⁺≥ n≥m)

-- -- -- --   ∅≱¹⁺ : ∀ {n} → ∅ ≱ ¹⁺ n
-- -- -- --   ∅≱¹⁺ (≥⁻¹ ∅≥²⁺n) = ∅≱¹⁺ ∅≥²⁺n

-- -- -- --   _∙_ : ∀ {n m} → n ≥ m → ∀ {l} → m ≥ l → n ≥ l
-- -- -- --   ≥∅ ∙ ≥∅ = ≥∅
-- -- -- --   ≥∅ ∙ ≥⁻¹ x₁ = ⊥-elim ({!∅≱¹⁺ !})
-- -- -- --   ≥⁻¹ x ∙ ≥∅ = {!!}
-- -- -- --   _∙_ {n} {m} (≥⁻¹ x) {l} (≥⁻¹ x₁) = ≥⁻¹ (x ∙ ¹⁺≥ x₁)

-- -- -- --   stop≥ : ∀ {n} → n ≱ ¹⁺ n
-- -- -- --   stop≥ {∅} (≥⁻¹ x) = ∅≱¹⁺ x
-- -- -- --   stop≥ {¹⁺ n} (≥⁻¹ (≥⁻¹ x)) = {!stop≥ x!}

-- -- -- --   ¹⁺≥¹⁺ : ∀ {n m} → n ≥ m → ¹⁺ n ≥ ¹⁺ m
-- -- -- --   ¹⁺≥¹⁺ {∅} ≥∅ = ≥⁻¹ {¹⁺ ∅} {!!}
-- -- -- --   ¹⁺≥¹⁺ {¹⁺ n} ≥∅ = {!!}
-- -- -- --   ¹⁺≥¹⁺ (≥⁻¹ x) = {!!}

-- -- -- --   ⁻¹≥⁻¹ : ∀ {n m} → ¹⁺ n ≥ ¹⁺ m → n ≥ m
-- -- -- --   ⁻¹≥⁻¹ {n} {∅} (≥⁻¹ x) = {!!}
-- -- -- --   ⁻¹≥⁻¹ {∅} {¹⁺ m} (≥⁻¹ x) = ⊥-elim (∅≱¹⁺ (⁻¹≥⁻¹ x))
-- -- -- --   ⁻¹≥⁻¹ {¹⁺ n} {¹⁺ m} (≥⁻¹ x) = (⁻¹≥⁻¹ x) ∙ ¹⁺≥ {!!}

-- -- -- -- --   1>=1 : ¹⁺ ∅ ≥ ¹⁺ ∅
-- -- -- -- --   1>=1 = ¹⁺ {!!}

-- -- -- -- --   {- ⋮↓ -}
-- -- -- -- --   {- h  ｛ A ∣ 7 ≥ 4 ｝ = ｛ A 7 4 , A 7 5 , A 7 6 , A 7 7 ｝ -}
-- -- -- -- --   {- g  ｛ A ∣ 4 ≥ 2 ｝ = ｛ A 4 2 , A 4 3 , A 4 4 ｝ -}
-- -- -- -- --   {- h∘g｛ A ∣ 7 ≥ 2 ｝ = ｛ A 7 2 , A 7 3 , A 7 4 , A 7 5 , A 7 6 , A 7 7 ｝ -}
-- -- -- -- --   {- f  ｛ A ∣ 2 ≥ 0 ｝ = ｛ ｝ -}
-- -- -- -- --   {- g∘f｛ A ∣ 4 ≥ 0 ｝ = ｛ A 4 0 , A 4 1 , A 4 2 , A 4 3 , A 4 4 ｝ -}
-- -- -- -- --   data ⋆[_/_≥_] {a} (A : ⋆ → ⋆ → Set a) (m : ⋆) : ⋆ → Set a where
-- -- -- -- --     ∅ : ⋆[ A / m ≥ ∅ ]
-- -- -- -- --     _∷_ : ∀ {n} → A m n → ⋆[ A / m ≥ ¹⁺ n ] → ⋆[ A / m ≥ n ]

-- -- -- -- --   _≥'_ : ⋆ → ⋆ → Set
-- -- -- -- --   m ≥' n = ⋆[ (λ _ _ → ⊤) / m ≥ n ]

-- -- -- -- --   data _≤_ : ⋆ → ⋆ → Set where
-- -- -- -- --     ∅ : ∀ {n} → ∅ ≤ n
-- -- -- -- --     ¹⁺_ : ∀ {m n} → m ≤ n → ¹⁺ m ≤ ¹⁺ n

-- -- -- -- --   {- ⃔⋱ -}
-- -- -- -- --   {- ｛ A ∣ 4 ≤ 7 ｝ = ｛ A 3 6 , A 2 5 , A 1 4 , A 0 3 ｝ -}
-- -- -- -- --   {- ｛ A ∣ 2 ≤ 4 ｝ = ｛ A 1 3 , A 0 2 ｝ -}
-- -- -- -- --   data ⋆[_/_≤_] {a} (A : ⋆ → ⋆ → Set a) : ⋆ → ⋆ → Set a where
-- -- -- -- --     ∅ : ∀ {n} → ⋆[ A / ∅ ≤ n ]
-- -- -- -- --     _∷_ : ∀ {m n} → A m n → ⋆[ A / m ≤ n ] → ⋆[ A / ¹⁺ m ≤ ¹⁺ n ]

-- -- -- -- -- --   ---- indices on first and second constructors: 1 and 2
-- -- -- -- -- --   data ⋆[_,_] : ⋆ → ⋆ → Set where
-- -- -- -- -- --     ∅ : ∀ {n} → ⋆[ n , n ]
-- -- -- -- -- --     ! : ∀ {m n} → ⋆[ m , n ] → ⋆[ m , ! n ]

-- -- -- -- -- --   data ⋆[_,_] : ⋆ → ⋆ → Set where
-- -- -- -- -- --     ∅ : ∀ {m n} → ⋆[ m , n ]
-- -- -- -- -- --     ! : ∀ {m n} → ⋆[ m , n ] → ⋆[ ! m , ! n ]

-- -- -- -- -- --   -- ?
-- -- -- -- -- --   data _≛_ : ⋆ → ⋆ → Set where
-- -- -- -- -- --     ∅ : ∅ ≛ ∅
-- -- -- -- -- --     ! : ∀ {m} → ⋆[ m , m ] → ⋆[ ! m , ! m ]

-- -- -- -- -- --   data [_/_↦_] {a} (A : ⋆ → ⋆ → Set a) : ⋆ → ⋆ → Set a where
-- -- -- -- -- --     ∅ : ∀ {n} → [ A / ∅ ↦ n ]
-- -- -- -- -- --     ! : ∀ {m n} → A m n → [ A / ! m ↦ ! n ]

-- -- -- -- -- -- module _ where -- Two constructors, where the second constructor adds a payload and a recursive element

-- -- -- -- -- -- -- List
-- -- -- -- -- -- data [_] {a} (A : Set a) : Set a where
-- -- -- -- -- --   ∅ : [ A ]
-- -- -- -- -- --   _,_ : A → [ A ] → [ A ]

-- -- -- -- -- -- -- Fin
-- -- -- -- -- -- ⋆[_] = ⋆[ ∅ ,_]

-- -- -- -- -- -- record ⊤ : Set where

-- -- -- -- -- -- record ∃ {a} {A : Set a} {b} (B : A → Set b) : Set (a ⊔ b) where
-- -- -- -- -- --   field
-- -- -- -- -- --     ⟱ : A
-- -- -- -- -- --     ⟰ : B ⟱

-- -- -- -- -- -- syntax ∃ (λ x → f) = ∃[ x ] f

-- -- -- -- -- -- _×_ : ∀ {a} (A : Set a) {b} (B : Set b) → Set (a ⊔ b)
-- -- -- -- -- -- A × B = ∃ {A = A} (λ _ → B)

-- -- -- -- -- -- --
-- -- -- -- -- -- [_↦_] : ⋆ → ⋆ → Set
-- -- -- -- -- -- [_↦_] = [ (λ m n → ⋆[ ! m ] × {![ m ↦ n ]!}) /_↦_]

-- -- -- -- --   -- open import Agda.Builtin.Equality
-- -- -- -- --   -- open import Data.Empty