open import Categories.Category
open import Categories.Category.CartesianClosed

open import Theory

module Categories.Category.Construction.Models
  {ℓ₁ ℓ₂ ℓ₃}
  (Th : Theory.Theory ℓ₁ ℓ₂ ℓ₃)
  {o ℓ e}
  (𝒞 : Category o ℓ e)
  (cartesianClosed : CartesianClosed 𝒞)
  where

  open import Categories.Category.Cartesian 𝒞
  open import Categories.Category.BinaryProducts 𝒞
  open import Categories.Object.Terminal 𝒞
  import Categories.Object.Product 𝒞 as P
  open import Categories.Morphism 𝒞
  open import Categories.Functor.Construction.Product using (Product)
  open import Categories.Functor.Construction.Exponential using (Exp)
  open import Categories.Functor.Properties using ([_]-resp-≅)

  open Category 𝒞
  open CartesianClosed cartesianClosed
  open Cartesian cartesian
  open BinaryProducts products
  module T = Terminal terminal
  open HomReasoning
  open import Categories.Morphism.Reasoning 𝒞

  open import Syntax
  open Theory.Theory Th
  open import Semantics 𝒞 cartesianClosed Sg
  open Signature Sg

  open import Data.Product using (Σ; Σ-syntax; proj₁; proj₂; _,_)

  open import Relation.Binary using (Rel; IsEquivalence)

  open import Level using (_⊔_)

  ⁂-id : forall {A B} -> id {A = A} ⁂ id {A = B} ≈ id
  ⁂-id = Equiv.trans (⟨⟩-cong₂ identityˡ identityˡ) η

  module Homomorphism {M N : Model 𝒞 cartesianClosed Th}
    (h : (g : Gr) -> ⟦ g ⟧G (proj₁ M) ≅ ⟦ g ⟧G (proj₁ N))
    where
    open _≅_
    open Iso

    H : (A : Type) -> ⟦ A ⟧T (proj₁ M) ≅ ⟦ A ⟧T (proj₁ N)
    H ⌊ g ⌋ = h g
    H Unit = up-to-iso terminal terminal
    H (A * A₁) = [ Product 𝒞 cartesian ]-resp-≅
      (record
        { from = from (H A) , from (H A₁)
        ; to = to (H A) , to (H A₁)
        ; iso = record { isoˡ = isoˡ (iso (H A)) , isoˡ (iso (H A₁)) ; isoʳ = isoʳ (iso (H A)) , isoʳ (iso (H A₁)) }
        })
    H (A => A₁) = [ Exp 𝒞 cartesianClosed ]-resp-≅
      (record
        { from = from (H A₁) , to (H A)
        ; to = to (H A₁) , from (H A)
        ; iso = record { isoˡ = isoˡ (iso (H A₁)) , isoˡ (iso (H A)) ; isoʳ = isoʳ (iso (H A₁)) , isoʳ (iso (H A)) }
        })

  record homomorphism (M N : Model 𝒞 cartesianClosed Th) : Set (ℓ₁ ⊔ ℓ₂ ⊔ ℓ ⊔ e) where
    field
      h : (g : Gr) -> ⟦ g ⟧G (proj₁ M) ≅ ⟦ g ⟧G (proj₁ N)

    open Homomorphism {M} {N} h public

    field
      comm : (f : Func) -> _≅_.from (H (cod f)) ∘ ⟦ f ⟧F (proj₁ M) ≈ ⟦ f ⟧F (proj₁ N) ∘ _≅_.from (H (dom f))

  module _ {M N : Model 𝒞 cartesianClosed Th} where
    _≗_ : Rel (homomorphism M N) (ℓ₁ ⊔ e)
    _≗_ x y = (g : Gr) -> _≅_.from (homomorphism.h x g) ≈ _≅_.from (homomorphism.h y g)

  module Id {M : Model 𝒞 cartesianClosed Th} where
    open Homomorphism {M} {M} (λ _ → IsEquivalence.refl ≅-isEquivalence)
    open Structure (proj₁ M)

    -- The components of the homomorphism generated by identity morphisms are also identities.
    H-id : forall A -> _≅_.from (H A) ≈ Category.id 𝒞
    H-id˘ : forall A -> _≅_.to (H A) ≈ Category.id 𝒞

    H-id ⌊ x ⌋ = Equiv.refl
    H-id Unit = T.!-unique (Category.id 𝒞)
    H-id (A * A₁) =
      begin
        _≅_.from (H A) ⁂ _≅_.from (H A₁)
      ≈⟨ ⁂-cong₂ (H-id A) (H-id A₁) ⟩
        Category.id 𝒞 ⁂ Category.id 𝒞
      ≈⟨ ⟨⟩-cong₂ identityˡ identityˡ ⟩
        ⟨ π₁ , π₂ ⟩
      ≈⟨ η ⟩
        Category.id 𝒞
      ∎
    H-id (A => A₁) =
      begin
        λg (_≅_.from (H A₁) ∘ eval′ ∘ (id ⁂ _≅_.to (H A)))
      ≈⟨ λ-cong (∘-resp-≈ˡ (H-id A₁)) ⟩
        λg (id ∘ eval′ ∘ (id ⁂ _≅_.to (H A)))
      ≈⟨ λ-cong (pullˡ identityˡ) ⟩
        λg (eval′ ∘ (id ⁂ _≅_.to (H A)))
      ≈⟨ λ-cong (∘-resp-≈ʳ (⁂-cong₂ Equiv.refl (H-id˘ A))) ⟩
        λg (eval′ ∘ (id ⁂ id))
      ≈⟨ λ-cong (elimʳ ⁂-id) ⟩
        λg eval′
      ≈⟨ η-id′ ⟩
        id
      ∎

    H-id˘ ⌊ x ⌋ = Equiv.refl
    H-id˘ Unit = T.!-unique id
    H-id˘ (A * A₁) =
      begin
        _≅_.to (H A) ⁂ _≅_.to (H A₁)
      ≈⟨ ⁂-cong₂ (H-id˘ A) (H-id˘ A₁) ⟩
        id ⁂ id
      ≈⟨ ⁂-id ⟩
        id
      ∎
    H-id˘ (A => A₁) =
      begin
        λg (_≅_.to (H A₁) ∘ eval′ ∘ (id ⁂ _≅_.from (H A)))
      ≈⟨ λ-cong (elimˡ (H-id˘ A₁)) ⟩
        λg (eval′ ∘ (id ⁂ _≅_.from (H A)))
      ≈⟨ λ-cong (∘-resp-≈ʳ (⁂-cong₂ Equiv.refl (H-id A))) ⟩
        λg (eval′ ∘ (id ⁂ id))
      ≈⟨ λ-cong (elimʳ ⁂-id) ⟩
        λg eval′
      ≈⟨ η-id′ ⟩
        id
      ∎

    comm : (f : Func) -> _≅_.from (H (cod f)) ∘ ⟦ f ⟧F ≈ ⟦ f ⟧F ∘ _≅_.from (H (dom f))
    comm f =
      begin
        _≅_.from (H (cod f)) ∘ ⟦ f ⟧F
      ≈⟨ elimˡ (H-id (cod f)) ⟩
        ⟦ f ⟧F
      ≈˘⟨ elimʳ (H-id (dom f)) ⟩
        ⟦ f ⟧F ∘ _≅_.from (H (dom f))
      ∎

    id′ : homomorphism M M
    id′ = record { h = λ _ → IsEquivalence.refl ≅-isEquivalence ; comm = comm }

  module Compose {M N O : Model 𝒞 cartesianClosed Th} where
    open _≅_

    compose : homomorphism N O -> homomorphism M N -> homomorphism M O
    compose x y = record
      { h = h
      ; comm = λ f →
          begin
            from (MO.H (cod f)) ∘ ⟦ f ⟧F (proj₁ M)
          ≈⟨ pushˡ (H-compose (cod f)) ⟩
            from (NO.H (cod f)) ∘ from (MN.H (cod f)) ∘ ⟦ f ⟧F (proj₁ M)
          ≈⟨ ∘-resp-≈ʳ (MN.comm f) ⟩
            from (NO.H (cod f)) ∘ ⟦ f ⟧F (proj₁ N) ∘ from (MN.H (dom f))
          ≈⟨ pullˡ (NO.comm f) ⟩
            (⟦ f ⟧F (proj₁ O) ∘ from (NO.H (dom f))) ∘ from (MN.H (dom f))
          ≈˘⟨ pushʳ (H-compose (dom f)) ⟩
            ⟦ f ⟧F (proj₁ O) ∘ from (MO.H (dom f))
          ∎
      }
      where
        h = λ g → IsEquivalence.trans ≅-isEquivalence (homomorphism.h y g) (homomorphism.h x g)
        module MO = Homomorphism h
        module MN = homomorphism y
        module NO = homomorphism x

        H-compose : forall A -> from (MO.H A) ≈ from (NO.H A) ∘ from (MN.H A)
        H-compose˘ : forall A -> to (MO.H A) ≈ to (MN.H A) ∘ to (NO.H A)

        H-compose ⌊ x ⌋ = Equiv.refl
        H-compose Unit = T.!-unique₂
        H-compose (A * A₁) = Equiv.trans (⁂-cong₂ (H-compose A) (H-compose A₁)) (Equiv.sym ⁂∘⁂)
        H-compose (A => A₁) =
          begin
            λg (from (MO.H A₁) ∘ eval′ ∘ (id ⁂ to (MO.H A)))
          ≈⟨ λ-cong (∘-resp-≈ˡ (H-compose A₁)) ⟩
            λg ((from (NO.H A₁) ∘ from (MN.H A₁)) ∘ eval′ ∘ (id ⁂ to (MO.H A)))
          ≈⟨ λ-cong assoc ⟩
            λg (from (NO.H A₁) ∘ from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MO.H A)))
          ≈⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (∘-resp-≈ʳ (⁂-cong₂ Equiv.refl (H-compose˘ A))))) ⟩
            λg (from (NO.H A₁) ∘ from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A) ∘ to (NO.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (∘-resp-≈ʳ second∘second))) ⟩
            λg (from (NO.H A₁) ∘ from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A)) ∘ (id ⁂ to (NO.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ assoc²') ⟩
            λg (from (NO.H A₁) ∘ (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ∘ (id ⁂ to (NO.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (pullˡ β′)) ⟩
            λg (from (NO.H A₁) ∘ eval′ ∘ (λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ⁂ id) ∘ (id ⁂ to (NO.H A)))
          ≈⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ ⁂∘⁂)) ⟩
            λg (from (NO.H A₁) ∘ eval′ ∘ (λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ∘ id ⁂ id ∘ to (NO.H A)))
          ≈⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (⁂-cong₂ identityʳ identityˡ))) ⟩
            λg (from (NO.H A₁) ∘ eval′ ∘ (λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ⁂ to (NO.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (⁂-cong₂ identityˡ identityʳ))) ⟩
            λg (from (NO.H A₁) ∘ eval′ ∘ (id ∘ λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ⁂ to (NO.H A) ∘ id))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ ⁂∘⁂)) ⟩
            λg (from (NO.H A₁) ∘ eval′ ∘ (id ⁂ to (NO.H A)) ∘ (λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ⁂ id))
          ≈˘⟨ λ-cong assoc²' ⟩
            λg ((from (NO.H A₁) ∘ eval′ ∘ (id ⁂ to (NO.H A))) ∘ (λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A))) ⁂ id))
          ≈˘⟨ CartesianClosed.exp.subst cartesianClosed product product ⟩
            λg (from (NO.H A₁) ∘ eval′ ∘ (id ⁂ to (NO.H A))) ∘ λg (from (MN.H A₁) ∘ eval′ ∘ (id ⁂ to (MN.H A)))
          ≈⟨ Equiv.refl ⟩
            from (NO.H (A => A₁)) ∘ from (MN.H (A => A₁))
          ∎

        H-compose˘ ⌊ x ⌋ = Equiv.refl
        H-compose˘ Unit = T.!-unique₂
        H-compose˘ (A * A₁) = Equiv.trans (⁂-cong₂ (H-compose˘ A) (H-compose˘ A₁)) (Equiv.sym ⁂∘⁂)
        H-compose˘ (A => A₁) =
          begin
            λg (to (MO.H A₁) ∘ eval′ ∘ (id ⁂ from (MO.H A)))
          ≈⟨ λ-cong (∘-resp-≈ˡ (H-compose˘ A₁)) ⟩
            λg ((to (MN.H A₁) ∘ to (NO.H A₁)) ∘ eval′ ∘ (id ⁂ from (MO.H A)))
          ≈⟨ λ-cong assoc ⟩
            λg (to (MN.H A₁) ∘ to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (MO.H A)))
          ≈⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (∘-resp-≈ʳ (⁂-cong₂ Equiv.refl (H-compose A))))) ⟩
            λg (to (MN.H A₁) ∘ to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A) ∘ from (MN.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (∘-resp-≈ʳ second∘second))) ⟩
            λg (to (MN.H A₁) ∘ to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A)) ∘ (id ⁂ from (MN.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ assoc²') ⟩
            λg (to (MN.H A₁) ∘ (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ∘ (id ⁂ from (MN.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (pullˡ β′)) ⟩
            λg (to (MN.H A₁) ∘ eval′ ∘ (λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ⁂ id) ∘ (id ⁂ from (MN.H A)))
          ≈⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ ⁂∘⁂)) ⟩
            λg (to (MN.H A₁) ∘ eval′ ∘ (λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ∘ id ⁂ id ∘ from (MN.H A)))
          ≈⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (⁂-cong₂ identityʳ identityˡ))) ⟩
            λg (to (MN.H A₁) ∘ eval′ ∘ (λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ⁂ from (MN.H A)))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ (⁂-cong₂ identityˡ identityʳ))) ⟩
            λg (to (MN.H A₁) ∘ eval′ ∘ (id ∘ λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ⁂ from (MN.H A) ∘ id))
          ≈˘⟨ λ-cong (∘-resp-≈ʳ (∘-resp-≈ʳ ⁂∘⁂)) ⟩
            λg (to (MN.H A₁) ∘ eval′ ∘ (id ⁂ from (MN.H A)) ∘ (λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ⁂ id))
          ≈˘⟨ λ-cong assoc²' ⟩
            λg ((to (MN.H A₁) ∘ eval′ ∘ (id ⁂ from (MN.H A))) ∘ (λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A))) ⁂ id))
          ≈˘⟨ CartesianClosed.exp.subst cartesianClosed product product ⟩
            λg (to (MN.H A₁) ∘ eval′ ∘ (id ⁂ from (MN.H A))) ∘ λg (to (NO.H A₁) ∘ eval′ ∘ (id ⁂ from (NO.H A)))
          ≈⟨ Equiv.refl ⟩
            to (MN.H (A => A₁)) ∘ to (NO.H (A => A₁))
          ∎

  Models : Category (ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃ ⊔ o ⊔ ℓ ⊔ e) (ℓ₁ ⊔ ℓ₂ ⊔ ℓ ⊔ e) (ℓ₁ ⊔ e)
  Models = record
             { Obj = Model 𝒞 cartesianClosed Th
             ; _⇒_ = homomorphism
             ; _≈_ = _≗_
             ; id = Id.id′
             ; _∘_ = Compose.compose
             ; assoc = λ _ → assoc
             ; sym-assoc = λ _ → sym-assoc
             ; identityˡ = λ _ → identityˡ
             ; identityʳ = λ _ → identityʳ
             ; identity² = λ _ → identity²
             ; equiv = record { refl = λ _ → IsEquivalence.refl equiv ; sym = λ x g → IsEquivalence.sym equiv (x g) ; trans = λ x x₁ g → IsEquivalence.trans equiv (x g) (x₁ g) }
             ; ∘-resp-≈ = λ x x₁ g → ∘-resp-≈ (x g) (x₁ g)
             }


{-# OPTIONS --without-K #-}
module LeftCancellation where

open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product using (_,_; _,′_; proj₁; proj₂)
open import Function renaming (_∘_ to _○_)

-- explicit 'using', to show how little of HoTT is needed
open import SimpleHoTT using (refl; ap; _∘_; !; _≡_; _≡⟨_⟩_ ; _∎)
open import Equivalences
open import TypeEquivalences using (swap₊)
open import Inspect

----------------------------------------------------------------------------
-- Very complex proof that we can cancel units on the left of ⊎

-- Some repeated patterns:
-- use injectivity of equivalences to go from f x ≡ f y to x ≡ y
injectivity : {A B : Set} (equiv : (⊤ ⊎ A) ≃ (⊤ ⊎ B)) → (a : A) → equiv ⋆ inj₁ tt ≡ equiv ⋆ inj₂ a → (inj₁ tt ≡ inj₂ a)
injectivity equiv x path = inj≃ equiv (inj₁ tt) (inj₂ x) path

-- Use that disjoint unions are, well, disjoint, to derive a contradiction
bad-path : {A : Set} → (x : A) → inj₁ tt ≡ inj₂ x → ⊥
bad-path x path = proj₁ (thm2-12-5 tt (inj₂ x)) path

left-cancel-⊤ : {A B : Set} →  ((⊤ ⊎ A) ≃ (⊤ ⊎ B)) → A ≃ B
left-cancel-⊤ {A} {B}  (f₁ , mkisequiv g₁ α₁ h₁ β₁) with f₁ (inj₁ tt) | inspect f₁ (inj₁ tt) | g₁ (inj₁ tt) | inspect g₁ (inj₁ tt)
left-cancel-⊤ {A} {B} (f₁ , mkisequiv g₁ α₁ h₁ β₁) | inj₁ tt | ⟪ eq₁ ⟫ | inj₁ tt | ⟪ eq₂ ⟫ = f , equiv₁ (mkqinv g α β)
  where equiv = (f₁ , mkisequiv g₁ α₁ h₁ β₁)
        f : A → B
        f a with f₁ (inj₂ a) | inspect f₁ (inj₂ a)
        f a | inj₂ b  | _ = b
        f a | inj₁ tt | ⟪ eq ⟫ with bad-path a inject
          where inject = injectivity equiv a (eq₁ ∘ ! eq)
        f a | inj₁ tt | ⟪ eq ⟫ | ()
        g : B → A
        g b with g₁ (inj₂ b) | inspect g₁ (inj₂ b)
        g b | inj₂ a  | _ = a
        g b | inj₁ tt | ⟪ eq ⟫ with bad-path b inject
          where inject = injectivity (sym≃ equiv) b (eq₂ ∘ ! eq)
        g b | inj₁ tt | ⟪ eq ⟫ | ()
        α : f ○ g ∼ id
        α b with g₁ (inj₂ b) | inspect g₁ (inj₂ b)
        α b | inj₁ tt | ⟪ eq ⟫ with bad-path b inject
          where inject = injectivity (sym≃ equiv) b (eq₂ ∘ ! eq)
        α b | inj₁ tt | ⟪ eq ⟫ | ()
        α b | inj₂ a  | ⟪ eq ⟫ with f₁ (inj₂ a) | inspect f₁ (inj₂ a)
        α b | inj₂ a | ⟪ eq ⟫ | inj₁ tt | ⟪ eq₃ ⟫ with bad-path a inject
          where inject = injectivity equiv a (eq₁ ∘ ! eq₃)
        α b | inj₂ a | ⟪ eq ⟫ | inj₁ tt | ⟪ eq₃ ⟫ | ()
        α b | inj₂ a | ⟪ eq ⟫ | inj₂ b′ | ⟪ eq₃ ⟫ =
            proj₁ (inj₁₁path b′ b) (ap swap₊ (! (ap f₁ eq ∘ eq₃) ∘ α₁ (inj₂ b)))
        β : g ○ f ∼ id
        β a with f₁ (inj₂ a) | inspect f₁ (inj₂ a)
        β a | inj₁ tt | ⟪ eq ⟫ with bad-path a inject
          where inject = injectivity equiv a (! (eq ∘ ! eq₁))
        ... | ()
        β a | inj₂ b | ⟪ eq ⟫ with g₁ (inj₂ b) | inspect g₁ (inj₂ b)
        ... | inj₁ tt | ⟪ eq₃ ⟫ with bad-path a inject
          where inject = injectivity equiv a (! (ap f₁ eq₃) ∘ (α₁ (inj₂ b)) ∘ ! eq)
        β a | inj₂ b | ⟪ eq ⟫ | inj₁ tt | ⟪ eq₃ ⟫ | ()
        β a | inj₂ b | ⟪ eq ⟫ | inj₂ a′ | ⟪ eq₃ ⟫ = proj₁ (inj₁₁path a′ a) (ap swap₊ ((! eq₃) ∘ ap g₁ (! eq) ∘ β₂ (inj₂ a)))
            where module EQ = qinv (equiv₂ {f = f₁} (proj₂ equiv))
                  β₂ = EQ.β

left-cancel-⊤ (f₁ , mkisequiv g α h β) | inj₁ tt | ⟪ eq₁ ⟫ | inj₂ a | ⟪ eq₂ ⟫ with bad-path a inject
  where equiv = (f₁ , mkisequiv g α h β)
        inject = injectivity equiv a (eq₁ ∘ ! (α (inj₁ tt)) ∘ (ap f₁ eq₂))
left-cancel-⊤ (f₁ , mkisequiv g α h β) | inj₁ tt | ⟪ eq₁ ⟫ | inj₂ a | ⟪ eq₂ ⟫ | ()

left-cancel-⊤ (f₁ , mkisequiv g α h β) | inj₂ b | ⟪ eq₁ ⟫ | inj₁ tt | ⟪ eq₂ ⟫ with bad-path b (! (α (inj₁ tt)) ∘ (ap f₁ eq₂) ∘ eq₁ )
... | ()
left-cancel-⊤ {A} {B} (f₁ , mkisequiv g₁ α₁ h₁ β₁) | inj₂ b₁ | ⟪ eq₁ ⟫ | inj₂ a₁ | ⟪ eq₂ ⟫ = f , equiv₁ (mkqinv g α β)
  where equiv = (f₁ ,′ mkisequiv g₁ α₁ h₁ β₁)
        module EQ = qinv (equiv₂ {f = f₁} (proj₂ equiv))
        β₂ = EQ.β
        f : A → B
        f a with f₁ (inj₂ a)
        f a | inj₂ b′  = b′
        f a | inj₁ tt  = b₁
        g : B → A
        g b with g₁ (inj₂ b)
        g b | inj₂ a′ = a′
        g b | inj₁ tt = a₁
        α : f ○ g ∼ id
        α b with g₁ (inj₂ b) | inspect g₁ (inj₂ b)
        ... |  inj₂ a′ | ⟪ eq ⟫ with f₁ (inj₂ a′) | inspect f₁ (inj₂ a′)
        ...     | inj₂ b′ | ⟪ eq₃ ⟫ = ! (proj₁ (inj₁₁path b b′) (ap swap₊ (! (α₁ (inj₂ b)) ∘ ap f₁ eq ∘ eq₃)))
        ...     | inj₁ tt | ⟪ eq₃ ⟫ with bad-path b (! (! (α₁ (inj₂ b)) ∘ (ap f₁ eq) ∘ eq₃))
        α b | inj₂ a′ | ⟪ eq ⟫ | inj₁ tt | ⟪ eq₃ ⟫ | ()
        α b | inj₁ tt | ⟪ eq ⟫ with f₁ (inj₂ a₁) | inspect f₁ (inj₂ a₁)
        α b | inj₁ tt | ⟪ eq ⟫ | inj₁ tt | ⟪ eq₃ ⟫ = proj₁ (inj₁₁path b₁ b) (ap swap₊ (!(! (α₁ (inj₂ b)) ∘ ap f₁ eq ∘ eq₁)))
        α b | inj₁ tt | ⟪ eq ⟫ | inj₂ b′ | ⟪ eq₃ ⟫ with bad-path b′ (! (α₁ (inj₁ tt)) ∘ ap f₁ eq₂ ∘ eq₃)
        ... | ()
        β : g ○ f ∼ id
        β a with f₁ (inj₂ a) | inspect f₁ (inj₂ a)
        β a | inj₁ tt | ⟪ eq ⟫ with g₁ (inj₂ b₁) | inspect g₁ (inj₂ b₁)
        ...    | inj₁ tt | ⟪ eq₃ ⟫ = proj₁ (inj₁₁path a₁ a) (ap swap₊ (! eq₂ ∘ ! (ap g₁ eq) ∘ β₂ (inj₂ a)))
        β a | inj₁ tt | ⟪ eq ⟫ | inj₂ a′ | ⟪ eq₃ ⟫ with bad-path a′ ((! (β₂ (inj₁ tt)) ∘ ap g₁ eq₁) ∘ eq₃)
        ... | ()
        β a | inj₂ b | ⟪ eq ⟫ with g₁ (inj₂ b) | inspect g₁ (inj₂ b)
        β a | inj₂ b | ⟪ eq₃ ⟫ | inj₁ tt | ⟪ eq ⟫ with bad-path a (! eq ∘ ap g₁ (! eq₃) ∘ β₂ (inj₂ a))
        ... | ()
        β a | inj₂ b | ⟪ eq₃ ⟫ | inj₂ a′ | ⟪ eq ⟫ = proj₁ (inj₁₁path a′ a) (ap swap₊ (! eq ∘ ap g₁ (! eq₃) ∘ β₂ (inj₂ a)))


module PredicateLifting where

import Level
open import Data.Empty
open import Data.Unit as Unit
open import Data.Nat
open import Data.List as List renaming ([] to Ø; [_] to [_]L)
open import NonEmptyList as NList
open import Data.Fin hiding (_+_)
open import Data.Vec as Vec renaming ([_] to [_]V; _++_ to _++V_)
open import Data.Product as Prod
open import Function

open import Relation.Binary.PropositionalEquality as PE hiding ([_])
open import Relation.Binary
open ≡-Reasoning

open import Common.Context as Context

open import Algebra
open Monoid {{ ... }} hiding (refl)

open import SyntaxRaw
open import Syntax

-- | Extend the parameter context of each type variable in the given
-- type context by one extra variable. We need this to specify the new
-- type variable context, in which the predicate lifting of a type lives.
-- See predL below for the correspond typing rule.
liftTyCtx : ∀ {Θ} → TyCtxMorP Ø Θ → TyCtx
liftTyCtx {Ø}     f       = Ø
liftTyCtx {Γ ∷ Θ} (A , f) = (∗ ∷ Γ) ∷ liftTyCtx f

-- | Extend a parameter context by a variable of the given type.
extTyVarParams : (Γ₂ : RawCtx) → PreType Ø Ø Γ₂ → RawCtx
extTyVarParams Γ₂ A = ∗ ∷ Γ₂

-- | Get the Yᵢ : (Γᵢ, xᵢ : Bᵢ) ⊸ ∗ corresponding to Xᵢ : Γᵢ ⊸ ∗ in
-- the lifted context, c.f. the typing rule for predL below.
liftTyVar : ∀{Θ Γ₂} → (f : TyCtxMorP Ø Θ) (X : TyVar Θ Γ₂) →
            TyVar (liftTyCtx f) (extTyVarParams Γ₂ (getTy f X))
liftTyVar f       zero       = zero
liftTyVar (A , f) (succ Γ X) =
  succ (extTyVarParams Γ (getTy f X)) (liftTyVar f X)

-- | Turn an instantiation into a substitution.
instToSubst : ∀{Γ₁} (A : U) (Γ₂ : RawCtx) → PreTerm Γ₁ Ø →
              CtxMorP (∗ ∷ Γ₂ ++ Γ₁) (∗ ∷ (Γ₂ ↑ A) ++ Γ₁)
instToSubst {Γ₁} A Γ₂ t =
  let f = weakenPO' (∗ ∷ Γ₂) t ∷ ctxProjP Γ₁ (∗ ∷ Γ₂)
  in subst (CtxMorP (∗ ∷ Γ₂ ++ Γ₁))
           (PE.sym (cong (_∷_ ∗ ) (assoc Γ₂ (A ∷ Ø) Γ₁)))
           (extendProj (∗ ∷ Γ₂) Ø f)

-- | Auxiliary function to define predL below.
predL₀ : ∀ {Θ Γ₁ Γ₂} →
         (A : Raw Θ Γ₁ Γ₂) → DecentType A → (f : TyCtxMorP Ø Θ) →
         liftTyCtx f ∣ ∗ ∷ Γ₂ ++ Γ₁ / Ø ⊸Ty
predL₀ ._ (DT-⊤ Θ Γ)                                 f =
  ⊤-PT (liftTyCtx f) (∗ ∷ Ø ++ Γ)
predL₀ ._ (DT-tyVar Γ₁ X)                            f =
  tyVarPT _ (liftTyVar f X) §ₜ ctxProjP' Ø _ Γ₁
predL₀ ._ (DT-inst {Γ₂ = Γ₂} B t B-dec t-dec)        f =
  let B' = predL₀ B B-dec f
  in substPT B' (instToSubst _ Γ₂ (mkPreTerm t-dec))
predL₀ ._ (DT-paramAbstr {Γ₂ = Γ₂} {B = B} Γ₁ A-dec) f =
  subst (λ u → PreType (liftTyCtx f) u Ø)
        (cong (_∷_ ∗) (PE.sym (assoc Γ₂ (B ∷ Ø) Γ₁)))
        (predL₀ _ A-dec f)
predL₀ {Θ} {.Γ₁} {Γ₂} ._ (DT-fp Γ₁ ρ D p)            f =
  fpPT (∗ ∷ Γ₂ ++ Γ₁) ρ (mkD' D p 0) §ₜ f'
  where
    f' : CtxMorP (∗ ∷ Γ₂ ++ Γ₁) (∗ ∷ Γ₂)
    f' = extendP ∗ (ctxProjP' Ø Γ₂ Γ₁)

    mkD' : (D : FpData Raw Θ Γ₂) → DecentFpData D → ℕ →
           FpDataP (liftTyCtx f) (∗ ∷ Γ₂)
    mkD' [ Γ , g , A ] (g-dec , A-dec) k =
      let A' = predL₀ A A-dec (substTy (fpPT Ø ρ (mkFpDataP {D = D} p)) f , f)
      in
        [ ( (∗ ∷ Γ)
          , (α (∗ ∷ Γ) Γ ∗ k §ₘ ctxidP (∗ ∷ Γ))
            ∷ (mkCtxMorP {Γ} {Γ₂} {g} g-dec • ctxProjP Γ (∗ ∷ Ø))
          , A' ) ]
    mkD' ((Γ , g , A) ∷ D₁) ((g-dec , A-dec) , q) k =
      let A' = predL₀ A A-dec (substTy (fpPT Ø ρ (mkFpDataP {D = D} p)) f , f)
      in ( ∗ ∷ Γ
          , (α (∗ ∷ Γ) Γ ∗ k §ₘ ctxidP (∗ ∷ Γ))
            ∷ (mkCtxMorP {Γ} {Γ₂} {g} g-dec • ctxProjP Γ (∗ ∷ Ø))
          , A' )
         ∷ mkD' D₁ q (suc k)

-- | Lift an open type A to predicates. More specifically, predL
-- implements the following rule, in which we write Ā for predL A.
--   X₁ : Δ₁ ⊸ Ty, ..., Xₙ : Δₙ ⊸ Ty | Γ₁ ⊢ A : Γ₂ ⊸ Ty
--   B₁ : Δ₁ ⊸ Ty
--   ...
--   Bₙ : Δ₁ ⊸ Ty
--  ================================================================= (predL)
--   Y₁ : (Δ₁, x₁ : B₁ ⊙ id) ⊸ Ty, ..., (Yₙ : Δₙ, xₙ : Bₙ ⊙ id) ⊸ Ty
--     | Γ₁, Γ₂, z : A[B₁/X₁, ..., Bₙ/Xₙ] ⊙ id
--     ⊢ Ā : Ty
predL : ∀ {Θ Γ₁ Γ₂} →
        PreType Θ Γ₁ Γ₂ →
        (f : TyCtxMorP Ø Θ) →
--      ===========================================================
        liftTyCtx f ∣ ∗ ∷ Γ₂ ++ Γ₁ / Ø ⊸Ty
predL (A , A-dec) = predL₀ A A-dec

-- | Special instance of the predicate lifting for types with one
-- free variable and no parameters.
--           X : Δ ⊸ Ty | Γ ⊢ A : Ty           B : Δ ⊸ Ty
--  =============================================================== (predL₁)
--     Y : (Δ, x : B ⊙ id) ⊸ ∗ | Γ, z : A[B/X] ⊢ Ā : Ty
predL₁ : ∀ {Δ Γ} →
        ((Δ ∷ Ø) ∣ Γ / Ø ⊸Ty) → (Ø ∣ Ø / Δ ⊸Ty) →
--      ===========================================================
        ((∗ ∷ Δ) ∷ Ø) ∣ ∗ ∷ Γ / Ø ⊸Ty
predL₁ A B = predL A (B , tt)


postulate
  A : Set

works : Set
works = let B : {X : Set} → Set
            B = let _ = Set in A
        in A

fails : Set
fails = let B : {X : Set} → Set
            B = A   -- Set !=< {X : Set} → Set
        in A


----------------------------------------------------------------------
-- Functional big-step evaluation of terms in the partiality monad
----------------------------------------------------------------------

module SystemF.Eval where

open import Codata.Musical.Notation
open import Category.Monad
open import Category.Monad.Partiality.All as All using (All; now; later)
open import Data.Fin using (Fin; zero; suc)
open import Data.Maybe as Maybe using (just; nothing)
open import Data.Maybe.Relation.Unary.Any as MaybeAny using (just)
open import Data.Nat using (_+_)
open import Data.Vec using ([])
open import Function
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary

open import PartialityAndFailure as PF hiding (fail)
open PF.Equality hiding (fail)
open PF.Equivalence
private module M {f} = RawMonad (PF.monad {f})

open import SystemF.Type
open import SystemF.Term
open import SystemF.WtTerm

open TypeSubst       using () renaming (_[/_]  to _[/tp_])
open TermTypeSubst   using () renaming (_[/_]  to _[/tmTp_])
open TermTermSubst   using () renaming (_[/_]  to _[/tmTm_])
open WtTermTypeSubst using () renaming (_[/_]′ to _[/⊢tmTp_])
open WtTermTermSubst using () renaming (_[/_]  to _[/⊢tmTm_])

----------------------------------------------------------------------
-- Functional call-by-value big-step semantics

-- The functional presentation of the big-step semantics below is
-- heavily inspired by Danielsson's ICFP'12 paper "Operational
-- Semantics Using the Partiality Monad".  While the paper describes a
-- closure-based semantics, the semantics given below is
-- substitution-based.  This simplifies the evaluation of recursive
-- terms, i.e those involving fixpoint combinators, which would
-- otherwise have to be evaluated in a cyclic evironment, i.e. one
-- already containing the value of the recursive term being evaluated.
--
-- NB: while it is not described in detail in the paper, Danielsson
-- actually provides an alternative, substitution-based implementation
-- in the accompanying code, which can be found at
--
--   http://www.cse.chalmers.se/~nad/publications/danielsson-semantics-partiality-monad.tgz
--
-- As pointed out in Danielsson's paper, the functional presentation
-- of the big-step semantics feels rather natural in that it follows
-- the form of an interpreter, and it has the added advantage of
-- proving that the semantics are deterministic and computable "for
-- free".
--
-- For more information about Danielson's paper see
--
--   http://www.cse.chalmers.se/~nad/publications/danielsson-semantics-partiality-monad.html

----------------------------------------------------------------------
-- Semantic domain and evaluation

-- Computations with potential failure and partiality effects.
Comp : ∀ m n → Set
Comp m n = (Val m n) ?⊥

-- Following Danielsson's approach, we formulate the evaluation
-- function _⇓′ in an "embedded language" to work around the
-- limitations of Agda's guarded coinduction.  The function _⇓′
-- returns "programs", i.e. instances of the type _?⊥P which
-- internalizes the monadic bind operation as a constructor.  In a
-- second step, these programs are interpreted in the
-- partiality-and-failure monad by the function _⇓.
--
-- For details about this technique, see e.g. Danielsson's PAR'10
-- paper "Beating the Productivity Checker Using Embedded Languages".
--
-- SystemF.Eval.NoWorkarounds contains an alternative version of the
-- semantics which does not use the above-mentioned workaround.  The
-- alternative definition, while (provably) equivalent to the one
-- given below, is more verbose and arguably less readable.  However
-- the associated type soundness proof is simpler in that it requires
-- no additional compositionality lemmas.
module _ where
  open PF.Workaround

  -- Computation "programs".
  CompP : ∀ m n → Set₁
  CompP m n = (Val m n) ?⊥P

  mutual

    infix 7 _⇓′ _[_]′ _·′_

    -- Evaluation of untyped (open) terms in _?⊥P.
    _⇓′ : ∀ {m n} → Term m n → CompP m n
    var x ⇓′      = fail
    Λ t ⇓′        = return (Λ t)
    λ' a t ⇓′     = return (λ' a t)
    μ a t ⇓′      = later (♯ (t [/tmTm μ a t ] ⇓′))
    (t [ a ]) ⇓′  = t ⇓′ >>= λ v → v [ a ]′
    (s · t) ⇓′    = s ⇓′ >>= λ f → t ⇓′ >>= λ v → f ·′ v
    fold a t ⇓′   = t ⇓′ >>= λ v → return (fold a v)
    unfold a t ⇓′ = t ⇓′ >>= λ v → unfold′ a v

    -- Call-by-value evaluation of type application in _?⊥P.
    _[_]′ : ∀ {m n} → Val m n → Type n → CompP m n
    (Λ t) [ a ]′ = later (♯ (t [/tmTp a ] ⇓′))
    _     [ _ ]′ = fail

    -- Call-by-value Evaluation of term application in _?⊥P.
    _·′_ : ∀ {m n} → Val m n → Val m n → CompP m n
    (λ' _ t) ·′ v = later (♯ (t [/tmTm ⌜ v ⌝ ] ⇓′))
    _        ·′ _ = fail

    -- Evaluation of recursive type unfolding in _?⊥P.
    unfold′ : ∀ {m n} → Type (1 + n) → Val m n → CompP m n
    unfold′ _ (fold _ v) = return v
    unfold′ a _          = fail

  infix 7 _⇓

  -- Evaluation of untyped (open) terms in the partiality monad.
  _⇓ : ∀ {m n} → Term m n → Comp m n
  t ⇓ = ⟦ t ⇓′ ⟧P

----------------------------------------------------------------------
-- The semantics _⇓ is compositional

module _ where
  open M
  open PF.Reasoning
  open PF.Workaround using (⟦_⟧P) renaming (_>>=_ to _>>=P_)
  open PF.Workaround.Correct

  infix 7 _[_]⇓ _·⇓_

  -- Short hands for relating the semantics of composite terms to the
  -- semantics of their subterms.

  _[_]⇓ : ∀ {m n} → Comp m n → Type n → Comp m n
  c [ a ]⇓ = c >>= λ v → ⟦ v [ a ]′ ⟧P

  _·⇓_ : ∀ {m n} → Comp m n → Comp m n → Comp m n
  c ·⇓ d = c >>= λ f → d >>= λ v → ⟦ f ·′ v ⟧P

  fold⇓ : ∀ {m n} → Type (1 + n) → Comp m n → Comp m n
  fold⇓ a c = c >>= λ v → return (fold a v)

  unfold⇓ : ∀ {m n} → Type (1 + n) → Comp m n → Comp m n
  unfold⇓ a c = c >>= λ v → ⟦ unfold′ a v ⟧P

  -- The semantics of type application is compositional.
  []-comp : ∀ {m n} (t : Term m n) (a : Type n) → t [ a ] ⇓ ≅ (t ⇓) [ a ]⇓
  []-comp t a = >>=-hom (t ⇓′) _

  -- The semantics of term application is compositional.
  ·-comp : ∀ {m n} (s t : Term m n) → s · t ⇓ ≅ (s ⇓) ·⇓ (t ⇓)
  ·-comp s t =
      s · t ⇓
    ≅⟨ >>=-hom (s ⇓′) _ ⟩
      (s ⇓ >>= λ f → ⟦ t ⇓′ >>=P (λ v → f ·′ v) ⟧P)
    ≅⟨ (s ⇓ ∎ >>=-cong λ _ → >>=-hom (t ⇓′) _) ⟩
      (s ⇓) ·⇓ (t ⇓)
    ∎

  -- The semantics of recursive type folding is compositional.
  fold-comp : ∀ {m n} (a : Type (1 + n)) (t : Term m n) →
              fold a t ⇓ ≅ fold⇓ a (t ⇓)
  fold-comp a t = >>=-hom (t ⇓′) _

  -- The semantics of recursive type unfolding is compositional.
  unfold-comp : ∀ {m n} (a : Type (1 + n)) (t : Term m n) →
                unfold a t ⇓ ≅ unfold⇓ a (t ⇓)
  unfold-comp a t = >>=-hom (t ⇓′) _

----------------------------------------------------------------------
-- Type soundness

open PF using (fail)

infix 4 _⊢comp_∈_

-- A computation is well-typed if it is a well-typed value or it takes
-- a step towards a well-typed computation.  Note that we exclude the
-- case of failing well-typed computations through the use of
-- Maybe.Any.
_⊢comp_∈_ : ∀ {m n} → Ctx m n → Comp m n → Type n → Set
Γ ⊢comp c ∈ a = All (MaybeAny.Any (λ v → Γ ⊢val v ∈ a)) c

-- Well-typed computations do not fail.
does-not-fail : ∀ {m n} {Γ : Ctx m n} {c a} → Γ ⊢comp c ∈ a → ¬ c ≈ fail
does-not-fail (now (MaybeAny.just _)) (now ())
does-not-fail (later ⊢c)     (laterˡ c-fails) = does-not-fail (♭ ⊢c) c-fails

-- It remains to prove that well-typed terms evaluate to well-typed
-- computations.  The proof ⊢_⇓ follows the same structure as _⇓ and
-- uses an analogous workaround to deal with guarded coinduction.  It
-- is formulated in the language AllP of of Partiality.All "programs"
-- defined in All.Alternative.
open All.Alternative
open PF.Workaround using (⟦_⟧P)

infix 4 ⊢compP_∈_ ⊢val_∈_

-- Closed well-typed computation "programs".
⊢compP_∈_ : Comp 0 0 → Type 0 → Set₁
⊢compP c ∈ a = AllP (MaybeAny.Any (λ v → [] ⊢val v ∈ a)) c

-- A short hand for closed well-typed values.
⊢val_∈_ : Val 0 0 → Type 0 → Set
⊢val v ∈ a = [] ⊢val v ∈ a

mutual

  infix 7 ⊢_⇓′ ⊢_[_]′ ⊢_·′_

  -- Evaluation of closed terms preserves well-typedness in AllP.
  ⊢_⇓′ : ∀ {t a} → [] ⊢ t ∈ a → ⊢compP t ⇓ ∈ a
  ⊢ var ()      ⇓′
  ⊢ Λ ⊢t        ⇓′   = now (just (Λ ⊢t))
  ⊢ λ' a ⊢t     ⇓′   = now (just (λ' a ⊢t))
  ⊢ μ a ⊢t      ⇓′   = later (♯ ⊢ ⊢t [/⊢tmTm μ a ⊢t ] ⇓′)
  ⊢_⇓′ {t [ a ]} (⊢t [ .a ] ) =
    t [ a ] ⇓        ≅⟨ []-comp t a ⟩P
    (t ⇓) [ a ]⇓      ⟨  ⊢ ⊢t ⇓′ >>=-congP (λ { .{_} (just ⊢v) →
                         ⊢ ⊢v [ a ]′ }) ⟩P
  ⊢_⇓′ {s · t} (⊢s · ⊢t) =
    s · t ⇓          ≅⟨ ·-comp s t ⟩P
    (s ⇓) ·⇓ (t ⇓)    ⟨ (⊢ ⊢s ⇓′ >>=-congP λ { .{_} (just ⊢f) →
                         ⊢ ⊢t ⇓′ >>=-congP λ { .{_} (just ⊢v) →
                         ⊢ ⊢f ·′ ⊢v }}) ⟩P
  ⊢_⇓′ {fold a t} (fold .a ⊢t) =
    fold a t ⇓       ≅⟨ fold-comp a t ⟩P
    fold⇓ a (t ⇓)     ⟨ (⊢ ⊢t ⇓′ >>=-congP λ { .{_} (just ⊢v) →
                         now (just (fold a ⊢v)) }) ⟩P
  ⊢_⇓′ {unfold a t} (unfold .a ⊢t) =
    unfold a t ⇓     ≅⟨ unfold-comp a t ⟩P
    unfold⇓ a (t ⇓)   ⟨ (⊢ ⊢t ⇓′ >>=-congP λ { .{_} (just ⊢v) →
                         ⊢unfold′ a ⊢v }) ⟩P

  -- Evaluation of type application preserves well-typedness in AllP.
  ⊢_[_]′ : ∀ {v a} → ⊢val v ∈ ∀' a → ∀ b → ⊢compP ⟦ v [ b ]′ ⟧P ∈ a [/tp b ]
  ⊢ Λ ⊢t [ a ]′ = later (♯ ⊢ ⊢t [/⊢tmTp a ] ⇓′)

  -- Evaluation of term application preserves well-typedness in AllP.
  ⊢_·′_ : ∀ {f v a b} → ⊢val f ∈ a →' b → ⊢val v ∈ a → ⊢compP ⟦ f ·′ v ⟧P ∈ b
  ⊢ λ' a ⊢t ·′ ⊢v = later (♯ ⊢ ⊢t [/⊢tmTm ⊢⌜ ⊢v ⌝ ] ⇓′)

  -- Evaluation of recursive type unfolding preserves well-typedness in AllP.
  ⊢unfold′ : ∀ {v} a → ⊢val v ∈ μ a → ⊢compP ⟦ unfold′ a v ⟧P ∈ a [/tp μ a ]
  ⊢unfold′ _ (fold ._ ⊢v) = now (just ⊢v)

infix 7 ⊢_⇓

-- Evaluation of closed terms preserves well-typedness.
⊢_⇓ : ∀ {t a} → [] ⊢ t ∈ a → [] ⊢comp t ⇓ ∈ a
⊢_⇓ = sound ∘ ⊢_⇓′

-- Type soundness: evaluation of well-typed terms does not fail.
type-soundness : ∀ {t a} → [] ⊢ t ∈ a → ¬ t ⇓ ≈ fail
type-soundness ⊢t = does-not-fail ⊢ ⊢t ⇓


{-# OPTIONS --without-K --allow-unsolved-metas #-}

module subuniverses where

import 14-univalence
open 14-univalence public

{-
is-local :
  {l1 l2 l3 l4 : Level}
  {I : UU l1} {A : I → UU l2} {B : I → UU l3} (f : (i : I) → A i → B i)
  (X : UU l4) → UU (l1 ⊔ (l2 ⊔ (l3 ⊔ l4)))
is-local {I = I} {B = B} f X =
  (i : I) → is-equiv (λ (h : B i → X) → h ∘ (f i))

is-subuniverse-is-local :
  {l1 l2 l3 l4 : Level}
  {I : UU l1} {A : I → UU l2} {B : I → UU l3} (f : (i : I) → A i → B i) →
  is-subuniverse (is-local {l4 = l4} f)
is-subuniverse-is-local f X = is-prop-Π (λ i → is-subtype-is-equiv _)
-}

universal-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2)
  (X : UU l1) (Y : total-subuniverse P) (l : X → pr1 Y) →
  UU ((lsuc l1) ⊔ l2)
universal-property-localization {l1} (pair P H) X (pair Y p) l =
  (Z : UU l1) (q : P Z) → is-equiv (λ (h : Y → Z) → h ∘ l)

universal-property-localization' :
  (l : Level) (α : Level → Level) (P : (l : Level) → subuniverse l (α l))
  (g : (l1 l2 : Level) → is-global-subuniverse α P l1 l2)
  {l1 l2 : Level} (X : UU l1) (Y : total-subuniverse (P l2)) (f : X → pr1 Y) →
  UU ((lsuc l) ⊔ ((α l) ⊔ (l1 ⊔ l2)))
universal-property-localization' l α P g X Y f =
  (Z : total-subuniverse (P l)) → is-equiv (λ (h : (pr1 Y) → (pr1 Z)) → h ∘ f)

is-prop-universal-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1)
  (Y : total-subuniverse P) (l : X → pr1 Y) →
  is-prop (universal-property-localization P X Y l)
is-prop-universal-property-localization (pair P H) X (pair Y p) l =
  is-prop-Π (λ Z → is-prop-Π (λ q → is-subtype-is-equiv _))

has-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  UU ((lsuc l1) ⊔ l2)
has-localization {l1} P X =
  Σ ( total-subuniverse P)
    ( λ Y → Σ (X → pr1 Y) (universal-property-localization P X Y))

Eq-localizations :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  ( s t : has-localization P X) → UU l1
Eq-localizations (pair P H) X
  (pair (pair Y p) (pair l up)) t =
  let Y' = pr1 (pr1 t)
      p' = pr1 (pr1 t)
      l' = pr1 (pr2 t)
      up' = pr2 (pr2 t)
  in
  Σ ( Y ≃ Y')
    ( λ e → ((map-equiv e) ∘ l) ~ l' )

reflexive-Eq-localizations :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (s : has-localization P X) → Eq-localizations P X s s
reflexive-Eq-localizations (pair P H) X (pair (pair Y p) (pair l up)) =
  pair (equiv-id Y) (htpy-refl l)

Eq-localizations-eq :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  ( s t : has-localization P X) → Id s t → Eq-localizations P X s t
Eq-localizations-eq P X s s refl = reflexive-Eq-localizations P X s

is-contr-total-Eq-localizations :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1)
  (s : has-localization P X) →
  is-contr (Σ (has-localization P X) (Eq-localizations P X s))
is-contr-total-Eq-localizations
  (pair P H) X (pair (pair Y p) (pair l up)) =
  is-contr-total-Eq-structure
    ( λ Y' l' e → ((map-equiv e) ∘ l) ~ (pr1 l'))
    ( is-contr-total-Eq-total-subuniverse (pair P H) (pair Y p))
    ( pair (pair Y p) (equiv-id Y))
    ( is-contr-total-Eq-substructure
      ( is-contr-total-htpy l)
      ( is-prop-universal-property-localization (pair P H) X (pair Y p))
      ( l)
      ( htpy-refl _)
      ( up))

is-equiv-Eq-localizations-eq :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  ( s t : has-localization P X) → is-equiv (Eq-localizations-eq P X s t)
is-equiv-Eq-localizations-eq P X s =
  fundamental-theorem-id s
  ( reflexive-Eq-localizations P X s)
  ( is-contr-total-Eq-localizations P X s)
  ( Eq-localizations-eq P X s)

eq-Eq-localizations :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1)
  ( s t : has-localization P X) → (Eq-localizations P X s t) → Id s t
eq-Eq-localizations P X s t =
  inv-is-equiv (is-equiv-Eq-localizations-eq P X s t)

uniqueness-localizations :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  ( s t : has-localization P X) → Eq-localizations P X s t
uniqueness-localizations (pair P H) X
  (pair (pair Y p) (pair l up)) (pair (pair Y' p') (pair l' up')) =
  pair
    ( pair
      ( inv-is-equiv (up Y' p') l')
      ( is-equiv-has-inverse
        ( pair
          ( inv-is-equiv (up' Y p) l)
          ( pair
            ( htpy-eq
              ( ap
                ( pr1 {B = λ h → Id (h ∘ l') l'})
                ( center
                  ( is-prop-is-contr
                    ( is-contr-map-is-equiv (up' Y' p') l')
                    ( pair
                      ( ( inv-is-equiv (up Y' p') l') ∘
                        ( inv-is-equiv (up' Y p) l))
                      ( ( ap
                          ( λ t → (inv-is-equiv (up Y' p') l') ∘ t)
                          ( issec-inv-is-equiv (up' Y p) l)) ∙
                        ( issec-inv-is-equiv (up Y' p') l')))
                    ( pair id refl)))))
            ( htpy-eq
              ( ap
                ( pr1 {B = λ h → Id (h ∘ l) l})
                ( center
                  ( is-prop-is-contr
                    ( is-contr-map-is-equiv (up Y p) l)
                    ( pair
                      ( ( inv-is-equiv (up' Y p) l) ∘
                        ( inv-is-equiv (up Y' p') l'))
                      ( ( ap
                          ( λ t → (inv-is-equiv (up' Y p) l) ∘ t)
                          ( issec-inv-is-equiv (up Y' p') l')) ∙
                         issec-inv-is-equiv (up' Y p) l))
                    ( pair id refl)))))))))
    ( htpy-eq (issec-inv-is-equiv (up Y' p') l'))

is-prop-localizations :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  is-prop (has-localization P X)
is-prop-localizations P X =
  is-prop-is-prop'
    ( λ Y Y' → eq-Eq-localizations P X Y Y'
      ( uniqueness-localizations P X Y Y'))

universal-property-localization-equiv-is-local :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : UU l1) (p : (pr1 P) Y) (l : X → Y) →
  is-equiv l → universal-property-localization P X (pair Y p) l
universal-property-localization-equiv-is-local
  (pair P H) X Y p l is-equiv-l Z q =
  is-equiv-precomp-is-equiv l is-equiv-l Z

universal-property-localization-id-is-local :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) (q : (pr1 P) X) →
  universal-property-localization P X (pair X q) id
universal-property-localization-id-is-local P X q =
  universal-property-localization-equiv-is-local P X X q id (is-equiv-id X)

is-equiv-localization-is-local :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  ( Y : has-localization P X) → (pr1 P) X → is-equiv (pr1 (pr2 Y))
is-equiv-localization-is-local
  (pair P H) X (pair (pair Y p) (pair l up)) q =
  is-equiv-right-factor
    ( id)
    ( inv-is-equiv (up X q) id)
    ( l)
    ( htpy-eq (inv (issec-inv-is-equiv (up X q) id)))
    ( pr2
      ( pr1
        ( uniqueness-localizations (pair P H) X
          ( pair (pair Y p) (pair l up))
          ( pair
            ( pair X q)
            ( pair id
              ( universal-property-localization-id-is-local
                (pair P H) X q))))))
    ( is-equiv-id X)

is-local-is-equiv-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  ( Y : has-localization P X) → is-equiv (pr1 (pr2 Y)) → (pr1 P) X
is-local-is-equiv-localization
  (pair P H) X (pair (pair Y p) (pair l up)) is-equiv-l =
  in-subuniverse-equiv' P (pair l is-equiv-l) p

strong-retraction-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : total-subuniverse P) (l : X → pr1 Y) → UU l1
strong-retraction-property-localization (pair P H) X (pair Y p) l =
  is-equiv (λ (h : Y → X) → h ∘ l)

retraction-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : total-subuniverse P) (l : X → pr1 Y) → UU l1
retraction-property-localization (pair P H) X (pair Y p) l =
  retr l

strong-retraction-property-localization-is-equiv-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : total-subuniverse P) (l : X → pr1 Y) →
  is-equiv l → strong-retraction-property-localization P X Y l
strong-retraction-property-localization-is-equiv-localization
  (pair P H) X (pair Y p) l is-equiv-l =
  is-equiv-precomp-is-equiv l is-equiv-l X

retraction-property-localization-strong-retraction-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : total-subuniverse P) (l : X → pr1 Y) →
  strong-retraction-property-localization P X Y l →
  retraction-property-localization P X Y l
retraction-property-localization-strong-retraction-property-localization
  (pair P H) X (pair Y p) l s =
  tot (λ h → htpy-eq) (center (is-contr-map-is-equiv s id))

is-equiv-localization-retraction-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : has-localization P X) →
  retraction-property-localization P X (pr1 Y) (pr1 (pr2 Y)) →
  is-equiv (pr1 (pr2 Y))
is-equiv-localization-retraction-property-localization
  (pair P H) X (pair (pair Y p) (pair l up)) (pair g isretr-g) =
  is-equiv-has-inverse
    ( pair g
      ( pair
        ( htpy-eq
          ( ap
            ( pr1 {B = λ (h : Y → Y) → Id (h ∘ l) l})
            ( center
              ( is-prop-is-contr
                ( is-contr-map-is-equiv (up Y p) l)
                ( pair (l ∘ g) (ap (λ t → l ∘ t) (eq-htpy isretr-g)))
                ( pair id refl)))))
        ( isretr-g)))

is-local-retraction-property-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (Y : has-localization P X) →
  retraction-property-localization P X (pr1 Y) (pr1 (pr2 Y)) →
  (pr1 P) X
is-local-retraction-property-localization P X Y r =
  is-local-is-equiv-localization P X Y
    ( is-equiv-localization-retraction-property-localization P X Y r)

is-local-has-localization-is-contr :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  is-contr X → has-localization P X → (pr1 P) X
is-local-has-localization-is-contr
  (pair P H) X is-contr-X (pair (pair Y p) (pair l up)) =
  is-local-retraction-property-localization (pair P H) X
    ( pair (pair Y p) (pair l up))
    ( pair
      ( λ _ → center is-contr-X)
      ( contraction is-contr-X))

has-localization-is-local-is-contr :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  is-contr X → (pr1 P) X → has-localization P X
has-localization-is-local-is-contr (pair P H) X is-contr-X p =
  pair
    ( pair X p)
    ( pair id (universal-property-localization-id-is-local (pair P H) X p))

is-contr-raise-unit :
  (l : Level) → is-contr (raise l unit)
is-contr-raise-unit l =
  is-contr-is-equiv' unit
    ( map-raise l unit)
    ( is-equiv-map-raise l unit)
    ( is-contr-unit)

is-local-unit-localization-unit :
  {l1 l2 : Level} (P : subuniverse l1 l2) →
  (Y : has-localization P (raise l1 unit)) →
  (pr1 P) (raise l1 unit)
is-local-unit-localization-unit P Y =
  is-local-has-localization-is-contr P (raise _ unit) (is-contr-raise-unit _) Y

toto-dependent-elimination-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (has-loc-X : has-localization P X) →
  let Y = pr1 (pr1 has-loc-X)
      l = pr1 (pr2 has-loc-X)
  in
  (Z : Y → UU l1) →
  Σ (Y → Y) (λ h → (y : Y) → Z (h y)) →
  Σ (X → Y) (λ h → (x : X) → Z (h x))
toto-dependent-elimination-localization (pair P H) X
  (pair (pair Y p) (pair l up)) Z =
  toto
    ( λ (h : X → Y) → (x : X) → Z (h x))
    ( λ h → h ∘ l)
    ( λ h h' x → h' (l x))

square-dependent-elimination-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (has-loc-X : has-localization P X) →
  let Y = pr1 (pr1 has-loc-X)
      l = pr1 (pr2 has-loc-X)
  in
  (Z : Y → UU l1) (q : (pr1 P) (Σ _ Z)) →
  ( ( λ (h : Y → Σ Y Z) → h ∘ l) ∘
    ( inv-choice-∞)) ~
  ( ( inv-choice-∞) ∘
    ( toto-dependent-elimination-localization P X has-loc-X Z))
square-dependent-elimination-localization
  (pair P H) X (pair (pair Y p) (pair l up)) Z q =
  htpy-refl

is-equiv-toto-dependent-elimination-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) (X : UU l1) →
  (has-loc-X : has-localization P X)
  (Z : pr1 (pr1 has-loc-X) → UU l1) (q : (pr1 P) (Σ _ Z)) →
  is-equiv (toto-dependent-elimination-localization P X has-loc-X Z)
is-equiv-toto-dependent-elimination-localization
  (pair P H) X (pair (pair Y p) (pair l up)) Z q =
  is-equiv-top-is-equiv-bottom-square
    ( inv-choice-∞)
    ( inv-choice-∞)
    ( toto-dependent-elimination-localization
      (pair P H) X (pair (pair Y p) (pair l up)) Z)
    ( λ h → h ∘ l)
    ( square-dependent-elimination-localization
      (pair P H) X (pair (pair Y p) (pair l up)) Z q)
    ( is-equiv-inv-choice-∞)
    ( is-equiv-inv-choice-∞)
    ( up (Σ Y Z) q)

dependent-elimination-localization :
  {l1 l2 : Level} (P : subuniverse l1 l2) →
  (X : UU l1) (Y : has-localization P X) →
  (Z : (pr1 (pr1 Y)) → UU l1) (q : (pr1 P) (Σ _ Z)) →
  is-equiv (λ (h : (y : (pr1 (pr1 Y))) → (Z y)) → λ x → h (pr1 (pr2 Y) x))
dependent-elimination-localization (pair P H) X (pair (pair Y p) (pair l up)) Z q =
  is-fiberwise-equiv-is-equiv-toto-is-equiv-base-map
    ( λ (h : X → Y) → (x : X) → Z (h x))
    ( λ (h : Y → Y) → h ∘ l)
    ( λ (h : Y → Y) (h' : (y : Y) → Z (h y)) (x : X) → h' (l x))
    ( up Y p)
    ( is-equiv-toto-dependent-elimination-localization
      (pair P H) X (pair (pair Y p) (pair l up)) Z q)
    ( id)

is-reflective-subuniverse :
  {l1 l2 : Level} (P : subuniverse l1 l2) → UU ((lsuc l1) ⊔ l2)
is-reflective-subuniverse {l1} P = (X : UU l1) → has-localization P X

reflective-subuniverse :
  (l1 l2 : Level) → UU ((lsuc l1) ⊔ (lsuc l2))
reflective-subuniverse l1 l2 = Σ (subuniverse l1 l2) is-reflective-subuniverse

is-local :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) →
  UU l1 → UU l2
is-local L = pr1 (pr1 L)

is-prop-is-local :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) →
  (X : UU l1) → is-prop (is-local L X)
is-prop-is-local L = pr2 (pr1 L)

total-reflective-subuniverse :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) → UU ((lsuc l1) ⊔ l2)
total-reflective-subuniverse L = total-subuniverse (pr1 L)

local-type-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2)
  (X : UU l1) → total-reflective-subuniverse L
local-type-localization L X = pr1 ((pr2 L) X)

type-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) →
  UU l1 → UU l1
type-localization L X = pr1 (local-type-localization L X)

is-local-type-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2)
  (X : UU l1) → is-local L (type-localization L X)
is-local-type-localization L X = pr2 (local-type-localization L X)

universal-map-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) (X : UU l1) →
  Σ ( X → type-localization L X)
    ( universal-property-localization (pr1 L) X (local-type-localization L X))
universal-map-localization L X = pr2 ((pr2 L) X)

unit-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2)
  (X : UU l1) → X → type-localization L X
unit-localization L X = pr1 (universal-map-localization L X)

universal-property-map-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) (X : UU l1) →
  universal-property-localization (pr1 L) X
    ( local-type-localization L X)
    ( unit-localization L X)
universal-property-map-localization L X = pr2 (universal-map-localization L X)

dependent-elimination-reflective-subuniverse :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) (X : UU l1) →
  (Y : type-localization L X → UU l1)
  (is-loc-total-Y : is-local L (Σ _ Y)) →
  is-equiv
    ( λ (h : (x' : type-localization L X) → Y x') x → h (unit-localization L X x))
dependent-elimination-reflective-subuniverse L X =
  dependent-elimination-localization (pr1 L) X ((pr2 L) X)

is-contr-square-localization :
  {l1 l2 : Level} (L : reflective-subuniverse l1 l2) {X Y : UU l1} (f : X → Y) →
  is-contr
    ( Σ (type-localization L X → type-localization L Y)
      ( λ Lf → coherence-square (unit-localization L X) f Lf (unit-localization L Y)))
is-contr-square-localization L f = {!!}


------------------------------------------------------------------------
-- Abstract typing contexts
------------------------------------------------------------------------

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

module Data.Context where

open import Data.Fin using (Fin)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Vec as Vec using (Vec; []; _∷_)
open import Relation.Unary using (Pred)

------------------------------------------------------------------------
-- Abstract typing contexts and context extensions.

infixr 5 _∷_

-- Typing contexts.
--
-- A |Ctx T n| is an indexed sequences of T-typed bindings mapping n
-- variables to T-types with 0 to (n - 1) free variables each.  Like
-- lists (Data.List) and vectors (Data.Vec), contexts are cons
-- sequences, i.e. new bindings are added to the front (rather than
-- the back, as is more common in the literature).  For example, a
-- typing context Γ represented in the usual notation as
--
--   Γ  =  xᵢ: Aᵢ, ..., x₁: A₁, x₀: A₀
--
-- is represented here by a term |Γ : Ctx Type i| of the form
--
--   Γ  =  A₀ ∷ A₁ ∷ ... ∷ Aᵢ
--
-- which is consistent with A₀ being the 0-th element of Γ, and with
-- the de Bruijn convention that the 0-th variable corresponds to the
-- closest binding.

data Ctx {ℓ} (T : Pred ℕ ℓ) : ℕ → Set ℓ where
  []  :                         Ctx T zero
  _∷_ : ∀ {n} → T n → Ctx T n → Ctx T (suc n)

module _ {ℓ} {T : Pred ℕ ℓ} where

  head : ∀ {n} → Ctx T (suc n) → T n
  head (t ∷ ts) = t

  tail : ∀ {n} → Ctx T (suc n) → Ctx T n
  tail (t ∷ ts) = ts

  -- Drop the m innermost elements of a context Γ.

  drop : ∀ {n} m → Ctx T (m + n) → Ctx T n
  drop zero         Γ  = Γ
  drop (suc m) (_ ∷ Γ) = drop m Γ

-- A map function that changes the entries in a context pointwise.

map : ∀ {ℓ₁ ℓ₂} {T₁ : Pred ℕ ℓ₁} {T₂ : Pred ℕ ℓ₂} {n} →
      (∀ {k} → T₁ k → T₂ k) → Ctx T₁ n → Ctx T₂ n
map f []      = []
map f (t ∷ Γ) = f t ∷ map f Γ

-- Extensions of typing contexts.
--
-- Context extensions are indexed sequences of bindings that can be
-- concatenated to the front of a typing context.  A |CtxExt T m n| is
-- an extension mapping n variables to T-types with m to (n + m - 1)
-- free variables each.
--
-- NOTE. It is tempting to define contexts as just a special case of
-- context extensions, i.e. as
--
--   Ctx T n  =  CtxExt T zero n
--
-- But this leads to problems when defining e.g. concatenation because
-- of the way context extensions are indexed.  This could be remedied
-- by indexing context extensions differently, but then the definition
-- of |mapExt| below becomes difficult.  An earlier version of this
-- module contained two different (but equivalent) representations for
-- context extensions, but this complicated (rather than simplified)
-- the development overall.

data CtxExt {ℓ} (T : Pred ℕ ℓ) (m : ℕ) : ℕ → Set ℓ where
  []  :                                    CtxExt T m zero
  _∷_ : ∀ {l} → T (l + m) → CtxExt T m l → CtxExt T m (suc l)

infixr 5 _++_

-- Concatenation of context extensions with contexts.

_++_ : ∀ {ℓ} {T : Pred ℕ ℓ} {m n} → CtxExt T m n → Ctx T m → Ctx T (n + m)
[]      ++ Γ = Γ
(t ∷ Δ) ++ Γ = t ∷ (Δ ++ Γ)

-- A map function that point-wise re-indexes and changes the entries
-- in a context extension.

mapExt : ∀ {ℓ₁ ℓ₂} {T₁ : Pred ℕ ℓ₁} {T₂ : Pred ℕ ℓ₂} {m n k} →
         (∀ l → T₁ (l + m) → T₂ (l + n)) → CtxExt T₁ m k → CtxExt T₂ n k
mapExt f []            = []
mapExt f (_∷_ {l} t Γ) = f l t ∷ mapExt (λ l → f l) Γ

-- Operations on contexts that require weakening of types.

module WeakenOps {ℓ} {T : Pred ℕ ℓ} (extension : Extension T) where

  -- Weakening of types.

  open Extension extension public

  -- Convert a context or context extension to its vector representation.

  toVec : ∀ {n} → Ctx T n → Vec (T n) n
  toVec []      = []
  toVec (t ∷ Γ) = weaken t /∷ toVec Γ

  extToVec : ∀ {k m n} → CtxExt T m n → Vec (T m) k → Vec (T (n + m)) (n + k)
  extToVec []      ts = ts
  extToVec (t ∷ Γ) ts = weaken t /∷ extToVec Γ ts

  -- Lookup the type of a variable in a context or context extension.

  lookup : ∀ {n} → Ctx T n → Fin n → T n
  lookup Γ x = Vec.lookup (toVec Γ) x

  extLookup : ∀ {k m n} → CtxExt T m n → Vec (T m) k → Fin (n + k) → T (n + m)
  extLookup Δ ts x = Vec.lookup (extToVec Δ ts) x

-- Operations on contexts that require substitutions in types.

module SubstOps {ℓ₁ ℓ₂} {T₁ : Pred ℕ ℓ₁} {T₂ : Pred ℕ ℓ₂}
                (application : Application T₁ T₂)
                (simple      : Simple T₂)
                where

  open Application application public -- Application of T′ substitutions to Ts.
  open Simple      simple      public -- Simple T′ substitutions.

  -- Application of substitutions to context extensions.

  _E/_ : ∀ {k m n} → CtxExt T₁ m k → Sub T₂ m n → CtxExt T₁ n k
  Γ E/ σ = mapExt (λ l t → t / σ ↑⋆ l) Γ


{-
Please do not move this file. Changes should only be made if necessary.

This file contains pointers to the code examples and main results from the paper:

Internalizing Representation Independence with Univalence

Carlo Angiuli, Evan Cavallo, Anders Mörtberg, Max Zeuner

Preprint: https://arxiv.org/abs/2009.05547

-}
{-# OPTIONS --safe #-}
module Cubical.Papers.RepresentationIndependence where

-- 2.1
import Agda.Builtin.Cubical.Path               as Path
import Cubical.Foundations.Prelude             as Prelude
-- 2.2
import Cubical.Foundations.Univalence          as Univalence
import Cubical.Foundations.HLevels             as HLevels
import Cubical.Foundations.Equiv               as Equivalences
import Cubical.Data.Sigma.Properties           as Sigma
-- 2.3
import Cubical.HITs.PropositionalTruncation    as PropositionalTruncation
import Cubical.HITs.Cost.Base                  as CostMonad
import Cubical.HITs.SetQuotients               as SetQuotients
import Cubical.HITs.Rationals.QuoQ             as SetQuoQ
import Cubical.HITs.Rationals.SigmaQ           as SigmaQ
-- 3.1
import Cubical.Foundations.SIP                 as SIP
import Cubical.Structures.Axioms               as Axioms
import Cubical.Algebra.Semigroup.Base          as Semigroup
open import Cubical.Data.Sigma.Base
-- 3.2
import Cubical.Structures.Pointed              as PointedStr
import Cubical.Structures.Constant             as ConstantStr
import Cubical.Structures.Product              as ProductStr
import Cubical.Structures.Function             as FunctionStr
import Cubical.Structures.Maybe                as MaybeStr
import Cubical.Foundations.Structure           as Structure
import Cubical.Structures.Auto                 as Auto
open import Cubical.Data.Maybe.Base
-- 4.1
import Cubical.Data.Vec.Base                   as Vector
import Cubical.Algebra.Matrix                  as Matrices
import Cubical.Data.FinData.Base               as Finite
open import Cubical.Algebra.AbGroup.Base
open import Cubical.Data.Bool.Base
-- 4.2
import Cubical.Structures.Queue                as Queues
import Cubical.Data.Queue.Truncated2List       as BatchedQueues
-- 5.1
import Cubical.Relation.Binary.Base            as BinRel
import Cubical.Relation.ZigZag.Base            as QER
import Cubical.Relation.ZigZag.Applications.MultiSet
                                               as MultiSets
-- 5.2
import Cubical.Foundations.RelationalStructure as RelStructure
import Cubical.Structures.Relational.Function  as RelFunction

-------------------------------------------------------------------------
-- 2. Programming in Cubical Type Theory
-- 2.1 Equalities as Paths
-- 2.2 Univalence
-- 2.3 Higher Inductive Types
-------------------------------------------------------------------------

-- 2.1 Equalities as Paths
open Path using (PathP) public
open Prelude using (_≡_ ; refl ; cong ; funExt
                        ; transport ; subst ; J) public

-- 2.2 Univalence
open Univalence using (ua ; uaβ) public

-- Sets and Propositions
open Prelude using (isProp ; isSet) public
open HLevels using (isPropΠ) public
open Prelude using (isContr) public

-- Equivalences and Isomorphisms
open Equivalences using (isEquiv ; _≃_) public
open Equivalences renaming (fiber to preim) public
open Sigma using (ΣPath≃PathΣ) public
open Equivalences renaming (propBiimpl→Equiv to prop≃) public

-- 2.3 Higher Inductive Types
-- Propositional Truncation
open PropositionalTruncation using (∥_∥ ; map) public
open CostMonad using (Cost ; Cost≡ ; _>>=_ ; return
                           ; fib ; fibTail) public
-- Computation
_ : fib 20 ≡ (6765 , PropositionalTruncation.∣ 21890 ∣)
_ = refl

_ : fibTail 20 ≡ (6765 , PropositionalTruncation.∣ 19 ∣)
_ = refl

-- Set Quotients
open SetQuotients using (_/_ ; setQuotUniversal) public
-- Rational Numbers
open SetQuoQ using (_∼_ ; ℚ) public
open SigmaQ renaming (ℚ to ℚ') public

-------------------------------------------------------------------------
-- 3. The Structure Identity Principle
-- 3.1 Structures
-- 3.2 Building Strucutres
-------------------------------------------------------------------------

-- 3.1 Structures
open SIP using (TypeWithStr ; StrEquiv ; _≃[_]_
                            ; UnivalentStr ; SIP ; sip) public

-- the last two terms above correspond to lemma 3.3
-- and corollary 3.4 respectively
open Axioms using ( AxiomsStructure ; AxiomsEquivStr
                  ; axiomsUnivalentStr ; transferAxioms) public

-- Monoids are defined using records and Σ-types in the library

RawMonoidStructure : Type → Type
RawMonoidStructure X = X × (X → X → X)

MonoidAxioms : (M : Type) → RawMonoidStructure M → Type
MonoidAxioms M (e , _·_) = Semigroup.IsSemigroup _·_
                         × ((x : M) → (x · e ≡ x) × (e · x ≡ x))

MonoidStructure : Type → Type
MonoidStructure = AxiomsStructure RawMonoidStructure MonoidAxioms

Monoid : Type₁
Monoid = TypeWithStr ℓ-zero MonoidStructure

MonoidEquiv : (M N : Monoid) → fst M ≃ fst N → Type
MonoidEquiv (_ , (εᴹ , _·_) , _) (_ , (εᴺ , _∗_) , _) (φ , _) =
  (φ εᴹ ≡ εᴺ) × (∀ x y → φ (x · y) ≡ (φ x) ∗ (φ y))

-- 3.2 Building Structures
-- Constant and Pointed Structures
open PointedStr using (PointedStructure ; PointedEquivStr
                                        ; pointedUnivalentStr) public
open ConstantStr using (ConstantStructure ; ConstantEquivStr
                                          ; constantUnivalentStr) public

-- Product Structures
open ProductStr using (ProductStructure ; ProductEquivStr
                                        ; productUnivalentStr) public

-- Function Structures
open FunctionStr using (FunctionEquivStr) public

-- Maybe Structures
open MaybeStr using (MaybeEquivStr) public

-- Transport Structures
open Structure using (EquivAction) public
open SIP using (TransportStr ; TransportStr→UnivalentStr
                             ; UnivalentStr→TransportStr) public
open Structure using (EquivAction→StrEquiv) public
open FunctionStr using (FunctionEquivStr+) public

-- Monoids Revisited

RawMonoid : Type₁
RawMonoid = TypeWithStr _ RawMonoidStructure

Monoid→RawMonoid : Monoid → RawMonoid
Monoid→RawMonoid (A , r , _) = (A , r)

RawMonoidEquivStr = Auto.AutoEquivStr RawMonoidStructure

rawMonoidUnivalentStr : UnivalentStr _ RawMonoidEquivStr
rawMonoidUnivalentStr = Auto.autoUnivalentStr RawMonoidStructure

isPropMonoidAxioms : (M : Type) (s : RawMonoidStructure M) → isProp (MonoidAxioms M s)
isPropMonoidAxioms M (e , _·_) =
  HLevels.isPropΣ
    (Semigroup.isPropIsSemigroup _·_)
    (λ α → isPropΠ λ _ →
      HLevels.isProp×
        (Semigroup.IsSemigroup.is-set α _ _)
        (Semigroup.IsSemigroup.is-set α _ _))

MonoidEquivStr : StrEquiv MonoidStructure ℓ-zero
MonoidEquivStr = AxiomsEquivStr RawMonoidEquivStr MonoidAxioms

monoidUnivalentStr : UnivalentStr MonoidStructure MonoidEquivStr
monoidUnivalentStr = axiomsUnivalentStr _ isPropMonoidAxioms rawMonoidUnivalentStr

MonoidΣPath : (M N : Monoid) → (M ≃[ MonoidEquivStr ] N) ≃ (M ≡ N)
MonoidΣPath = SIP monoidUnivalentStr

InducedMonoid : (M : Monoid) (N : RawMonoid) (e : M .fst ≃ N .fst)
                → RawMonoidEquivStr (Monoid→RawMonoid M) N e → Monoid
InducedMonoid M N e r =
  Axioms.inducedStructure rawMonoidUnivalentStr M N (e , r)

InducedMonoidPath : (M : Monoid) (N : RawMonoid) (e : M .fst ≃ N .fst)
                    (E : RawMonoidEquivStr (Monoid→RawMonoid M) N e)
                    → M ≡ InducedMonoid M N e E
InducedMonoidPath M N e E =
  MonoidΣPath M (InducedMonoid M N e E) .fst (e , E)

-- Automation
open Auto using (Transp[_] ; AutoEquivStr ; autoUnivalentStr) public
module _ (A : Type) (Aset : isSet A) where
 RawQueueEquivStr =
   AutoEquivStr (λ (X : Type) → X × (A → X → X) × (X → Transp[ Maybe (X × A) ]))

-------------------------------------------------------------------------
-- 4. Representation Independence through the SIP
-- 4.1 Matrices
-- 4.2 Queues
-------------------------------------------------------------------------

-- 4.1 Matrices
open Vector using (Vec) public
open Finite using (Fin ; _==_) public
open Matrices using (VecMatrix ; FinMatrix ; FinMatrix≡VecMatrix
                                           ; FinMatrix≃VecMatrix) public
open Matrices.FinMatrixAbGroup using (addFinMatrix ; addFinMatrixComm) public

-- example (not in the library)
open import Cubical.Data.Int renaming (Int to ℤ ; isSetInt to isSetℤ) hiding (-_)

ℤ-AbGroup : AbGroup ℓ-zero
ℤ-AbGroup = makeAbGroup {G = ℤ} 0 _+_ -_ isSetℤ +-assoc (λ x _ → x) rem +-comm
    where
    -_ : ℤ → ℤ
    - x = 0 - x
    rem : (x : ℤ) → x + (- x) ≡ 0
    rem x =  +-comm x (pos 0 - x) Prelude.∙ minusPlus x 0

module experiment where
  open Prelude

  M : FinMatrix ℤ 2 2
  M i j = if i == j then 1 else 0

  N : FinMatrix ℤ 2 2
  N i j = if i == j then 0 else 1

  replaceGoal : {A B : Type} {x y : A} → (e : A ≃ B)
                (h : invEq e (equivFun e x) ≡ invEq e (equivFun e y)) → x ≡ y
  replaceGoal e h = sym (retEq e _) ∙∙ h ∙∙ retEq e _

  _ : addFinMatrix ℤ-AbGroup M N ≡ (λ _ _ → 1)
  _ = replaceGoal (FinMatrix≃VecMatrix) refl

-- 4.2 Queues
open Queues.Queues-on using (RawQueueStructure ; QueueAxioms) public
open BatchedQueues.Truncated2List renaming (Q to BatchedQueueHIT)
                                  using (Raw-1≡2 ; WithLaws) public

-------------------------------------------------------------------------
-- 5. Structured Equivalences from Structured Relations
-- 5.1 Quasi-Equivalence Relations
-- 5.2 Structured Relations
-------------------------------------------------------------------------

-- 5.1 Quasi-Equivalence Relations
--Lemma (5.1)
open BinRel using (idPropRel ; invPropRel
                             ; compPropRel ; graphRel) public
-- Definitions (5.2) and (5.3)
open QER using (isZigZagComplete ; isQuasiEquivRel) public
-- Lemma (5.4)
open QER.QER→Equiv using (Thm ; bwd≡ToRel) public
-- Multisets
open MultiSets renaming (AList to AssocList) public
open MultiSets.Lists&ALists using (addIfEq ; R ; φ ; ψ
                                           ; List/Rᴸ≃AList/Rᴬᴸ) public
open MultiSets.Lists&ALists.L using (insert ; union ; count)
open MultiSets.Lists&ALists.AL using (insert ; union ; count)

-- 5.2 Structured Relations
open RelStructure using (StrRel) public
-- Definition (5.6)
open RelStructure using (SuitableStrRel) public
-- Theorem (5.7)
open RelStructure using (structuredQER→structuredEquiv) public
-- Definition (5.9)
open RelStructure using (StrRelAction) public
-- Lemma (5.10)
open RelStructure using (strRelQuotientComparison) public
-- Definition (5.11)
open RelStructure using (PositiveStrRel) public
-- Theorem (5.12)
open RelFunction using (functionSuitableRel) public
-- Multisets
-- (main is applying 5.7 to the example)
open MultiSets.Lists&ALists using (multisetShape ; isStructuredR ; main ; List/Rᴸ≡AList/Rᴬᴸ)
                            renaming ( hasAssociativeUnion to unionAssocAxiom
                                     ; LQassoc to LUnionAssoc
                                     ; ALQassoc to ALUnionAssoc) public


{-# OPTIONS --safe #-}

module Definition.Typed where

open import Definition.Untyped

open import Tools.Nat using (Nat)
open import Tools.Product
import Tools.PropositionalEquality as PE

infixl 30 _∙_
infix 30 Πⱼ_▹_▹_▹_

-- Well-typed variables
data _∷_^_∈_ : (x : Nat) (A : Term) (r : TypeInfo) (Γ : Con Term) → Set where
  here  : ∀ {Γ A r}                     →         0 ∷ wk1 A ^ r ∈ (Γ ∙ A ^ r )
  there : ∀ {Γ A rA B rB x} (h : x ∷ A ^ rA ∈ Γ) → Nat.suc x ∷ wk1 A ^ rA ∈ (Γ ∙ B ^ rB)

mutual
  -- Well-formed context
  data ⊢_ : Con Term → Set where
    ε   : ⊢ ε
    _∙_ : ∀ {Γ A r}
        → ⊢ Γ
        → Γ ⊢ A ^ r
        → ⊢ Γ ∙ A ^ r

  -- Well-formed type
  data _⊢_^_ (Γ : Con Term) : Term → TypeInfo → Set where
    Uⱼ    : ∀ {r} → ⊢ Γ → Γ ⊢ Univ r ¹ ^ [ ! , ∞ ]
    univ : ∀ {A r l}
         → Γ ⊢ A ∷ Univ r l ^ [ ! , next l ]
         → Γ ⊢ A ^ [ r , ι l ]

  -- Well-formed term of a type
  data _⊢_∷_^_ (Γ : Con Term) : Term → Term → TypeInfo → Set where
    univ : ∀ {r l l'}
         → l < l'
         → ⊢ Γ
         → Γ ⊢ (Univ r l) ∷ (Univ ! l') ^ [ ! , next l' ]
    ℕⱼ      : ⊢ Γ → Γ ⊢ ℕ ∷ U ⁰ ^ [ ! , ι ¹ ]
    Emptyⱼ : ∀ {l} → ⊢ Γ → Γ ⊢ Empty l ∷ SProp l ^ [ ! , next l ]
    Πⱼ_▹_▹_▹_ : ∀ {F rF lF G lG r l}
           → lF ≤ l
           → lG ≤ l
           → Γ     ⊢ F ∷ (Univ rF lF) ^ [ ! , next lF ]
           → Γ ∙ F ^ [ rF , ι lF ] ⊢ G ∷ (Univ r lG) ^ [ ! , next lG ]
           → Γ     ⊢ Π F ^ rF ° lF ▹ G ° lG ° l ∷ (Univ r l) ^ [ ! , next l ]
    ∃ⱼ_▹_ : ∀ {F G l}
            → Γ ⊢ F ∷ SProp l ^ [ ! , next l ]
            → Γ ∙ F ^ [ % , ι l ] ⊢ G ∷ SProp l ^ [ ! , next l ]
            → Γ ⊢ ∃ F ▹ G ∷ SProp l ^ [ ! , next l ]
    var    : ∀ {A rl x}
           → ⊢ Γ
           → x ∷ A ^ rl ∈ Γ
           → Γ ⊢ var x ∷ A ^ rl
    lamⱼ    : ∀ {F r l rF lF G lG t}
           → lF ≤ l
           → lG ≤ l
           → Γ     ⊢ F ^ [ rF , ι lF ]
           → Γ ∙ F ^ [ rF , ι lF ] ⊢ t ∷ G ^ [ r , ι lG ]
           → Γ     ⊢ lam F ▹ t ^ l ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ r , ι l ]
    _∘ⱼ_    : ∀ {g a F rF lF G lG r lΠ}
           → Γ ⊢     g ∷ Π F ^ rF ° lF ▹ G ° lG ° lΠ ^ [ r , ι lΠ ]
           → Γ ⊢     a ∷ F ^ [ rF , ι lF ]
           → Γ ⊢ g ∘ a ^ lΠ ∷ G [ a ] ^ [ r , ι lG ]
    ⦅_,_,_,_⦆ⱼ : ∀ {l F G t u}
             → Γ ⊢ F ^ [ % , ι l ]
             → Γ ∙ F ^ [ % , ι l ] ⊢ G ^ [ % , ι l ]
             → Γ ⊢ t ∷ F ^ [ % , ι l ]
             → Γ ⊢ u ∷ G [ t ] ^ [ % , ι l ]
             → Γ ⊢ ⦅ G , t , u ⦆ ∷ ∃ F ▹ G ^ [ % , ι l ]
    fstⱼ : ∀ {F G t l}
           → Γ ⊢ F ∷ SProp l ^ [ ! , next l ]
           → Γ ∙ F ^ [ % , ι l ] ⊢ G ∷ SProp l ^ [ ! , next l ]
           → Γ ⊢ t ∷ ∃ F ▹ G ^ [ % , ι l ]
           → Γ ⊢ fst t ∷ F ^ [ % , ι l ]
    sndⱼ : ∀ {F G t l}
           → Γ ⊢ F ∷ SProp l ^ [ ! , next l ]
           → Γ ∙ F ^ [ % , ι l ] ⊢ G ∷ SProp l ^ [ ! , next l ]
           → Γ ⊢ t ∷ ∃ F ▹ G ^ [ % , ι l ]
           → Γ ⊢ snd t ∷ G [ fst t ] ^ [ % , ι l ]
    zeroⱼ   : ⊢ Γ
           → Γ ⊢ zero ∷ ℕ ^ [ ! ,  ι ⁰ ]
    sucⱼ    : ∀ {n}
           → Γ ⊢ n ∷ ℕ ^ [ ! ,  ι ⁰ ]
           → Γ ⊢ suc n ∷ ℕ ^ [ ! ,  ι ⁰ ]
    natrecⱼ : ∀ {G rG lG s z n}
           → Γ ∙ ℕ ^ [ ! ,  ι ⁰ ] ⊢ G ^ [ rG , ι lG ]
           → Γ       ⊢ z ∷ G [ zero ] ^ [ rG , ι lG ]
           → Γ       ⊢ s ∷ Π ℕ ^ ! ° ⁰ ▹ (G ^ rG ° lG ▹▹ G [ suc (var Nat.zero) ]↑ ° lG ° lG) ° lG ° lG ^ [ rG , ι lG ]
           → Γ       ⊢ n ∷ ℕ ^ [ ! ,  ι ⁰ ]
           → Γ       ⊢ natrec lG G z s n ∷ G [ n ] ^ [ rG , ι lG ]
    Emptyrecⱼ : ∀ {A l lA rA e}
           → Γ ⊢ A ^ [ rA , ι lA ] → Γ ⊢ e ∷ Empty l ^ [ % ,  ι l ] -> Γ ⊢ Emptyrec lA l A e ∷ A ^ [ rA , ι lA ]
    Idⱼ : ∀ {A l t u}
          → Γ ⊢ A ∷ U l ^ [ ! , next l ]
          → Γ ⊢ t ∷ A ^ [ ! , ι l ]
          → Γ ⊢ u ∷ A ^ [ ! , ι l ]
          → Γ ⊢ Id A t u ∷ SProp l ^ [ ! , next l ]
    Idreflⱼ : ∀ {A l t}
              → Γ ⊢ t ∷ A ^ [ ! , ι l ]
              → Γ ⊢ Idrefl A t ∷ (Id A t t) ^ [ % , ι l ]
    transpⱼ : ∀ {A l P t s u e}
              → Γ ⊢ A ^ [ ! , l ]
              → Γ ∙ A ^ [ ! , l ] ⊢ P ^ [ % , l ]
              → Γ ⊢ t ∷ A ^ [ ! , l ]
              → Γ ⊢ s ∷ P [ t ] ^ [ % , l ]
              → Γ ⊢ u ∷ A ^ [ ! , l ]
              → Γ ⊢ e ∷ (Id A t u) ^ [ % , l ]
              → Γ ⊢ transp A P t s u e ∷ P [ u ] ^ [ % , l ]
    castⱼ : ∀ {A B r e t}
            → Γ ⊢ A ∷ Univ r ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ B ∷ Univ r ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ e ∷ (Id (Univ r ⁰) A B) ^ [ % , next ⁰ ]
            → Γ ⊢ t ∷ A ^ [ r , ι ⁰ ]
            → Γ ⊢ cast ⁰ A B e t ∷ B ^ [ r , ι ⁰ ]
    castreflⱼ : ∀ {A t}
                 → Γ ⊢ A ∷ U ⁰ ^ [ ! , next ⁰ ]
                 → Γ ⊢ t ∷ A ^ [ ! , ι ⁰ ]
                 → Γ ⊢ castrefl A t ∷ (Id A t (cast ⁰ A A (Idrefl (U ⁰) A) t)) ^ [ % , ι ⁰ ]
    conv   : ∀ {t A B r}
           → Γ ⊢ t ∷ A ^ r
           → Γ ⊢ A ≡ B ^ r
           → Γ ⊢ t ∷ B ^ r

  -- Type equality
  data _⊢_≡_^_ (Γ : Con Term) : Term → Term → TypeInfo → Set where
    univ   : ∀ {A B r l}
           → Γ ⊢ A ≡ B ∷ (Univ r l) ^ [ ! , next l ]
           → Γ ⊢ A ≡ B ^ [ r , ι l ]
    refl   : ∀ {A r}
           → Γ ⊢ A ^ r
           → Γ ⊢ A ≡ A ^ r
    sym    : ∀ {A B r}
           → Γ ⊢ A ≡ B ^ r
           → Γ ⊢ B ≡ A ^ r
    trans  : ∀ {A B C r}
           → Γ ⊢ A ≡ B ^ r
           → Γ ⊢ B ≡ C ^ r
           → Γ ⊢ A ≡ C ^ r

  -- Term equality
  data _⊢_≡_∷_^_ (Γ : Con Term) : Term → Term → Term → TypeInfo → Set where
    refl        : ∀ {t A l}
                → Γ ⊢ t ∷ A ^ [ ! , l ]
                → Γ ⊢ t ≡ t ∷ A ^ [ ! , l ]
    sym         : ∀ {t u A l}
                → Γ ⊢ t ≡ u ∷ A ^ [ ! , l ]
                → Γ ⊢ u ≡ t ∷ A ^ [ ! , l ]
    trans       : ∀ {t u v A l}
                → Γ ⊢ t ≡ u ∷ A ^ [ ! , l ]
                → Γ ⊢ u ≡ v ∷ A ^ [ ! , l ]
                → Γ ⊢ t ≡ v ∷ A ^ [ ! , l ]
    conv        : ∀ {A B r t u}
                → Γ ⊢ t ≡ u ∷ A ^ r
                → Γ ⊢ A ≡ B ^ r
                → Γ ⊢ t ≡ u ∷ B ^ r
    Π-cong      : ∀ {E F G H rF lF rG lG l}
                → lF ≤ l
                → lG ≤ l
                → Γ     ⊢ F ^ [ rF , ι lF ]
                → Γ     ⊢ F ≡ H       ∷ (Univ rF lF) ^ [ ! , next lF ]
                → Γ ∙ F ^ [ rF , ι lF ] ⊢ G ≡ E       ∷ (Univ rG lG) ^ [ ! , next lG ]
                → Γ     ⊢ Π F ^ rF ° lF ▹ G ° lG ° l ≡ Π H ^ rF ° lF ▹ E ° lG ° l ∷ (Univ rG l) ^ [ ! , next l ]
    ∃-cong      : ∀ {E F G H l}
                → Γ     ⊢ F ^ [ % , ι l ]
                → Γ     ⊢ F ≡ H       ∷ SProp l ^ [ ! , next l ]
                → Γ ∙ F ^ [ % , ι l ] ⊢ G ≡ E       ∷ SProp l ^ [ ! , next l ]
                → Γ     ⊢ ∃ F ▹ G ≡ ∃ H ▹ E ∷ SProp l ^ [ ! , next l ]
    app-cong    : ∀ {a b f g F G rF lF lG l}
                → Γ ⊢ f ≡ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
                → Γ ⊢ a ≡ b ∷ F ^ [ rF , ι lF ]
                → Γ ⊢ f ∘ a ^ l ≡ g ∘ b ^ l ∷ G [ a ] ^ [ ! , ι lG ]
    β-red       : ∀ {a t F rF lF G lG l}
                → lF ≤ l
                → lG ≤ l
                → Γ     ⊢ F ^ [ rF , ι lF ]
                → Γ ∙ F ^ [ rF , ι lF ] ⊢ t ∷ G ^ [ ! , ι lG ]
                → Γ     ⊢ a ∷ F ^ [ rF , ι lF ]
                → Γ     ⊢ (lam F ▹ t ^ l) ∘ a ^ l ≡ t [ a ] ∷ G [ a ] ^ [ ! , ι lG ]
    η-eq        : ∀ {f g F rF lF lG l G}
                → lF ≤ l
                → lG ≤ l
                → Γ     ⊢ F ^ [ rF , ι lF ]
                → Γ     ⊢ f ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
                → Γ     ⊢ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
                → Γ ∙ F ^ [ rF , ι lF ] ⊢ wk1 f ∘ var Nat.zero ^ l ≡ wk1 g ∘ var Nat.zero ^ l ∷ G ^ [ ! , ι lG ]
                → Γ     ⊢ f ≡ g ∷ Π F ^ rF ° lF ▹ G ° lG ° l ^ [ ! , ι l ]
    suc-cong    : ∀ {m n}
                → Γ ⊢ m ≡ n ∷ ℕ ^ [ ! ,  ι ⁰ ]
                → Γ ⊢ suc m ≡ suc n ∷ ℕ ^ [ ! ,  ι ⁰ ]
    natrec-cong : ∀ {z z′ s s′ n n′ F F′ l}
                → Γ ∙ ℕ ^ [ ! ,  ι ⁰ ] ⊢ F ≡ F′ ^ [ ! , ι l ]
                → Γ     ⊢ z ≡ z′ ∷ F [ zero ] ^ [ ! , ι l ]
                → Γ     ⊢ s ≡ s′ ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var Nat.zero) ]↑ ° l ° l) ° l ° l ^ [ ! , ι l  ]
                → Γ     ⊢ n ≡ n′ ∷ ℕ ^ [ ! ,  ι ⁰ ]
                → Γ     ⊢ natrec l F z s n ≡ natrec l F′ z′ s′ n′ ∷ F [ n ] ^ [ ! , ι l ]
    natrec-zero : ∀ {z s F l}
                → Γ ∙ ℕ ^ [ ! ,  ι ⁰ ] ⊢ F ^ [ ! , ι l ]
                → Γ     ⊢ z ∷ F [ zero ] ^ [ ! , ι l ]
                → Γ     ⊢ s ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var Nat.zero) ]↑ ° l ° l) ° l ° l ^ [ ! , ι l ]
                → Γ     ⊢ natrec l F z s zero ≡ z ∷ F [ zero ] ^ [ ! , ι l ]
    natrec-suc  : ∀ {n z s F l}
                → Γ     ⊢ n ∷ ℕ ^ [ ! ,  ι ⁰ ]
                → Γ ∙ ℕ ^ [ ! ,  ι ⁰ ] ⊢ F ^ [ ! , ι l ]
                → Γ     ⊢ z ∷ F [ zero ] ^ [ ! , ι l ]
                → Γ     ⊢ s ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var Nat.zero) ]↑ ° l ° l) ° l ° l ^ [ ! , ι l ]
                → Γ     ⊢ natrec l F z s (suc n) ≡ (s ∘ n ^ l) ∘ (natrec l F z s n) ^ l
                        ∷ F [ suc n ] ^ [ ! , ι l ]
    Emptyrec-cong : ∀ {A A' l lEmpty e e'}
                → Γ ⊢ A ≡ A' ^ [ ! , ι l ]
                → Γ ⊢ e ∷ Empty lEmpty ^ [ % , ι lEmpty ]
                → Γ ⊢ e' ∷ Empty lEmpty ^ [ % , ι lEmpty ]
                → Γ ⊢ Emptyrec l lEmpty  A e ≡ Emptyrec l lEmpty  A' e' ∷ A ^ [ ! , ι l ]
    proof-irrelevance : ∀ {t u A l}
                      → Γ ⊢ t ∷ A ^ [ % , l ]
                      → Γ ⊢ u ∷ A ^ [ % , l ]
                      → Γ ⊢ t ≡ u ∷ A ^ [ % , l ]
    Id-cong : ∀ {A A' l t t' u u'}
              → Γ ⊢ A ≡ A' ∷ Univ ! l ^ [ ! , next l ]
              → Γ ⊢ t ≡ t' ∷ A ^ [ ! , ι l ]
              → Γ ⊢ u ≡ u' ∷ A ^ [ ! , ι l ]
              → Γ ⊢ Id A t u ≡ Id A' t' u' ∷ SProp l ^ [ ! , next l ]
    Id-Π : ∀ {A rA lA lB l B t u}
           → lA ≤ l
           → lB ≤ l
           → Γ ⊢ A ∷ Univ rA lA ^ [ ! , next lA ]
           → Γ ∙ A ^ [ rA , ι lA ] ⊢ B ∷ Univ ! lB ^ [ ! , next lB ]
           → Γ ⊢ t ∷ (Π A ^ rA ° lA ▹ B ° lB ° l) ^ [ ! , ι l ]
           → Γ ⊢ u ∷ (Π A ^ rA ° lA ▹ B ° lB ° l) ^ [ ! , ι l ]
           → Γ ⊢ (Id (Π A ^ rA ° lA ▹ B ° lB ° l) t u)
                 ≡ Π A ^ rA ° lA ▹ (Id B ((wk1 t) ∘ (var 0) ^ l) ((wk1 u) ∘ (var 0) ^ l)) ° lB ° l
                 ∷ SProp l ^ [ ! , next l ]
    Id-ℕ-00 : ⊢ Γ
           → Γ ⊢ (Id ℕ zero zero)
                  ≡ Unit {⁰}
                  ∷ SProp ⁰ ^ [ ! , next ⁰ ]
    Id-ℕ-SS : ∀ {m n}
              → Γ ⊢ m ∷ ℕ ^ [ ! ,  ι ⁰ ]
              → Γ ⊢ n ∷ ℕ ^ [ ! ,  ι ⁰ ]
              → Γ ⊢ (Id ℕ (suc m) (suc n))
                    ≡ (Id ℕ m n)
                    ∷ SProp ⁰ ^ [ ! , next ⁰ ]
    Id-U-ΠΠ : ∀ {A A' rA B B'}
              → Γ ⊢ A ∷ (Univ rA ⁰) ^ [ ! , next ⁰ ]
              → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
              → Γ ⊢ A' ∷ (Univ rA ⁰) ^ [ ! , next ⁰ ]
              → Γ ∙ A' ^ [ rA , ι ⁰ ] ⊢ B' ∷ U ⁰ ^ [ ! , next ⁰ ]
              → Γ ⊢ (Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) (Π A' ^ rA ° ⁰ ▹ B' ° ⁰ ° ⁰))
                    ≡ ∃ (Id (Univ rA ⁰) A A') ▹
                      (Π (wk1 A') ^ rA ° ⁰ ▹ Id (U ⁰)
                        ((wk (lift (step id)) B) [ cast ⁰ (wk1 (wk1 A')) (wk1 (wk1 A)) (Idsym (Univ rA ⁰) (wk1 (wk1 A)) (wk1 (wk1 A')) (var 1)) (var 0) ]↑)
                        (wk (lift (step id)) B') ° ¹ ° ¹)
                  ∷ SProp ¹ ^ [ ! , next ¹ ]
    Id-U-ℕℕ : ⊢ Γ
            → Γ ⊢ Id (U ⁰) ℕ ℕ
                  ≡ Unit {¹}
                  ∷ (SProp ¹) ^ [ ! , next ¹ ]
    Id-SProp : ∀ {A B}
               → Γ ⊢ A ∷ SProp ⁰ ^ [ ! , next ⁰ ]
               → Γ ⊢ B ∷ SProp ⁰ ^ [ ! , next ⁰ ]
               → Γ ⊢ Id (SProp ⁰) A B
                     ≡ (A ^ % ° ⁰ ▹▹ B ° ⁰ ° ¹) ×× (B ^ % ° ⁰ ▹▹ A ° ⁰ ° ¹)
                     ∷ SProp ¹ ^ [ ! , next ¹ ]
    Id-ℕ-0S : ∀ {t}
            → Γ ⊢ t ∷ ℕ ^ [ ! , ι ⁰ ]
            → Γ ⊢ Id ℕ zero (suc t) ≡ Empty ⁰ ∷ (SProp ⁰) ^ [ ! , next ⁰ ]
    Id-ℕ-S0 : ∀ {t}
            → Γ ⊢ t ∷ ℕ ^ [ ! , ι ⁰ ]
            → Γ ⊢ Id ℕ (suc t) zero ≡ Empty ⁰ ∷ (SProp ⁰) ^ [ ! , next ⁰ ]
    Id-U-ℕΠ : ∀ {A rA B}
            → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ Id (U ⁰) ℕ (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) ≡ Empty ¹ ∷ SProp ¹ ^ [ ! , next ¹ ]
    Id-U-Πℕ : ∀ {A rA B}
            → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) ℕ ≡ Empty ¹ ∷ SProp ¹ ^ [ ! , next ¹ ]
    Id-U-ΠΠ!% : ∀ {A rA B A' rA' B' }
            → rA PE.≢ rA'
            → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ A' ∷ Univ rA' ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A' ^ [ rA' , ι ⁰ ] ⊢ B' ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) (Π A' ^ rA' ° ⁰ ▹ B' ° ⁰ ° ⁰) ≡ Empty ¹ ∷ SProp ¹ ^ [ ! , next ¹ ]
    cast-cong : ∀ {A A' B B' e e' t t'} → let l = ⁰ in
                  Γ ⊢ A ≡ A' ∷ U l ^ [ ! , next l ]
                → Γ ⊢ B ≡ B' ∷ U l ^ [ ! , next l ]
                → Γ ⊢ t ≡ t' ∷ A ^ [ ! , ι l ]
                → Γ ⊢ e ∷ (Id (U ⁰) A B) ^ [ % , next ⁰ ]
                → Γ ⊢ e' ∷ (Id (U ⁰) A' B') ^ [ % , next ⁰ ]
                → Γ ⊢ cast l A B e t ≡ cast l A' B' e' t' ∷ B ^ [ ! , ι l ]
    cast-Π : ∀ {A A' rA B B' e f} → let l = ⁰ in let lA = ⁰ in let lB = ⁰ in
               Γ ⊢ A ∷ (Univ rA lA) ^ [ ! , next lA ]
             → Γ ∙ A ^ [ rA , ι lA ] ⊢ B ∷ U lB ^ [ ! , next lB ]
             → Γ ⊢ A' ∷ (Univ rA lA) ^ [ ! , next lA ]
             → Γ ∙ A' ^ [ rA , ι lA ] ⊢ B' ∷ U lB ^ [ ! , next lB ]
             → Γ ⊢ e ∷ Id (U l) (Π A ^ rA ° lA ▹ B ° lB ° l) (Π A' ^ rA ° lA ▹ B' ° lB ° l) ^ [ % , next l ]
             → Γ ⊢ f ∷ (Π A ^ rA ° lA ▹ B ° lB ° l) ^ [ ! , ι l ]
             → Γ ⊢ (cast l (Π A ^ rA ° lA ▹ B ° lB ° l) (Π A' ^ rA ° lA ▹ B' ° lB ° l) e f)
               ≡ (lam A' ▹
                      (let a = cast l (wk1 A') (wk1 A) (Idsym (Univ rA l) (wk1 A) (wk1 A') (fst (wk1 e))) (var 0) in
                      cast l (B [ a ]↑) B' ((snd (wk1 e)) ∘ (var 0) ^ ¹) ((wk1 f) ∘ a ^ l))
                      ^ l)
                   ∷ Π A' ^ rA ° lA ▹ B' ° lB ° l ^ [ ! , ι l ]
    cast-ℕ-0 : ∀ {e}
               → Γ ⊢ e ∷ Id (U ⁰) ℕ ℕ ^ [ % , next ⁰ ]
               → Γ ⊢ cast ⁰ ℕ ℕ e zero
                   ≡ zero
                   ∷ ℕ ^ [ ! , ι ⁰ ]
    cast-ℕ-S : ∀ {e n}
               → Γ ⊢ e ∷ Id (U ⁰) ℕ ℕ ^ [ % , next ⁰ ]
               → Γ ⊢ n ∷ ℕ ^ [ ! , ι ⁰ ]
               → Γ ⊢ cast ⁰ ℕ ℕ e (suc n)
                   ≡ suc (cast ⁰ ℕ ℕ e n)
                   ∷ ℕ ^ [ ! , ι ⁰ ]

mutual
  data _⊢_⇒_∷_^_ (Γ : Con Term) : Term → Term → Term → TypeLevel → Set where
    conv         : ∀ {A B l t u}
                 → Γ ⊢ t ⇒ u ∷ A ^ l
                 → Γ ⊢ A ≡ B ^ [ ! , l ]
                 → Γ ⊢ t ⇒ u ∷ B ^ l
    app-subst    : ∀ {A B t u a rA lA lB l}
                 → Γ ⊢ t ⇒ u ∷ Π A ^ rA ° lA ▹ B ° lB ° l ^ ι l
                 → Γ ⊢ a ∷ A ^ [ rA , ι lA ]
                 → Γ ⊢ t ∘ a ^ l ⇒ u ∘ a ^ l  ∷ B [ a ] ^ ι lB
    β-red        : ∀ {A B lA lB a t rA l}
                 → lA ≤ l
                 → lB ≤ l
                 → Γ     ⊢ A ^ [ rA , ι lA ]
                 → Γ ∙ A ^ [ rA , ι lA ] ⊢ t ∷ B ^ [ ! , ι lB ]
                 → Γ     ⊢ a ∷ A ^ [ rA , ι lA ]
                 → Γ     ⊢ (lam A ▹ t ^ l) ∘ a ^ l ⇒ t [ a ] ∷ B [ a ] ^ ι lB
    natrec-subst : ∀ {z s n n′ F l}
                 → Γ ∙ ℕ ^ [ ! , ι ⁰ ] ⊢ F ^ [ ! , ι l ]
                 → Γ     ⊢ z ∷ F [ zero ] ^ [ ! , ι l ]
                 → Γ     ⊢ s ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var Nat.zero) ]↑ ° l ° l) ° l ° l ^ [ ! , ι l ]
                 → Γ     ⊢ n ⇒ n′ ∷ ℕ ^ ι ⁰
                 → Γ     ⊢ natrec l F z s n ⇒ natrec l F z s n′ ∷ F [ n ] ^ ι l
    natrec-zero  : ∀ {z s F l }
                 → Γ ∙ ℕ ^ [ ! , ι ⁰ ] ⊢ F ^ [ ! , ι l ]
                 → Γ     ⊢ z ∷ F [ zero ] ^ [ ! , ι l ]
                 → Γ     ⊢ s ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var Nat.zero) ]↑ ° l ° l ) ° l ° l ^ [ ! , ι l ]
                 → Γ     ⊢ natrec l F z s zero ⇒ z ∷ F [ zero ] ^ ι l
    natrec-suc   : ∀ {n z s F l}
                 → Γ     ⊢ n ∷ ℕ ^ [ ! , ι ⁰ ]
                 → Γ ∙ ℕ ^ [ ! , ι ⁰ ] ⊢ F ^ [ ! , ι l ]
                 → Γ     ⊢ z ∷ F [ zero ] ^ [ ! , ι l ]
                 → Γ     ⊢ s ∷ Π ℕ ^ ! ° ⁰ ▹ (F ^ ! ° l ▹▹ F [ suc (var Nat.zero) ]↑ ° l ° l) ° l ° l ^ [ ! , ι l ]
                 → Γ     ⊢ natrec l F z s (suc n) ⇒ (s ∘ n ^ l) ∘ (natrec l F z s n) ^ l
                         ∷ F [ suc n ] ^ ι l
    Id-subst : ∀ {A A' l t u}
              → Γ ⊢ A ⇒ A' ∷ Univ ! l ^ next l
              → Γ ⊢ t ∷ A ^ [ ! , ι l ]
              → Γ ⊢ u ∷ A ^ [ ! , ι l ]
              → Γ ⊢ Id A t u ⇒ Id A' t u ∷ SProp l ^ next l
    Id-ℕ-subst : ∀ {m m' n}
              → Γ ⊢ m ⇒ m' ∷ ℕ ^ ι ⁰
              → Γ ⊢ n ∷ ℕ ^ [ ! , ι ⁰ ]
              → Γ ⊢ Id ℕ m n ⇒ Id ℕ m' n ∷ SProp ⁰ ^ next ⁰
    Id-ℕ-0-subst : ∀ {n n'}
              → Γ ⊢ n ⇒ n' ∷ ℕ ^ ι ⁰
              → Γ ⊢ Id ℕ zero n ⇒ Id ℕ zero n' ∷ SProp ⁰ ^ next ⁰
    Id-ℕ-S-subst : ∀ {m n n'}
              → Γ ⊢ m ∷ ℕ ^ [ ! , ι ⁰ ]
              → Γ ⊢ n ⇒ n' ∷ ℕ ^ ι ⁰
              → Γ ⊢ Id ℕ (suc m) n ⇒ Id ℕ (suc m) n' ∷ SProp ⁰ ^ next ⁰
    Id-U-subst : ∀ {A A' B}
              → Γ ⊢ A ⇒ A' ∷ U ⁰  ^ next ⁰
              → Γ ⊢ B ∷ U ⁰  ^ [ ! , next ⁰  ]
              → Γ ⊢ Id (U ⁰ ) A B ⇒ Id (U ⁰) A' B ∷ SProp ¹ ^ next ¹
    Id-U-ℕ-subst : ∀ {B B'}
              → Γ ⊢ B ⇒ B' ∷ U ⁰ ^ next ⁰
              → Γ ⊢ Id (U ⁰) ℕ B ⇒ Id (U ⁰) ℕ B' ∷ SProp ¹ ^ next ¹
    Id-U-Π-subst : ∀ {A rA P B B'}
              → Γ ⊢ A ∷ (Univ rA ⁰) ^ [ ! , next ⁰ ]
              → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ P ∷ U ⁰ ^ [ ! , next ⁰ ]
              → Γ ⊢ B ⇒ B' ∷ U ⁰ ^ next ⁰
              → Γ ⊢ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰) B ⇒ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ P ° ⁰ ° ⁰) B' ∷ SProp ¹ ^ next ¹
    Id-Π : ∀ {A rA lA lB l B t u}
           → lA ≤ l
           → lB ≤ l
           → Γ ⊢ A ∷ Univ rA lA ^ [ ! , next lA ]
           → Γ ∙ A ^ [ rA , ι lA ] ⊢ B  ∷ Univ ! lB ^ [ ! , next lB ]
           → Γ ⊢ t ∷ (Π A ^ rA ° lA ▹ B ° lB ° l) ^ [ ! , ι l ]
           → Γ ⊢ u ∷ (Π A ^ rA ° lA ▹ B ° lB ° l) ^ [ ! , ι l ]
           → Γ ⊢ (Id (Π A ^ rA ° lA ▹ B ° lB ° l) t u)
                 ⇒ Π A ^ rA ° lA ▹ (Id B ((wk1 t) ∘ (var 0) ^ l) ((wk1 u) ∘ (var 0) ^ l)) ° lB ° l
                 ∷ SProp l ^ next l
    Id-ℕ-00 : ⊢ Γ
            → Γ ⊢ (Id ℕ zero zero)
                  ⇒ Unit {⁰}
                  ∷ SProp ⁰ ^ next ⁰
    Id-ℕ-SS : ∀ {m n}
              → Γ ⊢ m ∷ ℕ ^ [ ! , ι ⁰ ]
              → Γ ⊢ n ∷ ℕ ^ [ ! , ι ⁰ ]
              → Γ ⊢ (Id ℕ (suc m) (suc n))
                    ⇒ (Id ℕ m n)
                    ∷ SProp ⁰ ^ next ⁰
    Id-U-ΠΠ : ∀ {A A' rA B B'}
              → Γ ⊢ A ∷ (Univ rA ⁰) ^ [ ! , next ⁰ ]
              → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
              → Γ ⊢ A' ∷ (Univ rA ⁰) ^ [ ! , next ⁰ ]
              → Γ ∙ A' ^ [ rA , ι ⁰ ] ⊢ B' ∷ U ⁰ ^ [ ! , next ⁰ ]
              → Γ ⊢ (Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) (Π A' ^ rA ° ⁰ ▹ B' ° ⁰ ° ⁰))
                    ⇒ ∃ (Id (Univ rA ⁰) A A') ▹
                      (Π (wk1 A') ^ rA ° ⁰ ▹ Id (U ⁰)
                        ((wk1d B) [ cast ⁰ (wk1 (wk1 A')) (wk1 (wk1 A)) (Idsym (Univ rA ⁰) (wk1 (wk1 A)) (wk1 (wk1 A')) (var 1)) (var 0) ]↑)
                        (wk1d B') ° ¹ ° ¹)
                    ∷ SProp ¹ ^ next ¹
    Id-U-ℕℕ : ⊢ Γ
            → Γ ⊢ (Id (U ⁰) ℕ ℕ)
                  ⇒ Unit {¹}
                  ∷ SProp ¹ ^ next ¹
    Id-SProp : ∀ {A B}
               → Γ ⊢ A ∷ SProp ⁰ ^ [ ! , next ⁰ ]
               → Γ ⊢ B ∷ SProp ⁰ ^ [ ! , next ⁰ ]
               → Γ ⊢ Id (SProp ⁰) A B
                     ⇒ (A ^ % ° ⁰ ▹▹ B ° ⁰ ° ¹) ×× (B ^ % ° ⁰  ▹▹ A ° ⁰ ° ¹)
                     ∷ SProp ¹ ^ next ¹
    Id-ℕ-0S : ∀ {t}
            → Γ ⊢ t ∷ ℕ ^ [ ! , ι ⁰ ]
            → Γ ⊢ Id ℕ zero (suc t) ⇒ Empty ⁰ ∷ SProp ⁰ ^ next ⁰
    Id-ℕ-S0 : ∀ {t}
            → Γ ⊢ t ∷ ℕ ^ [ ! , ι ⁰ ]
            → Γ ⊢ Id ℕ (suc t) zero ⇒ Empty ⁰ ∷ SProp ⁰ ^ next ⁰
    Id-U-ℕΠ : ∀ {A rA B}
            → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ Id (U ⁰) ℕ (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) ⇒ Empty ¹ ∷ SProp ¹ ^ next ¹
    Id-U-Πℕ : ∀ {A rA B}
            → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) ℕ ⇒ Empty ¹ ∷ SProp ¹ ^ next ¹
    Id-U-ΠΠ!% : ∀ {A rA B A' rA' B' }
            → rA PE.≢ rA'
            → Γ ⊢ A ∷ Univ rA ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A ^ [ rA , ι ⁰ ] ⊢ B ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ A' ∷ Univ rA' ⁰ ^ [ ! , next ⁰ ]
            → Γ ∙ A' ^ [ rA' , ι ⁰ ] ⊢ B' ∷ U ⁰ ^ [ ! , next ⁰ ]
            → Γ ⊢ Id (U ⁰) (Π A ^ rA ° ⁰ ▹ B ° ⁰ ° ⁰) (Π A' ^ rA' ° ⁰ ▹ B' ° ⁰ ° ⁰) ⇒ Empty ¹ ∷ SProp ¹ ^ next ¹
    cast-subst : ∀ {A A' B e t} → let l = ⁰ in
                    Γ ⊢ A ⇒ A' ∷ U l ^ next l
                  → Γ ⊢ B ∷ U l ^ [ ! , next l ]
                  → Γ ⊢ e ∷ Id (U l) A B ^ [ % , next l ]
                  → Γ ⊢ t ∷ A ^ [ ! , ι l ]
                  → Γ ⊢ cast l A B e t ⇒ cast l A' B e t ∷ B ^ ι l
    cast-ℕ-subst : ∀ {B B' e t}
                  → Γ ⊢ B ⇒ B' ∷ U ⁰ ^ next ⁰
                  → Γ ⊢ e ∷ Id (U ⁰) ℕ B ^ [ % , next ⁰ ]
                  → Γ ⊢ t ∷ ℕ ^ [ ! , ι ⁰ ]
                  → Γ ⊢ cast ⁰ ℕ B e t ⇒ cast ⁰ ℕ B' e t ∷ B ^ ι ⁰
    cast-Π-subst : ∀ {A rA P B B' e t} → let l = ⁰ in let lA = ⁰ in let lP = ⁰ in
                    Γ ⊢ A ∷ (Univ rA lA) ^ [ ! , next lA ]
                  → Γ ∙ A ^ [ rA , ι lA ] ⊢ P ∷ U lP ^ [ ! , next lA ]
                  → Γ ⊢ B ⇒ B' ∷ U l ^ next l
                  → Γ ⊢ e ∷ Id (U l) (Π A ^ rA ° lA ▹ P ° lP ° l) B ^ [ % , next l ]
                  → Γ ⊢ t ∷ (Π A ^ rA ° lA ▹ P ° lP ° l) ^ [ ! , ι l ]
                  → Γ ⊢ cast l (Π A ^ rA ° lA ▹ P ° lP ° l) B e t ⇒ cast l (Π A ^ rA ° lA ▹ P ° lP ° l) B' e t ∷ B ^ ι l
    cast-Π : ∀ {A A' rA B B' e f} → let l = ⁰ in
               Γ ⊢ A ∷ (Univ rA l) ^ [ ! , next l ]
             → Γ ∙ A ^ [ rA , ι l ] ⊢ B ∷ U l ^ [ ! , next l ]
             → Γ ⊢ A' ∷ (Univ rA l) ^ [ ! , next l ]
             → Γ ∙ A' ^ [ rA , ι l ] ⊢ B' ∷ U l ^ [ ! , next l ]
             → Γ ⊢ e ∷ Id (U l) (Π A ^ rA ° l ▹ B ° l ° l) (Π A' ^ rA ° l  ▹ B' ° l ° l) ^ [ % , next l ]
             → Γ ⊢ f ∷ (Π A ^ rA ° l ▹ B ° l ° l) ^ [ ! , ι l ]
             → Γ ⊢ (cast l (Π A ^ rA ° l ▹ B ° l ° l) (Π A' ^ rA ° l ▹ B' ° l ° l) e f)
               ⇒ (lam A' ▹
                      (let a = cast l (wk1 A') (wk1 A) (Idsym (Univ rA l) (wk1 A) (wk1 A') (fst (wk1 e))) (var 0)
                       in cast l (B [ a ]↑) B' ((snd (wk1 e)) ∘ (var 0) ^ ¹) ((wk1 f) ∘ a ^ l))
                       ^ l )
                   ∷ Π A' ^ rA ° l ▹ B' ° l ° l ^ ι l
    cast-ℕ-0 : ∀ {e}
               → Γ ⊢ e ∷ Id (U ⁰) ℕ ℕ ^ [ % , next ⁰ ]
               → Γ ⊢ cast ⁰ ℕ ℕ e zero
                   ⇒ zero
                   ∷ ℕ ^ ι ⁰
    cast-ℕ-S : ∀ {e n}
               → Γ ⊢ e ∷ Id (U ⁰) ℕ ℕ ^ [ % , next ⁰ ]
               → Γ ⊢ n ∷ ℕ ^ [ ! , ι ⁰ ]
               → Γ ⊢ cast ⁰ ℕ ℕ e (suc n)
                   ⇒ suc (cast ⁰ ℕ ℕ e n)
                   ∷ ℕ ^ ι ⁰

    cast-ℕ-cong : ∀ {e t u}
               → Γ ⊢ e ∷ Id (U ⁰) ℕ ℕ ^ [ % , next ⁰ ]
               → Γ ⊢ t ⇒ u ∷ ℕ ^ ι ⁰
               → Γ ⊢ cast ⁰ ℕ ℕ e t
                   ⇒ cast ⁰ ℕ ℕ e u
                   ∷ ℕ ^ ι ⁰

  -- Type reduction
  data _⊢_⇒_^_ (Γ : Con Term) : Term → Term → TypeInfo → Set where
    univ : ∀ {A B r l}
         → Γ ⊢ A ⇒ B ∷ (Univ r l) ^ next l
         → Γ ⊢ A ⇒ B ^ [ r , ι l ]

-- Term reduction closure
data _⊢_⇒*_∷_^_ (Γ : Con Term) : Term → Term → Term → TypeLevel → Set where
  id  : ∀ {A l t}
      → Γ ⊢ t ∷ A ^ [ ! , l ]
      → Γ ⊢ t ⇒* t ∷ A ^ l
  _⇨_ : ∀ {A l t t′ u}
      → Γ ⊢ t  ⇒  t′ ∷ A ^ l
      → Γ ⊢ t′ ⇒* u  ∷ A ^ l
      → Γ ⊢ t  ⇒* u  ∷ A ^ l

-- Type reduction closure
data _⊢_⇒*_^_ (Γ : Con Term) : Term → Term → TypeInfo → Set where
  id  : ∀ {A r}
      → Γ ⊢ A ^ r
      → Γ ⊢ A ⇒* A ^ r
  _⇨_ : ∀ {A A′ B r}
      → Γ ⊢ A  ⇒  A′ ^ r
      → Γ ⊢ A′ ⇒* B  ^ r
      → Γ ⊢ A  ⇒* B  ^ r

-- Type reduction to whnf
_⊢_↘_^_ : (Γ : Con Term) → Term → Term → TypeInfo → Set
Γ ⊢ A ↘ B ^ r = Γ ⊢ A ⇒* B ^ r × Whnf B

-- Term reduction to whnf
_⊢_↘_∷_^_ : (Γ : Con Term) → Term → Term → Term → TypeLevel → Set
Γ ⊢ t ↘ u ∷ A ^ l = Γ ⊢ t ⇒* u ∷ A ^ l × Whnf u

-- Type equality with well-formed types
_⊢_:≡:_^_ : (Γ : Con Term) → Term → Term → TypeInfo → Set
Γ ⊢ A :≡: B ^ r = Γ ⊢ A ^ r × Γ ⊢ B ^ r × (Γ ⊢ A ≡ B ^ r)

-- Term equality with well-formed terms
_⊢_:≡:_∷_^_ : (Γ : Con Term) → Term → Term → Term → TypeInfo → Set
Γ ⊢ t :≡: u ∷ A ^ r = Γ ⊢ t ∷ A ^ r × Γ ⊢ u ∷ A ^ r × (Γ ⊢ t ≡ u ∷ A ^ r)

-- Type reduction closure with well-formed types
record _⊢_:⇒*:_^_ (Γ : Con Term) (A B : Term) (r : TypeInfo) : Set where
  constructor [[_,_,_]]
  field
    ⊢A : Γ ⊢ A ^ r
    ⊢B : Γ ⊢ B ^ r
    D  : Γ ⊢ A ⇒* B ^ r

open _⊢_:⇒*:_^_ using () renaming (D to red) public

-- Term reduction closure with well-formed terms
record _⊢_:⇒*:_∷_^_ (Γ : Con Term) (t u A : Term) (l : TypeLevel) : Set where
  constructor [[_,_,_]]
  field
    ⊢t : Γ ⊢ t ∷ A ^ [ ! , l ]
    ⊢u : Γ ⊢ u ∷ A ^ [ ! , l ]
    d  : Γ ⊢ t ⇒* u ∷ A ^ l

open _⊢_:⇒*:_∷_^_ using () renaming (d to redₜ) public

-- Well-formed substitutions.
data _⊢ˢ_∷_ (Δ : Con Term) (σ : Subst) : (Γ : Con Term) → Set where
  id : Δ ⊢ˢ σ ∷ ε
  _,_ : ∀ {Γ A rA}
      → Δ ⊢ˢ tail σ ∷ Γ
      → Δ ⊢ head σ ∷ subst (tail σ) A ^ rA
      → Δ ⊢ˢ σ ∷ Γ ∙ A ^ rA

-- Conversion of well-formed substitutions.
data _⊢ˢ_≡_∷_ (Δ : Con Term) (σ σ′ : Subst) : (Γ : Con Term) → Set where
  id : Δ ⊢ˢ σ ≡ σ′ ∷ ε
  _,_ : ∀ {Γ A rA}
      → Δ ⊢ˢ tail σ ≡ tail σ′ ∷ Γ
      → Δ ⊢ head σ ≡ head σ′ ∷ subst (tail σ) A ^ rA
      → Δ ⊢ˢ σ ≡ σ′ ∷ Γ ∙ A ^ rA

-- Note that we cannot use the well-formed substitutions.
-- For that, we need to prove the fundamental theorem for substitutions.

-- Some derivable rules
Unitⱼ : ∀ {Γ l} (⊢Γ : ⊢ Γ)
      → Γ ⊢ Unit ∷ SProp l ^ [ ! , next l ]
Unitⱼ ⊢Γ = Πⱼ ≡is≤ PE.refl ▹ ≡is≤ PE.refl ▹ Emptyⱼ ⊢Γ ▹ Emptyⱼ (⊢Γ ∙ univ (Emptyⱼ ⊢Γ))

typeUnit : ∀ {l} →  Type (Unit {l})
typeUnit = Πₙ

Ugenⱼ : ∀ {r Γ l} → ⊢ Γ → Γ ⊢ Univ r l ^ [ ! , next l ]
Ugenⱼ {l = ⁰} ⊢Γ = univ (univ 0<1 ⊢Γ)
Ugenⱼ {l = ¹} ⊢Γ = Uⱼ ⊢Γ