------------------------------------------------------------------------
-- Canonical kinding of Fω with interval kinds
------------------------------------------------------------------------

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

module FOmegaInt.Kinding.Canonical where

open import Data.Context.WellFormed
open import Data.Fin using (Fin; zero; suc)
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.List using ([]; _∷_; _∷ʳ_)
open import Data.Product using (∃; _,_; _×_)
open import Data.Vec as Vec using ([])
open import Level using () renaming (zero to lzero)
open import Relation.Binary.PropositionalEquality

open import FOmegaInt.Syntax
open import FOmegaInt.Syntax.HereditarySubstitution
open import FOmegaInt.Syntax.Normalization
import FOmegaInt.Kinding.Simple as SimpleKinding

------------------------------------------------------------------------
-- Canonical kinding derivations.
--
-- TODO: explain the point of this and how "canonical" kinding differs
-- from "algorithmic" kinding.
--
-- NOTE. In the rules below, we use (singleton) kind synthesis
-- judgments of the form `Γ ⊢Nf a ⇉ a ⋯ a' to ensure that `a' is a
-- well-kinded proper type rather than kind checking judgments of the
-- form `Γ ⊢Nf a ⇇ *'.  While the latter may seem more natural, it
-- involves the use of subsumption/subtyping to ensure that `Γ ⊢ a ⋯ a
-- <∷ *'.  As we will show, this is always true (if `a' is indeed a
-- proper type) but the extra use of subsumption complicates the
-- proofs of properties that require (partial) inversion of kinding
-- derivations.  See e.g. the Nf⇉-⋯-* and Nf⇇-⋯-* lemmas.

module Kinding where
  open ElimCtx
  open Syntax

  infix 4 _⊢_wf _ctx _⊢_kd
  infix 4 _⊢Nf_⇉_ _⊢Ne_∈_ _⊢Var_∈_ _⊢_⇉∙_⇉_ _⊢Nf_⇇_
  infix 4 _⊢_<∷_ _⊢_<:_ _⊢_<:_⇇_
  infix 4 _⊢_≅_ _⊢_≃_⇇_ _⊢_⇉∙_≃_⇉_

  mutual

    -- Well-formed type/kind ascriptions in typing contexts.
    --
    -- A raw kind ascriptions is considered well-formed if it
    -- corresponds to a well-formed normal kind.  Raw type ascriptions
    -- are are considered well-formed if they correspond to proper
    -- normal types.
    data _⊢_wf {n} (Γ : Ctx n) : ElimAsc n → Set where
      wf-kd : ∀ {a} → Γ ⊢ a kd        → Γ ⊢ kd a wf
      wf-tp : ∀ {a} → Γ ⊢Nf a ⇉ a ⋯ a → Γ ⊢ tp a wf

    -- Well-formed typing contexts.
    --
    -- Contexts and context extensions are well-formed if all the
    -- ascriptions they contain are well-formed.
    _ctx : ∀ {n} → Ctx n → Set
    Γ ctx = ContextFormation._wf _⊢_wf Γ

    -- Well-formedness checking for η-long β-normal kinds.
    data _⊢_kd {n} (Γ : Ctx n) : Kind Elim n → Set where
      kd-⋯ : ∀ {a b} → Γ ⊢Nf a ⇉ a ⋯ a → Γ ⊢Nf b ⇉ b ⋯ b → Γ ⊢ a ⋯ b kd
      kd-Π : ∀ {j k} → Γ ⊢ j kd → kd j ∷ Γ ⊢ k kd        → Γ ⊢ Π j k kd

    -- Kind synthesis for η-long β-normal types.
    data _⊢Nf_⇉_ {n} (Γ : Ctx n) : Elim n → Kind Elim n → Set where
      ⇉-⊥-f : Γ ctx → Γ ⊢Nf ⊥∙ ⇉ ⊥∙ ⋯ ⊥∙
      ⇉-⊤-f : Γ ctx → Γ ⊢Nf ⊤∙ ⇉ ⊤∙ ⋯ ⊤∙
      ⇉-∀-f : ∀ {k a} → Γ ⊢ k kd → kd k ∷ Γ ⊢Nf a ⇉ a ⋯ a →
              Γ ⊢Nf ∀∙ k a ⇉ ∀∙ k a ⋯ ∀∙ k a
      ⇉-→-f : ∀ {a b} → Γ ⊢Nf a ⇉ a ⋯ a → Γ ⊢Nf b ⇉ b ⋯ b →
              Γ ⊢Nf a ⇒∙ b ⇉ a ⇒∙ b ⋯ a ⇒∙ b
      ⇉-Π-i : ∀ {j a k} → Γ ⊢ j kd → kd j ∷ Γ ⊢Nf a ⇉ k → Γ ⊢Nf Λ∙ j a ⇉ Π j k
      ⇉-s-i : ∀ {a b c} → Γ ⊢Ne a ∈ b ⋯ c → Γ ⊢Nf a ⇉ a ⋯ a

    -- Canonical kinding of neutral types.
    --
    -- NOTE.  This is *not* a kind synthesis judgment!  See the
    -- comment on canonical variable kinding for an explanation.

    data _⊢Ne_∈_ {n} (Γ : Ctx n) : Elim n → Kind Elim n → Set where
      ∈-∙ : ∀ {x j k} {as : Spine n} → Γ ⊢Var x ∈ j →
            Γ ⊢ j ⇉∙ as ⇉ k → Γ ⊢Ne var x ∙ as ∈ k

    -- Canonical kinding of variables.
    --
    -- NOTE.  This would be a kind synthesis judgment if it weren't
    -- for the subsumption rule (⇇-⇑).  Thanks to this rule, the proof
    -- of the "context narrowing" property is easy to establish for
    -- canonical typing (see the ContextNarrowing module below).
    -- Without a proof of this property, the various lemmas about
    -- kinded hereditary substitutions become difficult to establish
    -- (see Kinding.HereditarySubstitution).  Unfortunately, proving
    -- that context narrowing is admissible *without* (⇇-⇑) would
    -- require precisely these substitution lemmas, leading to a
    -- cyclic dependency.  Introducing the subsumption rule for
    -- variables thus allows us to break that cycle.  On the other
    -- hand, the subsumption rule breaks functionality of the
    -- canonical kinding relations for variables and neutral types,
    -- i.e. their canonical kinds are no longer unique.  This is
    -- partly remedied by the singleton-introduction rule (⇉-s-i) in
    -- the kind synthesis judgment for normal types: neutrals have
    -- unique (singleton) kinds when viewed as normal forms.  However,
    -- neutral kinding is also used in subtyping, in particular by the
    -- bound projection rules (<:-⟨|) and (<:-|⟩).  Because the kinds
    -- of neutrals are not unique, neither are the bounds projected by
    -- these subtyping rules, and eliminating adjacent instances of
    -- (<:-⟨|) and (<:-|⟩) becomes difficult.  This, in turn,
    -- complicates transitivity elimination (i.e. a proof that
    -- transitivity of subtyping is admissible) in arbitrary contexts.
    -- However, it does not affect elimination of *top-level*
    -- occurrences of the transitivity rule (<:-trans), i.e. those in
    -- the empty contexts, because there are no closed neutral terms,
    -- and therefore no instances of the bound projection rules in
    -- top-level subtyping statements.

    data _⊢Var_∈_ {n} (Γ : Ctx n) : Fin n → Kind Elim n → Set where
      ⇉-var : ∀ {k} x → Γ ctx → lookup Γ x ≡ kd k → Γ ⊢Var x ∈ k
      ⇇-⇑   : ∀ {x j k} → Γ ⊢Var x ∈ j → Γ ⊢ j <∷ k → Γ ⊢ k kd →
              Γ ⊢Var x ∈ k

    -- Kind synthesis for spines: given the kind of the head and a
    -- spine, synthesize the overall kind of the elimination.
    data _⊢_⇉∙_⇉_ {n} (Γ : Ctx n)
      : Kind Elim n → Spine n → Kind Elim n → Set where
      ⇉-[] : ∀ {k} → Γ ⊢ k ⇉∙ [] ⇉ k
      ⇉-∷  : ∀ {a as j k l} → Γ ⊢Nf a ⇇ j → Γ ⊢ j kd →
             Γ ⊢ k Kind[ a ∈ ⌊ j ⌋ ] ⇉∙ as ⇉ l →
             Γ ⊢ Π j k ⇉∙ a ∷ as ⇉ l

    -- Kind checking for η-long β-normal types.
    data _⊢Nf_⇇_ {n} (Γ : Ctx n) : Elim n → Kind Elim n → Set where
      ⇇-⇑  : ∀ {a j k} → Γ ⊢Nf a ⇉ j → Γ ⊢ j <∷ k → Γ ⊢Nf a ⇇ k

    -- Canonical subkinding derivations.
    data _⊢_<∷_ {n} (Γ : Ctx n) : Kind Elim n → Kind Elim 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₂

    -- Canonical subtyping of proper types.
    data _⊢_<:_ {n} (Γ : Ctx n) : Elim n → Elim n → Set where
      <:-trans : ∀ {a b c} → Γ ⊢ a <: b → Γ ⊢ b <: c → Γ ⊢ a <: c
      <:-⊥     : ∀ {a} → Γ ⊢Nf a ⇉ a ⋯ a → Γ ⊢ ⊥∙ <: a
      <:-⊤     : ∀ {a} → Γ ⊢Nf a ⇉ a ⋯ a → Γ ⊢ a <: ⊤∙
      <:-∀     : ∀ {k₁ k₂ a₁ a₂} →
                 Γ ⊢ k₂ <∷ k₁ → kd k₂ ∷ Γ ⊢ a₁ <: a₂ →
                 Γ ⊢Nf ∀∙ k₁ a₁ ⇉  ∀∙ k₁ a₁ ⋯ ∀∙ k₁ a₁ →
                 Γ ⊢ ∀∙ k₁ a₁ <: ∀∙ k₂ a₂
      <:-→     : ∀ {a₁ a₂ b₁ b₂} →
                 Γ ⊢ a₂ <: a₁ → Γ ⊢ b₁ <: b₂ → Γ ⊢ a₁ ⇒∙ b₁ <: a₂ ⇒∙ b₂
      <:-∙     : ∀ {x as₁ as₂ k b c} →
                 Γ ⊢Var x ∈ k → Γ ⊢ k ⇉∙ as₁ ≃ as₂ ⇉ b ⋯ c →
                 Γ ⊢ var x ∙ as₁ <: var x ∙ as₂
      <:-⟨|    : ∀ {a b c} → Γ ⊢Ne a ∈ b ⋯ c → Γ ⊢ b <: a
      <:-|⟩    : ∀ {a b c} → Γ ⊢Ne a ∈ b ⋯ c → Γ ⊢ a <: c

    -- Checked/kind-driven subtyping.
    data _⊢_<:_⇇_ {n} (Γ : Ctx n) : Elim n → Elim n → Kind Elim n → Set where
      <:-⇇ : ∀ {a b c d} → Γ ⊢Nf a ⇇ c ⋯ d → Γ ⊢Nf b ⇇ c ⋯ d →
             Γ ⊢ a <: b → Γ ⊢ a <: b ⇇ c ⋯ d
      <:-λ : ∀ {j₁ j₂ a₁ a₂ j k} → kd j ∷ Γ ⊢ a₁ <: a₂ ⇇ k →
             Γ ⊢Nf Λ∙ j₁ a₁ ⇇ Π j k → Γ ⊢Nf Λ∙ j₂ a₂ ⇇ Π j k →
             Γ ⊢ Λ∙ j₁ a₁ <: Λ∙ j₂ a₂ ⇇ Π j k

    -- Canonical kind equality.
    data _⊢_≅_ {n} (Γ : Ctx n) : Kind Elim n → Kind Elim n → Set where
      <∷-antisym : ∀ {j k} → Γ ⊢ j kd → Γ ⊢ k kd →
                   Γ ⊢ j <∷ k → Γ ⊢ k <∷ j → Γ ⊢ j ≅ k

    -- Canonical type equality derivations with checked kinds.
    data _⊢_≃_⇇_ {n} (Γ : Ctx n) : Elim n → Elim n → Kind Elim n → Set where
      <:-antisym : ∀ {a b k} →
                   Γ ⊢ k kd → Γ ⊢ a <: b ⇇ k → Γ ⊢ b <: a ⇇ k → Γ ⊢ a ≃ b ⇇ k

    -- Canonical equality of spines.
    data _⊢_⇉∙_≃_⇉_ {n} (Γ : Ctx n)
      : Kind Elim n → Spine n → Spine n → Kind Elim n → Set where
      ≃-[] : ∀ {k} → Γ ⊢ k ⇉∙ [] ≃ [] ⇉ k
      ≃-∷  : ∀ {a as b bs j k l} →
             Γ ⊢ a ≃ b ⇇ j → Γ ⊢ k Kind[ a ∈ ⌊ j ⌋ ] ⇉∙ as ≃ bs ⇉ l →
             Γ ⊢ Π j k ⇉∙ a ∷ as ≃ b ∷ bs ⇉ l

  -- Well-formed context extensions.
  open ContextFormation _⊢_wf public
    hiding (_wf) renaming (_⊢_wfExt to _⊢_ext)

  -- A wrapper for the _⊢Var_∈_ judgment that also provides term
  -- variable bindings.

  infix 4 _⊢Var′_∈_

  data _⊢Var′_∈_ {n} (Γ : Ctx n) : Fin n → ElimAsc n → Set where
    ∈-tp : ∀ {x k} → Γ ⊢Var x ∈ k → Γ ⊢Var′ x ∈ kd k
    ∈-tm : ∀ x {a} → Γ ctx → lookup Γ x ≡ tp a → Γ ⊢Var′ x ∈ tp a

  -- 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 x Γ-ctx Γ[x]≡tp-a

------------------------------------------------------------------------
-- Simple properties of canonical kindings

open Syntax
open ElimCtx
open SimpleCtx using (kd; tp; ⌊_⌋Asc)
open Kinding
open ContextConversions using (⌊_⌋Ctx; module ⌊⌋Ctx-Lemmas)

-- An inversion lemma for _⊢_wf.

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

-- Subkinds have the same shape.

<∷-⌊⌋ : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j <∷ k → ⌊ j ⌋ ≡ ⌊ k ⌋
<∷-⌊⌋ (<∷-⋯ _ _)             = refl
<∷-⌊⌋ (<∷-Π j₂<∷j₁ k₁<∷k₂ _) = cong₂ _⇒_ (sym (<∷-⌊⌋ j₂<∷j₁)) (<∷-⌊⌋ k₁<∷k₂)

-- Equal kinds have the same shape.

≅-⌊⌋ : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → ⌊ j ⌋ ≡ ⌊ k ⌋
≅-⌊⌋ (<∷-antisym j-kd k-kd j<∷k k<∷j) = <∷-⌊⌋ j<∷k

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

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

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

≃⇒<:-⋯ : ∀ {n} {Γ : Ctx n} {a b c d} → Γ ⊢ a ≃ b ⇇ c ⋯ d → Γ ⊢ a <: b
≃⇒<:-⋯ (<:-antisym c⋯d-kd (<:-⇇ a⇇c⋯d b⇇c⋯d a<:b) b<:a⇇c⋯d) = a<:b

-- Symmetry of kind and type equality.

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

≃-sym : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a ≃ b ⇇ k → Γ ⊢ b ≃ a ⇇ k
≃-sym (<:-antisym k-kd a<:b b<:a) = <:-antisym k-kd b<:a a<:b

-- Transitivity of checked subtyping and type equality are admissible.

<:⇇-trans : ∀ {n} {Γ : Ctx n} {a b c k} →
            Γ ⊢ a <: b ⇇ k → Γ ⊢ b <: c ⇇ k → Γ ⊢ a <: c ⇇ k
<:⇇-trans (<:-⇇ a₁⇇b⋯c _ a₁<:a₂) (<:-⇇ _ a₃⇇b⋯c a₂<:a₃) =
  <:-⇇ a₁⇇b⋯c a₃⇇b⋯c (<:-trans a₁<:a₂ a₂<:a₃)
<:⇇-trans (<:-λ a₁<:a₂ Λj₁a₁⇇Πjk _) (<:-λ a₂<:a₃ _ Λj₃a₃⇇Πjk) =
  <:-λ (<:⇇-trans a₁<:a₂ a₂<:a₃) Λj₁a₁⇇Πjk Λj₃a₃⇇Πjk

≃-trans : ∀ {n} {Γ : Ctx n} {a b c k} →
          Γ ⊢ a ≃ b ⇇ k → Γ ⊢ b ≃ c ⇇ k → Γ ⊢ a ≃ c ⇇ k
≃-trans (<:-antisym k-kd a<:b⇇k b<:a⇇k) (<:-antisym _ b<:c⇇k c<:b⇇k) =
  <:-antisym k-kd (<:⇇-trans a<:b⇇k b<:c⇇k) (<:⇇-trans c<:b⇇k b<:a⇇k)

-- The synthesized kind of a normal proper type is exactly the singleton
-- containing that type.
Nf⇉-≡ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇉ b ⋯ c → a ≡ b × a ≡ c
Nf⇉-≡ (⇉-⊥-f Γ-ctx)       = refl , refl
Nf⇉-≡ (⇉-⊤-f Γ-ctx)       = refl , refl
Nf⇉-≡ (⇉-∀-f k-kd  b⇉b⋯b) = refl , refl
Nf⇉-≡ (⇉-→-f a⇉a⋯a b⇉b⋯b) = refl , refl
Nf⇉-≡ (⇉-s-i a∈b⋯c)       = refl , refl

-- Derived singleton introduction rules where the premises are
-- well-kinded normal forms rather than neutrals.

Nf⇉-s-i : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇉ b ⋯ c → Γ ⊢Nf a ⇉ a ⋯ a
Nf⇉-s-i a⇉b⋯c with Nf⇉-≡ a⇉b⋯c
Nf⇉-s-i a⇉a⋯a | refl , refl = a⇉a⋯a

Nf⇇-s-i : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇇ b ⋯ c → Γ ⊢Nf a ⇉ a ⋯ a
Nf⇇-s-i (⇇-⇑ a⇉b₁⋯c₁ (<∷-⋯ b₂<:b₁ c₁<:c₂)) = Nf⇉-s-i a⇉b₁⋯c₁

-- Inhabitants of interval kinds are proper types.

Nf⇉-⋯-* : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇉ b ⋯ c → Γ ⊢Nf a ⇇ ⌜*⌝
Nf⇉-⋯-* a⇉b⋯c with Nf⇉-≡ a⇉b⋯c
Nf⇉-⋯-* a⇉a⋯a | refl , refl = ⇇-⇑ a⇉a⋯a (<∷-⋯ (<:-⊥ a⇉a⋯a) (<:-⊤ a⇉a⋯a))

Nf⇇-⋯-* : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇇ b ⋯ c → Γ ⊢Nf a ⇇ ⌜*⌝
Nf⇇-⋯-* (⇇-⇑ a⇉b₁⋯c₁ (<∷-⋯ b₂<:b₁ c₁<:c₂)) = Nf⇉-⋯-* a⇉b₁⋯c₁

-- Validity of synthesized kinds: synthesized kinds are well-formed.
Nf⇉-valid : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇉ k → Γ ⊢ k kd
Nf⇉-valid (⇉-⊥-f Γ-ctx)       = kd-⋯ (⇉-⊥-f Γ-ctx) (⇉-⊥-f Γ-ctx)
Nf⇉-valid (⇉-⊤-f Γ-ctx)       = kd-⋯ (⇉-⊤-f Γ-ctx) (⇉-⊤-f Γ-ctx)
Nf⇉-valid (⇉-∀-f k-kd a⇉a⋯a)  = kd-⋯ (⇉-∀-f k-kd a⇉a⋯a) (⇉-∀-f k-kd a⇉a⋯a)
Nf⇉-valid (⇉-→-f a⇉a⋯a b⇉b⋯b) = kd-⋯ (⇉-→-f a⇉a⋯a b⇉b⋯b) (⇉-→-f a⇉a⋯a b⇉b⋯b)
Nf⇉-valid (⇉-Π-i j-kd a⇉k)    = kd-Π j-kd (Nf⇉-valid a⇉k)
Nf⇉-valid (⇉-s-i a∈b⋯c)       = kd-⋯ (⇉-s-i a∈b⋯c) (⇉-s-i a∈b⋯c)

-- Validity of checked subtyping: the checked subtyping judgment
-- relates well-kinded types.

<:⇇-valid : ∀ {n} {Γ : Ctx n} {a b k} →
            Γ ⊢ a <: b ⇇ k → Γ ⊢Nf a ⇇ k × Γ ⊢Nf b ⇇ k
<:⇇-valid (<:-⇇ a⇇k b⇇k a<:b)               = a⇇k , b⇇k
<:⇇-valid (<:-λ a₁<:a₂ Λj₁a₁⇇Πjk Λj₂a₂⇇Πjk) = Λj₁a₁⇇Πjk , Λj₂a₂⇇Πjk

-- Validity of kind and type equality: the equality judgments relate
-- well-formed kinds, resp. well-kinded types.

≅-valid : ∀ {n} {Γ : Ctx n} {j k} → Γ ⊢ j ≅ k → Γ ⊢ j kd × Γ ⊢ k kd
≅-valid (<∷-antisym j-kd k-kd j<∷k k<∷j) = j-kd , k-kd

≃-valid : ∀ {n} {Γ : Ctx n} {a b k} →
          Γ ⊢ a ≃ b ⇇ k → Γ ⊢Nf a ⇇ k × Γ ⊢Nf b ⇇ k
≃-valid (<:-antisym k-kd a<:b⇇k b<:a⇇k) = <:⇇-valid a<:b⇇k

≃-valid-kd : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a ≃ b ⇇ k → Γ ⊢ k kd
≃-valid-kd (<:-antisym k-kd a<:b b<:a) = k-kd

-- NOTE.  In order to prove validity for the remainder of the kinding,
-- subkinding and subtyping judgments, we first need to prove that
-- hereditary substitutions preserve well-formedness of kinds (see
-- Kinding.Canonical.HereditarySubstitution).  See the definition of
-- `Var∈-valid' below and the module Kinding.Canonical.Validity for
-- the remaining (strong) validity lemmas.

-- The synthesized kinds of neutrals kind-check.
Nf⇇-ne : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Ne a ∈ b ⋯ c → Γ ⊢Nf a ⇇ b ⋯ c
Nf⇇-ne (∈-∙ x∈k k⇉as⇉b⋯c) =
  ⇇-⇑ (⇉-s-i (∈-∙ x∈k k⇉as⇉b⋯c))
      (<∷-⋯ (<:-⟨| (∈-∙ x∈k k⇉as⇉b⋯c)) (<:-|⟩ (∈-∙ x∈k k⇉as⇉b⋯c)))

-- The contexts of (most of) the above judgments are well-formed.
--
-- NOTE. The exceptions are kinding and equality of spines, as the
-- ⇉-[] and ≃-[] rules offer no guarantee that the enclosing context
-- is well-formed.  This is not a problem in practice, since
-- well-kinded spines are used in the kinding of neutral types, the
-- head of which is guaranteed to be kinded in a well-formed context.
mutual

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

  Nf⇉-ctx : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇉ k → Γ ctx
  Nf⇉-ctx (⇉-⊥-f Γ-ctx)       = Γ-ctx
  Nf⇉-ctx (⇉-⊤-f Γ-ctx)       = Γ-ctx
  Nf⇉-ctx (⇉-∀-f k-kd  a⇉a⋯a) = kd-ctx k-kd
  Nf⇉-ctx (⇉-→-f a⇉a⋯a b⇉b⋯b) = Nf⇉-ctx a⇉a⋯a
  Nf⇉-ctx (⇉-Π-i j-kd  a⇉k)   = kd-ctx j-kd
  Nf⇉-ctx (⇉-s-i a∈b⋯c)       = Ne∈-ctx a∈b⋯c

  Ne∈-ctx : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Ne a ∈ k → Γ ctx
  Ne∈-ctx (∈-∙ x∈j j⇉as⇉k) = Var∈-ctx x∈j

  Var∈-ctx : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Var a ∈ k → Γ ctx
  Var∈-ctx (⇉-var x Γ-ctx _)   = Γ-ctx
  Var∈-ctx (⇇-⇑ x∈j j<∷k k-kd) = Var∈-ctx x∈j

  Nf⇇-ctx : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇇ k → Γ ctx
  Nf⇇-ctx (⇇-⇑ a⇉j j<∷k) = Nf⇉-ctx a⇉j

mutual

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

  <:-ctx : ∀ {n} {Γ : Ctx n} {a b} → Γ ⊢ a <: b → Γ ctx
  <:-ctx (<:-trans a<:b b<:c)   = <:-ctx a<:b
  <:-ctx (<:-⊥ a⇉a⋯a)           = Nf⇉-ctx a⇉a⋯a
  <:-ctx (<:-⊤ a⇉a⋯a)           = Nf⇉-ctx a⇉a⋯a
  <:-ctx (<:-∀ k₂<∷k₁ a₁<:a₂ _) = <∷-ctx k₂<∷k₁
  <:-ctx (<:-→ a₂<:a₁ b₁<:b₂)   = <:-ctx a₂<:a₁
  <:-ctx (<:-∙ x∈j as<:bs)      = Var∈-ctx x∈j
  <:-ctx (<:-⟨| a∈b⋯c)          = Ne∈-ctx a∈b⋯c
  <:-ctx (<:-|⟩ a∈b⋯c)          = Ne∈-ctx a∈b⋯c

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

wf-ctx : ∀ {n} {Γ : Ctx n} {a} → Γ ⊢ a wf → Γ ctx
wf-ctx (wf-kd k-kd)  = kd-ctx k-kd
wf-ctx (wf-tp a⇉a⋯a) = Nf⇉-ctx a⇉a⋯a

<:⇇-ctx : ∀ {n} {Γ : Ctx n} {a b k} → Γ ⊢ a <: b ⇇ k → Γ ctx
<:⇇-ctx (<:-⇇ a⇇k b⇇k a<:b)               = Nf⇇-ctx a⇇k
<:⇇-ctx (<:-λ a₁<:a₂ Λj₁a₁⇇Πjk Λj₂a₂⇇Πjk) = Nf⇇-ctx Λj₁a₁⇇Πjk

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

Var′∈-ctx : ∀ {n} {Γ : Ctx n} {x a} → Γ ⊢Var′ x ∈ a → Γ ctx
Var′∈-ctx (∈-tp x∈k)       = Var∈-ctx x∈k
Var′∈-ctx (∈-tm _ Γ-ctx _) = Γ-ctx

-- An admissible formation rule for the kind of proper types.

kd-⌜*⌝ : ∀ {n} {Γ : Ctx n} → Γ ctx → Γ ⊢ ⌜*⌝ kd
kd-⌜*⌝ Γ-ctx = kd-⋯ (⇉-⊥-f Γ-ctx) (⇉-⊤-f Γ-ctx)

-- Admissible formation rules for canonically kinded proper types.

Nf⇇-∀-f : ∀ {n} {Γ : Ctx n} {k a} →
          Γ ⊢ k kd → kd k ∷ Γ ⊢Nf a ⇇ ⌜*⌝ → Γ ⊢Nf ∀∙ k a ⇇ ⌜*⌝
Nf⇇-∀-f k-kd a⇇* =
  let a⇉a⋯a = Nf⇇-s-i a⇇*
  in Nf⇉-⋯-* (⇉-∀-f k-kd a⇉a⋯a)

Nf⇇-→-f : ∀ {n} {Γ : Ctx n} {a b} →
          Γ ⊢Nf a ⇇ ⌜*⌝ → Γ ⊢Nf b ⇇ ⌜*⌝ → Γ ⊢Nf a ⇒∙ b ⇇ ⌜*⌝
Nf⇇-→-f a⇇* b⇇* =
  let a⇉a⋯a = Nf⇇-s-i a⇇*
      b⇉b⋯b = Nf⇇-s-i b⇇*
  in Nf⇉-⋯-* (⇉-→-f a⇉a⋯a b⇉b⋯b)

-- Admissible kinding and equality rules for appending a normal form
-- to a spine.

⇉-∷ʳ : ∀ {n} {Γ : Ctx n} {as : Spine n} {a j k l} →
       Γ ⊢ j ⇉∙ as ⇉ Π k l → Γ ⊢Nf a ⇇ k → Γ ⊢ k kd →
       Γ ⊢ j ⇉∙ as ∷ʳ a ⇉ l Kind[ a ∈ ⌊ k ⌋ ]
⇉-∷ʳ ⇉-[]                       a⇇k k-kd = ⇉-∷ a⇇k k-kd ⇉-[]
⇉-∷ʳ (⇉-∷ a⇇j j-kd k[a]⇉as⇉Πlm) b⇇l l-kd =
  ⇉-∷ a⇇j j-kd (⇉-∷ʳ k[a]⇉as⇉Πlm b⇇l l-kd)

≃-∷ʳ : ∀ {n} {Γ : Ctx n} {as bs : Spine n} {a b j k l} →
       Γ ⊢ j ⇉∙ as ≃ bs ⇉ Π k l → Γ ⊢ a ≃ b ⇇ k →
       Γ ⊢ j ⇉∙ as ∷ʳ a ≃ bs ∷ʳ b ⇉ l Kind[ a ∈ ⌊ k ⌋ ]
≃-∷ʳ ≃-[]            a≃b = ≃-∷ a≃b ≃-[]
≃-∷ʳ (≃-∷ a≃b as≃bs) c≃d = ≃-∷ a≃b (≃-∷ʳ as≃bs c≃d)

----------------------------------------------------------------------
-- Well-kinded variable substitutions in canonically kinded types
--
-- We define two different kinds of well-kinded variable
-- substitutions:
--
--  1. well-kinded renamings, which don't change kinds of variables,
--
--  2. variable substitutions that also allow conversion/subsumption.
--
-- The first kind are used to prove e.g. that weakening preserve
-- (canonical) well-kindedness, while the second kind are used to
-- prove context conversion/narrowing preserves well-kindedness.  The
-- two definitions share a common generic core, which is instantiated
-- to obtain the concrete definitions.  The two separate definitions
-- are necessary because the latter requires the weakening lemma,
-- which in turn depends on the former.

-- Liftings between variable typings

record LiftTo-Var′∈ (_⊢V_∈_ : Typing ElimAsc Fin ElimAsc lzero) : Set₁ where
  open Substitution using (weakenElimAsc; _ElimAsc/Var_)

  typedSub : TypedSub ElimAsc Fin lzero
  typedSub = record
    { _⊢_wf               = _⊢_wf
    ; _⊢_∈_               = _⊢V_∈_
    ; typeExtension       = record { weaken = weakenElimAsc }
    ; typeTermApplication = record { _/_ = _ElimAsc/Var_ }
    ; termSimple          = VarSubst.simple
    }
  open TypedSub typedSub public
    renaming (_⊢/_∈_ to _⊢/Var_∈_) hiding (_⊢_wf; _⊢_∈_; typeExtension)

  -- Simple typed variable substitutions.

  field typedSimple : TypedSimple typedSub
  open TypedSimple typedSimple public renaming (lookup to /∈-lookup)

  -- Lifts well-typed Term₁-terms to well-typed Term₂-terms.

  field ∈-lift : ∀ {n} {Γ : Ctx n} {x a} → Γ ⊢V x ∈ a → Γ ⊢Var′ x ∈ a

module TypedVarSubstApp (_⊢V_∈_ : Typing ElimAsc Fin ElimAsc lzero)
                        (liftTyped : LiftTo-Var′∈ _⊢V_∈_)
                        where

  open LiftTo-Var′∈ liftTyped
  open Substitution hiding (subst; _/Var_) renaming (_Elim/Var_ to _/Var_)
  open RenamingCommutes using (Kind[∈⌊⌋]-/Var)

  -- A helper.

  ∈-↑′ : ∀ {m n} {Δ : Ctx n} {Γ : Ctx m} {k ρ} →
         Δ ⊢ k Kind′/Var ρ kd → Δ ⊢/Var ρ ∈ Γ →
         kd (k Kind′/Var ρ) ∷ Δ ⊢/Var ρ VarSubst.↑ ∈ kd k ∷ Γ
  ∈-↑′ k/ρ-kd ρ∈Γ = ∈-↑ (wf-kd k/ρ-kd) ρ∈Γ

  -- Convert between well-kindedness judgments for variables.

  V∈-Var∈ : ∀ {n} {Γ : Ctx n} {x a k} → a ≡ kd k →
            Γ ⊢V x ∈ a → Γ ⊢Var x ∈ k
  V∈-Var∈ refl xT∈kd-k with ∈-lift xT∈kd-k
  V∈-Var∈ refl xT∈kd-k | ∈-tp x∈k = x∈k

  mutual

    -- Renamings preserve well-formedness of ascriptions.

    wf-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a ρ} →
              Γ ⊢ a wf → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ a ElimAsc/Var ρ wf
    wf-/Var (wf-kd k-kd)  ρ∈Γ = wf-kd (kd-/Var k-kd ρ∈Γ)
    wf-/Var (wf-tp a⇉a⋯a) ρ∈Γ = wf-tp (Nf⇉-/Var a⇉a⋯a ρ∈Γ)

    -- Renamings preserve well-formedness of kinds.

    kd-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {k ρ} →
              Γ ⊢ k kd → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ k Kind′/Var ρ kd
    kd-/Var (kd-⋯ a⇉a⋯a b⇉b⋯b) ρ∈Γ =
      kd-⋯ (Nf⇉-/Var a⇉a⋯a ρ∈Γ) (Nf⇉-/Var b⇉b⋯b ρ∈Γ)
    kd-/Var (kd-Π j-kd  k-kd) ρ∈Γ =
      let j/ρ-kd = kd-/Var j-kd ρ∈Γ
      in kd-Π j/ρ-kd (kd-/Var k-kd (∈-↑′ j/ρ-kd ρ∈Γ))

    -- Renamings preserve synthesized kinds of normal types.

    Nf⇉-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a k ρ} →
               Γ ⊢Nf a ⇉ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Nf a /Var ρ ⇉ k Kind′/Var ρ
    Nf⇉-/Var (⇉-⊥-f Γ-ctx)       ρ∈Γ = ⇉-⊥-f (/∈-wf ρ∈Γ)
    Nf⇉-/Var (⇉-⊤-f Γ-ctx)       ρ∈Γ = ⇉-⊤-f (/∈-wf ρ∈Γ)
    Nf⇉-/Var (⇉-∀-f k-kd a⇉a⋯a)  ρ∈Γ =
      let k/ρ-kd = kd-/Var k-kd ρ∈Γ
      in ⇉-∀-f k/ρ-kd (Nf⇉-/Var a⇉a⋯a (∈-↑′ k/ρ-kd ρ∈Γ))
    Nf⇉-/Var (⇉-→-f a⇉a⋯a b⇉b⋯b) ρ∈Γ =
      ⇉-→-f (Nf⇉-/Var a⇉a⋯a ρ∈Γ) (Nf⇉-/Var b⇉b⋯b ρ∈Γ)
    Nf⇉-/Var (⇉-Π-i j-kd a⇉k)    ρ∈Γ =
      let j/ρ-kd = kd-/Var j-kd ρ∈Γ
      in ⇉-Π-i j/ρ-kd (Nf⇉-/Var a⇉k (∈-↑′ j/ρ-kd ρ∈Γ))
    Nf⇉-/Var (⇉-s-i a∈b⋯c)       ρ∈Γ = ⇉-s-i (Ne∈-/Var a∈b⋯c ρ∈Γ)

    -- Renamings preserve the kinds of variables.

    Var∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {x k ρ} →
                Γ ⊢Var x ∈ k → Δ ⊢/Var ρ ∈ Γ →
                Δ ⊢Var (Vec.lookup ρ x) ∈ k Kind′/Var ρ
    Var∈-/Var {ρ = ρ} (⇉-var x Γ-ctx Γ[x]≡kd-k) ρ∈Γ =
      V∈-Var∈ (cong (_ElimAsc/Var ρ) Γ[x]≡kd-k) (/∈-lookup ρ∈Γ x)
    Var∈-/Var (⇇-⇑ x∈j j<∷k k-kd) ρ∈Γ =
      ⇇-⇑ (Var∈-/Var x∈j ρ∈Γ) (<∷-/Var j<∷k ρ∈Γ) (kd-/Var k-kd ρ∈Γ)

    -- Renamings preserve synthesized kinds of neutral types.

    Ne∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a k ρ} →
               Γ ⊢Ne a ∈ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Ne a /Var ρ ∈ k Kind′/Var ρ
    Ne∈-/Var (∈-∙ x∈j k⇉as⇉l) ρ∈Γ =
      ∈-∙ (Var∈-/Var x∈j ρ∈Γ) (Sp⇉-/Var k⇉as⇉l ρ∈Γ)

    -- Renamings preserve spine kindings.

    Sp⇉-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {as j k ρ} →
               Γ ⊢ j ⇉∙ as ⇉ k → Δ ⊢/Var ρ ∈ Γ →
               Δ ⊢ j Kind′/Var ρ ⇉∙ as //Var ρ ⇉ k Kind′/Var ρ
    Sp⇉-/Var ⇉-[]                                ρ∈Γ = ⇉-[]
    Sp⇉-/Var (⇉-∷ {a} {_} {j} {k} a⇇j j-kd k[a]⇉as⇉l) ρ∈Γ =
      ⇉-∷ (Nf⇇-/Var a⇇j ρ∈Γ) (kd-/Var j-kd ρ∈Γ)
          (subst (_ ⊢_⇉∙ _ ⇉ _) (Kind[∈⌊⌋]-/Var k a j)
                 (Sp⇉-/Var k[a]⇉as⇉l ρ∈Γ))

    -- Renamings preserve checked kinds of neutral types.

    Nf⇇-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a k ρ} →
               Γ ⊢Nf a ⇇ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢Nf a /Var ρ ⇇ k Kind′/Var ρ
    Nf⇇-/Var (⇇-⇑ a⇉j j<∷k) ρ∈Γ = ⇇-⇑ (Nf⇉-/Var a⇉j ρ∈Γ) (<∷-/Var j<∷k ρ∈Γ)

    -- Renamings preserve subkinding.

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

    -- Renamings preserve subtyping.

    <:-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b ρ} →
              Γ ⊢ a <: b → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ a /Var ρ <: b /Var ρ
    <:-/Var (<:-trans a<:b b<:c) ρ∈Γ =
      <:-trans (<:-/Var a<:b ρ∈Γ) (<:-/Var b<:c ρ∈Γ)
    <:-/Var (<:-⊥ a⇉a⋯a) ρ∈Γ = <:-⊥ (Nf⇉-/Var a⇉a⋯a ρ∈Γ)
    <:-/Var (<:-⊤ a⇉a⋯a) ρ∈Γ = <:-⊤ (Nf⇉-/Var a⇉a⋯a ρ∈Γ)
    <:-/Var (<:-∀ k₂<∷k₁ a₁<:a₂ Πk₁a₁⇉Πk₁a₁⋯Πk₁a₁) ρ∈Γ =
      <:-∀ (<∷-/Var k₂<∷k₁ ρ∈Γ)
           (<:-/Var a₁<:a₂ (∈-↑ (<:-/Var-wf a₁<:a₂ ρ∈Γ) ρ∈Γ))
           (Nf⇉-/Var Πk₁a₁⇉Πk₁a₁⋯Πk₁a₁ ρ∈Γ)
    <:-/Var (<:-→ a₂<:a₁ b₁<:b₂) ρ∈Γ =
      <:-→ (<:-/Var a₂<:a₁ ρ∈Γ) (<:-/Var b₁<:b₂ ρ∈Γ)
    <:-/Var (<:-∙ x∈j j⇉as₁<:as₂⇉k) ρ∈Γ =
      <:-∙ (Var∈-/Var x∈j ρ∈Γ) (Sp≃-/Var j⇉as₁<:as₂⇉k ρ∈Γ)
    <:-/Var (<:-⟨| a∈b⋯c) ρ∈Γ = <:-⟨| (Ne∈-/Var a∈b⋯c ρ∈Γ)
    <:-/Var (<:-|⟩ a∈b⋯c) ρ∈Γ = <:-|⟩ (Ne∈-/Var a∈b⋯c ρ∈Γ)

    <:⇇-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b k ρ} →
               Γ ⊢ a <: b ⇇ k → Δ ⊢/Var ρ ∈ Γ →
               Δ ⊢ a /Var ρ <: b /Var ρ ⇇ k Kind′/Var ρ
    <:⇇-/Var (<:-⇇ a⇇k b⇇k a<:b) ρ∈Γ =
      <:-⇇ (Nf⇇-/Var a⇇k ρ∈Γ) (Nf⇇-/Var b⇇k ρ∈Γ) (<:-/Var a<:b ρ∈Γ)
    <:⇇-/Var (<:-λ a₁<:a₂ Λj₁a₁⇇Πjk Λj₂a₂⇇Πjk) ρ∈Γ =
      <:-λ (<:⇇-/Var a₁<:a₂ (∈-↑ (<:⇇-/Var-wf a₁<:a₂ ρ∈Γ) ρ∈Γ))
           (Nf⇇-/Var Λj₁a₁⇇Πjk ρ∈Γ) (Nf⇇-/Var Λj₂a₂⇇Πjk ρ∈Γ)

    -- Renamings preserve type equality.

    ≃-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b k ρ} →
              Γ ⊢ a ≃ b ⇇ k → Δ ⊢/Var ρ ∈ Γ →
              Δ ⊢ a /Var ρ ≃ b /Var ρ ⇇ k Kind′/Var ρ
    ≃-/Var (<:-antisym k-kd a<:b b<:a) ρ∈Γ =
      <:-antisym (kd-/Var k-kd ρ∈Γ) (<:⇇-/Var a<:b ρ∈Γ) (<:⇇-/Var b<:a ρ∈Γ)

    Sp≃-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {as bs j k ρ} →
                Γ ⊢ j ⇉∙ as ≃ bs ⇉ k → Δ ⊢/Var ρ ∈ Γ →
                Δ ⊢ j Kind′/Var ρ ⇉∙ as //Var ρ ≃ bs //Var ρ ⇉ k Kind′/Var ρ
    Sp≃-/Var ≃-[] ρ∈Γ = ≃-[]
    Sp≃-/Var (≃-∷ {a} {_} {_} {_} {j} {k} a≃b as≃bs) ρ∈Γ =
      ≃-∷ (≃-/Var a≃b ρ∈Γ)
          (subst (_ ⊢_⇉∙ _ ≃ _ ⇉ _) (Kind[∈⌊⌋]-/Var k a j)
                 (Sp≃-/Var as≃bs ρ∈Γ))

    -- Helpers (to satisfy the termination checker).
    --
    -- These are simply (manual) expansions of the form
    --
    --   XX-/Var-wf x ρ∈Γ  =  wf-/Var (wf-∷₁ (XX-ctx x)) ρ∈Γ
    --
    -- to ensure the above definitions remain structurally recursive
    -- in the various derivations.

    kd-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k ρ} →
                 kd j ∷ Γ ⊢ k kd → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ kd (j Kind′/Var ρ) wf
    kd-/Var-wf (kd-⋯ a∈a⋯a _) ρ∈Γ = Nf⇉-/Var-wf a∈a⋯a ρ∈Γ
    kd-/Var-wf (kd-Π j-kd _)  ρ∈Γ = kd-/Var-wf j-kd ρ∈Γ

    Nf⇉-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a j k ρ} →
                  kd j ∷ Γ ⊢Nf a ⇉ k → Δ ⊢/Var ρ ∈ Γ →
                  Δ ⊢ kd (j Kind′/Var ρ) wf
    Nf⇉-/Var-wf (⇉-⊥-f (j-wf ∷ _)) ρ∈Γ = wf-/Var j-wf ρ∈Γ
    Nf⇉-/Var-wf (⇉-⊤-f (j-wf ∷ _)) ρ∈Γ = wf-/Var j-wf ρ∈Γ
    Nf⇉-/Var-wf (⇉-∀-f k-kd _)     ρ∈Γ = kd-/Var-wf k-kd ρ∈Γ
    Nf⇉-/Var-wf (⇉-→-f a⇉a⋯a _)    ρ∈Γ = Nf⇉-/Var-wf a⇉a⋯a ρ∈Γ
    Nf⇉-/Var-wf (⇉-Π-i j-kd _)     ρ∈Γ = kd-/Var-wf j-kd ρ∈Γ
    Nf⇉-/Var-wf (⇉-s-i a∈b⋯c)      ρ∈Γ = Ne∈-/Var-wf a∈b⋯c ρ∈Γ

    Ne∈-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a j k ρ} →
                  kd j ∷ Γ ⊢Ne a ∈ k → Δ ⊢/Var ρ ∈ Γ →
                  Δ ⊢ kd (j Kind′/Var ρ) wf
    Ne∈-/Var-wf (∈-∙ x∈k _) ρ∈Γ = Var∈-/Var-wf x∈k ρ∈Γ

    Var∈-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a j k ρ} →
                   kd j ∷ Γ ⊢Var a ∈ k → Δ ⊢/Var ρ ∈ Γ →
                   Δ ⊢ kd (j Kind′/Var ρ) wf
    Var∈-/Var-wf (⇉-var x (j-wf ∷ _) _) ρ∈Γ = wf-/Var j-wf ρ∈Γ
    Var∈-/Var-wf (⇇-⇑ x∈j _ _)          ρ∈Γ = Var∈-/Var-wf x∈j ρ∈Γ

    Nf⇇-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a j k ρ} →
                  kd j ∷ Γ ⊢Nf a ⇇ k → Δ ⊢/Var ρ ∈ Γ →
                  Δ ⊢ kd (j Kind′/Var ρ) wf
    Nf⇇-/Var-wf (⇇-⇑ a⇉j _) ρ∈Γ = Nf⇉-/Var-wf a⇉j ρ∈Γ

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

    <:-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b j ρ} →
                 kd j ∷ Γ ⊢ a <: b → Δ ⊢/Var ρ ∈ Γ →
                 Δ ⊢ kd (j Kind′/Var ρ) wf
    <:-/Var-wf (<:-trans a<:b _) ρ∈Γ = <:-/Var-wf a<:b ρ∈Γ
    <:-/Var-wf (<:-⊥ a⇉a⋯a)      ρ∈Γ = Nf⇉-/Var-wf a⇉a⋯a ρ∈Γ
    <:-/Var-wf (<:-⊤ a⇉a⋯a)      ρ∈Γ = Nf⇉-/Var-wf a⇉a⋯a ρ∈Γ
    <:-/Var-wf (<:-∀ k₂<∷k₁ _ _) ρ∈Γ = <∷-/Var-wf k₂<∷k₁ ρ∈Γ
    <:-/Var-wf (<:-→ a₂<:a₁ _)   ρ∈Γ = <:-/Var-wf a₂<:a₁ ρ∈Γ
    <:-/Var-wf (<:-∙ x∈j _)      ρ∈Γ = Var∈-/Var-wf x∈j ρ∈Γ
    <:-/Var-wf (<:-⟨| a∈b⋯c)     ρ∈Γ = Ne∈-/Var-wf a∈b⋯c ρ∈Γ
    <:-/Var-wf (<:-|⟩ a∈b⋯c)     ρ∈Γ = Ne∈-/Var-wf a∈b⋯c ρ∈Γ

    <:⇇-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {a b j k ρ} →
                  kd j ∷ Γ ⊢ a <: b ⇇ k → Δ ⊢/Var ρ ∈ Γ →
                  Δ ⊢ kd (j Kind′/Var ρ) wf
    <:⇇-/Var-wf (<:-⇇ a⇇k _ _)       ρ∈Γ = Nf⇇-/Var-wf a⇇k ρ∈Γ
    <:⇇-/Var-wf (<:-λ _ Λj₁a₁⇇Πjk _) ρ∈Γ = Nf⇇-/Var-wf Λj₁a₁⇇Πjk ρ∈Γ

    ≅-/Var-wf : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k l ρ} →
                kd j ∷ Γ ⊢ k ≅ l → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ kd (j Kind′/Var ρ) wf
    ≅-/Var-wf (<∷-antisym _ _ j<∷k _) ρ∈Γ = <∷-/Var-wf j<∷k ρ∈Γ

  -- Renamings preserve kind equality.

  ≅-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {j k ρ} →
           Γ ⊢ j ≅ k → Δ ⊢/Var ρ ∈ Γ → Δ ⊢ j Kind′/Var ρ ≅ k Kind′/Var ρ
  ≅-/Var (<∷-antisym j-kd k-kd j<∷k k<∷j) ρ∈Γ =
    <∷-antisym (kd-/Var j-kd ρ∈Γ) (kd-/Var k-kd ρ∈Γ)
               (<∷-/Var j<∷k ρ∈Γ) (<∷-/Var k<∷j ρ∈Γ)

  -- Renamings preserve wrapped variable typing

  Var′∈-/Var : ∀ {m n} {Γ : Ctx m} {Δ : Ctx n} {x k ρ} →
               Γ ⊢Var′ x ∈ k → Δ ⊢/Var ρ ∈ Γ →
               Δ ⊢Var′ (Vec.lookup ρ x) ∈ k ElimAsc/Var ρ
  Var′∈-/Var         (∈-tp x∈k)               ρ∈Γ = ∈-tp (Var∈-/Var x∈k ρ∈Γ)
  Var′∈-/Var {ρ = ρ} (∈-tm x Γ-ctx Γ[x]≡tp-t) ρ∈Γ =
    subst (_ ⊢Var′ _ ∈_) (cong (_ElimAsc/Var ρ) Γ[x]≡tp-t)
          (∈-lift (/∈-lookup ρ∈Γ x))

-- Well-kinded renamings in canonically kinded types, i.e. lemmas
-- showing that renaming preserves kinding.
--
-- Note that these are pure renamings that cannot change the kinds of
-- variables (i.e. they cannot be used to implement context conversion
-- or narrowing).

module KindedRenaming where
  open Substitution using (termLikeLemmasElimAsc)

  typedVarSubst : TypedVarSubst ElimAsc lzero
  typedVarSubst = record
    { _⊢_wf              = _⊢_wf
    ; typeExtension      = ElimCtx.weakenOps
    ; typeVarApplication = AppLemmas.application appLemmas
    ; wf-wf              = wf-ctx
    ; /-wk               = refl
    ; id-vanishes        = id-vanishes
    ; /-⊙                 = /-⊙
    }
    where
      open TermLikeLemmas termLikeLemmasElimAsc using (varLiftAppLemmas)
      open LiftAppLemmas  varLiftAppLemmas

  open TypedVarSubst typedVarSubst
    hiding (_⊢_wf) renaming (_⊢Var_∈_ to _⊢GenVar_∈_)

  -- Liftings from generic variable typings to variable kindings.

  liftTyped : LiftTo-Var′∈ _⊢GenVar_∈_
  liftTyped = record
    { typedSimple  = typedSimple
    ; ∈-lift       = ∈-lift
    }
    where
      ∈-lift : ∀ {n} {Γ : Ctx n} {x a} → Γ ⊢GenVar x ∈ a → Γ ⊢Var′ x ∈ a
      ∈-lift (∈-var x Γ-ctx) = ∈-var′ x Γ-ctx

  open TypedVarSubstApp _⊢GenVar_∈_ liftTyped public
  open Substitution hiding (subst)

  -- Weakening preserves well-formedness of kinds.

  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 (∈-wk a-wf)

  -- Weakening preserves variable kinding.

  Var∈-weaken : ∀ {n} {Γ : Ctx n} {a x k} → Γ ⊢ a wf →
                Γ ⊢Var x ∈ k →
                a ∷ Γ ⊢Var Vec.lookup VarSubst.wk x ∈ weakenKind′ k
  Var∈-weaken a-wf x∈k = Var∈-/Var x∈k (∈-wk a-wf)

  Var′∈-weaken : ∀ {n} {Γ : Ctx n} {a x b} → Γ ⊢ a wf →
                 Γ ⊢Var′ x ∈ b → a ∷ Γ ⊢Var′ suc x ∈ weakenElimAsc b
  Var′∈-weaken a-wf x∈b =
    subst (_ ⊢Var′_∈ _) VarLemmas./-wk (Var′∈-/Var x∈b (∈-wk a-wf))

  -- Weakening preserves spine kinding.

  Sp⇉-weaken : ∀ {n} {Γ : Ctx n} {a bs j k} → Γ ⊢ a wf → Γ ⊢ j ⇉∙ bs ⇉ k →
               a ∷ Γ ⊢ weakenKind′ j ⇉∙ weakenSpine bs ⇉ weakenKind′ k
  Sp⇉-weaken a-wf j⇉bs⇉k = Sp⇉-/Var j⇉bs⇉k (∈-wk a-wf)

  -- Weakening preserves checked kinding.

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

  -- Weakening preserves subkinding.

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

  -- Weakening preserves subtyping.

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

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

  -- Weakening preserves well-formedness of ascriptions.

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

  -- Weakening preserves kind and type equality.

  ≃-weaken : ∀ {n} {Γ : Ctx n} {a b c k} → Γ ⊢ a wf → Γ ⊢ b ≃ c ⇇ k →
              a ∷ Γ ⊢ weakenElim b ≃ weakenElim c ⇇ weakenKind′ k
  ≃-weaken a-wf b≃c = ≃-/Var b≃c (∈-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 (∈-wk a-wf)

  Sp≃-weaken : ∀ {n} {Γ : Ctx n} {a bs cs j k} → Γ ⊢ a wf →
               Γ ⊢ j ⇉∙ bs ≃ cs ⇉ k →
               a ∷ Γ ⊢ weakenKind′ j ⇉∙ weakenSpine bs ≃ weakenSpine cs ⇉
                 weakenKind′ k
  Sp≃-weaken a-wf bs≃cs = Sp≃-/Var bs≃cs (∈-wk a-wf)

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

module WfCtxOps where
  open KindedRenaming using (wf-weaken)

  wfWeakenOps : WellFormedWeakenOps weakenOps
  wfWeakenOps = record { wf-weaken = wf-weaken }

  open WellFormedWeakenOps wfWeakenOps public
    hiding (wf-weaken) 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 → ElimCtx.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))

open WfCtxOps

-- A corollary (validity of variable kinding): the kinds of variables
-- are well-formed.

Var∈-valid : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Var a ∈ k → Γ ⊢ k kd
Var∈-valid (⇉-var x Γ-ctx Γ[x]≡kd-k) = lookup-kd x Γ-ctx Γ[x]≡kd-k
Var∈-valid (⇇-⇑ x∈j j<∷k k-kd)       = k-kd

----------------------------------------------------------------------
-- Context narrowing
--
-- The various judgments are preserved by narrowing of kind
-- ascriptions in their contexts.

module ContextNarrowing where
  open Substitution
    using (termLikeLemmasElim; termLikeLemmasKind′; termLikeLemmasElimAsc)
  open TermLikeLemmas termLikeLemmasElimAsc using (varLiftAppLemmas)
  open LiftAppLemmas varLiftAppLemmas
  open KindedRenaming using (kd-weaken; <∷-weaken; Var′∈-weaken)
  private
    module EL = LiftAppLemmas
                (TermLikeLemmas.varLiftAppLemmas termLikeLemmasElim)
    module KL = LiftAppLemmas
                (TermLikeLemmas.varLiftAppLemmas termLikeLemmasKind′)

  -- NOTE. Rather than proving context narrowing directly by induction
  -- on typing derivations, we instead define a more flexible variant
  -- of well-typed variable substitutions based on the canonical
  -- variable kinding judgment (_⊢Var_∈_).  This judgment features a
  -- subsumption rule (∈-⇑), which is not available in the generic
  -- variable judgment from Data.Fin.Substitutions.Typed that we used
  -- to define basic typed renamings.  With support for subsumption in
  -- typed renamings, we get context narrowing "for free", as it is
  -- just another variable substitution (one that happens to change
  -- the kind rather than the name of a variable).  This way of
  -- proving context narrowing is more convenient since we can reuse
  -- the generic lemmas proven for typed variable substitution and
  -- avoid some explicit fiddling with context.
  --
  -- Note also that we could not have defined typed renamings
  -- directly using the _⊢Var_∈_ judgment since that would have
  -- required a weakening lemma for subkiding, which in turn is
  -- implemented via typed renamings.

  -- The trivial lifting from _⊢Var′_∈_ to itself, and simple typed
  -- variable substitutions.

  liftTyped : LiftTo-Var′∈ _⊢Var′_∈_
  liftTyped = record
    { typedSimple      = record
      { typedWeakenOps = record
        { ∈-weaken     = Var′∈-weaken
        ; ∈-wf         = Var′∈-ctx
        ; /-wk         = refl
        ; /-weaken     = /-weaken
        ; weaken-/-∷   = weaken-/-∷
        }
      ; ∈-var          = ∈-var′
      ; wf-wf          = wf-ctx
      ; id-vanishes    = id-vanishes
      }
    ; ∈-lift           = λ x∈a → x∈a
    }
    where
      weakenLemmas : WeakenLemmas ElimAsc Fin
      weakenLemmas = record { appLemmas = appLemmas ; /-wk = refl }
      open WeakenLemmas weakenLemmas

  open LiftTo-Var′∈ liftTyped
  open TypedVarSubstApp _⊢Var′_∈_ liftTyped

  -- A typed renaming that narrows the kind of the first type
  -- variable.

  ∈-<∷-sub : ∀ {n} {Γ : Ctx n} {j k} →
             Γ ⊢ j kd → Γ ⊢ j <∷ k → (kd k ∷ Γ) ctx →
             kd j ∷ Γ ⊢/Var id ∈ kd k ∷ Γ
  ∈-<∷-sub j-kd j<∷k k∷Γ-ctx =
    ∈-tsub (∈-tp (⇇-⇑ x∈k (<∷-weaken j-wf j<∷k) k-kd))
    where
      j-wf  = wf-kd j-kd
      Γ-ctx = kd-ctx j-kd
      x∈k   = ⇉-var zero (j-wf ∷ Γ-ctx) refl
      k-kd  = kd-weaken j-wf (wf-kd-inv (wf-∷₁ k∷Γ-ctx))

  -- Narrowing the kind of the first type variable preserves
  -- well-formedness of kinds.

  ⇓-kd : ∀ {n} {Γ : Ctx n} {j₁ j₂ k} →
         Γ ⊢ j₁ kd → Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ k kd → kd j₁ ∷ Γ ⊢ k kd
  ⇓-kd j₁-kd j₁<∷j₂ k-kd =
    subst (_ ⊢_kd) (KL.id-vanishes _)
          (kd-/Var k-kd (∈-<∷-sub j₁-kd j₁<∷j₂ (kd-ctx k-kd)))

  -- Narrowing the kind of the first type variable preserves
  -- well-kindedness.

  ⇓-Nf⇉ : ∀ {n} {Γ : Ctx n} {j₁ j₂ a k} →
          Γ ⊢ j₁ kd → Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢Nf a ⇉ k → kd j₁ ∷ Γ ⊢Nf a ⇉ k
  ⇓-Nf⇉ j₁-kd j₁<∷j₂ a⇉k =
    subst₂ (_ ⊢Nf_⇉_) (EL.id-vanishes _) (KL.id-vanishes _)
           (Nf⇉-/Var a⇉k (∈-<∷-sub j₁-kd j₁<∷j₂ (Nf⇉-ctx a⇉k)))

  -- Narrowing the kind of the first type variable preserves
  -- subkinding and subtyping.

  ⇓-<∷ : ∀ {n} {Γ : Ctx n} {j₁ j₂ k₁ k₂} →
         Γ ⊢ j₁ kd → Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ k₁ <∷ k₂ → kd j₁ ∷ Γ ⊢ k₁ <∷ k₂
  ⇓-<∷ j₁-kd j₁<∷j₂ k₁<∷k₂ =
    subst₂ (_ ⊢_<∷_) (KL.id-vanishes _) (KL.id-vanishes _)
           (<∷-/Var k₁<∷k₂ (∈-<∷-sub j₁-kd j₁<∷j₂ (<∷-ctx k₁<∷k₂)))

  ⇓-<: : ∀ {n} {Γ : Ctx n} {j₁ j₂ a₁ a₂} →
         Γ ⊢ j₁ kd → Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ a₁ <: a₂ → kd j₁ ∷ Γ ⊢ a₁ <: a₂
  ⇓-<: j₁-kd j₁<∷j₂ a₁<:a₂ =
    subst₂ (_ ⊢_<:_) (EL.id-vanishes _) (EL.id-vanishes _)
           (<:-/Var a₁<:a₂ (∈-<∷-sub j₁-kd j₁<∷j₂ (<:-ctx a₁<:a₂)))

  ⇓-<:⇇ : ∀ {n} {Γ : Ctx n} {j₁ j₂ a₁ a₂ k} →
          Γ ⊢ j₁ kd → Γ ⊢ j₁ <∷ j₂ → kd j₂ ∷ Γ ⊢ a₁ <: a₂ ⇇ k →
          kd j₁ ∷ Γ ⊢ a₁ <: a₂ ⇇ k
  ⇓-<:⇇ j₁-kd j₁<∷j₂ a₁<:a₂∈k =
    subst (_ ⊢ _ <: _ ⇇_) (KL.id-vanishes _)
          (subst₂ (_ ⊢_<:_⇇ _) (EL.id-vanishes _) (EL.id-vanishes _)
                  (<:⇇-/Var a₁<:a₂∈k
                            (∈-<∷-sub j₁-kd j₁<∷j₂ (<:⇇-ctx a₁<:a₂∈k))))

open KindedRenaming
open ContextNarrowing
open Substitution hiding (subst)
private module TV = TypedVarSubst typedVarSubst

-- Some corollaries of context narrowing: transitivity of subkinding
-- and kind equality are admissible.

<∷-trans : ∀ {n} {Γ : Ctx n} {j k l} → Γ ⊢ j <∷ k → Γ ⊢ k <∷ l → Γ ⊢ j <∷ l
<∷-trans (<∷-⋯ a₂<:a₁ b₁<:b₂) (<∷-⋯ a₃<:a₂ b₂<:b₃) =
  <∷-⋯ (<:-trans a₃<:a₂ a₂<:a₁) (<:-trans b₁<:b₂ b₂<:b₃)
<∷-trans (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd) (<∷-Π j₃<∷j₂ k₂<∷k₃ _) =
  let j₃-kd = wf-kd-inv (wf-∷₁ (<∷-ctx k₂<∷k₃))
  in <∷-Π (<∷-trans j₃<∷j₂ j₂<∷j₁)
          (<∷-trans (⇓-<∷ j₃-kd j₃<∷j₂ k₁<∷k₂) k₂<∷k₃) Πj₁k₁-kd

≅-trans : ∀ {n} {Γ : Ctx n} {j k l} → Γ ⊢ j ≅ k → Γ ⊢ k ≅ l → Γ ⊢ j ≅ l
≅-trans (<∷-antisym j-kd _ j<∷k k<∷j) (<∷-antisym _ l-kd k<∷l l<∷k) =
  <∷-antisym j-kd l-kd (<∷-trans j<∷k k<∷l) (<∷-trans l<∷k k<∷j)

-- Some more corollaries: subsumption is admissible in canonical kind
-- checking, checked subtyping and kind equality.
--
-- NOTE. The proof of (<:⇇-⇑) is by induction on subkinding
-- derivations (the second hypothesis) rather than kinding derivations
-- (the first hypothesis).

Nf⇇-⇑ : ∀ {n} {Γ : Ctx n} {a j k} → Γ ⊢Nf a ⇇ j → Γ ⊢ j <∷ k → Γ ⊢Nf a ⇇ k
Nf⇇-⇑ (⇇-⇑ a⇇j₁ j₁<∷j₂) j₂<∷j₃ = ⇇-⇑ a⇇j₁ (<∷-trans j₁<∷j₂ j₂<∷j₃)

<:⇇-⇑ : ∀ {n} {Γ : Ctx n} {a b j k} →
        Γ ⊢ a <: b ⇇ j → Γ ⊢ j <∷ k → Γ ⊢ k kd → Γ ⊢ a <: b ⇇ k
<:⇇-⇑ (<:-⇇ a⇇c₁⋯d₁ b⇇c₁⋯d₁ a<:b) (<∷-⋯ c₂<:c₁ d₁<:d₂) _ =
  <:-⇇ (Nf⇇-⇑ a⇇c₁⋯d₁ (<∷-⋯ c₂<:c₁ d₁<:d₂))
       (Nf⇇-⇑ b⇇c₁⋯d₁ (<∷-⋯ c₂<:c₁ d₁<:d₂)) a<:b
<:⇇-⇑ (<:-λ a₁<:a₂ Λj₁a₁⇇Πk₁l₁ Λj₂a₂⇇Πk₁l₁) (<∷-Π k₂<∷k₁ l₁<∷l₂ Πk₁l₁-kd)
      (kd-Π k₂-kd l₂-kd) =
  <:-λ (<:⇇-⇑ (⇓-<:⇇ k₂-kd k₂<∷k₁ a₁<:a₂) l₁<∷l₂ l₂-kd)
       (Nf⇇-⇑ Λj₁a₁⇇Πk₁l₁ (<∷-Π k₂<∷k₁ l₁<∷l₂ Πk₁l₁-kd))
       (Nf⇇-⇑ Λj₂a₂⇇Πk₁l₁ (<∷-Π k₂<∷k₁ l₁<∷l₂ Πk₁l₁-kd))

≃-⇑ : ∀ {n} {Γ : Ctx n} {a b j k} →
      Γ ⊢ a ≃ b ⇇ j → Γ ⊢ j <∷ k → Γ ⊢ k kd → Γ ⊢ a ≃ b ⇇ k
≃-⇑ (<:-antisym j-kd a<:b b<:a) j<∷k k-kd =
  <:-antisym k-kd (<:⇇-⇑ a<:b j<∷k k-kd) (<:⇇-⇑ b<:a j<∷k k-kd)

------------------------------------------------------------------------
-- Reflexivity of the various relations.
--
-- NOTE. The proof is by mutual induction in the structure of the
-- types and kinds being related to themselves, and then by
-- case-analysis on formation/kinding derivations (rather than
-- induction on the typing/kinding derivations directly).  For
-- example, the proof of (<:⇇-reflNf⇉-⇑) is not decreasing in the
-- kinding derivation of `a' in the type abstraction (Π-intro) case.
-- To avoid clutter, we do not make the corresponding type/kind
-- parameters explicit in the implementations below: thanks to the
-- structure of canonical formation/kinding, every kind/type
-- constructor corresponds to exactly one kinding/typing rule
-- (i.e. the rules are syntax directed).

mutual

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

  -- Reflexivity of canonical subtyping.

  <:-reflNf⇉ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇉ b ⋯ c → Γ ⊢ a <: a
  <:-reflNf⇉ (⇉-⊥-f Γ-ctx)       = <:-⊥ (⇉-⊥-f Γ-ctx)
  <:-reflNf⇉ (⇉-⊤-f Γ-ctx)       = <:-⊤ (⇉-⊤-f Γ-ctx)
  <:-reflNf⇉ (⇉-∀-f k-kd a⇉a⋯a)  =
    <:-∀ (<∷-refl k-kd) (<:-reflNf⇉ a⇉a⋯a) (⇉-∀-f k-kd a⇉a⋯a)
  <:-reflNf⇉ (⇉-→-f a⇉a⋯a b⇉b⋯b) =
    <:-→ (<:-reflNf⇉ a⇉a⋯a) (<:-reflNf⇉ b⇉b⋯b)
  <:-reflNf⇉ (⇉-s-i (∈-∙ x∈j j⇉as⇉k)) = <:-∙ x∈j (≃-reflSp⇉ j⇉as⇉k)

  <:⇇-reflNf⇉-⇑ : ∀ {n} {Γ : Ctx n} {a j k} →
                  Γ ⊢Nf a ⇉ j → Γ ⊢ j <∷ k → Γ ⊢ k kd → Γ ⊢ a <: a ⇇ k
  <:⇇-reflNf⇉-⇑ a⇉b₁⋯c₁ (<∷-⋯ b₂<:b₁ c₁<:c₂) _ =
    let a⇇b₂⋯c₂ = ⇇-⇑ a⇉b₁⋯c₁ (<∷-⋯ b₂<:b₁ c₁<:c₂)
    in <:-⇇ a⇇b₂⋯c₂ a⇇b₂⋯c₂ (<:-reflNf⇉ a⇉b₁⋯c₁)
  <:⇇-reflNf⇉-⇑ (⇉-Π-i j₁-kd a⇉k₁) (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd)
                (kd-Π j₂-kd k₂-kd) =
    let a<:a⇇k₂ = <:⇇-reflNf⇉-⇑ (⇓-Nf⇉ j₂-kd j₂<∷j₁ a⇉k₁) k₁<∷k₂ k₂-kd
        Λj₁a⇇Πj₂k₂ = ⇇-⇑ (⇉-Π-i j₁-kd a⇉k₁) (<∷-Π j₂<∷j₁ k₁<∷k₂ Πj₁k₁-kd)
    in <:-λ a<:a⇇k₂ Λj₁a⇇Πj₂k₂ Λj₁a⇇Πj₂k₂

  <:⇇-reflNf⇇ : ∀ {n} {Γ : Ctx n} {a k} →
                Γ ⊢Nf a ⇇ k → Γ ⊢ k kd → Γ ⊢ a <: a ⇇ k
  <:⇇-reflNf⇇ (⇇-⇑ a⇉k j<∷k) k-kd = <:⇇-reflNf⇉-⇑ a⇉k j<∷k k-kd

  -- Reflexivity of canonical spine equality.
  ≃-reflSp⇉ : ∀ {n} {Γ : Ctx n} {as j k} →
              Γ ⊢ j ⇉∙ as ⇉ k → Γ ⊢ j ⇉∙ as ≃ as ⇉ k
  ≃-reflSp⇉ ⇉-[]                     = ≃-[]
  ≃-reflSp⇉ (⇉-∷ a⇇j j-kd k[a]⇉as⇉l) =
    ≃-∷ (≃-reflNf⇇ a⇇j j-kd) (≃-reflSp⇉ k[a]⇉as⇉l)

  -- A checked variant of reflexivity.
  ≃-reflNf⇇ : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇇ k → Γ ⊢ k kd → Γ ⊢ a ≃ a ⇇ k
  ≃-reflNf⇇ a⇇k k-kd =
    <:-antisym k-kd (<:⇇-reflNf⇇ a⇇k k-kd) (<:⇇-reflNf⇇ a⇇k k-kd)

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

-- A shorthand.
<:⇇-reflNf⇉ : ∀ {n} {Γ : Ctx n} {a k} →
              Γ ⊢Nf a ⇉ k → Γ ⊢ k kd → Γ ⊢ a <: a ⇇ k
<:⇇-reflNf⇉ a⇉k k-kd = <:⇇-reflNf⇉-⇑ a⇉k (<∷-refl k-kd) k-kd

-- Some corollaryies of reflexivity.

-- The synthesized kinds of normal forms kind-check.
Nf⇉⇒Nf⇇ : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇉ k → Γ ⊢Nf a ⇇ k
Nf⇉⇒Nf⇇ a⇉k = ⇇-⇑ a⇉k (<∷-refl (Nf⇉-valid a⇉k))

-- An admissible operator introduction rule accepting a checked body.
Nf⇇-Π-i : ∀ {n} {Γ : Ctx n} {j a k} →
          Γ ⊢ j kd → kd j ∷ Γ ⊢Nf a ⇇ k → Γ ⊢Nf Λ∙ j a ⇇ Π j k
Nf⇇-Π-i j-kd (⇇-⇑ a⇉l l<∷k) =
  ⇇-⇑ (⇉-Π-i j-kd a⇉l) (<∷-Π (<∷-refl j-kd) l<∷k (kd-Π j-kd (Nf⇉-valid a⇉l)))

-- Admissible projection rules for canonically kinded proper types.

<:-⟨|-Nf⇉ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇉ b ⋯ c → Γ ⊢ b <: a
<:-⟨|-Nf⇉ a⇉b⋯c with Nf⇉-≡ a⇉b⋯c
<:-⟨|-Nf⇉ a⇉a⋯a | refl , refl = <:-reflNf⇉ a⇉a⋯a

<:-⟨|-Nf⇇ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇇ b ⋯ c → Γ ⊢ b <: a
<:-⟨|-Nf⇇ (⇇-⇑ a⇉b₁⋯c₁ (<∷-⋯ b₂<:b₁ c₁<:c₂)) =
  <:-trans b₂<:b₁ (<:-⟨|-Nf⇉ a⇉b₁⋯c₁)

<:-|⟩-Nf⇉ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇉ b ⋯ c → Γ ⊢ a <: c
<:-|⟩-Nf⇉ a⇉b⋯c with Nf⇉-≡ a⇉b⋯c
<:-|⟩-Nf⇉ a⇉a⋯a | refl , refl = <:-reflNf⇉ a⇉a⋯a

<:-|⟩-Nf⇇ : ∀ {n} {Γ : Ctx n} {a b c} → Γ ⊢Nf a ⇇ b ⋯ c → Γ ⊢ a <: c
<:-|⟩-Nf⇇ (⇇-⇑ a⇉b₁⋯c₁ (<∷-⋯ b₂<:b₁ c₁<:c₂)) =
  <:-trans (<:-|⟩-Nf⇉ a⇉b₁⋯c₁) c₁<:c₂

-- An admissible interval rule for checked subtyping.
<:⇇-⋯-i : ∀ {n} {Γ : Ctx n} {a b c d} →
          Γ ⊢ a <: b ⇇ c ⋯ d → Γ ⊢ a <: b ⇇ a ⋯ b
<:⇇-⋯-i (<:-⇇ a⇇c⋯d b⇇c⋯d a<:b) =
  let a⇉a⋯a = Nf⇇-s-i a⇇c⋯d
      b⇉b⋯b = Nf⇇-s-i b⇇c⋯d
  in <:-⇇ (⇇-⇑ a⇉a⋯a (<∷-⋯ (<:-reflNf⇉ a⇉a⋯a) a<:b))
          (⇇-⇑ b⇉b⋯b (<∷-⋯ a<:b (<:-reflNf⇉ b⇉b⋯b))) a<:b

-- An inversion lemma about variable kinding.
Var∈-inv : ∀ {n} {Γ : Ctx n} {x k} → Γ ⊢Var x ∈ k →
           ∃ λ j → lookup Γ x ≡ kd j × Γ ⊢ j <∷ k × Γ ⊢ j kd × Γ ⊢ k kd
Var∈-inv (⇉-var x Γ-ctx Γ[x]≡kd-j) =
  let j-kd = lookup-kd x Γ-ctx Γ[x]≡kd-j
  in _ , Γ[x]≡kd-j , <∷-refl j-kd , j-kd , j-kd
Var∈-inv (⇇-⇑ x∈j j<∷k k-kd) =
  let l , Γ[x]≡kd-l , l<∷j , l-kd , _ = Var∈-inv x∈j
  in l , Γ[x]≡kd-l , <∷-trans l<∷j j<∷k , l-kd , k-kd

-- A "canonical forms" lemma for operator equality.
≃-Π-can : ∀ {n} {Γ : Ctx n} {a₁ a₂ j k} → Γ ⊢ a₁ ≃ a₂ ⇇ Π j k →
          ∃ λ j₁ → ∃ λ b₁ → ∃ λ j₂ → ∃ λ b₂ →
            Γ ⊢Nf Λ∙ j₁ b₁ ⇇ Π j k × Γ ⊢Nf Λ∙ j₂ b₂ ⇇ Π j k × Γ ⊢ Π j k kd ×
              Γ ⊢ j <∷ j₁ × Γ ⊢ j <∷ j₂ ×
              kd j ∷ Γ ⊢ b₁ <: b₂ ⇇ k × kd j ∷ Γ ⊢ b₂ <: b₁ ⇇ k ×
              a₁ ≡ Λ∙ j₁ b₁ × a₂ ≡ Λ∙ j₂ b₂
≃-Π-can (<:-antisym Πjk-kd
          (<:-λ a₁<:a₂
            (⇇-⇑ (⇉-Π-i j₁-kd a₁⇉k₁) (<∷-Π j<∷j₁ k₁<∷k Πj₁k₁-kd))
            (⇇-⇑ (⇉-Π-i j₂-kd a₂⇉k₂) (<∷-Π j<∷j₂ k₂<∷k Πj₂k₂-kd)))
          (<:-λ a₂<:a₁ _ _)) =
  _ , _ , _ , _ ,
  (⇇-⇑ (⇉-Π-i j₁-kd a₁⇉k₁) (<∷-Π j<∷j₁ k₁<∷k Πj₁k₁-kd)) ,
  (⇇-⇑ (⇉-Π-i j₂-kd a₂⇉k₂) (<∷-Π j<∷j₂ k₂<∷k Πj₂k₂-kd)) ,
  Πjk-kd , j<∷j₁ , j<∷j₂ , a₁<:a₂ , a₂<:a₁ , refl , refl

------------------------------------------------------------------------
-- Simplification of well-formed kinding.

module _ where
  open SimpleKinding
  open SimpleKinding.Kinding
    renaming (_⊢Var_∈_ to _⊢sVar_∈_; _⊢Ne_∈_ to _⊢sNe_∈_)
  open KindedHereditarySubstitution
  open ≡-Reasoning

  -- Simplification of well-formedness and kinding: well-formed kinds
  -- resp. well-kinded normal forms, neutrals and spines are also
  -- simply well-formed resp. well-kinded.

  Var∈-sVar∈ : ∀ {n} {Γ : Ctx n} {a k} →
               Γ ⊢Var a ∈ k → ⌊ Γ ⌋Ctx ⊢sVar a ∈ ⌊ k ⌋
  Var∈-sVar∈ {_} {Γ} {_} {k} (⇉-var x Γ-ctx Γ[x]≡kd-k) = ∈-var x (begin
    SimpleCtx.lookup ⌊ Γ ⌋Ctx x   ≡⟨ ⌊⌋Asc-lookup Γ x ⟩
    ⌊ ElimCtx.lookup Γ x ⌋Asc     ≡⟨ cong ⌊_⌋Asc Γ[x]≡kd-k ⟩
    kd ⌊ k ⌋                      ∎)
    where open ContextConversions
  Var∈-sVar∈ (⇇-⇑ x∈j j<∷k k-kd) =
    subst (_ ⊢sVar _ ∈_) (<∷-⌊⌋ j<∷k) (Var∈-sVar∈ x∈j)

  mutual

    kd-kds : ∀ {n} {Γ : Ctx n} {k} → Γ ⊢ k kd → ⌊ Γ ⌋Ctx ⊢ k kds
    kd-kds (kd-⋯ a∈a⋯a b∈b⋯b) = kds-⋯ (Nf⇉-Nf∈ a∈a⋯a) (Nf⇉-Nf∈ b∈b⋯b)
    kd-kds (kd-Π j-kd k-kd)   = kds-Π (kd-kds j-kd) (kd-kds k-kd)

    Nf⇉-Nf∈ : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇉ k → ⌊ Γ ⌋Ctx ⊢Nf a ∈ ⌊ k ⌋
    Nf⇉-Nf∈ (⇉-⊥-f Γ-ctx)       = ∈-⊥-f
    Nf⇉-Nf∈ (⇉-⊤-f Γ-ctx)       = ∈-⊤-f
    Nf⇉-Nf∈ (⇉-∀-f k-kd a⇉a⋯a)  = ∈-∀-f (kd-kds k-kd) (Nf⇉-Nf∈ a⇉a⋯a)
    Nf⇉-Nf∈ (⇉-→-f a∈a⋯a b∈b⋯b) = ∈-→-f (Nf⇉-Nf∈ a∈a⋯a) (Nf⇉-Nf∈ b∈b⋯b)
    Nf⇉-Nf∈ (⇉-Π-i j-kd a⇉k)    = ∈-Π-i (kd-kds j-kd) (Nf⇉-Nf∈ a⇉k)
    Nf⇉-Nf∈ (⇉-s-i a∈b⋯c)       = ∈-ne (Ne∈-sNe∈ a∈b⋯c)

    Ne∈-sNe∈ : ∀ {n} {Γ : Ctx n} {a k} →
               Γ ⊢Ne a ∈ k → ⌊ Γ ⌋Ctx ⊢sNe a ∈ ⌊ k ⌋
    Ne∈-sNe∈ (∈-∙ x∈j j⇉as⇉k) = ∈-∙ (Var∈-sVar∈ x∈j) (Sp⇉-Sp∈ j⇉as⇉k)

    Sp⇉-Sp∈ : ∀ {n} {Γ : Ctx n} {as j k} → Γ ⊢ j ⇉∙ as ⇉ k →
              ⌊ Γ ⌋Ctx ⊢ ⌊ j ⌋ ∋∙ as ∈ ⌊ k ⌋
    Sp⇉-Sp∈ ⇉-[] = ∈-[]
    Sp⇉-Sp∈ (⇉-∷ a⇇j j-kd k[a]⇉as⇉l) =
      ∈-∷ (Nf⇇-Nf∈ a⇇j)
          (subst (_ ⊢_∋∙ _ ∈ _) (⌊⌋-Kind/⟨⟩ _) (Sp⇉-Sp∈ k[a]⇉as⇉l))

    Nf⇇-Nf∈ : ∀ {n} {Γ : Ctx n} {a k} → Γ ⊢Nf a ⇇ k → ⌊ Γ ⌋Ctx ⊢Nf a ∈ ⌊ k ⌋
    Nf⇇-Nf∈ (⇇-⇑ a⇉j j<∷k) = subst (_ ⊢Nf _ ∈_) (<∷-⌊⌋ j<∷k) (Nf⇉-Nf∈ a⇉j)


{-# OPTIONS --without-K --safe --no-sized-types --no-guardedness
            --no-subtyping #-}

module Agda.Builtin.Float where

open import Agda.Builtin.Bool
open import Agda.Builtin.Int
open import Agda.Builtin.Maybe
open import Agda.Builtin.Nat
open import Agda.Builtin.Sigma
open import Agda.Builtin.String
open import Agda.Builtin.Word

postulate Float : Set
{-# BUILTIN FLOAT Float #-}

primitive
  -- Relations
  primFloatInequality        : Float → Float → Bool
  primFloatEquality          : Float → Float → Bool
  primFloatLess              : Float → Float → Bool
  primFloatIsInfinite        : Float → Bool
  primFloatIsNaN             : Float → Bool
  primFloatIsNegativeZero    : Float → Bool
  primFloatIsSafeInteger     : Float → Bool
  -- Conversions
  primFloatToWord64          : Float → Word64
  primNatToFloat             : Nat → Float
  primIntToFloat             : Int → Float
  primFloatRound             : Float → Maybe Int
  primFloatFloor             : Float → Maybe Int
  primFloatCeiling           : Float → Maybe Int
  primFloatToRatio           : Float → (Σ Int λ _ → Int)
  primRatioToFloat           : Int → Int → Float
  primFloatDecode            : Float → Maybe (Σ Int λ _ → Int)
  primFloatEncode            : Int → Int → Maybe Float
  primShowFloat              : Float → String
  -- Operations
  primFloatPlus              : Float → Float → Float
  primFloatMinus             : Float → Float → Float
  primFloatTimes             : Float → Float → Float
  primFloatDiv               : Float → Float → Float
  primFloatPow               : Float → Float → Float
  primFloatNegate            : Float → Float
  primFloatSqrt              : Float → Float
  primFloatExp               : Float → Float
  primFloatLog               : Float → Float
  primFloatSin               : Float → Float
  primFloatCos               : Float → Float
  primFloatTan               : Float → Float
  primFloatASin              : Float → Float
  primFloatACos              : Float → Float
  primFloatATan              : Float → Float
  primFloatATan2             : Float → Float → Float
  primFloatSinh              : Float → Float
  primFloatCosh              : Float → Float
  primFloatTanh              : Float → Float
  primFloatASinh             : Float → Float
  primFloatACosh             : Float → Float
  primFloatATanh             : Float → Float

{-# COMPILE JS
    primFloatRound = function(x) {
        x = agdaRTS._primFloatRound(x);
        if (x === null) {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["nothing"];
        }
        else {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["just"](x);
        }
    };
#-}
{-# COMPILE JS
    primFloatFloor = function(x) {
        x = agdaRTS._primFloatFloor(x);
        if (x === null) {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["nothing"];
        }
        else {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["just"](x);
        }
    };
#-}
{-# COMPILE JS
    primFloatCeiling = function(x) {
        x = agdaRTS._primFloatCeiling(x);
        if (x === null) {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["nothing"];
        }
        else {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["just"](x);
        }
    };
#-}
{-# COMPILE JS
    primFloatToRatio = function(x) {
        x = agdaRTS._primFloatToRatio(x);
        return z_jAgda_Agda_Builtin_Sigma["_,_"](x.numerator)(x.denominator);
    };
#-}
{-# COMPILE JS
    primFloatDecode = function(x) {
        x = agdaRTS._primFloatDecode(x);
        if (x === null) {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["nothing"];
        }
        else {
            return z_jAgda_Agda_Builtin_Maybe["Maybe"]["just"](
                z_jAgda_Agda_Builtin_Sigma["_,_"](x.mantissa)(x.exponent));
        }
    };
#-}
{-# COMPILE JS
    primFloatEncode = function(x) {
        return function (y) {
            x = agdaRTS.uprimFloatEncode(x, y);
            if (x === null) {
                return z_jAgda_Agda_Builtin_Maybe["Maybe"]["nothing"];
            }
            else {
                return z_jAgda_Agda_Builtin_Maybe["Maybe"]["just"](x);
            }
        }
    };
#-}

primFloatNumericalEquality = primFloatEquality
{-# WARNING_ON_USAGE primFloatNumericalEquality
"Warning: primFloatNumericalEquality was deprecated in Agda v2.6.2.
Please use primFloatEquality instead."
#-}

primFloatNumericalLess = primFloatLess
{-# WARNING_ON_USAGE primFloatNumericalLess
"Warning: primFloatNumericalLess was deprecated in Agda v2.6.2.
Please use primFloatLess instead."
#-}

primRound = primFloatRound
{-# WARNING_ON_USAGE primRound
"Warning: primRound was deprecated in Agda v2.6.2.
Please use primFloatRound instead."
#-}

primFloor = primFloatFloor
{-# WARNING_ON_USAGE primFloor
"Warning: primFloor was deprecated in Agda v2.6.2.
Please use primFloatFloor instead."
#-}

primCeiling = primFloatCeiling
{-# WARNING_ON_USAGE primCeiling
"Warning: primCeiling was deprecated in Agda v2.6.2.
Please use primFloatCeiling instead."
#-}

primExp = primFloatExp
{-# WARNING_ON_USAGE primExp
"Warning: primExp was deprecated in Agda v2.6.2.
Please use primFloatExp instead."
#-}

primLog = primFloatLog
{-# WARNING_ON_USAGE primLog
"Warning: primLog was deprecated in Agda v2.6.2.
Please use primFloatLog instead."
#-}

primSin = primFloatSin
{-# WARNING_ON_USAGE primSin
"Warning: primSin was deprecated in Agda v2.6.2.
Please use primFloatSin instead."
#-}

primCos = primFloatCos
{-# WARNING_ON_USAGE primCos
"Warning: primCos was deprecated in Agda v2.6.2.
Please use primFloatCos instead."
#-}

primTan = primFloatTan
{-# WARNING_ON_USAGE primTan
"Warning: primTan was deprecated in Agda v2.6.2.
Please use primFloatTan instead."
#-}

primASin = primFloatASin
{-# WARNING_ON_USAGE primASin
"Warning: primASin was deprecated in Agda v2.6.2.
Please use primFloatASin instead."
#-}

primACos = primFloatACos
{-# WARNING_ON_USAGE primACos
"Warning: primACos was deprecated in Agda v2.6.2.
Please use primFloatACos instead."
#-}

primATan = primFloatATan
{-# WARNING_ON_USAGE primATan
"Warning: primATan was deprecated in Agda v2.6.2.
Please use primFloatATan instead."
#-}

primATan2 = primFloatATan2
{-# WARNING_ON_USAGE primATan2
"Warning: primATan2 was deprecated in Agda v2.6.2.
Please use primFloatATan2 instead."
#-}


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

{-
Imports everything that is not imported by something else.
This is not supposed to be used anywhere, this is just a simple way to
do `make all'

This file is intentionally named index.agda so that
Agda will generate index.html.
-}

module index where

import Base
import Spaces.IntervalProps
import Algebra.F2NotCommutative
import Spaces.LoopSpaceCircle
import Spaces.LoopSpaceDecidableWedgeCircles
import Homotopy.PullbackIsPullback
import Homotopy.PushoutIsPushout
import Homotopy.Truncation
import Sets.QuotientUP
import Spaces.PikSn
import Homotopy.VanKampen
import Homotopy.Cover
import Homotopy.Cover.ExamplePi1Circle


open import Oscar.Prelude
open import Oscar.Class
open import Oscar.Class.HasEquivalence
open import Oscar.Class.Quadricity
open import Oscar.Class.Injectivity
open import Oscar.Class.Congruity
open import Oscar.Class.Leftstar
open import Oscar.Class.Properthing
open import Oscar.Class.Smap
open import Oscar.Class.Symmetry
open import Oscar.Data.Fin
open import Oscar.Data.Substitunction
open import Oscar.Data.Term
open import Oscar.Data.Surjcollation
open import Oscar.Data.Surjextenscollation
open import Oscar.Data.Proposequality
open import Oscar.Property.Setoid.Proposequality
open import Oscar.Property.Setoid.Proposextensequality
open import Oscar.Property.Functor.SubstitunctionExtensionTerm
open import Oscar.Property.Thickandthin.FinTermProposequalityMaybeProposequality
import Oscar.Class.Congruity.Proposequality
import Oscar.Class.Congruity.Proposextensequality
import Oscar.Class.Surjection.⋆
open import Oscar.Property.Setoid.ṖropertyEquivalence
import Oscar.Class.Properthing.Ṗroperty
open import Oscar.Data.ProductIndexEquivalence
import Oscar.Data.ExtensionṖroperty
open import Oscar.Data.ProperlyExtensionNothing
import Oscar.Class.Properthing.ExtensionṖroperty
import Oscar.Class.Smap.TransitiveExtensionLeftṖroperty
import Oscar.Class.Smap.ExtensionṖroperty
import Oscar.Class.[ExtensibleType].Proposequality
import Oscar.Class.HasEquivalence.ExtensionṖroperty
import Oscar.Class.HasEquivalence.Ṗroperty
import Oscar.Class.HasEquivalence.Substitunction
open import Oscar.Class.Reflexivity
open import Oscar.Class.Similarity
open import Oscar.Class.Quadricity
open import Oscar.Class.Surjection
open import Oscar.Class.Leftunit

module Oscar.Property.Propergroup.Substitunction where

module _
  {𝔭} {𝔓 : Ø 𝔭}
  {ℓ : Ł}
  where
  open Substitunction 𝔓

  instance

    𝓢urjectextenscongruityArrowṖropertySubstitunction : Similarity,smaphomarrow!.class Substitunction (LeftṖroperty ℓ Substitunction) _≈_
    𝓢urjectextenscongruityArrowṖropertySubstitunction .⋆ _ (∁ P⇔Q) .π₀ = P⇔Q

    𝓢urjectextenscongruityArrowExtensionṖropertySubstitunction : Similarity,smaphomarrow!.class Substitunction (LeftExtensionṖroperty ℓ Substitunction _≈_) _≈_
    𝓢urjectextenscongruityArrowExtensionṖropertySubstitunction .⋆ _ (∁ (∁ P⇔Q)) .π₀ = ∁ P⇔Q -- P⇔Q

module _
  {𝔭} {𝔓 : Ø 𝔭}
  where
  open Term 𝔓

  module S = Surjcollation (Arrow Fin Term) _≡_
  module Ṡ = Surjextenscollation (Arrow Fin Term) _≡̇_

  instance

    𝒫roperfact1UnifiesSubstitunctionFork : ∀ {n} → Properfact1.class S._⟹_ (_fork_ {n = n})
    𝒫roperfact1UnifiesSubstitunctionFork .⋆ _ _ _ _ .π₀ = (λ s≡t → injectivity₂,₀,₁ s≡t , injectivity₂,₀,₂ s≡t) , uncurry (congruity₂ _fork_)

    𝒫roperfact1ExtensionalUnifiesSubstitunctionFork : ∀ {n} → Properfact1.class Ṡ._⟹_ (_fork_ {n = n})
    𝒫roperfact1ExtensionalUnifiesSubstitunctionFork .⋆ _ _ _ _ .π₀ .π₀ = (λ s≡t → injectivity₂,₀,₁ s≡t , injectivity₂,₀,₂ s≡t) , uncurry (congruity₂ _fork_)

  instance

    𝓕actsurj3Regular : ∀ {ℓ} → Leftunit,equivalence,smaphomarrow!.class (Arrow Fin Term) (LeftṖroperty ℓ (Arrow Fin Term))
    𝓕actsurj3Regular .⋆ .π₀ = ¡ , ¡

    𝓕actsurj3Extension : ∀ {ℓ} → Leftunit,smaphomarrow.class (Arrow Fin Term) (LeftExtensionṖroperty ℓ (Arrow Fin Term) (Pointwise Proposequality)) ε surjection smaparrow _≈_
    𝓕actsurj3Extension .⋆ .π₀ .π₀ = ¡ , ¡

  open Substitunction 𝔓

  instance

    𝓕actsurj4Regular : ∀ {ℓ} → Leftstar,smaphomarrow!.class (LeftṖroperty ℓ (Arrow Fin Term)) (Arrow Fin Term) Nothing
    𝓕actsurj4Regular .⋆ _ (∁ nop) .π₀ = nop

    𝓕actsurj4Extension : ∀ {ℓ} → Leftstar,smaphomarrow!.class (LeftExtensionṖroperty ℓ Substitunction (Pointwise Proposequality)) (Arrow Fin Term) Nothing
    𝓕actsurj4Extension .⋆ _ (∁ nop) .π₀ = nop

  instance

    𝓕actsurj6Extension : ∀ {ℓ} → Similarity,cosmaphomarrow!.class Substitunction (ArrowExtensionṖroperty ℓ Fin Term Proposequality) _≈_ _≈_
    𝓕actsurj6Extension .⋆ P f≐g .π₀ .π₀ {f = h} = π₁ P (congruity (smap h) ∘ f≐g) , π₁ P (symmetry (congruity (smap h) ∘ f≐g)) -- FIXME replacing symmetry with Sym.[] fails


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

module Definition.Typed.Consequences.Substitution where

open import Definition.Untyped
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Typed.EqRelInstance
open import Definition.Typed.Weakening
open import Definition.Typed.Consequences.Syntactic
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Properties
open import Definition.LogicalRelation.Substitution
open import Definition.LogicalRelation.Substitution.Irrelevance
open import Definition.LogicalRelation.Fundamental

open import Tools.Product
import Tools.PropositionalEquality as PE

-- Well-formed substitution of types.
substitution : ∀ {A Γ Δ σ} → Γ ⊢ A → Δ ⊢ˢ σ ∷ Γ → ⊢ Δ → Δ ⊢ subst σ A
substitution A σ ⊢Δ with fundamental A | fundamentalSubst (wf A) ⊢Δ σ
substitution A σ ⊢Δ | [Γ] , [A] | [Γ]′ , [σ] =
  escape (proj₁ ([A] ⊢Δ (irrelevanceSubst [Γ]′ [Γ] ⊢Δ ⊢Δ [σ])))

-- Well-formed substitution of type equality.
substitutionEq : ∀ {A B Γ Δ σ σ′}
               → Γ ⊢ A ≡ B → Δ ⊢ˢ σ ≡ σ′ ∷ Γ → ⊢ Δ → Δ ⊢ subst σ A ≡ subst σ′ B
substitutionEq A≡B σ ⊢Δ with fundamentalEq A≡B | fundamentalSubstEq (wfEq A≡B) ⊢Δ σ
substitutionEq A≡B σ ⊢Δ | [Γ] , [A] , [B] , [A≡B] | [Γ]′ , [σ] , [σ′] , [σ≡σ′]  =
  let [σ]′ = irrelevanceSubst [Γ]′ [Γ] ⊢Δ ⊢Δ [σ]
      [σ′]′ = irrelevanceSubst [Γ]′ [Γ] ⊢Δ ⊢Δ [σ′]
      [σ≡σ′]′ = irrelevanceSubstEq [Γ]′ [Γ] ⊢Δ ⊢Δ [σ] [σ]′ [σ≡σ′]
  in  escapeEq (proj₁ ([A] ⊢Δ [σ]′))
                   (transEq (proj₁ ([A] ⊢Δ [σ]′)) (proj₁ ([B] ⊢Δ [σ]′))
                            (proj₁ ([B] ⊢Δ [σ′]′)) ([A≡B] ⊢Δ [σ]′)
                            (proj₂ ([B] ⊢Δ [σ]′) [σ′]′ [σ≡σ′]′))

-- Well-formed substitution of terms.
substitutionTerm : ∀ {t A Γ Δ σ}
               → Γ ⊢ t ∷ A → Δ ⊢ˢ σ ∷ Γ → ⊢ Δ → Δ ⊢ subst σ t ∷ subst σ A
substitutionTerm t σ ⊢Δ with fundamentalTerm t | fundamentalSubst (wfTerm t) ⊢Δ σ
substitutionTerm t σ ⊢Δ | [Γ] , [A] , [t] | [Γ]′ , [σ] =
  let [σ]′ = irrelevanceSubst [Γ]′ [Γ] ⊢Δ ⊢Δ [σ]
  in  escapeTerm (proj₁ ([A] ⊢Δ [σ]′)) (proj₁ ([t] ⊢Δ [σ]′))

-- Well-formed substitution of term equality.
substitutionEqTerm : ∀ {t u A Γ Δ σ σ′}
                   → Γ ⊢ t ≡ u ∷ A → Δ ⊢ˢ σ ≡ σ′ ∷ Γ → ⊢ Δ
                   → Δ ⊢ subst σ t ≡ subst σ′ u ∷ subst σ A
substitutionEqTerm t≡u σ≡σ′ ⊢Δ with fundamentalTermEq t≡u
                                  | fundamentalSubstEq (wfEqTerm t≡u) ⊢Δ σ≡σ′
... | [Γ] , modelsTermEq [A] [t] [u] [t≡u] | [Γ]′ , [σ] , [σ′] , [σ≡σ′] =
  let [σ]′ = irrelevanceSubst [Γ]′ [Γ] ⊢Δ ⊢Δ [σ]
      [σ′]′ = irrelevanceSubst [Γ]′ [Γ] ⊢Δ ⊢Δ [σ′]
      [σ≡σ′]′ = irrelevanceSubstEq [Γ]′ [Γ] ⊢Δ ⊢Δ [σ] [σ]′ [σ≡σ′]
  in  escapeTermEq (proj₁ ([A] ⊢Δ [σ]′))
                       (transEqTerm (proj₁ ([A] ⊢Δ [σ]′)) ([t≡u] ⊢Δ [σ]′)
                                    (proj₂ ([u] ⊢Δ [σ]′) [σ′]′ [σ≡σ′]′))

-- Reflexivity of well-formed substitution.
substRefl : ∀ {σ Γ Δ}
          → Δ ⊢ˢ σ ∷ Γ
          → Δ ⊢ˢ σ ≡ σ ∷ Γ
substRefl id = id
substRefl (σ , x) = substRefl σ , refl x

-- Weakening of well-formed substitution.
wkSubst′ : ∀ {ρ σ Γ Δ Δ′} (⊢Γ : ⊢ Γ) (⊢Δ : ⊢ Δ) (⊢Δ′ : ⊢ Δ′)
           ([ρ] : ρ ∷ Δ′ ⊆ Δ)
           ([σ] : Δ ⊢ˢ σ ∷ Γ)
         → Δ′ ⊢ˢ ρ •ₛ σ ∷ Γ
wkSubst′ ε ⊢Δ ⊢Δ′ ρ id = id
wkSubst′ (_∙_ {Γ} {A} ⊢Γ ⊢A) ⊢Δ ⊢Δ′ ρ (tailσ , headσ) =
  wkSubst′ ⊢Γ ⊢Δ ⊢Δ′ ρ tailσ
  , PE.subst (λ x → _ ⊢ _ ∷ x) (wk-subst A) (wkTerm ρ ⊢Δ′ headσ)

-- Weakening of well-formed substitution by one.
wk1Subst′ : ∀ {F σ Γ Δ} (⊢Γ : ⊢ Γ) (⊢Δ : ⊢ Δ)
            (⊢F : Δ ⊢ F)
            ([σ] : Δ ⊢ˢ σ ∷ Γ)
          → (Δ ∙ F) ⊢ˢ wk1Subst σ ∷ Γ
wk1Subst′ {F} {σ} {Γ} {Δ} ⊢Γ ⊢Δ ⊢F [σ] =
  wkSubst′ ⊢Γ ⊢Δ (⊢Δ ∙ ⊢F) (step id) [σ]

-- Lifting of well-formed substitution.
liftSubst′ : ∀ {F σ Γ Δ} (⊢Γ : ⊢ Γ) (⊢Δ : ⊢ Δ)
             (⊢F  : Γ ⊢ F)
             ([σ] : Δ ⊢ˢ σ ∷ Γ)
           → (Δ ∙ subst σ F) ⊢ˢ liftSubst σ ∷ Γ ∙ F
liftSubst′ {F} {σ} {Γ} {Δ} ⊢Γ ⊢Δ ⊢F [σ] =
  let ⊢Δ∙F = ⊢Δ ∙ substitution ⊢F [σ] ⊢Δ
  in  wkSubst′ ⊢Γ ⊢Δ ⊢Δ∙F (step id) [σ]
  ,   var ⊢Δ∙F (PE.subst (λ x → 0 ∷ x ∈ (Δ ∙ subst σ F))
                         (wk-subst F) here)

-- Well-formed identity substitution.
idSubst′ : ∀ {Γ} (⊢Γ : ⊢ Γ)
         → Γ ⊢ˢ idSubst ∷ Γ
idSubst′ ε = id
idSubst′ (_∙_ {Γ} {A} ⊢Γ ⊢A) =
  wk1Subst′ ⊢Γ ⊢Γ ⊢A (idSubst′ ⊢Γ)
  , PE.subst (λ x → Γ ∙ A ⊢ _ ∷ x) (wk1-tailId A) (var (⊢Γ ∙ ⊢A) here)

-- Well-formed substitution composition.
substComp′ : ∀ {σ σ′ Γ Δ Δ′} (⊢Γ : ⊢ Γ) (⊢Δ : ⊢ Δ) (⊢Δ′ : ⊢ Δ′)
             ([σ] : Δ′ ⊢ˢ σ ∷ Δ)
             ([σ′] : Δ ⊢ˢ σ′ ∷ Γ)
           → Δ′ ⊢ˢ σ ₛ•ₛ σ′ ∷ Γ
substComp′ ε ⊢Δ ⊢Δ′ [σ] id = id
substComp′ (_∙_ {Γ} {A} ⊢Γ ⊢A) ⊢Δ ⊢Δ′ [σ] ([tailσ′] , [headσ′]) =
  substComp′ ⊢Γ ⊢Δ ⊢Δ′ [σ] [tailσ′]
  , PE.subst (λ x → _ ⊢ _ ∷ x) (substCompEq A)
             (substitutionTerm [headσ′] [σ] ⊢Δ′)

-- Well-formed singleton substitution of terms.
singleSubst : ∀ {A t Γ} → Γ ⊢ t ∷ A → Γ ⊢ˢ sgSubst t ∷ Γ ∙ A
singleSubst {A} t =
  let ⊢Γ = wfTerm t
  in  idSubst′ ⊢Γ , PE.subst (λ x → _ ⊢ _ ∷ x) (PE.sym (subst-id A)) t

-- Well-formed singleton substitution of term equality.
singleSubstEq : ∀ {A t u Γ} → Γ ⊢ t ≡ u ∷ A
              → Γ ⊢ˢ sgSubst t ≡ sgSubst u ∷ Γ ∙ A
singleSubstEq {A} t =
  let ⊢Γ = wfEqTerm t
  in  substRefl (idSubst′ ⊢Γ) , PE.subst (λ x → _ ⊢ _ ≡ _ ∷ x) (PE.sym (subst-id A)) t

-- Well-formed singleton substitution of terms with lifting.
singleSubst↑ : ∀ {A t Γ} → Γ ∙ A ⊢ t ∷ wk1 A → Γ ∙ A ⊢ˢ consSubst (wk1Subst idSubst) t ∷ Γ ∙ A
singleSubst↑ {A} t with wfTerm t
... | ⊢Γ ∙ ⊢A = wk1Subst′ ⊢Γ ⊢Γ ⊢A (idSubst′ ⊢Γ)
              , PE.subst (λ x → _ ∙ A ⊢ _ ∷ x) (wk1-tailId A) t

-- Well-formed singleton substitution of term equality with lifting.
singleSubst↑Eq : ∀ {A t u Γ} → Γ ∙ A ⊢ t ≡ u ∷ wk1 A
              → Γ ∙ A ⊢ˢ consSubst (wk1Subst idSubst) t ≡ consSubst (wk1Subst idSubst) u ∷ Γ ∙ A
singleSubst↑Eq {A} t with wfEqTerm t
... | ⊢Γ ∙ ⊢A = substRefl (wk1Subst′ ⊢Γ ⊢Γ ⊢A (idSubst′ ⊢Γ))
              , PE.subst (λ x → _ ∙ A ⊢ _ ≡ _ ∷ x) (wk1-tailId A) t

-- Helper lemmas for single substitution

substType : ∀ {t F G Γ} → Γ ∙ F ⊢ G → Γ ⊢ t ∷ F → Γ ⊢ G [ t ]
substType {t} {F} {G} ⊢G ⊢t =
  let ⊢Γ = wfTerm ⊢t
  in  substitution ⊢G (singleSubst ⊢t) ⊢Γ

substTypeEq : ∀ {t u F G E Γ} → Γ ∙ F ⊢ G ≡ E → Γ ⊢ t ≡ u ∷ F → Γ ⊢ G [ t ] ≡ E [ u ]
substTypeEq {F = F} ⊢G ⊢t =
  let ⊢Γ = wfEqTerm ⊢t
  in  substitutionEq ⊢G (singleSubstEq ⊢t) ⊢Γ

substTerm : ∀ {F G t f Γ} → Γ ∙ F ⊢ f ∷ G → Γ ⊢ t ∷ F → Γ ⊢ f [ t ] ∷ G [ t ]
substTerm {F} {G} {t} {f} ⊢f ⊢t =
  let ⊢Γ = wfTerm ⊢t
  in  substitutionTerm ⊢f (singleSubst ⊢t) ⊢Γ

substTypeΠ : ∀ {t F G Γ} → Γ ⊢ Π F ▹ G → Γ ⊢ t ∷ F → Γ ⊢ G [ t ]
substTypeΠ ΠFG t with syntacticΠ ΠFG
substTypeΠ ΠFG t | F , G = substType G t

subst↑Type : ∀ {t F G Γ}
           → Γ ∙ F ⊢ G
           → Γ ∙ F ⊢ t ∷ wk1 F
           → Γ ∙ F ⊢ G [ t ]↑
subst↑Type ⊢G ⊢t = substitution ⊢G (singleSubst↑ ⊢t) (wfTerm ⊢t)

subst↑TypeEq : ∀ {t u F G E Γ}
             → Γ ∙ F ⊢ G ≡ E
             → Γ ∙ F ⊢ t ≡ u ∷ wk1 F
             → Γ ∙ F ⊢ G [ t ]↑ ≡ E [ u ]↑
subst↑TypeEq ⊢G ⊢t = substitutionEq ⊢G (singleSubst↑Eq ⊢t) (wfEqTerm ⊢t)


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

module Base where

  open import lib.Basics

  open import lib.Funext
  open import lib.NType2
  open import lib.types.Truncation
  open import lib.types.Bool

  quodlibet : {i : ULevel} {A : Type i} → ⊥ → A
  quodlibet = ⊥-elim

  -- infix notation for piping a term argument into a dependent function
  _|>_ : ∀ {i j} {A : Type i} {B : A → Type j}
        (a : A) (f : (a : A) → B a) → B a
  a |> f = f a

  -- infix notation for applying a path between two functions to an argument to give a path from f x to g x
  _at_ : ∀ {i j} {A : Type i} {P : A → Type j} {f g : Π A P} (p : f == g) → f ∼ g
  p at x = app= p x

  End : ∀ {i} (X : Type i) → Type i
  End X = X → X

  -- composition of a dependent function with a fiberwise dependent function
  infixl 80 _>>_
  _>>_ : ∀ {i j k} {A : Type i} {B : A → Type j} {C : (a : A) → (B a → Type k)}
          → (f : Π A B) → (g : {a : A} → Π (B a) (C a)) → Π A (λ a → C a (f a))
  f >> g = λ x → g (f x)

  -- Crisp function types (the domain must be crisp)
  _::→_ : {@♭ i : ULevel} {j : ULevel} (@♭ A : Type i) (B : Type j) → Type (lmax i j)
  A ::→ B = (@♭ _ : A) → B

  crispen : {@♭ i : ULevel} {j : ULevel} {@♭ A : Type i} {B : A → Type j}
            → ((a : A) → B a)
            → ((@♭ a : A) → B a)
  crispen f a = f a

  const : {i j : ULevel} {A : Type i} {B : Type j}
          (b : B) → A → B
  const b _ = b

  crisp-const : {@♭ i : ULevel} {j : ULevel} {@♭ A : Type i} {B : Type j}
                (b : B) → A ::→ B
  crisp-const b _ = b

  -- Crisp function extensionality (postulated for now, because I'm lazy)
  postulate
    -- domain is a crisp term of a crisp type
    ♭λ= : {@♭ i : ULevel} {j : ULevel} {@♭ A : Type i}
          {B : A → Type j} {f g : (@♭ a : A) → B a}
          (h : (@♭ a : A) → f a == g a)
          → f == g
  -- Fully crisp function extensionalty: domain and codomain are crisp types
  ♭♭λ= : {@♭ i j : ULevel} {@♭ A : Type i}
          {@♭ B : A → Type j} {@♭ f g : (@♭ a : A) → B a}
          (@♭ h : (@♭ a : A) → f a == g a)
          → f == g
  ♭♭λ= h = ♭λ= h

  -- The "identity path" is called refl
  -- refl : ∀ {i} { A : Type i} {a : A} → a == a
  pattern refl = idp

  _is-an-equiv = is-equiv

  _is-an-equivalence-because_is-inverse-by_and_ : ∀ {i} {j} {A : Type i} {B : Type j}
                                                  → (f : A → B)
                                                    (g : B → A) (f-g : (b : B) → f (g b) == b)
                                                    (g-f : (a : A) → g (f a) == a) → is-equiv f
  f is-an-equivalence-because g is-inverse-by f-g and g-f = is-eq f g f-g g-f

  _is-split-inj : {i j : ULevel} {A : Type i} {B : Type j} (s : A → B) → Type (lmax i j)
  _is-split-inj {A = A} {B = B} s = Σ (B → A) (λ r → (a : A) → r (s a) == a)

  -- Implicit and Explicit function types are equivalent

  imp-equiv-exp : {i j : ULevel} {A : Type i} {B : A → Type j}
                  → ({a : A} → B a) ≃ ((a : A) → B a)
  imp-equiv-exp = (λ f x → f {x}) ,
                    record
                    { g = λ z {x} → z x
                    ; f-g = λ b → idp
                    ; g-f = λ a → idp
                    ; adj = λ a → idp
                    }

  exp-equiv-imp : {i j : ULevel} {A : Type i} {B : A → Type j}
                  → ((a : A) → B a) ≃ ({a : A} → B a)
  exp-equiv-imp = imp-equiv-exp ⁻¹

  _⊔→_ : {i j k l : ULevel} {A : Type i} {B : Type j} {C : Type k} {D : Type l}
          (f : A → C) (g : B → D)
          → A ⊔ B → C ⊔ D
  (f ⊔→ g) (inl a)  = inl (f a)
  (f ⊔→ g) (inr b)  = inr (g b)

  _⊔e_ : {i j k l : ULevel} {A : Type i} {B : Type j} {C : Type k} {D : Type l}
          (f : A ≃ C) (g : B ≃ D)
          → A ⊔ B ≃ C ⊔ D
  f ⊔e g =
    equiv
      ((–> f) ⊔→ (–> g))
      ((<– f) ⊔→ (<– g))
      (λ {(inl c) → ap inl ((<–-inv-r f) c) ; (inr d) → ap inr ((<–-inv-r g) d) })
      (λ {(inl a) → ap inl ((<–-inv-l f) a) ; (inr b) → ap inr ((<–-inv-l g) b) })

  -- A non-instance version of this function because I find that instance so annoying
  ≃-preserves-level : ∀ {i j} {A : Type i} {B : Type j} {n : ℕ₋₂} (e : A ≃ B)
                      → has-level n A → has-level n B
  ≃-preserves-level e p = equiv-preserves-level e {{p}}

  -- An equivalence version of the above
  ≃-preserves-level-eq : ∀ {i j} {A : Type i} {B : Type j} {n : ℕ₋₂} (e : A ≃ B)
                          → has-level n A ≃ has-level n B
  ≃-preserves-level-eq e =
    equiv
      (≃-preserves-level e)
      (≃-preserves-level (e ⁻¹))
      (λ b → prop-path has-level-is-prop _ _)
      (λ a → prop-path has-level-is-prop _ _)

  -- An equivalence between has-level-aux and has-level
  has-level-def-eq : {i : ULevel} {A : Type i} {n : ℕ₋₂}
                      → (has-level n A) ≃ (has-level-aux n A)
  has-level-def-eq {A = A} {n = n} = equiv has-level-apply has-level-in (λ b → refl) (λ a → refl)

  -- Compositional reasoning (like equational reasoning, but for composing)
  infixr 15 _→∎
  infixr 10 _–⟨_⟩→_

  _–⟨_⟩→_ : ∀ {i j k} (A : Type i) {B : A → Type j} {C : (a : A) → B a → Type k}
            → (f : (a : A) → B a) → (g : {a : A} → (b : B a) → C a b)
            → ((a : A) → C a (f a))
  _ –⟨ f ⟩→ g = f >> g

  _→∎ : ∀ {i} (A : Type i) → A → A
  _→∎ = idf

  {-
  -- SKETCHING
  lemma : ∀ {i j} (A : Type i) (B : Type j) (a₀ : A)
          (p : (f : A → B) (a : A) → (f a₀) == (f a))
          → (const {A = A} {B = B}) is-an-equiv
  lemma A B a₀ p = is-eq const (λ f → f a₀) fro-to to-fro
    where
      to-fro : ∀ a → ((const a) a₀) == a
      to-fro a = refl

      fro-to : ∀ (f : A → B) → (const (f a₀)) == f
      fro-to f =
        λ= $
          λ a → p f a

  module _ {i j} (R : Type i) (r₀ : R) (B : Type j) (S : Type j)  where
    postulate
      σ : B → S
      contr : (γ : R → S) (r : R) → (γ r₀) == (γ r)

    _is-discrete : ∀ {k} (X : Type k) → Type _
    X is-discrete = (const {A = R} {B = X}) is-an-equiv

    S-is-discrete : S is-discrete
    S-is-discrete = lemma R S r₀ contr

  -}


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

open import lib.Basics
open import lib.NType2
open import lib.types.Pi
open import lib.types.Group
open import lib.types.Int
open import lib.types.List
open import lib.types.Word
open import lib.types.SetQuotient
open import lib.groups.Homomorphism

module lib.groups.GeneratedGroup {i m} where

module GeneratedGroup (A : Type i) (R : Rel (Word A) m) where

  -- [qwr-sym] is not needed, but it seems more principled to
  -- make [QuotWordRel] an equivalence relation.
  data QuotWordRel : Word A → Word A → Type (lmax i m) where
    qwr-refl : ∀ {l₁ l₂} → l₁ == l₂ → QuotWordRel l₁ l₂
    qwr-trans : ∀ {l₁ l₂ l₃} → QuotWordRel l₁ l₂ → QuotWordRel l₂ l₃ → QuotWordRel l₁ l₃
    qwr-sym : ∀ {l₁ l₂} → QuotWordRel l₁ l₂ → QuotWordRel l₂ l₁
    qwr-cong : ∀ {l₁ l₂ l₃ l₄} → QuotWordRel l₁ l₂ → QuotWordRel l₃ l₄ → QuotWordRel (l₁ ++ l₃) (l₂ ++ l₄)
    qwr-flip-r : ∀ x₁ → QuotWordRel (x₁ :: flip x₁ :: nil) nil
    qwr-rel : ∀ {l₁ l₂} → R l₁ l₂ → QuotWordRel l₁ l₂

  qwr-cong-l : ∀ {l₁ l₂} → QuotWordRel l₁ l₂ → ∀ l₃ → QuotWordRel (l₁ ++ l₃) (l₂ ++ l₃)
  qwr-cong-l qwr l₃ = qwr-cong qwr (qwr-refl idp)

  qwr-cong-r : ∀ l₁ {l₂ l₃} → QuotWordRel l₂ l₃ → QuotWordRel (l₁ ++ l₂) (l₁ ++ l₃)
  qwr-cong-r l₁ qwr = qwr-cong (qwr-refl (idp {a = l₁})) qwr

  abstract
    infixr 10 _qwr⟨_⟩_
    infixr 10 _qwr⟨id⟩_
    infix  15 _qwr∎

    _qwr⟨_⟩_ : ∀  l₁ {l₂ l₃} → QuotWordRel l₁ l₂ → QuotWordRel l₂ l₃ → QuotWordRel l₁ l₃
    _ qwr⟨ p ⟩ q = qwr-trans p q

    _qwr⟨id⟩_ : ∀  l₁ {l₂} → QuotWordRel l₁ l₂ → QuotWordRel l₁ l₂
    _ qwr⟨id⟩ q = q

    _qwr∎ : ∀ l → QuotWordRel l l
    _ qwr∎ = qwr-refl idp

  qwr-flip-l : ∀ x₁ → QuotWordRel (flip x₁ :: x₁ :: nil) nil
  qwr-flip-l x₁ =
    flip x₁ :: x₁ :: nil
      qwr⟨ qwr-refl (ap (λ s → flip x₁ :: s :: nil) (! (flip-flip x₁))) ⟩
    flip x₁ :: flip (flip x₁) :: nil
      qwr⟨ qwr-flip-r (flip x₁) ⟩
    nil qwr∎

  -- The quotient
  QuotWord : Type (lmax i m)
  QuotWord = SetQuot QuotWordRel

  qw[_] : Word A → QuotWord
  qw[_] = q[_]

  {-
    In the case where this module is used to realize some free construction F
    (e.g. free group, free abelian group), this function becomes the unit of
    the adjunction F -| U (where U is the forgetful functor).
  -}
  insert : A → QuotWord
  insert a = qw[ inl a :: nil ]

  module QuotWordElim {k} {P : QuotWord → Type k}
    {{_ : {x : QuotWord} → is-set (P x)}} (incl* : (a : Word A) → P qw[ a ])
    (rel* : ∀ {a₁ a₂} (r : QuotWordRel a₁ a₂) → incl* a₁ == incl* a₂ [ P ↓ quot-rel r ])
    = SetQuotElim incl* rel*
  open QuotWordElim public renaming (f to QuotWord-elim) hiding (quot-rel-β)

  module QuotWordRec {k} {B : Type k} {{_ : is-set B}} (incl* : Word A → B)
    (rel* : ∀ {a₁ a₂} (r : QuotWordRel a₁ a₂) → incl* a₁ == incl* a₂)
    = SetQuotRec incl* rel*
  open QuotWordRec public renaming (f to QuotWord-rec)

  private
    infixl 80 _⊞_
    _⊞_ : QuotWord → QuotWord → QuotWord
    _⊞_ = QuotWord-rec
      (λ l₁ → QuotWord-rec (λ l₂ → qw[ l₁ ++ l₂ ])
        (λ r → quot-rel $ qwr-cong-r l₁ r))
      (λ {l₁} {l₁'} r → λ= $ QuotWord-elim
        (λ l₂ → quot-rel $ qwr-cong-l r l₂)
        (λ _ → prop-has-all-paths-↓))

    abstract
      qwr-cancel-l : ∀ l
        → QuotWordRel (Word-inverse l ++ l) nil
      qwr-cancel-l nil = qwr-refl idp
      qwr-cancel-l (x :: l) =
        Word-inverse (x :: l) ++ (x :: l)
          qwr⟨id⟩
        (Word-inverse l ++ (flip x :: nil)) ++ (x :: l)
          qwr⟨ qwr-refl (++-assoc (reverse (Word-flip l)) (flip x :: nil) (x :: l)) ⟩
        Word-inverse l ++ ((flip x :: nil) ++ (x :: l))
          qwr⟨id⟩
        Word-inverse l ++ (flip x :: x :: l)
          qwr⟨ qwr-cong-r (reverse (Word-flip l)) (qwr-cong-l (qwr-flip-l x) l) ⟩
        Word-inverse l ++ l
          qwr⟨ qwr-cancel-l l ⟩
        nil qwr∎

      qwr-cancel-r : ∀ l
        → QuotWordRel (l ++ Word-inverse l) nil
      qwr-cancel-r l =
        l ++ Word-inverse l
          qwr⟨ qwr-refl (ap (_++ Word-inverse l) (! (Word-inverse-inverse l))) ⟩
        Word-inverse (Word-inverse l) ++ Word-inverse l
          qwr⟨ qwr-cancel-l (Word-inverse l) ⟩
        nil qwr∎

      qwr-cong-inverse : ∀ {l₁ l₂}
        → QuotWordRel l₁ l₂ → QuotWordRel (Word-inverse l₁) (Word-inverse l₂)
      qwr-cong-inverse {l₁} {l₂} r =
        Word-inverse l₁
          qwr⟨ qwr-refl (! (++-unit-r (Word-inverse l₁))) ⟩
        Word-inverse l₁ ++ nil
          qwr⟨ qwr-cong-r (Word-inverse l₁) (qwr-sym (qwr-cancel-r l₂)) ⟩
        Word-inverse l₁ ++ (l₂ ++ Word-inverse l₂)
          qwr⟨ qwr-cong-r (Word-inverse l₁) (qwr-cong-l (qwr-sym r) (Word-inverse l₂)) ⟩
        reverse (Word-flip l₁) ++ (l₁ ++ Word-inverse l₂)
          qwr⟨ qwr-refl (! (++-assoc (Word-inverse l₁) l₁ (Word-inverse l₂))) ⟩
        (Word-inverse l₁ ++ l₁) ++ Word-inverse l₂
          qwr⟨ qwr-cong-l (qwr-cancel-l l₁) (Word-inverse l₂) ⟩
        Word-inverse l₂ qwr∎

    ⊟ : QuotWord → QuotWord
    ⊟ = QuotWord-rec (qw[_] ∘ Word-inverse)
      (λ r → quot-rel $ qwr-cong-inverse r)

    ⊞-unit : QuotWord
    ⊞-unit = qw[ nil ]

    abstract
      ⊞-unit-l : ∀ g → ⊞-unit ⊞ g == g
      ⊞-unit-l = QuotWord-elim
        (λ _ → idp)
        (λ _ → prop-has-all-paths-↓)

      ⊞-assoc : ∀ g₁ g₂ g₃ → (g₁ ⊞ g₂) ⊞ g₃ == g₁ ⊞ (g₂ ⊞ g₃)
      ⊞-assoc = QuotWord-elim
        (λ l₁ → QuotWord-elim
          (λ l₂ → QuotWord-elim
            (λ l₃ → ap qw[_] $ ++-assoc l₁ l₂ l₃)
            (λ _ → prop-has-all-paths-↓))
          (λ _ → prop-has-all-paths-↓))
        (λ _ → prop-has-all-paths-↓)

      ⊟-inv-l : ∀ g → (⊟ g) ⊞ g == ⊞-unit
      ⊟-inv-l = QuotWord-elim
        (λ l → quot-rel (qwr-cancel-l l))
        (λ _ → prop-has-all-paths-↓)

  QuotWord-group-structure : GroupStructure QuotWord
  QuotWord-group-structure = record
    { ident = ⊞-unit
    ; inv = ⊟
    ; comp = _⊞_
    ; unit-l = ⊞-unit-l
    ; assoc = ⊞-assoc
    ; inv-l = ⊟-inv-l
    }

  GenGroup : Group (lmax i m)
  GenGroup = group _ QuotWord-group-structure

  module GenGroup = Group GenGroup
  open GenGroup using (El) public

  abstract
    pres-exp : ∀ (a : A) z →
      qw[ Word-exp a z ] == GenGroup.exp qw[ inl a :: nil ] z
    pres-exp a (pos O) = idp
    pres-exp a (pos (S O)) = idp
    pres-exp a (pos (S (S n))) =
      ap (GenGroup.comp qw[ inl a :: nil ]) (pres-exp a (pos (S n)))
    pres-exp a (negsucc O) = idp
    pres-exp a (negsucc (S n)) =
      ap (GenGroup.comp qw[ inr a :: nil ]) (pres-exp a (negsucc n))

    rel-holds : ∀ {w₁} {w₂} (r : R w₁ w₂) → qw[ w₁ ] == qw[ w₂ ]
    rel-holds r = quot-rel (qwr-rel r)

  -- freeness
  module HomomorphismEquiv {j} (G : Group j) where

    private
      module G = Group G
    open RelationRespectingFunctions A R G public

    module _ (fun : RelationRespectingFunction) where

      private
        module fun = RelationRespectingFunction fun
        f* = fun.f

        abstract
          Word-extendᴳ-emap : ∀ {l₁ l₂}
            → QuotWordRel l₁ l₂
            → Word-extendᴳ G f* l₁ == Word-extendᴳ G f* l₂
          Word-extendᴳ-emap (qwr-refl idp) = idp
          Word-extendᴳ-emap (qwr-trans qwr qwr₁) = (Word-extendᴳ-emap qwr) ∙ (Word-extendᴳ-emap qwr₁)
          Word-extendᴳ-emap (qwr-sym qwr) = ! (Word-extendᴳ-emap qwr)
          Word-extendᴳ-emap (qwr-flip-r x) =
            G.comp (PlusMinus-extendᴳ G f* x) (PlusMinus-extendᴳ G f* (flip x))
              =⟨ ap (G.comp (PlusMinus-extendᴳ G f* x)) (PlusMinus-extendᴳ-flip G f* x) ⟩
            G.comp (PlusMinus-extendᴳ G f* x) (G.inv (PlusMinus-extendᴳ G f* x))
              =⟨ G.inv-r (PlusMinus-extendᴳ G f* x) ⟩
            G.ident =∎
          Word-extendᴳ-emap (qwr-cong {l₁} {l₂} {l₃} {l₄} qwr qwr') =
            Word-extendᴳ G f* (l₁ ++ l₃)
              =⟨ Word-extendᴳ-++ G f* l₁ l₃ ⟩
            G.comp (Word-extendᴳ G f* l₁) (Word-extendᴳ G f* l₃)
              =⟨ ap2 G.comp (Word-extendᴳ-emap qwr) (Word-extendᴳ-emap qwr') ⟩
            G.comp (Word-extendᴳ G f* l₂) (Word-extendᴳ G f* l₄)
              =⟨ ! (Word-extendᴳ-++ G f* l₂ l₄) ⟩
            Word-extendᴳ G f* (l₂ ++ l₄) =∎
          Word-extendᴳ-emap (qwr-rel r) = fun.respects r

      extend : (GenGroup →ᴳ G)
      extend = record {M} where
        module M where
          f : QuotWord → G.El
          f = QuotWord-rec (Word-extendᴳ G f*)
              (λ r → Word-extendᴳ-emap r)
          abstract
            pres-comp : preserves-comp GenGroup.comp G.comp f
            pres-comp =
              QuotWord-elim
                (λ l₁ → QuotWord-elim
                  (λ l₂ → Word-extendᴳ-++ G f* l₁ l₂)
                  (λ _ → prop-has-all-paths-↓))
                (λ _ → prop-has-all-paths-↓)

    private
      module Lemma (hom : GenGroup →ᴳ G) where
        private
          open GroupHom hom
          restr : A → G.El
          restr a = f qw[ inl a :: nil ]

        abstract
          PlusMinus-extendᴳ-hom : ∀ x → PlusMinus-extendᴳ G restr x == f qw[ x :: nil ]
          PlusMinus-extendᴳ-hom (inl x) = idp
          PlusMinus-extendᴳ-hom (inr x) = ! $ pres-inv qw[ inl x :: nil ]

          Word-extendᴳ-hom : ∀ l → Word-extendᴳ G restr l == f qw[ l ]
          Word-extendᴳ-hom nil = ! pres-ident
          Word-extendᴳ-hom (x :: nil) = PlusMinus-extendᴳ-hom x
          Word-extendᴳ-hom (x :: l@(_ :: _)) =
            G.comp (PlusMinus-extendᴳ G restr x) (Word-extendᴳ G restr l)
              =⟨ ap2 G.comp (PlusMinus-extendᴳ-hom x) (Word-extendᴳ-hom l) ⟩
            G.comp (f qw[ x :: nil ]) (f qw[ l ])
              =⟨ ! (pres-comp _ _) ⟩
            f qw[ x :: l ] =∎

          restr-respects-rel : ∀ {l₁ l₂} → R l₁ l₂ → Word-extendᴳ G restr l₁ == Word-extendᴳ G restr l₂
          restr-respects-rel {l₁} {l₂} r =
            Word-extendᴳ G restr l₁
              =⟨ Word-extendᴳ-hom l₁ ⟩
            f qw[ l₁ ]
              =⟨ ap f (quot-rel (qwr-rel r)) ⟩
            f qw[ l₂ ]
              =⟨ ! (Word-extendᴳ-hom l₂) ⟩
            Word-extendᴳ G restr l₂ =∎

        restrict : RelationRespectingFunction
        restrict = rel-res-fun restr restr-respects-rel

      open Lemma

    extend-is-equiv : is-equiv extend
    extend-is-equiv = is-eq _ from to-from from-to where
      to = extend
      from = restrict

      abstract
        to-from : ∀ h → to (from h) == h
        to-from h = group-hom= $ λ= $ QuotWord-elim
          (λ l → Word-extendᴳ-hom h l)
          (λ _ → prop-has-all-paths-↓)

        from-to : ∀ fun → from (to fun) == fun
        from-to fun = RelationRespectingFunction= (λ= λ a → idp)

    extend-equiv : RelationRespectingFunction ≃ (GenGroup →ᴳ G)
    extend-equiv = extend , extend-is-equiv


module OlderBasicILP.Indirect.Translation where

open import OlderBasicILP.Indirect public

import OlderBasicILP.Indirect.Hilbert.Sequential as HS
import OlderBasicILP.Indirect.Hilbert.Nested as HN
import OlderBasicILP.Indirect.Gentzen as G

open HS using () renaming (_⊢×_ to HS⟨_⊢×_⟩ ; _⊢_ to HS⟨_⊢_⟩) public
open HN using () renaming (_⊢_ to HN⟨_⊢_⟩ ; _⊢⋆_ to HN⟨_⊢⋆_⟩) public
open G using  () renaming (_⊢_ to G⟨_⊢_⟩ ; _⊢⋆_ to G⟨_⊢⋆_⟩) public

-- Translation from sequential Hilbert-style to nested.

hs→hnᵀᵐ : HS.Tm → HN.Tm
hs→hnᵀᵐ TS = aux TS 0
  where
    aux : HS.Tm → ℕ → HN.Tm
    aux HS.NIL         zero    = {!!}
    aux (HS.VAR I TS)  zero    = HN.VAR I
    aux (HS.MP I J TS) zero    = HN.APP (aux TS I) (aux TS J)
    aux (HS.CI TS)     zero    = HN.CI
    aux (HS.CK TS)     zero    = HN.CK
    aux (HS.CS TS)     zero    = HN.CS
    aux (HS.NEC SS TS) zero    = HN.BOX (aux SS 0)
    aux (HS.CDIST TS)  zero    = HN.CDIST
    aux (HS.CUP TS)    zero    = HN.CUP
    aux (HS.CDOWN TS)  zero    = HN.CDOWN
    aux (HS.CPAIR TS)  zero    = HN.CPAIR
    aux (HS.CFST TS)   zero    = HN.CFST
    aux (HS.CSND TS)   zero    = HN.CSND
    aux (HS.UNIT TS)   zero    = HN.UNIT
    aux HS.NIL         (suc K) = {!!}
    aux (HS.VAR I TS)  (suc K) = aux TS K
    aux (HS.MP I J TS) (suc K) = aux TS K
    aux (HS.CI TS)     (suc K) = aux TS K
    aux (HS.CK TS)     (suc K) = aux TS K
    aux (HS.CS TS)     (suc K) = aux TS K
    aux (HS.NEC SS TS) (suc K) = aux TS K
    aux (HS.CDIST TS)  (suc K) = aux TS K
    aux (HS.CUP TS)    (suc K) = aux TS K
    aux (HS.CDOWN TS)  (suc K) = aux TS K
    aux (HS.CPAIR TS)  (suc K) = aux TS K
    aux (HS.CFST TS)   (suc K) = aux TS K
    aux (HS.CSND TS)   (suc K) = aux TS K
    aux (HS.UNIT TS)   (suc K) = aux TS K

hs→hnᵀʸ : Ty HS.Tm → Ty HN.Tm
hs→hnᵀʸ (α P)   = α P
hs→hnᵀʸ (A ▻ B) = hs→hnᵀʸ A ▻ hs→hnᵀʸ B
hs→hnᵀʸ (T ⦂ A) = hs→hnᵀᵐ T ⦂ hs→hnᵀʸ A
hs→hnᵀʸ (A ∧ B) = hs→hnᵀʸ A ∧ hs→hnᵀʸ B
hs→hnᵀʸ ⊤      = ⊤

hs→hnᵀʸ⋆ : Cx (Ty HS.Tm) → Cx (Ty HN.Tm)
hs→hnᵀʸ⋆ ∅       = ∅
hs→hnᵀʸ⋆ (Γ , A) = hs→hnᵀʸ⋆ Γ , hs→hnᵀʸ A

hs→hnᴵˣ : ∀ {A Γ} → A ∈ Γ → hs→hnᵀʸ A ∈ hs→hnᵀʸ⋆ Γ
hs→hnᴵˣ top     = top
hs→hnᴵˣ (pop i) = pop (hs→hnᴵˣ i)

hs→hn : ∀ {A Γ} → HS⟨ Γ ⊢ A ⟩ → HN⟨ hs→hnᵀʸ⋆ Γ ⊢ hs→hnᵀʸ A ⟩
hs→hn (Ξ , ts) = aux ts top
  where
    aux : ∀ {A Γ Ξ} → HS⟨ Γ ⊢× Ξ ⟩ → A ∈ Ξ → HN⟨ hs→hnᵀʸ⋆ Γ ⊢ hs→hnᵀʸ A ⟩
    aux (HS.var i ts)  top     = HN.var (hs→hnᴵˣ i)
    aux (HS.mp i j ts) top     = HN.app (aux ts i) (aux ts j)
    aux (HS.ci ts)     top     = HN.ci
    aux (HS.ck ts)     top     = HN.ck
    aux (HS.cs ts)     top     = HN.cs
    aux (HS.nec ss ts) top     = {!HN.box (aux ss top)!}
    aux (HS.cdist ts)  top     = {!HN.cdist!}
    aux (HS.cup ts)    top     = {!HN.cup!}
    aux (HS.cdown ts)  top     = HN.cdown
    aux (HS.cpair ts)  top     = HN.cpair
    aux (HS.cfst ts)   top     = HN.cfst
    aux (HS.csnd ts)   top     = HN.csnd
    aux (HS.unit ts)   top     = HN.unit
    aux (HS.var i ts)  (pop k) = aux ts k
    aux (HS.mp i j ts) (pop k) = aux ts k
    aux (HS.ci ts)     (pop k) = aux ts k
    aux (HS.ck ts)     (pop k) = aux ts k
    aux (HS.cs ts)     (pop k) = aux ts k
    aux (HS.nec ss ts) (pop k) = aux ts k
    aux (HS.cdist ts)  (pop k) = aux ts k
    aux (HS.cup ts)    (pop k) = aux ts k
    aux (HS.cdown ts)  (pop k) = aux ts k
    aux (HS.cpair ts)  (pop k) = aux ts k
    aux (HS.cfst ts)   (pop k) = aux ts k
    aux (HS.csnd ts)   (pop k) = aux ts k
    aux (HS.unit ts)   (pop k) = aux ts k

-- Translation from nested Hilbert-style to sequential.

hn→hsᵀᵐ : HN.Tm → HS.Tm
hn→hsᵀᵐ (HN.VAR I)   = HS.VAR I HS.NIL
hn→hsᵀᵐ (HN.APP T U) = HS.APP (hn→hsᵀᵐ T) (hn→hsᵀᵐ U)
hn→hsᵀᵐ HN.CI        = HS.CI HS.NIL
hn→hsᵀᵐ HN.CK        = HS.CK HS.NIL
hn→hsᵀᵐ HN.CS        = HS.CS HS.NIL
hn→hsᵀᵐ (HN.BOX T)   = HS.BOX (hn→hsᵀᵐ T)
hn→hsᵀᵐ HN.CDIST     = HS.CDIST HS.NIL
hn→hsᵀᵐ HN.CUP       = HS.CUP HS.NIL
hn→hsᵀᵐ HN.CDOWN     = HS.CDOWN HS.NIL
hn→hsᵀᵐ HN.CPAIR     = HS.CPAIR HS.NIL
hn→hsᵀᵐ HN.CFST      = HS.CFST HS.NIL
hn→hsᵀᵐ HN.CSND      = HS.CSND HS.NIL
hn→hsᵀᵐ HN.UNIT      = HS.UNIT HS.NIL

hn→hsᵀʸ : Ty HN.Tm → Ty HS.Tm
hn→hsᵀʸ (α P)   = α P
hn→hsᵀʸ (A ▻ B) = hn→hsᵀʸ A ▻ hn→hsᵀʸ B
hn→hsᵀʸ (T ⦂ A) = hn→hsᵀᵐ T ⦂ hn→hsᵀʸ A
hn→hsᵀʸ (A ∧ B) = hn→hsᵀʸ A ∧ hn→hsᵀʸ B
hn→hsᵀʸ ⊤      = ⊤

hn→hsᵀʸ⋆ : Cx (Ty HN.Tm) → Cx (Ty HS.Tm)
hn→hsᵀʸ⋆ ∅       = ∅
hn→hsᵀʸ⋆ (Γ , A) = hn→hsᵀʸ⋆ Γ , hn→hsᵀʸ A

hn→hsᴵˣ : ∀ {A Γ} → A ∈ Γ → hn→hsᵀʸ A ∈ hn→hsᵀʸ⋆ Γ
hn→hsᴵˣ top     = top
hn→hsᴵˣ (pop i) = pop (hn→hsᴵˣ i)

hn→hs : ∀ {A Γ} → HN⟨ Γ ⊢ A ⟩ → HS⟨ hn→hsᵀʸ⋆ Γ ⊢ hn→hsᵀʸ A ⟩
hn→hs (HN.var i)   = ∅ , HS.var (hn→hsᴵˣ i) HS.nil
hn→hs (HN.app t u) = HS.app (hn→hs t) (hn→hs u)
hn→hs HN.ci        = ∅ , HS.ci HS.nil
hn→hs HN.ck        = ∅ , HS.ck HS.nil
hn→hs HN.cs        = ∅ , HS.cs HS.nil
hn→hs (HN.box t)   = {!HS.box (hn→hs t)!}
hn→hs HN.cdist     = ∅ , HS.cdist HS.nil
hn→hs HN.cup       = ∅ , HS.cup HS.nil
hn→hs HN.cdown     = ∅ , HS.cdown HS.nil
hn→hs HN.cpair     = ∅ , HS.cpair HS.nil
hn→hs HN.cfst      = ∅ , HS.cfst HS.nil
hn→hs HN.csnd      = ∅ , HS.csnd HS.nil
hn→hs HN.unit      = ∅ , HS.unit HS.nil

-- Deduction theorem for sequential Hilbert-style.

hs→hn→hsᵀᵐ : ∀ {TS} → hn→hsᵀᵐ (hs→hnᵀᵐ TS) ≡ TS
hs→hn→hsᵀᵐ {HS.NIL}       = {!refl!}
hs→hn→hsᵀᵐ {HS.VAR I TS}  = cong² HS.VAR refl {!!}
hs→hn→hsᵀᵐ {HS.MP I J TS} = {!!}
hs→hn→hsᵀᵐ {HS.CI TS}     = {!!}
hs→hn→hsᵀᵐ {HS.CK TS}     = {!!}
hs→hn→hsᵀᵐ {HS.CS TS}     = {!!}
hs→hn→hsᵀᵐ {HS.NEC SS TS} = {!!}
hs→hn→hsᵀᵐ {HS.CDIST TS}  = {!!}
hs→hn→hsᵀᵐ {HS.CUP TS}    = {!!}
hs→hn→hsᵀᵐ {HS.CDOWN TS}  = {!!}
hs→hn→hsᵀᵐ {HS.CPAIR TS}  = {!!}
hs→hn→hsᵀᵐ {HS.CFST TS}   = {!!}
hs→hn→hsᵀᵐ {HS.CSND TS}   = {!!}
hs→hn→hsᵀᵐ {HS.UNIT TS}   = {!!}

hs→hn→hsᵀʸ : ∀ {A} → hn→hsᵀʸ (hs→hnᵀʸ A) ≡ A
hs→hn→hsᵀʸ {α P}   = refl
hs→hn→hsᵀʸ {A ▻ B} = cong² _▻_ hs→hn→hsᵀʸ hs→hn→hsᵀʸ
hs→hn→hsᵀʸ {T ⦂ A} = cong² _⦂_ hs→hn→hsᵀᵐ hs→hn→hsᵀʸ
hs→hn→hsᵀʸ {A ∧ B} = cong² _∧_ hs→hn→hsᵀʸ hs→hn→hsᵀʸ
hs→hn→hsᵀʸ {⊤}    = refl

-- hs-lam : ∀ {A B Γ} → HS⟨ Γ , A ⊢ B ⟩ → HS⟨ Γ ⊢ A ▻ B ⟩
-- hs-lam t = {!hn→hs (HN.lam (hs→hn t))!}
-- hn→hs ∘ HN.lam ∘ hs→hn

-- Translation from nested Hilbert-style to Gentzen-style.

hn→gᵀᵐ : HN.Tm → G.Tm
hn→gᵀᵐ (HN.VAR I)   = G.VAR I
hn→gᵀᵐ (HN.APP T U) = G.APP (hn→gᵀᵐ T) (hn→gᵀᵐ U)
hn→gᵀᵐ HN.CI        = G.CI
hn→gᵀᵐ HN.CK        = G.CK
hn→gᵀᵐ HN.CS        = G.CS
hn→gᵀᵐ (HN.BOX T)   = G.BOX (hn→gᵀᵐ T)
hn→gᵀᵐ HN.CDIST     = G.CDIST
hn→gᵀᵐ HN.CUP       = G.CUP
hn→gᵀᵐ HN.CDOWN     = G.CDOWN
hn→gᵀᵐ HN.CPAIR     = G.CPAIR
hn→gᵀᵐ HN.CFST      = G.CFST
hn→gᵀᵐ HN.CSND      = G.CSND
hn→gᵀᵐ HN.UNIT      = G.UNIT

hn→gᵀʸ : Ty HN.Tm → Ty G.Tm
hn→gᵀʸ (α P)   = α P
hn→gᵀʸ (A ▻ B) = hn→gᵀʸ A ▻ hn→gᵀʸ B
hn→gᵀʸ (T ⦂ A) = hn→gᵀᵐ T ⦂ hn→gᵀʸ A
hn→gᵀʸ (A ∧ B) = hn→gᵀʸ A ∧ hn→gᵀʸ B
hn→gᵀʸ ⊤      = ⊤

hn→gᵀʸ⋆ : Cx (Ty HN.Tm) → Cx (Ty G.Tm)
hn→gᵀʸ⋆ ∅       = ∅
hn→gᵀʸ⋆ (Γ , A) = hn→gᵀʸ⋆ Γ , hn→gᵀʸ A

hn→gᴵˣ : ∀ {A Γ} → A ∈ Γ → hn→gᵀʸ A ∈ hn→gᵀʸ⋆ Γ
hn→gᴵˣ top     = top
hn→gᴵˣ (pop i) = pop (hn→gᴵˣ i)

hn→g : ∀ {A Γ} → HN⟨ Γ ⊢ A ⟩ → G⟨ hn→gᵀʸ⋆ Γ ⊢ hn→gᵀʸ A ⟩
hn→g (HN.var i)   = G.var (hn→gᴵˣ i)
hn→g (HN.app t u) = G.app (hn→g t) (hn→g u)
hn→g HN.ci        = G.ci
hn→g HN.ck        = G.ck
hn→g HN.cs        = G.cs
hn→g (HN.box t)   = {!G.box (hn→g t)!}
hn→g HN.cdist     = {!G.cdist!}
hn→g HN.cup       = {!G.cup!}
hn→g HN.cdown     = G.cdown
hn→g HN.cpair     = G.cpair
hn→g HN.cfst      = G.cfst
hn→g HN.csnd      = G.csnd
hn→g HN.unit      = G.unit

-- Translation from sequential Hilbert-style to Gentzen-style.

hs→gᵀʸ : Ty HS.Tm → Ty G.Tm
hs→gᵀʸ = hn→gᵀʸ ∘ hs→hnᵀʸ

hs→gᵀʸ⋆ : Cx (Ty HS.Tm) → Cx (Ty G.Tm)
hs→gᵀʸ⋆ = hn→gᵀʸ⋆ ∘ hs→hnᵀʸ⋆

hs→g : ∀ {A Γ} → HS⟨ Γ ⊢ A ⟩ → G⟨ hs→gᵀʸ⋆ Γ ⊢ hs→gᵀʸ A ⟩
hs→g = hn→g ∘ hs→hn

-- Translation from Gentzen-style to Hilbert-style.

mutual
  g→hnᵀᵐ : G.Tm → HN.Tm
  g→hnᵀᵐ (G.VAR I)         = HN.VAR I
  g→hnᵀᵐ (G.LAM T)         = HN.LAM (g→hnᵀᵐ T)
  g→hnᵀᵐ (G.APP T U)       = HN.APP (g→hnᵀᵐ T) (g→hnᵀᵐ U)
  g→hnᵀᵐ (G.MULTIBOX TS U) = HN.MULTIBOX (g→hnᵀᵐ⋆ TS) (g→hnᵀᵐ U)
  g→hnᵀᵐ (G.DOWN T)        = HN.DOWN (g→hnᵀᵐ T)
  g→hnᵀᵐ (G.PAIR T U)      = HN.PAIR (g→hnᵀᵐ T) (g→hnᵀᵐ U)
  g→hnᵀᵐ (G.FST T)         = HN.FST (g→hnᵀᵐ T)
  g→hnᵀᵐ (G.SND T)         = HN.SND (g→hnᵀᵐ T)
  g→hnᵀᵐ G.UNIT            = HN.UNIT

  g→hnᵀᵐ⋆ : Cx G.Tm → Cx HN.Tm
  g→hnᵀᵐ⋆ ∅        = ∅
  g→hnᵀᵐ⋆ (TS , T) = g→hnᵀᵐ⋆ TS , g→hnᵀᵐ T

g→hnᵀʸ : Ty G.Tm → Ty HN.Tm
g→hnᵀʸ (α P)   = α P
g→hnᵀʸ (A ▻ B) = g→hnᵀʸ A ▻ g→hnᵀʸ B
g→hnᵀʸ (T ⦂ A) = g→hnᵀᵐ T ⦂ g→hnᵀʸ A
g→hnᵀʸ (A ∧ B) = g→hnᵀʸ A ∧ g→hnᵀʸ B
g→hnᵀʸ ⊤      = ⊤

g→hnᵀʸ⋆ : Cx (Ty G.Tm) → Cx (Ty HN.Tm)
g→hnᵀʸ⋆ ∅       = ∅
g→hnᵀʸ⋆ (Γ , A) = g→hnᵀʸ⋆ Γ , g→hnᵀʸ A

g→hnᴵˣ : ∀ {A Γ} → A ∈ Γ → g→hnᵀʸ A ∈ g→hnᵀʸ⋆ Γ
g→hnᴵˣ top     = top
g→hnᴵˣ (pop i) = pop (g→hnᴵˣ i)

mutual
  g→hn : ∀ {A Γ} → G⟨ Γ ⊢ A ⟩ → HN⟨ g→hnᵀʸ⋆ Γ ⊢ g→hnᵀʸ A ⟩
  g→hn (G.var i)         = HN.var (g→hnᴵˣ i)
  g→hn (G.lam t)         = HN.lam (g→hn t)
  g→hn (G.app t u)       = HN.app (g→hn t) (g→hn u)
  g→hn (G.multibox ts u) = {!HN.multibox (g→hn⋆ ts) (g→hn u)!}
  g→hn (G.down t)        = HN.down (g→hn t)
  g→hn (G.pair t u)      = HN.pair (g→hn t) (g→hn u)
  g→hn (G.fst t)         = HN.fst (g→hn t)
  g→hn (G.snd t)         = HN.snd (g→hn t)
  g→hn G.unit            = HN.unit

  g→hn⋆ : ∀ {Ξ Γ} → G⟨ Γ ⊢⋆ Ξ ⟩ → HN⟨ g→hnᵀʸ⋆ Γ ⊢⋆ g→hnᵀʸ⋆ Ξ ⟩
  g→hn⋆ {∅}     ∙        = ∙
  g→hn⋆ {Ξ , A} (ts , t) = g→hn⋆ ts , g→hn t

-- Translation from Gentzen-style to sequential Hilbert-style.

g→hsᵀʸ : Ty G.Tm → Ty HS.Tm
g→hsᵀʸ = hn→hsᵀʸ ∘ g→hnᵀʸ

g→hsᵀʸ⋆ : Cx (Ty G.Tm) → Cx (Ty HS.Tm)
g→hsᵀʸ⋆ = hn→hsᵀʸ⋆ ∘ g→hnᵀʸ⋆

g→hs : ∀ {A Γ} → G⟨ Γ ⊢ A ⟩ → HS⟨ g→hsᵀʸ⋆ Γ ⊢ g→hsᵀʸ A ⟩
g→hs = hn→hs ∘ g→hn


------------------------------------------------------------------------
-- A self-interpreter (without correctness proof)
------------------------------------------------------------------------

module Self-interpreter where

open import Prelude hiding (const)

-- To simplify the development, let's work with actual natural numbers
-- as variables and constants (see
-- Atom.one-can-restrict-attention-to-χ-ℕ-atoms).

open import Atom

open import Chi            χ-ℕ-atoms
open import Constants      χ-ℕ-atoms
open import Free-variables χ-ℕ-atoms

open χ-atoms χ-ℕ-atoms

open import Combinators

-- Substitution.

internal-subst : Exp
internal-subst = lambda v-x (lambda v-new body)
  module Internal-subst where
  private
    rec-or-lambda : _ → _
    rec-or-lambda = λ c →
      branch c (v-y ∷ v-e ∷ []) (
        case (equal-ℕ (var v-x) (var v-y)) (
          branch c-true [] (const c (var v-y ∷ var v-e ∷ [])) ∷
          branch c-false [] (
            const c (var v-y ∷ apply (var v-subst) (var v-e) ∷ [])) ∷
          []))

  branches =
    branch c-apply (v-e₁ ∷ v-e₂ ∷ []) (
      const c-apply (apply (var v-subst) (var v-e₁) ∷
                     apply (var v-subst) (var v-e₂) ∷ [])) ∷
    branch c-case (v-e ∷ v-bs ∷ []) (
      const c-case (
        apply (var v-subst) (var v-e) ∷
        apply (var v-subst) (var v-bs) ∷ [])) ∷
    rec-or-lambda c-rec ∷
    rec-or-lambda c-lambda ∷
    branch c-const (v-c ∷ v-es ∷ []) (
      const c-const (var v-c ∷ apply (var v-subst) (var v-es) ∷ [])) ∷
    branch c-var (v-y ∷ []) (
      case (equal-ℕ (var v-x) (var v-y)) (
        branch c-true [] (var v-new) ∷
        branch c-false [] (const c-var (var v-y ∷ [])) ∷ [])) ∷
    branch c-branch (v-c ∷ v-ys ∷ v-e ∷ []) (
      const c-branch (
        var v-c ∷
        var v-ys ∷
        case (member (var v-x) (var v-ys)) (
          branch c-true [] (var v-e) ∷
          branch c-false [] (apply (var v-subst) (var v-e)) ∷
          []) ∷ [])) ∷
    branch c-nil [] (const c-nil []) ∷
    branch c-cons (v-e ∷ v-es ∷ []) (
      const c-cons (apply (var v-subst) (var v-e) ∷
                    apply (var v-subst) (var v-es) ∷ [])) ∷
    []

  body = rec v-subst (lambda v-e (case (var v-e) branches))

-- Searches for a branch matching a given natural number.

internal-lookup : Exp
internal-lookup =
  lambda v-c (rec v-lookup (lambda v-bs (case (var v-bs) (
    branch c-cons (v-b ∷ v-bs ∷ []) (case (var v-b) (
      branch c-branch (v-c′ ∷ v-underscore ∷ v-underscore ∷ []) (
        case (equal-ℕ (var v-c) (var v-c′)) (
          branch c-false [] (apply (var v-lookup) (var v-bs)) ∷
          branch c-true [] (var v-b) ∷ [])) ∷ [])) ∷ []))))

-- Tries to apply multiple substitutions.

internal-substs : Exp
internal-substs =
  rec v-substs (lambda v-xs (lambda v-es (lambda v-e′ (
    case (var v-xs) (
      branch c-nil [] (case (var v-es) (
        branch c-nil [] (var v-e′) ∷ [])) ∷
      branch c-cons (v-x ∷ v-xs ∷ []) (case (var v-es) (
        branch c-cons (v-e ∷ v-es ∷ []) (
          apply (apply (apply internal-subst (var v-x)) (var v-e))
            (apply (apply (apply (var v-substs) (var v-xs))
               (var v-es)) (var v-e′))) ∷ [])) ∷ [])))))

-- A map function. The application of map to the function is done at
-- the meta-level in order to simplify proofs.

map : Exp → Exp
map f = rec v-map (lambda v-xs (case (var v-xs) branches))
  module Map where
  branches =
    branch c-nil [] (const c-nil []) ∷
    branch c-cons (v-x ∷ v-xs ∷ []) (
      const c-cons (apply f (var v-x) ∷
                    apply (var v-map) (var v-xs) ∷
                    [])) ∷
    []

-- The self-interpreter.

eval : Exp
eval =
  rec v-eval (lambda v-p (case (var v-p) branches))
  module Eval where
  apply-branch =
    branch c-lambda (v-x ∷ v-e ∷ []) (
      apply (var v-eval) (
        apply (apply (apply internal-subst (var v-x))
                 (apply (var v-eval) (var v-e₂)))
          (var v-e))) ∷
    []

  case-body₂ =
    case (apply (apply internal-lookup (var v-c)) (var v-bs)) (
      branch c-branch (v-underscore ∷ v-xs ∷ v-e ∷ []) (
        apply (var v-eval) (
          apply (apply (apply internal-substs (var v-xs))
            (var v-es)) (var v-e))) ∷
      [])

  case-body₁ =
    case (apply (var v-eval) (var v-e)) (
      branch c-const (v-c ∷ v-es ∷ []) case-body₂ ∷
      [])

  branches =
    branch c-apply (v-e₁ ∷ v-e₂ ∷ []) (
      case (apply (var v-eval) (var v-e₁)) apply-branch) ∷
    branch c-case (v-e ∷ v-bs ∷ []) case-body₁ ∷
    branch c-rec (v-x ∷ v-e ∷ []) (
      apply (var v-eval) (
        apply (apply (apply internal-subst (var v-x))
                 (const c-rec (var v-x ∷ var v-e ∷ [])))
          (var v-e))) ∷
    branch c-lambda (v-x ∷ v-e ∷ []) (
      const c-lambda (var v-x ∷ var v-e ∷ [])) ∷
    branch c-const (v-c ∷ v-es ∷ []) (
      const c-const (var v-c ∷
                     apply (map (var v-eval)) (var v-es) ∷
                     [])) ∷
    []

eval-closed : Closed eval
eval-closed = from-⊎ (closed? eval)