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

-- This module defines D<: normal form, proves its transitivity and shows its
-- equivalence to the original D<:. It turns out that D<: normal form can be used to
-- prove undecidability.
module DsubEquivSimpler where

open import Data.List as List
open import Data.List.All as All
open import Data.Nat as ℕ
open import Data.Maybe as Maybe
open import Data.Product
open import Data.Sum
open import Data.Empty renaming (⊥ to False)
open import Data.Vec as Vec renaming (_∷_ to _‼_ ; [] to nil) hiding (_++_)
open import Function

open import Data.Maybe.Properties as Maybeₚ
open import Data.Nat.Properties as ℕₚ
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality as ≡

open import Induction.Nat

open import DsubDef
open import Dsub
open import Utils

-- D<: Normal Form
--
-- The following judgment defines D<: normal form as in Figure 4.
infix 4 _⊢″_<:_
data _⊢″_<:_ : Env → Typ → Typ → Set where
  <:⊤  : ∀ {Γ T} → Γ ⊢″ T <: ⊤
  ⊥<:  : ∀ {Γ T} → Γ ⊢″ ⊥ <: T
  refl : ∀ {Γ T} → Γ ⊢″ T <: T
  bnd  : ∀ {Γ S U S′ U′} →
           Γ ⊢″ S′ <: S →
           Γ ⊢″ U <: U′ →
           Γ ⊢″ ⟨A: S ⋯ U ⟩ <: ⟨A: S′ ⋯ U′ ⟩
  Π<:  : ∀ {Γ S U S′ U′} →
           Γ ⊢″ S′ <: S →
           Γ ‣ S′ ! ⊢″ U <: U′ →
           Γ ⊢″ Π S ∙ U <: Π S′ ∙ U′
  sel₁ : ∀ {Γ n T S} →
           env-lookup Γ n ≡ just T →
           Γ ⊢″ T <: ⟨A: S ⋯ ⊤ ⟩ →
           Γ ⊢″ S <: n ∙A
  sel₂ : ∀ {Γ n T U} →
           env-lookup Γ n ≡ just T →
           Γ ⊢″ T <: ⟨A: ⊥ ⋯ U ⟩ →
           Γ ⊢″ n ∙A <: U
  <:>  : ∀ {Γ n T S U} →
           env-lookup Γ n ≡ just T →
           Γ ⊢″ T <: ⟨A: S ⋯ ⊤ ⟩ →
           Γ ⊢″ T <: ⟨A: ⊥ ⋯ U ⟩ →
           Γ ⊢″ S <: U

-- First we show that D<: normal form admits weakening.
<:″-weakening-gen : ∀ {Γ S U} →
                      Γ ⊢″ S <: U →
                    ∀ Γ₁ Γ₂ T →
                      Γ ≡ Γ₁ ‣ Γ₂ →
                      Γ₁ ‣ T ! ‣ Γ₂ ⇑ ⊢″ S ↑ length Γ₂ <: U ↑ length Γ₂
<:″-weakening-gen <:⊤ Γ₁ Γ₂ T eqΓ               = <:⊤
<:″-weakening-gen ⊥<: Γ₁ Γ₂ T eqΓ               = ⊥<:
<:″-weakening-gen refl Γ₁ Γ₂ T eqΓ              = refl
<:″-weakening-gen (bnd S′<:S U<:U′) Γ₁ Γ₂ T eqΓ = bnd (<:″-weakening-gen S′<:S Γ₁ Γ₂ T eqΓ)
                                                      (<:″-weakening-gen U<:U′ Γ₁ Γ₂ T eqΓ)
<:″-weakening-gen (Π<: S′<:S U<:U′) Γ₁ Γ₂ T eqΓ = Π<: (<:″-weakening-gen S′<:S Γ₁ Γ₂ T eqΓ)
                                                      (<:″-weakening-gen U<:U′ Γ₁ (_ ∷ Γ₂) T (cong (_ ∷_) eqΓ))
<:″-weakening-gen (sel₁ {_} {n} T∈Γ T<:B) Γ₁ Γ₂ T eqΓ
  rewrite ↑-var n (length Γ₂)                   = sel₁ (↦∈-weaken′ T∈Γ Γ₁ Γ₂ T eqΓ)
                                                       (<:″-weakening-gen T<:B Γ₁ Γ₂ T eqΓ)
<:″-weakening-gen (sel₂ {_} {n} T∈Γ T<:B) Γ₁ Γ₂ T eqΓ
  rewrite ↑-var n (length Γ₂)                   = sel₂ (↦∈-weaken′ T∈Γ Γ₁ Γ₂ T eqΓ)
                                                       (<:″-weakening-gen T<:B Γ₁ Γ₂ T eqΓ)
<:″-weakening-gen (<:> {_} {n} T∈Γ T<:B T<:B′) Γ₁ Γ₂ T eqΓ
  rewrite ↑-var n (length Γ₂)                   = <:> (↦∈-weaken′ T∈Γ Γ₁ Γ₂ T eqΓ)
                                                      (<:″-weakening-gen T<:B Γ₁ Γ₂ T eqΓ)
                                                      (<:″-weakening-gen T<:B′ Γ₁ Γ₂ T eqΓ)

<:″-weakening : ∀ {Γ₁ Γ₂ S U} T →
                  Γ₁ ‣ Γ₂ ⊢″ S <: U →
                  Γ₁ ‣ T ! ‣ Γ₂ ⇑ ⊢″ S ↑ length Γ₂ <: U ↑ length Γ₂
<:″-weakening T S<:U = <:″-weakening-gen S<:U _ _ T refl

<:″-weakening-hd : ∀ {Γ S U} T →
                     Γ ⊢″ S <: U →
                     Γ ‣ T ! ⊢″ S ↑ 0 <: U ↑ 0
<:″-weakening-hd T = <:″-weakening {Γ₂ = []} T

-- Then we work on transitivity.
module SimplerTransitivity where
  -- This predicate asserts that the first context replaces _one_ type in the second
  -- context with its subtype. That is, the first context is more "precise" than the
  -- second context.
  infix 4 _≺:[_]_
  data _≺:[_]_ : Env → ℕ → Env → Set where
    ≺[_,_] : ∀ {Γ U} S → Γ ⊢″ S <: U → Γ ‣ S ! ≺:[ 0 ] Γ ‣ U !
    _∷_    : ∀ {Γ₁ n Γ₂} T → Γ₁ ≺:[ n ] Γ₂ → Γ₁ ‣ T ! ≺:[ suc n ] Γ₂ ‣ T !

  <:∈-find : ∀ {x T Γ Γ′ n} →
               x ↦ T ∈ Γ →
               Γ′ ≺:[ n ] Γ →
               x ≡ n × (∃ λ T′ → n ↦ T′ ∈ Γ′ × Γ′ ⊢″ T′ <: T) ⊎ x ≢ n × x ↦ T ∈ Γ′
  <:∈-find hd ≺[ T′ , T′<:T ]           = inj₁ (refl , T′ ↑ 0 , hd , <:″-weakening-hd T′ T′<:T)
  <:∈-find hd (T ∷ Γ′≺:Γ)               = inj₂ ((λ ()) , hd)
  <:∈-find (tl T∈Γ) ≺[ T′ , T′<:T ]     = inj₂ ((λ ()) , tl T∈Γ)
  <:∈-find (tl T∈Γ) (S ∷ Γ′≺:Γ) with <:∈-find T∈Γ Γ′≺:Γ
  ... | inj₁ (x≡n , T′ , T′∈Γ′ , T′<:T) = inj₁ (cong suc x≡n , T′ ↑ 0 , tl T′∈Γ′ , <:″-weakening-hd S T′<:T)
  ... | inj₂ (x≢n , T∈Γ′)               = inj₂ (x≢n ∘ suc-injective , tl T∈Γ′)

  <:∈-find′ : ∀ {x T Γ Γ′ n} →
                env-lookup Γ x ≡ just T →
                Γ′ ≺:[ n ] Γ →
                x ≡ n × (∃ λ T′ → env-lookup Γ′ n ≡ just T′ × Γ′ ⊢″ T′ <: T) ⊎ x ≢ n × env-lookup Γ′ x ≡ just T
  <:∈-find′ T∈Γ Γ′≺Γ with <:∈-find (lookup⇒↦∈ T∈Γ) Γ′≺Γ
  ... | inj₁ (x≡n , T′ , T′∈Γ′ , T′<:T) = inj₁ (x≡n , T′ , ↦∈⇒lookup T′∈Γ′ , T′<:T)
  ... | inj₂ (x≢n , T∈Γ′)               = inj₂ (x≢n , ↦∈⇒lookup T∈Γ′)

  private
    trans-on : Typ → Set
    trans-on T = ∀ {Γ S U} → Γ ⊢″ S <: T → Γ ⊢″ T <: U → Γ ⊢″ S <: U

    narrow-on : Typ → Set
    narrow-on T = ∀ {Γ Γ′ n S U} →
                    Γ ⊢″ S <: U →
                    Γ′ ≺:[ n ] Γ →
                    env-lookup Γ n ≡ just T →
                    Γ′ ⊢″ S <: U

  -- Per Definition 11, this defines type declaration hierarchy.
  ⟨A:⟩-layer : Typ → List Typ → Typ
  ⟨A:⟩-layer T []      = T
  ⟨A:⟩-layer T (S ∷ l) = ⟨A: S ⋯ ⟨A:⟩-layer T l ⟩

  -- Now we start proving transitivity and narrowing via a mutual induction.  This
  -- mutual induction begins exactly as presented in the paper: an induction of
  -- lexicographical order of the triple (T, 𝒟₁, 𝒟₂). Notice that this is
  -- automatically handled by Agda's termination checker, which makes the problem very
  -- nice to work with.
  --
  -- If one loads this file from Emacs, one shall see that some cases below are
  -- highlighted. The reason is that those cases are not defined
  -- _definitionally_. More details can be found in:
  -- https://agda.readthedocs.io/en/v2.5.4.2/language/function-definitions.html#case-trees.
  --
  -- In fact, those non-definitional cases hides many cases away, as they expand to
  -- numerous definitional cases, which is necessarily the case in Coq. The fact that
  -- Agda's termination checking is permissive and Agda allows non-definitional cases
  -- has significantly improved productivity of showing transitivity. In Coq, this
  -- would have required significant amount of technical setup which can easily wipe
  -- away the main idea of the proof and might not be finished within a reasonable
  -- time interval.
  mutual
    <:′-trans-rec : ∀ T → (∀ T′ → Typ-measure T′ < Typ-measure T → trans-on T′ × narrow-on T′) → trans-on T
    <:′-trans-rec (n ∙A) rec (sel₁ T∈Γ T<:B) (sel₂ T′∈Γ T<:B′)
      with ≡.trans (≡.sym T∈Γ) T′∈Γ
    ... | refl                                                           = <:> T′∈Γ T<:B T<:B′
    <:′-trans-rec ⟨A: S′ ⋯ U′ ⟩ rec (bnd S′<:S U<:U′) (bnd S″<:S U′<:U″) = bnd (<:′-trans-rec S′ (λ T′ T′<S′ → rec T′ (≤-step (≤-stepsʳ _ T′<S′))) S″<:S S′<:S)
                                                                               (<:′-trans-rec U′ (λ T′ T′<U′ → rec T′ (≤-step (≤-stepsˡ _ T′<U′))) U<:U′ U′<:U″)

    <:′-trans-rec (Π S′ ∙ U′) rec (Π<: S′<:S″ U″<:U′) (Π<: S‴<:S′ U′<:U‴)
      = Π<: (<:′-trans-rec S′ (λ T′ T′<S′ → rec T′ (≤-step (≤-stepsʳ _ T′<S′))) S‴<:S′ S′<:S″)
            (<:′-trans-rec U′ (λ T′ T′<U′ → rec T′ (≤-step (≤-stepsˡ _ T′<U′)))
                           (proj₂ (rec (S′ ↑ 0) (s≤s $ ≤-stepsʳ _ $ ≤-reflexive (Typ-measure-↑ S′ 0)))
                                  U″<:U′
                                  ≺[ _ , S‴<:S′ ]
                                  refl)
                           U′<:U‴)

    <:′-trans-rec T rec ⊥<: T<:U                     = ⊥<:
    <:′-trans-rec T rec refl T<:U                    = T<:U
    <:′-trans-rec T rec S<:T <:⊤                     = <:⊤
    <:′-trans-rec T rec S<:T refl                    = S<:T
    <:′-trans-rec T rec (sel₂ T′∈Γ T′<:B) T<:U       = sel₂ T′∈Γ (⟨A<:⟩-traverseʳ T rec T<:U T′<:B _ refl)
    <:′-trans-rec T rec (<:> T′∈Γ T′<:B T′<:B′) T<:U = <:> T′∈Γ T′<:B (⟨A<:⟩-traverseʳ T rec T<:U T′<:B′ _ refl)
    <:′-trans-rec T rec S<:T (sel₁ T′∈Γ T′<:B)       = sel₁ T′∈Γ (⟨A<:⟩-traverseˡ T rec S<:T T′<:B [] refl)
    <:′-trans-rec T rec S<:T (<:> T′∈Γ T′<:B T′<:B′) = <:> T′∈Γ (⟨A<:⟩-traverseˡ T rec S<:T T′<:B [] refl) T′<:B′

    ⟨A<:⟩-traverseʳ : ∀ T →
                        (∀ T′ → Typ-measure T′ < Typ-measure T → trans-on T′ × narrow-on T′) →
                      ∀ {Γ U} →
                        Γ ⊢″ T <: U →
                      ∀ {S S′ T′} →
                        Γ ⊢″ S <: ⟨A: S′ ⋯ T′ ⟩ →
                      ∀ l →
                        T′ ≡ ⟨A:⟩-layer T l →
                        Γ ⊢″ S <: ⟨A: S′ ⋯ ⟨A:⟩-layer U l ⟩
    ⟨A<:⟩-traverseʳ T rec T<:U ⊥<: l eqT′                        = ⊥<:
    ⟨A<:⟩-traverseʳ T rec T<:U refl [] refl                      = bnd refl T<:U
    ⟨A<:⟩-traverseʳ T rec T<:U refl (S′ ∷ l) refl                = bnd refl (⟨A<:⟩-traverseʳ T rec T<:U refl l refl)
    ⟨A<:⟩-traverseʳ T rec T<:U (bnd S₂<:S₁ U₁<:T) [] refl        = bnd S₂<:S₁ (<:′-trans-rec T rec U₁<:T T<:U)
    ⟨A<:⟩-traverseʳ T rec T<:U (bnd S₂<:S₁ U₁<:U₂) (S′ ∷ l) refl = bnd S₂<:S₁ (⟨A<:⟩-traverseʳ T rec T<:U U₁<:U₂ l refl)
    ⟨A<:⟩-traverseʳ T rec T<:U (sel₂ T′∈Γ T′<:B) l refl          = sel₂ T′∈Γ (⟨A<:⟩-traverseʳ T rec T<:U T′<:B (_ ∷ l) refl)
    ⟨A<:⟩-traverseʳ T rec T<:U (<:> T′∈Γ T′<:B T′<:B′) l refl    = <:> T′∈Γ T′<:B (⟨A<:⟩-traverseʳ T rec T<:U T′<:B′ (_ ∷ l) refl)

    ⟨A<:⟩-traverseˡ : ∀ T →
                        (∀ T′ → Typ-measure T′ < Typ-measure T → trans-on T′ × narrow-on T′) →
                      ∀ {Γ S} →
                        Γ ⊢″ S <: T →
                      ∀ {S′ T′} →
                        Γ ⊢″ S′ <: T′ →
                      ∀ {U} l →
                        T′ ≡ ⟨A:⟩-layer ⟨A: T ⋯ U ⟩ l →
                        Γ ⊢″ S′ <: ⟨A:⟩-layer ⟨A: S ⋯ U ⟩ l
    ⟨A<:⟩-traverseˡ T rec S<:T <:⊤ [] ()
    ⟨A<:⟩-traverseˡ T rec S<:T <:⊤ (_ ∷ l) ()
    ⟨A<:⟩-traverseˡ T rec S<:T ⊥<: l eqT′                        = ⊥<:
    ⟨A<:⟩-traverseˡ T rec S<:T refl [] refl                      = bnd S<:T refl
    ⟨A<:⟩-traverseˡ T rec S<:T refl (S′ ∷ l) refl                = bnd refl (⟨A<:⟩-traverseˡ T rec S<:T refl l refl)
    ⟨A<:⟩-traverseˡ T rec S<:T (bnd T<:S₁ U₁<:U₂) [] refl        = bnd (<:′-trans-rec T rec S<:T T<:S₁) U₁<:U₂
    ⟨A<:⟩-traverseˡ T rec S<:T (bnd S₂<:S₁ U₁<:U₂) (S′ ∷ l) refl = bnd S₂<:S₁ (⟨A<:⟩-traverseˡ T rec S<:T U₁<:U₂ l refl)
    ⟨A<:⟩-traverseˡ T rec S<:T (Π<: S₂<:S₁ U₁<:U₂) [] ()
    ⟨A<:⟩-traverseˡ T rec S<:T (Π<: S₂<:S₁ U₁<:U₂) (_ ∷ l) ()
    ⟨A<:⟩-traverseˡ T rec S<:T (sel₁ T′∈Γ T′<:B) [] ()
    ⟨A<:⟩-traverseˡ T rec S<:T (sel₁ T′∈Γ T′<:B) (_ ∷ l) ()
    ⟨A<:⟩-traverseˡ T rec S<:T (sel₂ T′∈Γ T′<:B) {U} l refl      = sel₂ T′∈Γ (⟨A<:⟩-traverseˡ T rec S<:T T′<:B {U} (_ ∷ l) refl)
    ⟨A<:⟩-traverseˡ T rec S<:T (<:> T′∈Γ T′<:B T′<:B′) l refl    = <:> T′∈Γ T′<:B (⟨A<:⟩-traverseˡ T rec S<:T T′<:B′ (_ ∷ l) refl)

  <:″-narrow-on : ∀ T → (∀ T′ → Typ-measure T′ ≡ Typ-measure T → trans-on T′) → narrow-on T
  <:″-narrow-on T trans <:⊤ Γ′≺Γ T∈Γ               = <:⊤
  <:″-narrow-on T trans ⊥<: Γ′≺Γ T∈Γ               = ⊥<:
  <:″-narrow-on T trans refl Γ′≺Γ T∈Γ              = refl
  <:″-narrow-on T trans (bnd S′<:S U<:U′) Γ′≺Γ T∈Γ = bnd (<:″-narrow-on T trans S′<:S Γ′≺Γ T∈Γ)
                                                         (<:″-narrow-on T trans U<:U′ Γ′≺Γ T∈Γ)
  <:″-narrow-on T trans {Γ} {Γ′} {n} (Π<: {S′ = S′} S′<:S U<:U′) Γ′≺Γ T∈Γ
    = Π<: (<:″-narrow-on T trans S′<:S Γ′≺Γ T∈Γ)
          (<:″-narrow-on (T ↑ 0)
                         (λ T′ eq → trans T′ (≡.trans eq (Typ-measure-↑ T 0)))
                         U<:U′ (_ ∷ Γ′≺Γ)
                         (↦∈⇒lookup (tl {n} {T′ = S′} {Γ} (lookup⇒↦∈ T∈Γ))))

  <:″-narrow-on T trans (sel₁ T′∈Γ T′<:B) Γ′≺Γ T∈Γ
    with <:∈-find′ T′∈Γ Γ′≺Γ
  ...  | inj₁ (refl , T″ , T″∈Γ′ , T″<:T)
    rewrite just-injective (≡.trans (≡.sym T′∈Γ) T∈Γ) = sel₁ T″∈Γ′ (trans T refl T″<:T (<:″-narrow-on T trans T′<:B Γ′≺Γ T∈Γ))
  ... | inj₂ (x≢n , T′∈Γ′)                            = sel₁ T′∈Γ′ (<:″-narrow-on T trans T′<:B Γ′≺Γ T∈Γ)
  <:″-narrow-on T trans (sel₂ T′∈Γ T′<:B) Γ′≺Γ T∈Γ
    with <:∈-find′ T′∈Γ Γ′≺Γ
  ...  | inj₁ (refl , T″ , T″∈Γ′ , T″<:T)
    rewrite just-injective (≡.trans (≡.sym T′∈Γ) T∈Γ) = sel₂ T″∈Γ′ (trans T refl T″<:T (<:″-narrow-on T trans T′<:B Γ′≺Γ T∈Γ))
  ... | inj₂ (x≢n , T′∈Γ′)                            = sel₂ T′∈Γ′ (<:″-narrow-on T trans T′<:B Γ′≺Γ T∈Γ)
  <:″-narrow-on T trans (<:> T′∈Γ T′<:B T′<:B′) Γ′≺Γ T∈Γ
    with <:∈-find′ T′∈Γ Γ′≺Γ
  ...  | inj₁ (refl , T″ , T″∈Γ′ , T″<:T)
    rewrite just-injective (≡.trans (≡.sym T′∈Γ) T∈Γ) = <:> T″∈Γ′
                                                            (trans T refl T″<:T (<:″-narrow-on T trans T′<:B Γ′≺Γ T∈Γ))
                                                            (trans T refl T″<:T (<:″-narrow-on T trans T′<:B′ Γ′≺Γ T∈Γ))
  ... | inj₂ (x≢n , T′∈Γ′)                            = <:> T′∈Γ′
                                                            (<:″-narrow-on T trans T′<:B Γ′≺Γ T∈Γ)
                                                            (<:″-narrow-on T trans T′<:B′ Γ′≺Γ T∈Γ)

  -- The previous functions achieve the inductive hypotheses of the well-founded
  -- induction on the size of the type T, and the following function combines those
  -- inductive hypotheses and conclude transitivity and narrowing.
  <:″-trans-narrow : ∀ T → trans-on T × narrow-on T
  <:″-trans-narrow = wfRec _ aux
    where open Measure <-wellFounded Typ-measure
          aux : ∀ T → (∀ T′ → Typ-measure T′ < Typ-measure T → trans-on T′ × narrow-on T′) → trans-on T × narrow-on T
          aux T rec = <:′-trans-rec T rec
                    , <:″-narrow-on T (λ T′ T′≡T →
                                         <:′-trans-rec T′ λ T″ T″<T′ → rec T″ (≤-trans T″<T′ (≤-reflexive T′≡T)))

  <:″-trans : ∀ {T} → trans-on T
  <:″-trans {T} = proj₁ (<:″-trans-narrow T)

  <:″-narrow : ∀ {T} → narrow-on T
  <:″-narrow {T} = proj₂ (<:″-trans-narrow T)

open SimplerTransitivity using (<:″-trans ; <:″-narrow) public

-- the following two functions show that the original D<: and D<: normal form are
-- equivalent.
<:⇒<:″ : ∀ {Γ S U} → Γ ⊢ S <: U → Γ ⊢″ S <: U
<:⇒<:″ dtop                 = <:⊤
<:⇒<:″ dbot                 = ⊥<:
<:⇒<:″ drefl                = refl
<:⇒<:″ (dtrans T S<:T T<:U) = <:″-trans (<:⇒<:″ S<:T) (<:⇒<:″ T<:U)
<:⇒<:″ (dbnd S′<:S U<:U′)   = bnd (<:⇒<:″ S′<:S) (<:⇒<:″ U<:U′)
<:⇒<:″ (dall S′<:S U<:U′)   = Π<: (<:⇒<:″ S′<:S) (<:⇒<:″ U<:U′)
<:⇒<:″ (dsel₁ T∈Γ T<:B)     = sel₁ T∈Γ (<:⇒<:″ T<:B)
<:⇒<:″ (dsel₂ T∈Γ T<:B)     = sel₂ T∈Γ (<:⇒<:″ T<:B)

<:″⇒<: : ∀ {Γ S U} → Γ ⊢″ S <: U → Γ ⊢ S <: U
<:″⇒<: <:⊤                  = dtop
<:″⇒<: ⊥<:                  = dbot
<:″⇒<: refl                 = drefl
<:″⇒<: (bnd S′<:S U<:U′)    = dbnd (<:″⇒<: S′<:S) (<:″⇒<: U<:U′)
<:″⇒<: (Π<: S′<:S U<:U′)    = dall (<:″⇒<: S′<:S) (<:″⇒<: U<:U′)
<:″⇒<: (sel₁ T∈Γ T<:B)      = dsel₁ T∈Γ (<:″⇒<: T<:B)
<:″⇒<: (sel₂ T∈Γ T<:B)      = dsel₂ T∈Γ (<:″⇒<: T<:B)
<:″⇒<: (<:> T∈Γ T<:B S<:B′) = dtrans _ (dsel₁ T∈Γ (dtrans _ (<:″⇒<: T<:B) (dbnd drefl dtop)))
                                       (dsel₂ T∈Γ (dtrans _ (<:″⇒<: S<:B′) (dbnd dbot drefl)))
-- D<: subtyping is undecidable.
module Undecidability′ where
  open import DsubReduced
  open FsubMinusToDsubR using (⟦_⟧ ; ⟪_⟫ ; D<:ᵣ⇒F<: ; F<:⇒D<:ᵣ ; ⟪⟫-contraEnv ; ⟦⟧-covar)
  open DsubInvProperties contraInvertible
  open import FsubMinus

  <:″⇒<:ᵣ : ∀ {Γ S U} →
              Γ ⊢″ S <: U →
              ContraEnv Γ → Covar S → Covar U →
              Γ ⊢ᵣ S <: U
  <:″⇒<:ᵣ <:⊤ cΓ cS cU                                           = drtop cS
  <:″⇒<:ᵣ ⊥<: cΓ () cU
  <:″⇒<:ᵣ refl cΓ cS cU                                          = drrefl cU
  <:″⇒<:ᵣ (bnd S′<:S U<:U′) cΓ cS ()
  <:″⇒<:ᵣ (Π<: <:⊤ U<:U′) cΓ () cU
  <:″⇒<:ᵣ (Π<: ⊥<: U<:U′) cΓ cS ()
  <:″⇒<:ᵣ (Π<: refl U<:U′) cΓ (cvΠ cS cU) (cvΠ cS′ cU′)          = drall cS′ cU cS′ cU′
                                                                         (drrefl cS′)
                                                                         (<:″⇒<:ᵣ U<:U′ (ctt cS′ ∷ cΓ) cU cU′)
  <:″⇒<:ᵣ (Π<: (bnd _ S′<:S) U<:U′) cΓ (cvΠ cS cU) (cvΠ cS′ cU′) = drall cS cU cS′ cU′
                                                                         (<:″⇒<:ᵣ S′<:S cΓ cS′ cS)
                                                                         (<:″⇒<:ᵣ U<:U′ (ctt cS′ ∷ cΓ) cU cU′)
  <:″⇒<:ᵣ (Π<: (Π<: S′<:S S′<:S₁) U<:U′) cΓ () cU
  <:″⇒<:ᵣ (Π<: (sel₁ x S′<:S) U<:U′) cΓ () cU
  <:″⇒<:ᵣ (Π<: (sel₂ x S′<:S) U<:U′) cΓ cS ()
  <:″⇒<:ᵣ (Π<: (<:> T∈Γ T<:B T<:B′) U<:U′) cΓ (cvΠ _ _) (cvΠ _ _)
    with lookupContraEnv T∈Γ cΓ
  ... | ctt _                                                    = case ⟨A:⟩<:⟨A:⟩′ (<:″⇒<: T<:B) cΓ of λ ()
  <:″⇒<:ᵣ (sel₁ T∈Γ T<:B) cΓ cS cU
    with lookupContraEnv T∈Γ cΓ
  ... | ctt _  rewrite ⟨A:⟩<:⟨A:⟩′ (<:″⇒<: T<:B) cΓ              = case cS of λ ()
  <:″⇒<:ᵣ (sel₂ T∈Γ T<:B) cΓ cS cU
    with lookupContraEnv T∈Γ cΓ
  ... | ctt cT                                                   = drsel T∈Γ cT (aux T<:B cΓ cT cU)
    where aux : ∀ {Γ T S U} → Γ ⊢″ ⟨A: ⊥ ⋯ T ⟩ <: ⟨A: S ⋯ U ⟩ → ContraEnv Γ → Covar T → Covar U → Γ ⊢ᵣ T <: U
          aux refl cΓ cT cU                                      = drrefl cU
          aux (bnd _ T<:U) cΓ cT cU                              = <:″⇒<:ᵣ T<:U cΓ cT cU
          aux (<:> T′∈Γ T′<:B _) cΓ cT cU
            with lookupContraEnv T′∈Γ cΓ
          ... | ctt _                                            = case ⟨A:⟩<:⟨A:⟩′ (<:″⇒<: T′<:B) cΓ of λ ()
  <:″⇒<:ᵣ (<:> T∈Γ T<:B T<:B′) cΓ cS cU
    with lookupContraEnv T∈Γ cΓ
  ... | ctt _  rewrite ⟨A:⟩<:⟨A:⟩′ (<:″⇒<: T<:B) cΓ              = case cS of λ ()

  <:ᵣ⇒<: : ∀ {Γ S U} → Γ ⊢ᵣ S <: U → Γ ⊢ S <: U
  <:ᵣ⇒<: (drtop _)                   = dtop
  <:ᵣ⇒<: (drrefl _)                  = drefl
  <:ᵣ⇒<: (drall _ _ _ _ S′<:S U<:U′) = dall (dbnd dbot (<:ᵣ⇒<: S′<:S)) (<:ᵣ⇒<: U<:U′)
  <:ᵣ⇒<: (drsel T∈Γ _ T<:B)          = dtrans _ (dsel₂ T∈Γ drefl) (<:ᵣ⇒<: T<:B)

  open FsubMinus.FsubMinus

  F<:⇒D<: : ∀ {Γ S U} → Γ ⊢F S <: U → ⟪ Γ ⟫ ⊢ ⟦ S ⟧ <: ⟦ U ⟧
  F<:⇒D<: = <:ᵣ⇒<: ∘ F<:⇒D<:ᵣ

  D<:⇒F<: : ∀ {Γ S U} → ⟪ Γ ⟫ ⊢ ⟦ S ⟧ <: ⟦ U ⟧ → Γ ⊢F S <: U
  D<:⇒F<: S<:U = D<:ᵣ⇒F<: (<:″⇒<:ᵣ (<:⇒<:″ S<:U) (⟪⟫-contraEnv _) (⟦⟧-covar _) (⟦⟧-covar _))
                          refl refl refl


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

open import Syntax
open import Renaming
open import Substitution
open import Instantiation

module SyntaxMap where

  open Signature
  open Expression
  open Equality

  infix 5 _→ᵐ_

  -- syntax map

  _→ᵐ_ : Signature → Signature → Set
  𝕊 →ᵐ 𝕋 = ∀ {cl} (S : symb 𝕊 cl) → Expr 𝕋 (obj cl) (symb-arg 𝕊 S) 𝟘

  -- equality of syntax maps

  infix 4 _≈ᵐ_

  _≈ᵐ_ : ∀ {𝕊 𝕋} (f g : 𝕊 →ᵐ 𝕋) → Set
  _≈ᵐ_ {𝕊 = 𝕊} {𝕋 = 𝕋} f g =
    ∀ {cl} (S : symb 𝕊 cl) → f S ≈ g S

  -- equality is an equivalence relation

  ≈ᵐ-refl : ∀ {𝕊 𝕋} {f : 𝕊 →ᵐ 𝕋} → f ≈ᵐ f
  ≈ᵐ-refl {𝕋 = 𝕋} S = ≈-refl

  ≈ᵐ-sym : ∀ {𝕊 𝕋} {f g : 𝕊 →ᵐ 𝕋} → f ≈ᵐ g → g ≈ᵐ f
  ≈ᵐ-sym {𝕋 = 𝕋} ξ S = ≈-sym (ξ S)

  ≈ᵐ-trans : ∀ {𝕊 𝕋} {f g h : 𝕊 →ᵐ 𝕋} → f ≈ᵐ g → g ≈ᵐ h → f ≈ᵐ h
  ≈ᵐ-trans {𝕋 = 𝕋} ζ ξ S = ≈-trans (ζ S) (ξ S)

  -- The identity raw syntax map

  𝟙ᵐ : ∀ {𝕊} → (𝕊 →ᵐ 𝕊)
  𝟙ᵐ {𝕊} S = expr-symb S (expr-meta-generic 𝕊)

  -- Action of a raw syntax map

  infixr 10 [_]ᵐ_

  [_]ᵐ_ : ∀ {𝕊 𝕋} → (𝕊 →ᵐ 𝕋) → ∀ {cl 𝕄 γ} → Expr 𝕊 𝕄 cl γ → Expr 𝕋 𝕄 cl γ
  [ f ]ᵐ (expr-var x) = expr-var x
  [_]ᵐ_ {𝕋 = 𝕋} f {𝕄 = 𝕄} (expr-symb S es) =
    [ (λ M → [ f ]ᵐ es M) ]ⁱ ([ 𝟘-initial ]ʳ f S)
  [ f ]ᵐ (expr-meta M ts) = expr-meta M (λ i → [ f ]ᵐ (ts i))
  [ f ]ᵐ expr-eqty = expr-eqty
  [ f ]ᵐ expr-eqtm = expr-eqtm

  -- Action preserves equality

  []ᵐ-resp-≈ : ∀ {𝕊 𝕋} {cl 𝕄 γ} (f : 𝕊 →ᵐ 𝕋) {t u : Expr 𝕊 𝕄 cl γ} →
               t ≈ u → [ f ]ᵐ t ≈ [ f ]ᵐ u
  []ᵐ-resp-≈ f (≈-≡ ξ) = ≈-≡ (cong ([ f ]ᵐ_) ξ)
  []ᵐ-resp-≈ {𝕋 = 𝕋} f (≈-symb {S = S} ξ) =
    []ⁱ-resp-≈ⁱ ([ 𝟘-initial ]ʳ f S) λ M → []ᵐ-resp-≈ f (ξ M)
  []ᵐ-resp-≈ f (≈-meta ξ) = ≈-meta (λ i → []ᵐ-resp-≈ f (ξ i))

  []ᵐ-resp-≈ᵐ : ∀ {𝕊 𝕋} {cl 𝕄 γ} {f g : 𝕊 →ᵐ 𝕋} (t : Expr 𝕊 𝕄 cl γ) →
               f ≈ᵐ g → [ f ]ᵐ t ≈ [ g ]ᵐ t
  []ᵐ-resp-≈ᵐ (expr-var x) ξ = ≈-refl
  []ᵐ-resp-≈ᵐ {f = f} {g = g} (expr-symb S es) ξ =
     []ⁱ-resp-≈ⁱ-≈ {I = λ M → [ f ]ᵐ es M} {J = λ M → [ g ]ᵐ es M}
       (λ M → []ᵐ-resp-≈ᵐ (es M) ξ)
       ([]ʳ-resp-≈ 𝟘-initial (ξ S))
  []ᵐ-resp-≈ᵐ (expr-meta M ts) ξ = ≈-meta (λ i → []ᵐ-resp-≈ᵐ (ts i) ξ)
  []ᵐ-resp-≈ᵐ expr-eqty ξ = ≈-eqty
  []ᵐ-resp-≈ᵐ expr-eqtm ξ = ≈-eqtm

  []ᵐ-resp-≈ᵐ-≈ : ∀ {𝕊 𝕋} {cl 𝕄 γ} {f g : 𝕊 →ᵐ 𝕋} {t u : Expr 𝕊 𝕄 cl γ} →
                  f ≈ᵐ g → t ≈ u → [ f ]ᵐ t ≈ [ g ]ᵐ u
  []ᵐ-resp-≈ᵐ-≈ {g = g} {t = t} ζ ξ = ≈-trans ([]ᵐ-resp-≈ᵐ t ζ) ([]ᵐ-resp-≈ g ξ)

  -- Composition of raw syntax maps

  infixl 7 _∘ᵐ_

  _∘ᵐ_ : ∀ {𝕊 𝕋 𝕌} → (𝕋 →ᵐ 𝕌) → (𝕊 →ᵐ 𝕋) → (𝕊 →ᵐ 𝕌)
  (g ∘ᵐ f) S =  [ g ]ᵐ (f S)

  -- Action preserves identity
  module _ {𝕊} where
    open Equality
    open Renaming
    open Substitution

    [𝟙ᵐ] : ∀ {cl 𝕄 γ} (t : Expr 𝕊 cl 𝕄 γ) → [ 𝟙ᵐ ]ᵐ t ≈ t
    [𝟙ᵐ] (expr-var x) = ≈-refl
    [𝟙ᵐ] (expr-symb S es) =
      ≈-symb (λ {cⁱ γⁱ} i → [𝟙ᵐ]-arg cⁱ γⁱ i)
        where [𝟙ᵐ]-arg : ∀ cⁱ γⁱ (i : [ cⁱ , γⁱ ]∈ symb-arg 𝕊 S) → _
              [𝟙ᵐ]-arg (obj x) γⁱ i =
                ≈-trans
                  ([]ˢ-resp-≈ _ ([]ʳ-resp-≈ _ ([𝟙ᵐ] (es i))))
                  (≈-trans (≈-sym ([ˢ∘ʳ] (es i))) ([]ˢ-id (λ { (var-left _) → ≈-refl ; (var-right _) → ≈-refl })))
              [𝟙ᵐ]-arg EqTy γⁱ i = ≈-eqty
              [𝟙ᵐ]-arg EqTm γⁱ i = ≈-eqtm
    [𝟙ᵐ] (expr-meta M ts) = ≈-meta λ i → [𝟙ᵐ] (ts i)
    [𝟙ᵐ] expr-eqty = ≈-eqty
    [𝟙ᵐ] expr-eqtm = ≈-eqtm

  -- interaction of maps with instantiation and substitution
  module _ {𝕊 𝕋} where
    open Substitution

    infixl 7 _ᵐ∘ˢ_
    _ᵐ∘ˢ_ : ∀ {𝕊 𝕋} {𝕄 γ δ} (f : 𝕊 →ᵐ 𝕋) (σ : 𝕊 % 𝕄 ∥ γ →ˢ δ) → (𝕋 % 𝕄 ∥ γ →ˢ δ)
    (f ᵐ∘ˢ σ) x = [ f ]ᵐ σ x

    []ᵐ-[]ʳ : ∀ {f : 𝕊 →ᵐ 𝕋} {cl 𝕄 γ δ} {ρ : γ →ʳ δ} (t : Expr 𝕊 cl 𝕄 γ) →
              ([ f ]ᵐ ([ ρ ]ʳ t)) ≈ ([ ρ ]ʳ [ f ]ᵐ t)
    []ᵐ-[]ʳ (expr-var x) = ≈-refl
    []ᵐ-[]ʳ {f = f} {ρ = ρ} (expr-symb S es) =
      ≈-trans
        ([]ⁱ-resp-≈ⁱ ([ 𝟘-initial ]ʳ f S) λ M → []ᵐ-[]ʳ (es M))
        (≈-trans
           ([]ⁱ-resp-≈ⁱ-≈
              {t = [ 𝟘-initial ]ʳ f S}
              {u = [ ρ ]ʳ ([ 𝟘-initial ]ʳ f S)}
              (λ M → ≈-refl)
              (≈-trans ([]ʳ-resp-≡ʳ (f S) (λ {()})) ([∘ʳ] (f S))))
           (≈-sym ([ʳ∘ⁱ] ([ 𝟘-initial ]ʳ f S))))
    []ᵐ-[]ʳ (expr-meta M ts) = ≈-meta (λ i → []ᵐ-[]ʳ (ts i))
    []ᵐ-[]ʳ expr-eqty = ≈-eqty
    []ᵐ-[]ʳ expr-eqtm = ≈-eqtm

    []ᵐ-[]ˢ : ∀ {cl 𝕄 γ δ} {f : 𝕊 →ᵐ 𝕋} {σ : 𝕊 % 𝕄 ∥ γ →ˢ δ} (t : Expr 𝕊 cl 𝕄 γ) →
              [ f ]ᵐ ([ σ ]ˢ t) ≈ [ f ᵐ∘ˢ σ ]ˢ [ f ]ᵐ t
    []ᵐ-[]ˢ (expr-var x) = ≈-refl
    []ᵐ-[]ˢ {f = f} {σ = σ} (expr-symb S es) =
      ≈-trans
        ([]ⁱ-resp-≈ⁱ ([ 𝟘-initial ]ʳ f S) (λ M → []ᵐ-[]ˢ (es M)))
        (≈-trans
           ([]ⁱ-resp-≈ⁱ
              ([ 𝟘-initial ]ʳ f S)
              (λ M → []ˢ-resp-≈ˢ
                       (λ { (var-left x) → []ᵐ-[]ʳ (σ x)
                          ; (var-right _) → ≈-refl})
                       ([ f ]ᵐ es M)))
           (≈-sym ([]ˢ-[]ⁱ {ρ = 𝟘-initial} (f S) λ {()})))
    []ᵐ-[]ˢ (expr-meta M ts) = ≈-meta (λ i → []ᵐ-[]ˢ (ts i))
    []ᵐ-[]ˢ expr-eqty = ≈-eqty
    []ᵐ-[]ˢ expr-eqtm = ≈-eqtm

    infixl 7 _ᵐ∘ⁱ_
    _ᵐ∘ⁱ_ : ∀ {𝕂 𝕄 γ} (f : 𝕊 →ᵐ 𝕋) (I : 𝕊 % 𝕂 →ⁱ 𝕄 ∥ γ) → 𝕋 % 𝕂 →ⁱ 𝕄 ∥ γ
    (f ᵐ∘ⁱ I) M =  [ f ]ᵐ I M

    ⇑ⁱ-resp-ᵐ∘ⁱ : ∀ {𝕂 𝕄 γ δ} {f : 𝕊 →ᵐ 𝕋} {I : 𝕊 % 𝕂 →ⁱ 𝕄 ∥ γ} →
                  ⇑ⁱ {δ = δ} (f ᵐ∘ⁱ I) ≈ⁱ f ᵐ∘ⁱ ⇑ⁱ I
    ⇑ⁱ-resp-ᵐ∘ⁱ {I = I} M = ≈-sym ([]ᵐ-[]ʳ (I M))

    []ᵐ-[]ⁱ : ∀ {cl 𝕂 𝕄 γ} {f : 𝕊 →ᵐ 𝕋} {I : 𝕊 % 𝕂 →ⁱ 𝕄 ∥ γ} (t : Expr 𝕊 cl 𝕂 γ) →
              [ f ]ᵐ ([ I ]ⁱ t) ≈ [ f ᵐ∘ⁱ I ]ⁱ [ f ]ᵐ t
    []ᵐ-[]ⁱ (expr-var x) = ≈-refl
    []ᵐ-[]ⁱ {f = f} {I = I} (expr-symb S es) =
      ≈-trans
        ([]ⁱ-resp-≈ⁱ
           ([ 𝟘-initial ]ʳ f S)
           λ M → ≈-trans
                   ([]ᵐ-[]ⁱ (es M))
                   ([]ⁱ-resp-≈ⁱ ([ f ]ᵐ es M) (≈ⁱ-sym (⇑ⁱ-resp-ᵐ∘ⁱ {I = I}))))
        ([∘ⁱ] ([ 𝟘-initial ]ʳ f S))
    []ᵐ-[]ⁱ {f = f} {I = I} (expr-meta M ts) =
      ≈-trans
        ([]ᵐ-[]ˢ (I M))
        ([]ˢ-resp-≈ˢ (λ { (var-left _) → ≈-refl ; (var-right x) → []ᵐ-[]ⁱ (ts x)}) ([ f ]ᵐ I M))
    []ᵐ-[]ⁱ expr-eqty = ≈-eqty
    []ᵐ-[]ⁱ expr-eqtm = ≈-eqtm

  -- idenity is right-neutral

  []ᵐ-meta-generic : ∀ {𝕊 𝕋} {𝕄 γ} {f : 𝕊 →ᵐ 𝕋} {clᴹ γᴹ} {M : [ clᴹ , γᴹ ]∈ 𝕄} →
                     [ f ]ᵐ (expr-meta-generic 𝕊 {γ = γ} M) ≈ expr-meta-generic 𝕋 M
  []ᵐ-meta-generic {clᴹ = obj _} = ≈-refl
  []ᵐ-meta-generic {clᴹ = EqTy} = ≈-eqty
  []ᵐ-meta-generic {clᴹ = EqTm} = ≈-eqtm

  𝟙ᵐ-right : ∀ {𝕊 𝕋} {f : 𝕊 →ᵐ 𝕋} → f ∘ᵐ 𝟙ᵐ ≈ᵐ f
  𝟙ᵐ-right {f = f} S =
    ≈-trans
      ([]ⁱ-resp-≈ⁱ ([ 𝟘-initial ]ʳ (f S)) λ M → []ᵐ-meta-generic {M = M})
      (≈-trans ([𝟙ⁱ] ([ 𝟘-initial ]ʳ f S)) ([]ʳ-id (λ { ()})))

  -- Action preserves composition

  module _ {𝕊 𝕋 𝕌} where
    [∘ᵐ] : ∀ {f : 𝕊 →ᵐ 𝕋} {g : 𝕋 →ᵐ 𝕌} {cl 𝕄 γ} (t : Expr 𝕊 cl 𝕄 γ) → [ g ∘ᵐ f ]ᵐ t ≈ [ g ]ᵐ [ f ]ᵐ t
    [∘ᵐ] (expr-var x) = ≈-refl
    [∘ᵐ] {f = f} {g = g} (expr-symb S es) =
      ≈-trans
        ([]ⁱ-resp-≈ⁱ-≈ (λ M → [∘ᵐ] (es M)) (≈-sym ([]ᵐ-[]ʳ (f S))))
        (≈-sym ([]ᵐ-[]ⁱ ([ 𝟘-initial ]ʳ f S)))
    [∘ᵐ] (expr-meta M ts) = ≈-meta (λ i → [∘ᵐ] (ts i))
    [∘ᵐ] expr-eqty = ≈-eqty
    [∘ᵐ] expr-eqtm = ≈-eqtm

  -- Associativity of composition

  assocᵐ : ∀ {𝕊 𝕋 𝕌 𝕍} {f : 𝕊 →ᵐ 𝕋} {g : 𝕋 →ᵐ 𝕌} {h : 𝕌 →ᵐ 𝕍} →
           (h ∘ᵐ g) ∘ᵐ f ≈ᵐ h ∘ᵐ (g ∘ᵐ f)
  assocᵐ {f = f} S = [∘ᵐ] (f S)

  -- The category of signatures and syntax maps

  module _ where

   open Categories.Category

   SyntaxMaps : Category (suc zero) zero zero
   SyntaxMaps =
     record
       { Obj = Signature
       ; _⇒_ = _→ᵐ_
       ; _≈_ = _≈ᵐ_
       ; id = 𝟙ᵐ
       ; _∘_ = _∘ᵐ_
       ; assoc = λ {_} {_} {_} {_} {f} {_} {_} {_} S → [∘ᵐ] (f S)
       ; sym-assoc = λ {_} {_} {_} {𝕍} {f} {_} {_} {_} S → ≈-sym ([∘ᵐ] (f S))
       ; identityˡ = λ S → [𝟙ᵐ] _
       ; identityʳ = λ {_} {_} {f} {_} → 𝟙ᵐ-right {f = f}
       ; identity² = λ _ → [𝟙ᵐ] _
       ; equiv = record { refl = λ _ → ≈-refl ; sym = ≈ᵐ-sym ; trans = ≈ᵐ-trans }
       ; ∘-resp-≈ = λ ζ ξ S → []ᵐ-resp-≈ᵐ-≈ ζ (ξ S)
       }


{-# OPTIONS --allow-unsolved-metas #-}
module HasSubstantiveDischarge where

open import OscarPrelude

record HasSubstitution (T : Set) (S : Set) : Set where
  field
    _◃_ : S → T → T

open HasSubstitution ⦃ … ⦄ public

record HasPairUnification (T : Set) (S : Set) : Set where
  field
    ⦃ hasSubstitution ⦄ : HasSubstitution T S

  Property : Set₁
  Property = S × S → Set

  Nothing : Property → Set
  Nothing P = ∀ s → P s → ⊥

  IsPairUnifier : (t₁ t₂ : T) → Property
  IsPairUnifier t₁ t₂ s = let s₁ , s₂ = s in s₁ ◃ t₁ ≡ s₂ ◃ t₂

  field
    unify : (t₁ t₂ : T) → Dec ∘ ∃ $ IsPairUnifier t₁ t₂

open HasPairUnification ⦃ … ⦄ public

open import HasNegation

record HasSubstantiveDischarge (A : Set) : Set₁
 where
  field
    _o≽o_ : A → A → Set

  field
    ⦃ hasNegation ⦄ : HasNegation A

  _⋡_ : A → A → Set
  _⋡_ (+) (-) = ¬ + o≽o -

  field
    ≽-reflexive : (x : A) → x o≽o x
    ≽-consistent : (+ - : A) → + o≽o - → + ⋡ ~ -
    ≽-contrapositive : ∀ x y → ~ x o≽o y → x o≽o ~ y

  ≽-contrapositive' : ∀ x y → x o≽o ~ y → ~ x o≽o y
  ≽-contrapositive' = {!!}

open HasSubstantiveDischarge ⦃ … ⦄ public

{-# DISPLAY HasSubstantiveDischarge._o≽o_ _ = _o≽o_ #-}

open import Membership

record HasGenericSubstantiveDischarge (A : Set) (B : Set) : Set₁
 where
  field
    _≽_ : A → B → Set

open HasGenericSubstantiveDischarge ⦃ … ⦄ public

{-# DISPLAY HasGenericSubstantiveDischarge._≽_ _ = _≽_ #-}

instance HasGenericSubstantiveDischargeReflexive : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → HasGenericSubstantiveDischarge A A
HasGenericSubstantiveDischarge._≽_ HasGenericSubstantiveDischargeReflexive = _o≽o_

instance HasGenericSubstantiveDischargeAList : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → HasGenericSubstantiveDischarge A (List A)
HasGenericSubstantiveDischarge._≽_ (HasGenericSubstantiveDischargeAList {A}) (+) -s = ∃ λ (i : A) → i ∈ -s × (+ ≽ i)

instance HasGenericSubstantiveDischargeListA : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → HasGenericSubstantiveDischarge (List A) A
HasGenericSubstantiveDischarge._≽_ (HasGenericSubstantiveDischargeListA {A}) (+s) (-) = ∃ λ (c : A) → c ∈ +s × c ≽ -

instance HasGenericSubstantiveDischargeListList : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → HasGenericSubstantiveDischarge (List A) (List A)
HasGenericSubstantiveDischarge._≽_ (HasGenericSubstantiveDischargeListList {A}) +s -s = ∀ (i : A) → i ∈ -s → +s ≽ i

⋆≽⋆-reflexive : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → (xs : List A) → xs ≽ xs
⋆≽⋆-reflexive _ i i∈ys⋆ = _ , i∈ys⋆ , ≽-reflexive i

◁_ : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → List A → Set
◁_ {A} +s = ∃ λ (s : A) → s ∈ +s × +s ≽ ~ s

⋪_ : ∀ {A} ⦃ _ : HasSubstantiveDischarge A ⦄ → List A → Set
⋪_ = ¬_ ∘ ◁_

record HasDecidableSubstantiveDischarge (A : Set) : Set₁
 where
  field
    ⦃ hasSubstantiveDischarge ⦄ : HasSubstantiveDischarge A
    _≽?_ : (+ - : A) → Dec $ + o≽o -

open HasDecidableSubstantiveDischarge ⦃ … ⦄ public

{-# DISPLAY HasDecidableSubstantiveDischarge._≽?_ _ = _≽?_ #-}

_⋆≽?_ : ∀ {A} ⦃ _ : HasDecidableSubstantiveDischarge A ⦄ → (+s : List A) → (- : A) → Dec (+s ≽ -)
_⋆≽?_ [] - = no λ { (_ , () , _)}
_⋆≽?_ (+ ∷ +s) - with + ≽? -
… | yes +≽- = yes $ _ , zero , +≽-
… | no +⋡- with +s ⋆≽? -
… | yes (+' , +'∈+s , +'≽-) = yes $ _ , suc +'∈+s , +'≽-
… | no +s⋡- = no λ { (_ , zero , +≽-) → +⋡- +≽-
                   ; (+' , suc +'∈+s , +'≽-) → +s⋡- $ _ , +'∈+s , +'≽- }

_≽⋆?_ : ∀ {A} ⦃ _ : HasDecidableSubstantiveDischarge A ⦄ → (+ : A) → (-s : List A) → Dec (+ ≽ -s)
+ ≽⋆? [] = no (λ { (fst₁ , () , snd₁)})
+ ≽⋆? (- ∷ -s) with + ≽? -
… | yes +≽- = yes (_ , zero , +≽-)
… | no +⋡- with + ≽⋆? -s
… | yes (_ , i∈-s , +≽⋆i) = yes (_ , suc i∈-s , +≽⋆i)
… | no +⋡⋆-s = no (λ { (_ , zero , +≽-) → +⋡- +≽-
                     ; (_ , suc -'∈-s , +≽-') → +⋡⋆-s (_ , -'∈-s , +≽-')})

_⋆≽⋆?_ : ∀ {A} ⦃ _ : HasDecidableSubstantiveDischarge A ⦄ → (+s : List A) → (-s : List A) → Dec (+s ≽ -s)
_⋆≽⋆?_ +s [] = yes λ _ ()
_⋆≽⋆?_ +s (- ∷ -s) with +s ⋆≽? -
… | no +s⋡- = no λ +s≽-∷-s → +s⋡- $ +s≽-∷-s (-) zero
… | yes +s≽- with +s ⋆≽⋆? -s
… | yes +s≽-s = yes λ { _ zero → +s≽-
                      ; -' (suc -'∈-∷-s) → +s≽-s -' -'∈-∷-s }
… | no +s⋡-s = no λ +s≽-∷-s → +s⋡-s λ +' +'∈-s → +s≽-∷-s +' (suc +'∈-s)

◁?_ : ∀ {+} ⦃ _ : HasDecidableSubstantiveDischarge (+) ⦄
            → (+s : List +) → Dec $ ◁ +s
◁? [] = no λ {(_ , () , _)}
◁?_ (+ ∷ +s) with +s ⋆≽? ~ +
… | yes (_ , +'∈+s , +'≽~+) = yes $ _ , zero , _ , suc +'∈+s , +'≽~+
… | no +s⋆⋡~+ with ~ + ≽⋆? +s
… | yes (_ , c∈+s , ~+≽c) = yes (_ , suc c∈+s , _ , zero , ≽-contrapositive (+) _ ~+≽c)
… | no ~+⋡⋆+s with ◁? +s
… | yes (s , s∈+s , c , c∈+s , c≽~s) = yes $ _ , suc s∈+s , _ , suc c∈+s , c≽~s
… | no ⋪+s = no
  λ { (_ , zero , _ , zero , c≽~s) → ≽-consistent (+) (+) (≽-reflexive +) c≽~s
  ; (_ , zero , c , suc c∈+s , c≽~s) → +s⋆⋡~+ (_ , c∈+s , c≽~s)
  ; (s , suc s∈+s , .+ , zero , +≽~s) → ~+⋡⋆+s (_ , s∈+s , ≽-contrapositive' (+) s +≽~s)
  ; (s , suc s∈+s , c , suc c∈+s , c≽~s) → ⋪+s (_ , s∈+s , _ , c∈+s , c≽~s) }


------------------------------------------------------------------------
-- A class of algebraic structures, based on non-recursive simple
-- types, satisfies the property that isomorphic instances of a
-- structure are equal (assuming univalence)
------------------------------------------------------------------------

-- In fact, isomorphism and equality are basically the same thing, and
-- the main theorem can be instantiated with several different
-- "universes", not only the one based on simple types.

-- This module is similar to
-- Univalence-axiom.Isomorphism-is-equality.Simple, but the
-- definitions of isomorphism used below are perhaps closer to the
-- "standard" ones. Carrier types also live in Type rather than Type₁
-- (at the cost of quite a bit of lifting).

-- This module has been developed in collaboration with Thierry
-- Coquand.

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

open import Equality

module Univalence-axiom.Isomorphism-is-equality.Simple.Variant
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Bijection eq as B using (_↔_)
open Derived-definitions-and-properties eq
  renaming (lower-extensionality to lower-ext)
open import Equality.Decision-procedures eq
open import Equivalence eq as Eq using (_≃_)
open import Function-universe eq hiding (id; _∘_)
open import H-level eq
open import H-level.Closure eq
open import Logical-equivalence using (_⇔_; module _⇔_)
open import Preimage eq
open import Prelude as P hiding (id)
open import Univalence-axiom eq

------------------------------------------------------------------------
-- Universes with some extra stuff

-- A record type packing up some assumptions.

record Assumptions : Type₃ where
  field

    -- Univalence at three different levels.

    univ  : Univalence (# 0)
    univ₁ : Univalence (# 1)
    univ₂ : Univalence (# 2)

  abstract

    -- Extensionality.

    ext : Extensionality (# 1) (# 1)
    ext = dependent-extensionality univ₂ univ₁

    ext₀ : Extensionality (# 0) (# 0)
    ext₀ = dependent-extensionality univ₁ univ

-- Universes with some extra stuff.

record Universe : Type₃ where
  field

    -- Codes for something.

    U : Type₁

    -- Interpretation of codes.

    El : U → Type → Type₁

    -- A predicate, possibly specifying what it means for a bijection
    -- to be an isomorphism between two elements.

    Is-isomorphism : ∀ a {B C} → B ↔ C → El a B → El a C → Type₁

    -- El a, seen as a predicate, respects equivalences (assuming
    -- univalence).

    resp : Assumptions → ∀ a {B C} → B ≃ C → El a B → El a C

    -- The resp function respects identities (assuming univalence).

    resp-id : (ass : Assumptions) →
              ∀ a {B} (x : El a B) → resp ass a Eq.id x ≡ x

  -- An alternative definition of Is-isomorphism, (possibly) defined
  -- using univalence.

  Is-isomorphism′ : Assumptions →
                    ∀ a {B C} → B ↔ C → El a B → El a C → Type₁
  Is-isomorphism′ ass a B↔C x y = resp ass a (Eq.↔⇒≃ B↔C) x ≡ y

  field

    -- Is-isomorphism and Is-isomorphism′ are isomorphic (assuming
    -- univalence).

    isomorphism-definitions-isomorphic :
      (ass : Assumptions) →
      ∀ a {B C} (B↔C : B ↔ C) {x y} →
      Is-isomorphism a B↔C x y ↔ Is-isomorphism′ ass a B↔C x y

  -- Another alternative definition of Is-isomorphism, defined using
  -- univalence.

  Is-isomorphism″ : Assumptions →
                    ∀ a {B C} → B ↔ C → El a B → El a C → Type₁
  Is-isomorphism″ ass a B↔C x y =
    subst (El a) (≃⇒≡ univ (Eq.↔⇒≃ B↔C)) x ≡ y
    where open Assumptions ass

  abstract

    -- Every element is isomorphic to itself, transported along the
    -- isomorphism.

    isomorphic-to-itself :
      (ass : Assumptions) → let open Assumptions ass in
      ∀ a {B C} (B↔C : B ↔ C) x →
      Is-isomorphism′ ass a B↔C x
        (subst (El a) (≃⇒≡ univ (Eq.↔⇒≃ B↔C)) x)
    isomorphic-to-itself ass a B↔C x =
      transport-theorem (El a) (resp ass a) (resp-id ass a)
                        univ (Eq.↔⇒≃ B↔C) x
      where open Assumptions ass

    -- Is-isomorphism and Is-isomorphism″ are isomorphic (assuming
    -- univalence).

    isomorphism-definitions-isomorphic₂ :
      (ass : Assumptions) →
      ∀ a {B C} (B↔C : B ↔ C) {x y} →
      Is-isomorphism a B↔C x y ↔ Is-isomorphism″ ass a B↔C x y
    isomorphism-definitions-isomorphic₂ ass a B↔C {x} {y} =
      Is-isomorphism      a B↔C x y  ↝⟨ isomorphism-definitions-isomorphic ass a B↔C ⟩
      Is-isomorphism′ ass a B↔C x y  ↝⟨ ≡⇒↝ _ $ cong (λ z → z ≡ y) $ isomorphic-to-itself ass a B↔C x ⟩□
      Is-isomorphism″ ass a B↔C x y  □

------------------------------------------------------------------------
-- A universe-indexed family of classes of structures

module Class (Univ : Universe) where

  open Universe Univ

  -- Codes for structures.

  Code : Type₃
  Code =
    -- A code.
    Σ U λ a →

    -- A proposition.
    (C : Set (# 0)) → El a ⌞ C ⌟ → Σ Type₁ λ P →
      -- The proposition should be propositional (assuming
      -- univalence).
      Assumptions → Is-proposition P

  -- Interpretation of the codes. The elements of "Instance c" are
  -- instances of the structure encoded by c.

  Instance : Code → Type₁
  Instance (a , P) =
    -- A carrier set.
    Σ (Set (# 0)) λ C →

    -- An element.
    Σ (El a ⌞ C ⌟) λ x →

    -- The element should satisfy the proposition.
    proj₁ (P C x)

  -- The carrier type.

  Carrier : ∀ c → Instance c → Type
  Carrier _ I = ⌞ proj₁ I ⌟

  -- The "element".

  element : ∀ c (I : Instance c) → El (proj₁ c) (Carrier c I)
  element _ I = proj₁ (proj₂ I)

  -- One can prove that two instances of a structure are equal by
  -- proving that the carrier types and "elements" (suitably
  -- transported) are equal (assuming univalence).

  instances-equal↔ :
    Assumptions →
    ∀ c {I₁ I₂} →
    (I₁ ≡ I₂) ↔
    ∃ λ (C-eq : Carrier c I₁ ≡ Carrier c I₂) →
      subst (El (proj₁ c)) C-eq (element c I₁) ≡ element c I₂
  instances-equal↔ ass (a , P)
                   {(C₁ , S₁) , x₁ , p₁} {(C₂ , S₂) , x₂ , p₂} =

    ((C₁ , λ {_ _} → S₁) , x₁ , p₁) ≡ ((C₂ , λ {_ _} → S₂) , x₂ , p₂)  ↔⟨ inverse $ Eq.≃-≡ $ Eq.↔⇒≃ bij ⟩

    ((C₁ , x₁) , ((λ {_ _} → S₁) , p₁)) ≡
    ((C₂ , x₂) , ((λ {_ _} → S₂) , p₂))                                ↝⟨ inverse $ ignore-propositional-component prop ⟩

    ((C₁ , x₁) ≡ (C₂ , x₂))                                            ↝⟨ inverse B.Σ-≡,≡↔≡ ⟩□

    (∃ λ (C-eq : C₁ ≡ C₂) → subst (El a) C-eq x₁ ≡ x₂)                 □

    where
    bij : Instance (a , P) ↔
          Σ (Σ Type (El a)) λ { (C , x) →
          Σ (Is-set C) λ S → proj₁ (P (C , S) x) }
    bij =
      (Σ (Σ Type Is-set) λ { (C , S) →
         Σ (El a C) λ x → proj₁ (P (C , S) x) })    ↝⟨ inverse Σ-assoc ⟩

      (Σ Type λ C → Σ (Is-set C) λ S →
         Σ (El a C) λ x → proj₁ (P (C , S) x))      ↝⟨ ∃-cong (λ _ → ∃-comm) ⟩

      (Σ Type λ C → Σ (El a C) λ x →
         Σ (Is-set C) λ S → proj₁ (P (C , S) x))    ↝⟨ Σ-assoc ⟩□

      (Σ (Σ Type (El a)) λ { (C , x) →
         Σ (Is-set C) λ S → proj₁ (P (C , S) x) })  □

    prop : Is-proposition
             (Σ (Is-set C₂) λ S₂ → proj₁ (P (C₂ , S₂) x₂))
    prop =
      Σ-closure 1
        (H-level-propositional (Assumptions.ext₀ ass) 2)
        (λ S₂ → proj₂ (P (C₂ , S₂) x₂) ass)

  -- Structure isomorphisms.

  Isomorphic : ∀ c → Instance c → Instance c → Type₁
  Isomorphic (a , _) ((C₁ , _) , x₁ , _) ((C₂ , _) , x₂ , _) =
    Σ (C₁ ↔ C₂) λ C₁↔C₂ → Is-isomorphism a C₁↔C₂ x₁ x₂

  abstract

    -- The type of isomorphisms between two instances of a structure
    -- is isomorphic to the type of equalities between the same
    -- instances (assuming univalence).
    --
    -- In short, isomorphism is isomorphic to equality.

    isomorphic↔equal :
      Assumptions →
      ∀ c {I₁ I₂} → Isomorphic c I₁ I₂ ↔ (I₁ ≡ I₂)
    isomorphic↔equal ass c {I₁} {I₂} =

      (∃ λ (C-eq : Carrier c I₁ ↔ Carrier c I₂) →
         Is-isomorphism (proj₁ c) C-eq (element c I₁) (element c I₂))    ↝⟨ ∃-cong (λ C-eq → isomorphism-definitions-isomorphic₂
                                                                                ass (proj₁ c) C-eq) ⟩
      (∃ λ (C-eq : Carrier c I₁ ↔ Carrier c I₂) →
         subst (El (proj₁ c)) (≃⇒≡ univ (Eq.↔⇒≃ C-eq)) (element c I₁) ≡
         element c I₂)                                                   ↝⟨ Σ-cong (Eq.↔↔≃ ext₀ (proj₂ (proj₁ I₁))) (λ _ → _ □) ⟩

      (∃ λ (C-eq : Carrier c I₁ ≃ Carrier c I₂) →
         subst (El (proj₁ c)) (≃⇒≡ univ C-eq) (element c I₁) ≡
         element c I₂)                                                   ↝⟨ inverse $
                                                                              Σ-cong (≡≃≃ univ) (λ C-eq → ≡⇒↝ _ $ sym $
                                                                                cong (λ eq → subst (El (proj₁ c)) eq (element c I₁) ≡
                                                                                element c I₂)
                                                                                (_≃_.left-inverse-of (≡≃≃ univ) C-eq)) ⟩
      (∃ λ (C-eq : Carrier c I₁ ≡ Carrier c I₂) →
         subst (El (proj₁ c)) C-eq (element c I₁) ≡ element c I₂)        ↝⟨ inverse $ instances-equal↔ ass c ⟩□

      (I₁ ≡ I₂)                                                          □

      where open Assumptions ass

    -- The first part of the from component of the preceding lemma is
    -- extensionally equal to a simple function. (The codomain of the
    -- second part is propositional whenever El (proj₁ c) applied to
    -- either carrier type is a set.)

    proj₁-from-isomorphic↔equal :
      ∀ ass c {I J} (I≡J : I ≡ J) →
      proj₁ (_↔_.from (isomorphic↔equal ass c) I≡J) ≡
      _≃_.bijection (≡⇒≃ (cong (proj₁ ∘ proj₁) I≡J))
    proj₁-from-isomorphic↔equal ass (a , P) I≡J =
      let A = Instance (a , P)
          B = Σ (∃ (El a)) λ { (C , x) →
                ∃ λ (S : Is-set C) → proj₁ (P (C , S) x) }
      in

      cong (_≃_.bijection ∘ ≡⇒≃) (
        proj₁ (Σ-≡,≡←≡ (proj₁ (Σ-≡,≡←≡ (cong {B = B}
          (λ { ((C , S) , x , p) → (C , x) , S , p }) I≡J))))            ≡⟨ cong (proj₁ ∘ Σ-≡,≡←≡) $ proj₁-Σ-≡,≡←≡ _ ⟩
        proj₁ (Σ-≡,≡←≡ (cong proj₁ (cong {B = B}
          (λ { ((C , S) , x , p) → (C , x) , S , p }) I≡J)))             ≡⟨ cong (proj₁ ∘ Σ-≡,≡←≡) $
                                                                              cong-∘ {B = B} proj₁
                                                                                (λ { ((C , S) , x , p) → (C , x) , S , p }) _ ⟩
        proj₁ (Σ-≡,≡←≡ (cong {A = A}
          (λ { ((C , S) , x , p) → C , x }) I≡J))                        ≡⟨ proj₁-Σ-≡,≡←≡ _ ⟩
        cong proj₁ (cong {A = A} (λ { ((C , S) , x , p) → C , x }) I≡J)  ≡⟨ cong-∘ {A = A} proj₁ (λ { ((C , S) , x , p) → C , x }) _ ⟩∎
        cong (proj₁ ∘ proj₁) I≡J                                         ∎)

    -- The type of (lifted) isomorphisms between two instances of a
    -- structure is equal to the type of equalities between the same
    -- instances (assuming univalence).
    --
    -- In short, isomorphism is equal to equality.

    isomorphic≡equal :
      Assumptions →
      ∀ c {I₁ I₂} → Isomorphic c I₁ I₂ ≡ (I₁ ≡ I₂)
    isomorphic≡equal ass c {I₁} {I₂} =
      ≃⇒≡ univ₁ $ Eq.↔⇒≃ (isomorphic↔equal ass c)
      where open Assumptions ass

------------------------------------------------------------------------
-- A universe of non-recursive, simple types

-- Codes for types.

infixr 20 _⊗_
infixr 15 _⊕_
infixr 10 _⇾_

data U : Type₁ where
  id prop     : U
  k           : Type → U
  _⇾_ _⊕_ _⊗_ : U → U → U

-- Interpretation of types.

El : U → Type₁ → Type₁
El id      B = B
El prop    B = Proposition (# 0)
El (k A)   B = ↑ _ A
El (a ⇾ b) B = El a B → El b B
El (a ⊕ b) B = El a B ⊎ El b B
El (a ⊗ b) B = El a B × El b B

-- El a preserves equivalences (assuming extensionality).

cast : Extensionality (# 1) (# 1) →
       ∀ a {B C} → B ≃ C → El a B ≃ El a C
cast ext id      B≃C = B≃C
cast ext prop    B≃C = Eq.id
cast ext (k A)   B≃C = Eq.id
cast ext (a ⇾ b) B≃C = →-cong ext (cast ext a B≃C) (cast ext b B≃C)
cast ext (a ⊕ b) B≃C = cast ext a B≃C ⊎-cong cast ext b B≃C
cast ext (a ⊗ b) B≃C = cast ext a B≃C ×-cong cast ext b B≃C

abstract

  -- The cast function respects identities (assuming extensionality).

  cast-id : (ext : Extensionality (# 1) (# 1)) →
            ∀ a {B} → cast ext a (Eq.id {A = B}) ≡ Eq.id
  cast-id ext id      = refl _
  cast-id ext prop    = refl _
  cast-id ext (k A)   = refl _
  cast-id ext (a ⇾ b) = Eq.lift-equality ext $ cong _≃_.to $
                          cong₂ (→-cong ext) (cast-id ext a) (cast-id ext b)
  cast-id ext (a ⊗ b) = Eq.lift-equality ext $ cong _≃_.to $
                          cong₂ _×-cong_ (cast-id ext a) (cast-id ext b)
  cast-id ext (a ⊕ b) =
    cast ext a Eq.id ⊎-cong cast ext b Eq.id  ≡⟨ cong₂ _⊎-cong_ (cast-id ext a) (cast-id ext b) ⟩
    Eq.⟨ [ inj₁ , inj₂ ] , _ ⟩                ≡⟨ Eq.lift-equality ext (apply-ext ext [ refl ∘ inj₁ , refl ∘ inj₂ ]) ⟩∎
    Eq.id                                     ∎

-- The property of being an isomorphism between two elements.

Is-isomorphism : ∀ a {B C} → B ↔ C → El a B → El a C → Type₁
Is-isomorphism id      B↔C = λ x y → _↔_.to B↔C x ≡ y
Is-isomorphism prop    B↔C = λ { (P , _) (Q , _) → ↑ _ (P ⇔ Q) }
Is-isomorphism (k A)   B↔C = λ x y → x ≡ y
Is-isomorphism (a ⇾ b) B↔C = Is-isomorphism a B↔C →-rel
                             Is-isomorphism b B↔C
Is-isomorphism (a ⊕ b) B↔C = Is-isomorphism a B↔C ⊎-rel
                             Is-isomorphism b B↔C
Is-isomorphism (a ⊗ b) B↔C = Is-isomorphism a B↔C ×-rel
                             Is-isomorphism b B↔C

-- Another definition of "being an isomorphism" (defined using
-- extensionality).

Is-isomorphism′ : Extensionality (# 1) (# 1) →
                  ∀ a {B C} → B ↔ C → El a B → El a C → Type₁
Is-isomorphism′ ext a B↔C x y = _≃_.to (cast ext a (Eq.↔⇒≃ B↔C)) x ≡ y

abstract

  -- The two definitions of "being an isomorphism" are "isomorphic"
  -- (in bijective correspondence), assuming univalence.

  isomorphism-definitions-isomorphic :
    (ass : Assumptions) → let open Assumptions ass in
    ∀ a {B C} (B↔C : B ↔ C) {x y} →
    Is-isomorphism a B↔C x y ↔ Is-isomorphism′ ext a B↔C x y

  isomorphism-definitions-isomorphic ass id B↔C {x} {y} =

    (_↔_.to B↔C x ≡ y)  □

  isomorphism-definitions-isomorphic ass prop B↔C {P} {Q} =

    ↑ _ (proj₁ P ⇔ proj₁ Q)  ↝⟨ B.↑↔ ⟩

    (proj₁ P ⇔ proj₁ Q)      ↝⟨ Eq.⇔↔≃ ext₀ (proj₂ P) (proj₂ Q) ⟩

    (proj₁ P ≃ proj₁ Q)      ↔⟨ inverse $ ≡≃≃ univ ⟩

    (proj₁ P ≡ proj₁ Q)      ↝⟨ ignore-propositional-component (H-level-propositional ext₀ 1) ⟩□

    (P ≡ Q)                □

    where open Assumptions ass

  isomorphism-definitions-isomorphic ass (k A) B↔C {x} {y} =

    (x ≡ y) □

  isomorphism-definitions-isomorphic ass (a ⇾ b) B↔C {f} {g} =
    let B≃C = Eq.↔⇒≃ B↔C in

    (∀ x y → Is-isomorphism a B↔C x y →
             Is-isomorphism b B↔C (f x) (g y))                     ↝⟨ ∀-cong ext (λ _ → ∀-cong ext λ _ →
                                                                        →-cong ext (isomorphism-definitions-isomorphic ass a B↔C)
                                                                                (isomorphism-definitions-isomorphic ass b B↔C)) ⟩
    (∀ x y → to (cast ext a B≃C) x ≡ y →
             to (cast ext b B≃C) (f x) ≡ g y)                      ↝⟨ inverse $ ∀-cong ext (λ x →
                                                                        ∀-intro (λ y _ → to (cast ext b B≃C) (f x) ≡ g y) ext) ⟩
    (∀ x → to (cast ext b B≃C) (f x) ≡ g (to (cast ext a B≃C) x))  ↔⟨ Eq.extensionality-isomorphism ext ⟩

    (to (cast ext b B≃C) ∘ f ≡ g ∘ to (cast ext a B≃C))            ↝⟨ inverse $ ∘from≡↔≡∘to ext (cast ext a B≃C) ⟩□

    (to (cast ext b B≃C) ∘ f ∘ from (cast ext a B≃C) ≡ g)          □

    where
    open _≃_
    open Assumptions ass

  isomorphism-definitions-isomorphic ass (a ⊕ b) B↔C {inj₁ x} {inj₁ y} =
    let B≃C = Eq.↔⇒≃ B↔C in

    Is-isomorphism a B↔C x y                 ↝⟨ isomorphism-definitions-isomorphic ass a B↔C ⟩

    (to (cast ext a B≃C) x ≡ y)              ↝⟨ B.≡↔inj₁≡inj₁ ⟩□

    (inj₁ (to (cast ext a B≃C) x) ≡ inj₁ y)  □

    where
    open _≃_
    open Assumptions ass

  isomorphism-definitions-isomorphic ass (a ⊕ b) B↔C {inj₂ x} {inj₂ y} =
    let B≃C = Eq.↔⇒≃ B↔C in

    Is-isomorphism b B↔C x y                 ↝⟨ isomorphism-definitions-isomorphic ass b B↔C ⟩

    (to (cast ext b B≃C) x ≡ y)              ↝⟨ B.≡↔inj₂≡inj₂ ⟩□

    (inj₂ (to (cast ext b B≃C) x) ≡ inj₂ y)  □

    where
    open _≃_
    open Assumptions ass

  isomorphism-definitions-isomorphic ass (a ⊕ b) B↔C {inj₁ x} {inj₂ y} =

    ⊥                  ↝⟨ B.⊥↔uninhabited ⊎.inj₁≢inj₂ ⟩□

    (inj₁ _ ≡ inj₂ _)  □

  isomorphism-definitions-isomorphic ass (a ⊕ b) B↔C {inj₂ x} {inj₁ y} =

    ⊥                  ↝⟨ B.⊥↔uninhabited (⊎.inj₁≢inj₂ ∘ sym) ⟩□

    (inj₂ _ ≡ inj₁ _)  □

  isomorphism-definitions-isomorphic ass (a ⊗ b) B↔C {x , u} {y , v} =
    let B≃C = Eq.↔⇒≃ B↔C in

    Is-isomorphism  a B↔C x y × Is-isomorphism  b B↔C u v        ↝⟨ isomorphism-definitions-isomorphic ass a B↔C ×-cong
                                                                    isomorphism-definitions-isomorphic ass b B↔C ⟩
    (to (cast ext a B≃C) x ≡ y × to (cast ext b B≃C) u ≡ v)      ↝⟨ ≡×≡↔≡ ⟩□

    ((to (cast ext a B≃C) x , to (cast ext b B≃C) u) ≡ (y , v))  □

    where
    open _≃_
    open Assumptions ass

-- The universe above is a "universe with some extra stuff".

simple : Universe
simple = record
  { U              = U
  ; El             = λ a → El a ∘ ↑ _
  ; Is-isomorphism = λ a B↔C → Is-isomorphism a (↑-cong B↔C)
  ; resp           = λ ass a → _≃_.to ∘ cast (ext ass) a ∘ ↑-cong
  ; resp-id        = λ ass a x → cong (λ f → _≃_.to f x) (
                       cast (ext ass) a (↑-cong Eq.id)  ≡⟨ cong (cast (ext ass) a) $ Eq.lift-equality (ext ass) (refl _) ⟩
                       cast (ext ass) a Eq.id           ≡⟨ cast-id (ext ass) a ⟩∎
                       Eq.id                            ∎)
  ; isomorphism-definitions-isomorphic = λ ass a B↔C {x y} →
      Is-isomorphism a (↑-cong B↔C) x y                        ↝⟨ isomorphism-definitions-isomorphic ass a (↑-cong B↔C) ⟩
      (_≃_.to (cast (ext ass) a (Eq.↔⇒≃ (↑-cong B↔C))) x ≡ y)  ↝⟨ ≡⇒↝ _ $ cong (λ eq → _≃_.to (cast (ext ass) a eq) x ≡ y) $
                                                                    Eq.lift-equality (ext ass) (refl _) ⟩□
      (_≃_.to (cast (ext ass) a (↑-cong (Eq.↔⇒≃ B↔C))) x ≡ y)  □
  }
  where open Assumptions

-- Let us use this universe in the examples below.

open Class simple

------------------------------------------------------------------------
-- An example: monoids

monoid : Code
monoid =
  -- Binary operation.
  (id ⇾ id ⇾ id) ⊗

  -- Identity.
  id ,

  λ { (_ , M-set) (_∙_ , e) →

      (-- Left and right identity laws.
       (∀ x → (e ∙ x) ≡ x) ×
       (∀ x → (x ∙ e) ≡ x) ×

       -- Associativity.
       (∀ x y z → (x ∙ (y ∙ z)) ≡ ((x ∙ y) ∙ z))) ,

       -- The laws are propositional (assuming extensionality).
      λ ass → let open Assumptions ass in
        ×-closure 1  (Π-closure ext 1 λ _ →
                      ↑-closure 2 M-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      ↑-closure 2 M-set)
                     (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 M-set)) }

-- The interpretation of the code is reasonable.

Instance-monoid :

  Instance monoid
    ≡
  Σ (Set (# 0)) λ { (M , _) →
  Σ ((↑ _ M → ↑ _ M → ↑ _ M) × ↑ _ M) λ { (_∙_ , e) →
  (∀ x → (e ∙ x) ≡ x) ×
  (∀ x → (x ∙ e) ≡ x) ×
  (∀ x y z → (x ∙ (y ∙ z)) ≡ ((x ∙ y) ∙ z)) }}

Instance-monoid = refl _

-- The notion of isomorphism that we get is also reasonable.

Isomorphic-monoid :
  ∀ {M₁} {S₁ : Is-set M₁} {_∙₁_ e₁ laws₁}
    {M₂} {S₂ : Is-set M₂} {_∙₂_ e₂ laws₂} →

  Isomorphic monoid ((M₁ , S₁) , (_∙₁_ , e₁) , laws₁)
                    ((M₂ , S₂) , (_∙₂_ , e₂) , laws₂)
    ≡
  Σ (M₁ ↔ M₂) λ M₁↔M₂ → let open _↔_ (↑-cong M₁↔M₂) in
  (∀ x y → to x ≡ y → ∀ u v → to u ≡ v → to (x ∙₁ u) ≡ (y ∙₂ v)) ×
  to e₁ ≡ e₂

Isomorphic-monoid = refl _

-- Note that this definition of isomorphism is isomorphic to a more
-- standard one (assuming extensionality).

Isomorphism-monoid-isomorphic-to-standard :
  Extensionality (# 1) (# 1) →
  ∀ {M₁} {S₁ : Is-set M₁} {_∙₁_ e₁ laws₁}
    {M₂} {S₂ : Is-set M₂} {_∙₂_ e₂ laws₂} →

  Isomorphic monoid ((M₁ , S₁) , (_∙₁_ , e₁) , laws₁)
                    ((M₂ , S₂) , (_∙₂_ , e₂) , laws₂)
    ↔
  Σ (M₁ ↔ M₂) λ M₁↔M₂ → let open _↔_ (↑-cong M₁↔M₂) in
  (∀ x y → to (x ∙₁ y) ≡ (to x ∙₂ to y)) ×
  to e₁ ≡ e₂

Isomorphism-monoid-isomorphic-to-standard ext
  {M₁} {S₁} {_∙₁_} {e₁} {M₂ = M₂} {_∙₂_ = _∙₂_} {e₂} =

  (Σ (M₁ ↔ M₂) λ M₁↔M₂ → let open _↔_ (↑-cong M₁↔M₂) in
   (∀ x y → to x ≡ y → ∀ u v → to u ≡ v → to (x ∙₁ u) ≡ (y ∙₂ v)) ×
   to e₁ ≡ e₂)                                                       ↝⟨ inverse $ ∃-cong (λ _ →
                                                                          (∀-cong ext λ _ → ∀-intro (λ _ _ → _) ext) ×-cong (_ □)) ⟩
  (Σ (M₁ ↔ M₂) λ M₁↔M₂ → let open _↔_ (↑-cong M₁↔M₂) in
   (∀ x u v → to u ≡ v → to (x ∙₁ u) ≡ (to x ∙₂ v)) ×
   to e₁ ≡ e₂)                                                       ↝⟨ inverse $ ∃-cong (λ _ →
                                                                          (∀-cong ext λ _ → ∀-cong ext λ _ → ∀-intro (λ _ _ → _) ext)
                                                                            ×-cong
                                                                          (_ □)) ⟩□
  (Σ (M₁ ↔ M₂) λ M₁↔M₂ → let open _↔_ (↑-cong M₁↔M₂) in
   (∀ x u → to (x ∙₁ u) ≡ (to x ∙₂ to u)) ×
   to e₁ ≡ e₂)                                                       □

------------------------------------------------------------------------
-- An example: posets

poset : Code
poset =
  -- The ordering relation.
  (id ⇾ id ⇾ prop) ,

  λ { (P , P-set) Le →

    let _≤_ : ↑ _ P → ↑ _ P → Type
        _≤_ x y = proj₁ (Le x y)
    in

     -- Reflexivity.
    ((∀ x → x ≤ x) ×

     -- Transitivity.
     (∀ x y z → x ≤ y → y ≤ z → x ≤ z) ×

     -- Antisymmetry.
     (∀ x y → x ≤ y → y ≤ x → x ≡ y)) ,

    λ ass → let open Assumptions ass in
      ×-closure 1  (Π-closure (lower-ext (# 0) _ ext) 1 λ _ →
                    proj₂ (Le _ _))
      (×-closure 1 (Π-closure ext                     1 λ _ →
                    Π-closure ext                     1 λ _ →
                    Π-closure (lower-ext (# 0) _ ext) 1 λ _ →
                    Π-closure ext₀                    1 λ _ →
                    Π-closure ext₀                    1 λ _ →
                    proj₂ (Le _ _))
                   (Π-closure ext                     1 λ _ →
                    Π-closure ext                     1 λ _ →
                    Π-closure (lower-ext _ (# 0) ext) 1 λ _ →
                    Π-closure (lower-ext _ (# 0) ext) 1 λ _ →
                    ↑-closure 2 P-set)) }

-- The interpretation of the code is reasonable.

Instance-poset :

  Instance poset
    ≡
  Σ (Set (# 0)) λ { (P , _) →
  Σ (↑ _ P → ↑ _ P → Proposition (# 0)) λ Le →
  let _≤_ : ↑ _ P → ↑ _ P → Type
      _≤_ x y = proj₁ (Le x y)
  in
  (∀ x → x ≤ x) ×
  (∀ x y z → x ≤ y → y ≤ z → x ≤ z) ×
  (∀ x y → x ≤ y → y ≤ x → x ≡ y) }

Instance-poset = refl _

-- The notion of isomorphism that we get is also reasonable. It is the
-- usual notion of "order isomorphism".

Isomorphic-poset :
  ∀ {P₁} {S₁ : Is-set P₁} {Le₁ laws₁}
    {P₂} {S₂ : Is-set P₂} {Le₂ laws₂} →
  let _≤₁_ : ↑ _ P₁ → ↑ _ P₁ → Type
      _≤₁_ x y = proj₁ (Le₁ x y)

      _≤₂_ : ↑ _ P₂ → ↑ _ P₂ → Type
      _≤₂_ x y = proj₁ (Le₂ x y)
  in

  Isomorphic poset ((P₁ , S₁) , Le₁ , laws₁) ((P₂ , S₂) , Le₂ , laws₂)
    ≡
  Σ (P₁ ↔ P₂) λ P₁↔P₂ → let open _↔_ (↑-cong P₁↔P₂) in
  ∀ a b → to a ≡ b → ∀ c d → to c ≡ d → ↑ _ ((a ≤₁ c) ⇔ (b ≤₂ d))

Isomorphic-poset = refl _

------------------------------------------------------------------------
-- An example: discrete fields

-- Discrete fields.

discrete-field : Code
discrete-field =
  -- Addition.
  (id ⇾ id ⇾ id) ⊗

  -- Zero.
  id ⊗

  -- Multiplication.
  (id ⇾ id ⇾ id) ⊗

  -- One.
  id ⊗

  -- Minus.
  (id ⇾ id) ⊗

  -- Multiplicative inverse (a partial operation).
  (id ⇾ k ⊤ ⊕ id) ,

  λ { (_ , F-set) (_+_ , 0# , _*_ , 1# , -_ , _⁻¹) →

       -- Associativity.
      ((∀ x y z → (x + (y + z)) ≡ ((x + y) + z)) ×
       (∀ x y z → (x * (y * z)) ≡ ((x * y) * z)) ×

       -- Commutativity.
       (∀ x y → (x + y) ≡ (y + x)) ×
       (∀ x y → (x * y) ≡ (y * x)) ×

       -- Distributivity.
       (∀ x y z → (x * (y + z)) ≡ ((x * y) + (x * z))) ×

       -- Identity laws.
       (∀ x → (x + 0#) ≡ x) ×
       (∀ x → (x * 1#) ≡ x) ×

       -- Zero and one are distinct.
       0# ≢ 1# ×

       -- Inverse laws.
       (∀ x → (x + (- x)) ≡ 0#) ×
       (∀ x → (x ⁻¹) ≡ inj₁ (lift tt) → x ≡ 0#) ×
       (∀ x y → (x ⁻¹) ≡ inj₂ y → (x * y) ≡ 1#)) ,

      λ ass → let open Assumptions ass in
        ×-closure 1  (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure (lower-ext (# 0) (# 1) ext) 1 λ _ →
                      ⊥-propositional)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)
                     (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 F-set)))))))))) }

-- The interpretation of the code is reasonable.

Instance-discrete-field :

  Instance discrete-field
    ≡
  Σ (Set (# 0)) λ { (F , _) →
  Σ ((↑ _ F → ↑ _ F → ↑ _ F) × ↑ _ F × (↑ _ F → ↑ _ F → ↑ _ F) ×
     ↑ _ F × (↑ _ F → ↑ _ F) × (↑ _ F → ↑ (# 1) ⊤ ⊎ ↑ _ F))
    λ { (_+_ , 0# , _*_ , 1# , -_ , _⁻¹) →
  (∀ x y z → (x + (y + z)) ≡ ((x + y) + z)) ×
  (∀ x y z → (x * (y * z)) ≡ ((x * y) * z)) ×
  (∀ x y → (x + y) ≡ (y + x)) ×
  (∀ x y → (x * y) ≡ (y * x)) ×
  (∀ x y z → (x * (y + z)) ≡ ((x * y) + (x * z))) ×
  (∀ x → (x + 0#) ≡ x) ×
  (∀ x → (x * 1#) ≡ x) ×
  0# ≢ 1# ×
  (∀ x → (x + (- x)) ≡ 0#) ×
  (∀ x → (x ⁻¹) ≡ inj₁ (lift tt) → x ≡ 0#) ×
  (∀ x y → (x ⁻¹) ≡ inj₂ y → (x * y) ≡ 1#) }}

Instance-discrete-field = refl _

-- The notion of isomorphism that we get is also reasonable.

Isomorphic-discrete-field :
  ∀ {F₁} {S₁ : Is-set F₁} {_+₁_ 0₁ _*₁_ 1₁ -₁_ _⁻¹₁ laws₁}
    {F₂} {S₂ : Is-set F₂} {_+₂_ 0₂ _*₂_ 1₂ -₂_ _⁻¹₂ laws₂} →

  Isomorphic discrete-field
    ((F₁ , S₁) , (_+₁_ , 0₁ , _*₁_ , 1₁ , -₁_ , _⁻¹₁) , laws₁)
    ((F₂ , S₂) , (_+₂_ , 0₂ , _*₂_ , 1₂ , -₂_ , _⁻¹₂) , laws₂)
    ≡
  Σ (F₁ ↔ F₂) λ F₁↔F₂ → let open _↔_ (↑-cong F₁↔F₂) in
  (∀ x y → to x ≡ y → ∀ u v → to u ≡ v → to (x +₁ u) ≡ (y +₂ v)) ×
  to 0₁ ≡ 0₂ ×
  (∀ x y → to x ≡ y → ∀ u v → to u ≡ v → to (x *₁ u) ≡ (y *₂ v)) ×
  to 1₁ ≡ 1₂ ×
  (∀ x y → to x ≡ y → to (-₁ x) ≡ (-₂ y)) ×
  (∀ x y → to x ≡ y →
     ((λ _ _ → lift tt ≡ lift tt) ⊎-rel (λ u v → to u ≡ v))
       (x ⁻¹₁) (y ⁻¹₂))

Isomorphic-discrete-field = refl _

------------------------------------------------------------------------
-- An example: vector spaces over discrete fields

-- Vector spaces over a particular discrete field.

vector-space : Instance discrete-field → Code
vector-space ((F , _) , (_+F_ , _ , _*F_ , 1F , _ , _) , _) =
  -- Addition.
  (id ⇾ id ⇾ id) ⊗

  -- Scalar multiplication.
  (k F ⇾ id ⇾ id) ⊗

  -- Zero vector.
  id ⊗

  -- Additive inverse.
  (id ⇾ id) ,

  λ { (_ , V-set) (_+_ , _*_ , 0V , -_) →

       -- Associativity.
      ((∀ u v w → (u + (v + w)) ≡ ((u + v) + w)) ×
       (∀ x y v → (x * (y * v)) ≡ ((x *F y) * v)) ×

       -- Commutativity.
       (∀ u v → (u + v) ≡ (v + u)) ×

       -- Distributivity.
       (∀ x u v → (x * (u + v)) ≡ ((x * u) + (x * v))) ×
       (∀ x y v → ((x +F y) * v) ≡ ((x * v) + (y * v))) ×

       -- Identity laws.
       (∀ v → (v + 0V) ≡ v) ×
       (∀ v → (1F * v) ≡ v) ×

       -- Inverse law.
       (∀ v → (v + (- v)) ≡ 0V)) ,

      λ ass → let open Assumptions ass in
        ×-closure 1  (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
        (×-closure 1 (Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set)
                     (Π-closure ext 1 λ _ →
                      ↑-closure 2 V-set))))))) }

-- The interpretation of the code is reasonable.

Instance-vector-space :
  ∀ {F} {S : Is-set F} {_+F_ 0F _*F_ 1F -F_ _⁻¹F laws} →

  Instance (vector-space
              ((F , S) , (_+F_ , 0F , _*F_ , 1F , -F_ , _⁻¹F) , laws))
    ≡
  Σ (Set (# 0)) λ { (V , _) →
  Σ ((↑ _ V → ↑ _ V → ↑ _ V) × (↑ _ F → ↑ _ V → ↑ _ V) × ↑ _ V ×
     (↑ _ V → ↑ _ V))
    λ { (_+_ , _*_ , 0V , -_) →
  (∀ u v w → (u + (v + w)) ≡ ((u + v) + w)) ×
  (∀ x y v → (x * (y * v)) ≡ ((x *F y) * v)) ×
  (∀ u v → (u + v) ≡ (v + u)) ×
  (∀ x u v → (x * (u + v)) ≡ ((x * u) + (x * v))) ×
  (∀ x y v → ((x +F y) * v) ≡ ((x * v) + (y * v))) ×
  (∀ v → (v + 0V) ≡ v) ×
  (∀ v → (1F * v) ≡ v) ×
  (∀ v → (v + (- v)) ≡ 0V) }}

Instance-vector-space = refl _

-- The notion of isomorphism that we get is also reasonable.

Isomorphic-vector-space :
  ∀ {F V₁} {S₁ : Is-set V₁} {_+₁_ _*₁_ 0₁ -₁_ laws₁}
      {V₂} {S₂ : Is-set V₂} {_+₂_ _*₂_ 0₂ -₂_ laws₂} →

  Isomorphic (vector-space F)
             ((V₁ , S₁) , (_+₁_ , _*₁_ , 0₁ , -₁_) , laws₁)
             ((V₂ , S₂) , (_+₂_ , _*₂_ , 0₂ , -₂_) , laws₂)
    ≡
  Σ (V₁ ↔ V₂) λ V₁↔V₂ → let open _↔_ (↑-cong V₁↔V₂) in
  (∀ a b → to a ≡ b → ∀ u v → to u ≡ v → to (a +₁ u) ≡ (b +₂ v)) ×
  (∀ x y →    x ≡ y → ∀ u v → to u ≡ v → to (x *₁ u) ≡ (y *₂ v)) ×
  to 0₁ ≡ 0₂ ×
  (∀ u v → to u ≡ v → to (-₁ u) ≡ (-₂ v))

Isomorphic-vector-space = refl _

------------------------------------------------------------------------
-- An example: sets equipped with fixpoint operators

set-with-fixpoint-operator : Code
set-with-fixpoint-operator =
  (id ⇾ id) ⇾ id ,

  λ { (_ , F-set) fix →

     -- The fixpoint operator property.
    (∀ f → f (fix f) ≡ fix f) ,

    λ ass → let open Assumptions ass in
      Π-closure ext 1 λ _ →
      ↑-closure 2 F-set }

-- The usual unfolding lemmas.

Instance-set-with-fixpoint-operator :

  Instance set-with-fixpoint-operator
    ≡
  Σ (Set (# 0)) λ { (F , _) →
  Σ ((↑ _ F → ↑ _ F) → ↑ _ F) λ fix →
  ∀ f → f (fix f) ≡ fix f }

Instance-set-with-fixpoint-operator = refl _

Isomorphic-set-with-fixpoint-operator :
  ∀ {F₁} {S₁ : Is-set F₁} {fix₁ law₁}
    {F₂} {S₂ : Is-set F₂} {fix₂ law₂} →

  Isomorphic set-with-fixpoint-operator
             ((F₁ , S₁) , fix₁ , law₁) ((F₂ , S₂) , fix₂ , law₂)
    ≡
  Σ (F₁ ↔ F₂) λ F₁↔F₂ → let open _↔_ (↑-cong F₁↔F₂) in
  ∀ f g → (∀ x y → to x ≡ y → to (f x) ≡ g y) → to (fix₁ f) ≡ fix₂ g

Isomorphic-set-with-fixpoint-operator = refl _