------------------------------------------------------------------------
-- Extra lemmas about substitutions
------------------------------------------------------------------------

module Data.Fin.Substitution.ExtraLemmas where

open import Data.Fin as Fin using (Fin; zero; suc)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Nat using (ℕ; zero; suc; _+_)
open import Data.Star using (Star; ε; _◅_; _▻_)
open import Data.Vec using (Vec; _∷_; lookup; map)
open import Data.Vec.All hiding (lookup; map)
open import Data.Vec.Properties using (lookup-map; map-cong; map-∘)
open import Function using (_∘_; _$_; flip)
open import Relation.Binary.PropositionalEquality as PropEq hiding (subst)
open PropEq.≡-Reasoning

-- Simple extension of substitutions.
--
-- FIXME: this should go into Data.Fin.Substitution.
record Extension (T : ℕ → Set) : Set where
  infixr 5 _/∷_

  field weaken : ∀ {n} → T n → T (suc n)  -- Weakens Ts.

  -- Extension.
  _/∷_ : ∀ {m n} → T (suc n) → Sub T m n → Sub T (suc m) (suc n)
  t /∷ ρ = t ∷ map weaken ρ

-- Helper module
module SimpleExt {T} (simple : Simple T) where
  open Simple simple public

  extension : Extension T
  extension = record { weaken = weaken }
  open Extension extension public hiding (weaken)

-- An generalized version of Data.Fin.Lemmas.Lemmas₀
--
-- FIXME: this should go into Data.Fin.Substitution.Lemmas.
module ExtLemmas₀ {T} (lemmas₀ : Lemmas₀ T) where

  open Lemmas₀   lemmas₀ public hiding (lookup-map-weaken-↑⋆)
  open SimpleExt simple

  -- A generalized variant of Lemmas₀.lookup-map-weaken-↑⋆.
  lookup-map-weaken-↑⋆ : ∀ {m n} k x {ρ : Sub T m n} {t} →
                         lookup x (map weaken ρ ↑⋆ k) ≡
                         lookup (Fin.lift k suc x) ((t /∷ ρ) ↑⋆ k)
  lookup-map-weaken-↑⋆ zero    x               = refl
  lookup-map-weaken-↑⋆ (suc k) zero            = refl
  lookup-map-weaken-↑⋆ (suc k) (suc x) {ρ} {t} = begin
      lookup x (map weaken (map weaken ρ ↑⋆ k))
    ≡⟨ lookup-map x weaken _ ⟩
      weaken (lookup x (map weaken ρ ↑⋆ k))
    ≡⟨ cong weaken (lookup-map-weaken-↑⋆ k x) ⟩
      weaken (lookup (Fin.lift k suc x) ((t /∷ ρ) ↑⋆ k))
    ≡⟨ sym (lookup-map (Fin.lift k suc x) weaken _) ⟩
      lookup (Fin.lift k suc x) (map weaken ((t /∷ ρ) ↑⋆ k))
    ∎

-- A generalized version of Data.Fin.Lemmas.Lemmas₄
--
-- FIXME: this should go into Data.Fin.Substitution.Lemmas.
module ExtLemmas₄ {T} (lemmas₄ : Lemmas₄ T) where

  open Lemmas₄    lemmas₄ public hiding (⊙-wk; wk-commutes)
  open Lemmas₃    lemmas₃        using (lookup-wk-↑⋆-⊙; /✶-↑✶′)
  open SimpleExt  simple         using (_/∷_)
  open ExtLemmas₀ lemmas₀        using (lookup-map-weaken-↑⋆)

  private

    ⊙-wk′ : ∀ {m n} {ρ : Sub T m n} {t} k →
            ρ ↑⋆ k ⊙ wk ↑⋆ k ≡ wk ↑⋆ k ⊙ (t /∷ ρ) ↑⋆ k
    ⊙-wk′ {ρ = ρ} {t} k = sym (begin
      wk ↑⋆ k ⊙ (t /∷ ρ) ↑⋆ k   ≡⟨ lemma ⟩
      map weaken ρ ↑⋆ k         ≡⟨ cong (λ ρ′ → ρ′ ↑⋆ k) map-weaken ⟩
      (ρ ⊙ wk) ↑⋆ k             ≡⟨ ↑⋆-distrib k ⟩
      ρ ↑⋆ k ⊙ wk ↑⋆ k          ∎)
      where
      lemma = extensionality λ x → begin
          lookup x (wk ↑⋆ k ⊙ (t /∷ ρ) ↑⋆ k)
        ≡⟨ lookup-wk-↑⋆-⊙ k ⟩
          lookup (Fin.lift k suc x) ((t /∷ ρ) ↑⋆ k)
        ≡⟨ sym (lookup-map-weaken-↑⋆ k x) ⟩
          lookup x (map weaken ρ ↑⋆ k)
        ∎

  ⊙-wk : ∀ {m n} {ρ : Sub T m n} {t} → ρ ⊙ wk ≡ wk ⊙ (t /∷ ρ)
  ⊙-wk = ⊙-wk′ zero

  wk-commutes : ∀ {m n} {ρ : Sub T m n} {t′} t →
                t / ρ / wk ≡ t / wk / (t′ /∷ ρ)
  wk-commutes {ρ = ρ} {t} = /✶-↑✶′ (ε ▻ ρ ▻ wk) (ε ▻ wk ▻ (t /∷ ρ)) ⊙-wk′ zero

  -- A handy helper lemma: weakening commutes with substitution.
  weaken-/ : ∀ {m n} {ρ : Sub T m n} {t′} t →
             weaken (t / ρ) ≡ weaken t / (t′ /∷ ρ)
  weaken-/ {ρ = ρ} {t′} t = begin
    weaken (t / ρ)         ≡⟨ sym /-wk ⟩
    t / ρ / wk             ≡⟨ wk-commutes t ⟩
    t / wk / (t′ /∷ ρ)     ≡⟨ cong₂ _/_ /-wk refl ⟩
    weaken t / (t′ /∷ ρ)   ∎

-- A generalize version of Data.Fin.Lemmas.AppLemmas
--
-- FIXME: this should go into Data.Fin.Substitution.Lemmas.
module ExtAppLemmas {T₁ T₂} (appLemmas : AppLemmas T₁ T₂) where

  open AppLemmas appLemmas public hiding (wk-commutes)
  open SimpleExt simple           using (_/∷_)
  private module L₄ = ExtLemmas₄ lemmas₄

  wk-commutes : ∀ {m n} {ρ : Sub T₂ m n} {t′} t →
                t / ρ / wk ≡ t / wk / (t′ /∷ ρ)
  wk-commutes {ρ = ρ} {t} = ⨀→/✶ (ε ▻ ρ ▻ wk) (ε ▻ wk ▻ (t /∷ ρ)) L₄.⊙-wk

-- Lemmas relating T₃ substitutions in T₁ and T₂.
record LiftAppLemmas (T₁ T₂ T₃ : ℕ → Set) : Set where
  field
    lift          : ∀ {n} → T₃ n → T₂ n
    application₁₃ : Application T₁ T₃
    application₂₃ : Application T₂ T₃
    lemmas₂       : Lemmas₄     T₂
    lemmas₃       : Lemmas₄     T₃

  private
    module L₂ = ExtLemmas₄  lemmas₂
    module L₃ = ExtLemmas₄  lemmas₃
    module A₁ = Application application₁₃
    module A₂ = Application application₂₃

  field
    -- Lifting commutes with application of T₃ substitutions.
    lift-/ : ∀ {m n} t {σ : Sub T₃ m n} → lift (t L₃./ σ) ≡ lift t A₂./ σ

    -- Lifting preserves variables.
    lift-var : ∀ {n} (x : Fin n) → lift (L₃.var x) ≡ L₂.var x

    -- Sequences of T₃ substitutions are equivalent when applied to
    -- T₁s if they are equivalent when applied to T₂ variables.
    /✶-↑✶ :
      ∀ {m n} (σs₁ σs₂ : Subs T₃ m n) →
      (∀ k x → L₂.var x A₂./✶ σs₁ L₃.↑✶ k ≡ L₂.var x A₂./✶ σs₂ L₃.↑✶ k) →
       ∀ k t → t        A₁./✶ σs₁ L₃.↑✶ k ≡ t      A₁./✶ σs₂ L₃.↑✶ k

  lift-lookup-⊙ : ∀ {m n k} x {σ₁ : Sub T₃ m n} {σ₂ : Sub T₃ n k} →
                  lift (lookup x (σ₁ L₃.⊙ σ₂)) ≡ lift (lookup x σ₁) A₂./ σ₂
  lift-lookup-⊙ x {σ₁} {σ₂} = begin
    lift (lookup x (σ₁ L₃.⊙ σ₂))   ≡⟨ cong lift (L₃.lookup-⊙ x) ⟩
    lift (lookup x σ₁ L₃./ σ₂)     ≡⟨ lift-/ _ ⟩
    lift (lookup x σ₁) A₂./ σ₂     ∎

  lift-lookup-⨀ : ∀ {m n} x (σs : Subs T₃ m n) →
                  lift (lookup x (L₃.⨀ σs)) ≡ L₂.var x A₂./✶ σs
  lift-lookup-⨀ x ε = begin
    lift (lookup x L₃.id)    ≡⟨ cong lift (L₃.lookup-id x) ⟩
    lift (L₃.var x)          ≡⟨ lift-var x ⟩
    L₂.var x                 ∎
  lift-lookup-⨀ x (σ ◅ ε) = begin
    lift (lookup x σ)        ≡⟨ cong lift (sym L₃.var-/) ⟩
    lift (L₃.var x L₃./ σ)   ≡⟨ lift-/ _ ⟩
    lift (L₃.var x) A₂./ σ   ≡⟨ cong₂ A₂._/_ (lift-var x) refl ⟩
    L₂.var x A₂./ σ          ∎
  lift-lookup-⨀ x (σ ◅ (σ′ ◅ σs′)) = begin
      lift (lookup x (L₃.⨀ σs L₃.⊙ σ))
    ≡⟨ lift-lookup-⊙ x ⟩
      lift (lookup x (L₃.⨀ σs)) A₂./ σ
    ≡⟨ cong₂ A₂._/_ (lift-lookup-⨀ x (σ′ ◅ σs′)) refl ⟩
      L₂.var x A₂./✶ σs A₂./ σ
    ∎
    where σs = σ′ ◅ σs′

  -- Sequences of T₃ substitutions are equivalent when applied to
  -- T₁s if they are equivalent when applied as composites.
  /✶-↑✶′ : ∀ {m n} (σs₁ σs₂ : Subs T₃ m n) →
           (∀ k → L₃.⨀ (σs₁ L₃.↑✶ k) ≡ L₃.⨀ (σs₂ L₃.↑✶ k)) →
            ∀ k t → t A₁./✶ σs₁ L₃.↑✶ k ≡ t A₁./✶ σs₂ L₃.↑✶ k
  /✶-↑✶′ σs₁ σs₂ hyp = /✶-↑✶ σs₁ σs₂ (λ k x → begin
      L₂.var x A₂./✶ σs₁ L₃.↑✶ k
    ≡⟨ sym (lift-lookup-⨀ x (σs₁ L₃.↑✶ k)) ⟩
      lift (lookup x (L₃.⨀ (σs₁ L₃.↑✶ k)))
    ≡⟨ cong (lift ∘ lookup x) (hyp k) ⟩
      lift (lookup x (L₃.⨀ (σs₂ L₃.↑✶ k)))
    ≡⟨ lift-lookup-⨀ x (σs₂ L₃.↑✶ k) ⟩
      L₂.var x A₂./✶ σs₂ L₃.↑✶ k
    ∎)

  -- Derived lemmas about applications of T₃ substitutions to T₁s.
  appLemmas : AppLemmas T₁ T₃
  appLemmas = record
    { application = application₁₃
    ; lemmas₄     = lemmas₃
    ; id-vanishes = /✶-↑✶′ (ε ▻ L₃.id) ε L₃.id-↑⋆ 0
    ; /-⊙         = /✶-↑✶′ (ε ▻ _ L₃.⊙ _) (ε ▻ _ ▻ _) L₃.↑⋆-distrib 0
    }
  open ExtAppLemmas appLemmas public
    hiding (application; lemmas₂; lemmas₃; var; weaken; subst; simple)

-- Lemmas relating T₂ and T₃ substitutions in T₁.
record LiftSubLemmas (T₁ T₂ T₃ : ℕ → Set) : Set where
  field
    application₁₂ : Application   T₁ T₂
    liftAppLemmas : LiftAppLemmas T₁ T₂ T₃

  open LiftAppLemmas liftAppLemmas hiding (/✶-↑✶; /-wk)
  private
    module L₃  = ExtLemmas₄  lemmas₃
    module L₂  = ExtLemmas₄  lemmas₂
    module A₁₂ = Application application₁₂
    module A₁₃ = Application (AppLemmas.application appLemmas)
    module A₂₃ = Application application₂₃

  field
    -- Weakening commutes with lifting.
    weaken-lift : ∀ {n} (t : T₃ n) → L₂.weaken (lift t) ≡ lift (L₃.weaken t)

    -- Applying a composition of T₂ substitutions to T₁s
    -- corresponds to two consecutive applications.
    /-⊙₂ : ∀ {m n k} {σ₁ : Sub T₂ m n} {σ₂ : Sub T₂ n k} t →
           t A₁₂./ σ₁ L₂.⊙ σ₂ ≡ t A₁₂./ σ₁ A₁₂./ σ₂

    -- Sequences of T₃ substitutions are equivalent to T₂
    -- substitutions when applied to T₁s if they are equivalent when
    -- applied to variables.
    /✶-↑✶₁ :
      ∀ {m n} (σs₁ : Subs T₃ m n) (σs₂ : Subs T₂ m n) →
      (∀ k x → L₂.var x A₂₃./✶ σs₁ ↑✶ k ≡ L₂.var x L₂./✶  σs₂ L₂.↑✶ k) →
       ∀ k t → t        A₁₃./✶ σs₁ ↑✶ k ≡ t        A₁₂./✶ σs₂ L₂.↑✶ k

    -- Sequences of T₃ substitutions are equivalent to T₂
    -- substitutions when applied to T₂s if they are equivalent when
    -- applied to variables.
    /✶-↑✶₂ :
      ∀ {m n} (σs₁ : Subs T₃ m n) (σs₂ : Subs T₂ m n) →
      (∀ k x → L₂.var x A₂₃./✶ σs₁ ↑✶ k ≡ L₂.var x L₂./✶ σs₂ L₂.↑✶ k) →
       ∀ k t → t        A₂₃./✶ σs₁ ↑✶ k ≡ t        L₂./✶ σs₂ L₂.↑✶ k

  -- Lifting of T₃ substitutions to T₂ substitutions.
  liftSub : ∀ {m n} → Sub T₃ m n → Sub T₂ m n
  liftSub σ = map lift σ

  -- The two types of lifting commute.
  liftSub-↑⋆ : ∀ {m n} (σ : Sub T₃ m n) k →
               liftSub σ L₂.↑⋆ k ≡ liftSub (σ ↑⋆ k)
  liftSub-↑⋆ σ zero    = refl
  liftSub-↑⋆ σ (suc k) = cong₂ _∷_ (sym (lift-var _)) (begin
    map L₂.weaken (liftSub σ L₂.↑⋆ k)   ≡⟨ cong (map _) (liftSub-↑⋆ σ k) ⟩
    map L₂.weaken (map lift (σ ↑⋆ k))   ≡⟨ sym (map-∘ _ _ _) ⟩
    map (L₂.weaken ∘ lift) (σ ↑⋆ k)     ≡⟨ map-cong weaken-lift _ ⟩
    map (lift ∘ L₃.weaken)  (σ ↑⋆ k)    ≡⟨ map-∘ _ _ _ ⟩
    map lift (map L₃.weaken (σ ↑⋆ k))   ∎)

  -- The identity substitutions are equivalent up to lifting.
  liftSub-id : ∀ {n} → liftSub (L₃.id {n}) ≡ L₂.id {n}
  liftSub-id {zero}  = refl
  liftSub-id {suc n} = begin
    liftSub (L₃.id L₃.↑)  ≡⟨ sym (liftSub-↑⋆ L₃.id 1) ⟩
    liftSub L₃.id L₂.↑    ≡⟨ cong L₂._↑ liftSub-id ⟩
    L₂.id ∎

  -- Weakening is equivalent up to lifting.
  liftSub-wk⋆ : ∀ k {n} → liftSub (L₃.wk⋆ k {n}) ≡ L₂.wk⋆ k {n}
  liftSub-wk⋆ zero    = liftSub-id
  liftSub-wk⋆ (suc k) = begin
    liftSub (map L₃.weaken (L₃.wk⋆ k))   ≡⟨ sym (map-∘ _ _ _) ⟩
    map (lift ∘ L₃.weaken) (L₃.wk⋆ k)    ≡⟨ sym (map-cong weaken-lift _) ⟩
    map (L₂.weaken ∘ lift) (L₃.wk⋆ k)    ≡⟨ map-∘ _ _ _ ⟩
    map L₂.weaken (liftSub (L₃.wk⋆ k))   ≡⟨ cong (map _) (liftSub-wk⋆ k) ⟩
    map L₂.weaken (L₂.wk⋆ k)             ∎

  -- Weakening is equivalent up to lifting.
  liftSub-wk : ∀ {n} → liftSub (L₃.wk {n}) ≡ L₂.wk {n}
  liftSub-wk = liftSub-wk⋆ 1

  -- Single variable substitution is equivalent up to lifting.
  liftSub-sub : ∀ {n} (t : T₃ n) → liftSub (L₃.sub t) ≡ L₂.sub (lift t)
  liftSub-sub t = cong₂ _∷_ refl liftSub-id

  -- Lifting commutes with application to variables.
  var-/-liftSub-↑⋆ : ∀ {m n} (σ : Sub T₃ m n) k x →
                     L₂.var x A₂₃./ σ ↑⋆ k ≡ L₂.var x L₂./ liftSub σ L₂.↑⋆ k
  var-/-liftSub-↑⋆ σ k x = begin
      L₂.var x A₂₃./ σ ↑⋆ k
    ≡⟨ cong₂ A₂₃._/_ (sym (lift-var x)) refl ⟩
      lift (L₃.var x) A₂₃./ σ ↑⋆ k
    ≡⟨ sym (lift-/ _) ⟩
      lift (L₃.var x L₃./ σ ↑⋆ k)
    ≡⟨ cong lift L₃.var-/ ⟩
      lift (lookup x (σ ↑⋆ k))
    ≡⟨ sym (lookup-map x _ _) ⟩
      lookup x (liftSub (σ ↑⋆ k))
    ≡⟨ sym L₂.var-/ ⟩
      L₂.var x L₂./ liftSub (σ ↑⋆ k)
    ≡⟨ cong (L₂._/_ (L₂.var x)) (sym (liftSub-↑⋆ σ k)) ⟩
      L₂.var x L₂./ liftSub σ L₂.↑⋆ k
    ∎

  -- Lifting commutes with application.
  /-liftSub : ∀ {m n} t {σ : Sub T₃ m n} →
              t A₁₃./ σ ≡ t A₁₂./ liftSub σ
  /-liftSub t {σ} = /✶-↑✶₁ (ε ▻ σ) (ε ▻ liftSub σ)
                           (var-/-liftSub-↑⋆ σ) zero t

  -- Weakening is equivalent up to choice of application.
  /-wk : ∀ {n} {t : T₁ n} → t A₁₃./ L₃.wk ≡ t A₁₂./ L₂.wk
  /-wk {t = t} = begin
    t A₁₃./ L₃.wk           ≡⟨ /-liftSub t ⟩
    t A₁₂./ liftSub L₃.wk   ≡⟨ cong₂ A₁₂._/_ refl liftSub-wk ⟩
    t A₁₂./ L₂.wk           ∎

  -- Single-variable substitution is equivalent up to choice of
  -- application.
  /-sub : ∀ {n} t (s : T₃ n) →
          t A₁₃./ L₃.sub s ≡ t A₁₂./ L₂.sub (lift s)
  /-sub t s = begin
    t A₁₃./ L₃.sub s             ≡⟨ /-liftSub t ⟩
    t A₁₂./ liftSub (L₃.sub s)   ≡⟨ cong₂ A₁₂._/_ refl (liftSub-sub s) ⟩
    t A₁₂./ L₂.sub (lift s)      ∎

  -- Lifting commutes with application.
  /-sub-↑ : ∀ {m n} t s (σ : Sub T₃ m n) →
            t A₁₂./ L₂.sub s A₁₃./ σ ≡ (t A₁₃./ σ ↑) A₁₂./ L₂.sub (s A₂₃./ σ)
  /-sub-↑ t s σ = begin
      t A₁₂./ L₂.sub s A₁₃./ σ
    ≡⟨ /-liftSub _ ⟩
      t A₁₂./ L₂.sub s A₁₂./ liftSub σ
    ≡⟨ sym (/-⊙₂ t) ⟩
      t A₁₂./ (L₂.sub s L₂.⊙ liftSub σ)
    ≡⟨ cong₂ A₁₂._/_ refl (L₂.sub-⊙ s) ⟩
      t A₁₂./ (liftSub σ L₂.↑ L₂.⊙ L₂.sub (s L₂./ liftSub σ))
    ≡⟨ /-⊙₂ t ⟩
      t A₁₂./ liftSub σ L₂.↑ A₁₂./ L₂.sub (s L₂./ liftSub σ)
    ≡⟨ cong₂ (A₁₂._/_ ∘ A₁₂._/_ t) (liftSub-↑⋆ _ 1)
             (cong L₂.sub (sym (/-liftSub₂ s))) ⟩
      t A₁₂./ liftSub (σ ↑) A₁₂./ L₂.sub (s A₂₃./ σ)
    ≡⟨ cong₂ A₁₂._/_ (sym (/-liftSub t)) refl ⟩
      t A₁₃./ σ ↑ A₁₂./ L₂.sub (s A₂₃./ σ)
    ∎
    where
      /-liftSub₂ : ∀ {m n} s {σ : Sub T₃ m n} →
                   s A₂₃./ σ ≡ s L₂./ liftSub σ
      /-liftSub₂ s {σ} = /✶-↑✶₂ (ε ▻ σ) (ε ▻ liftSub σ)
                                (var-/-liftSub-↑⋆ σ) zero s

-- Lemmas for a term-like T₁ derived from term lemmas for T₂
record TermLikeLemmas (T₁ T₂ : ℕ → Set) : Set₁ where
  field
    app         : ∀ {T₃} → Lift T₃ T₂ → ∀ {m n} → T₁ m → Sub T₃ m n → T₁ n
    termLemmas  : TermLemmas T₂

  open TermLemmas termLemmas  using (termSubst)
  open TermSubst  termSubst   using (var; termLift; varLift; module Lifted)

  field /✶-↑✶₁ : ∀ {T₃} {lift : Lift T₃ T₂} →
                 let open Application (record { _/_ = app lift })
                       using ()     renaming (_/✶_ to _/✶₁_)
                     open Lifted lift
                       using (_↑✶_) renaming (_/✶_ to _/✶₂_)
                 in
                 ∀ {m n} (σs₁ : Subs T₃ m n) (σs₂ : Subs T₃ m n) →
                 (∀ k x → var x /✶₂ σs₁ ↑✶ k ≡ var x /✶₂ σs₂ ↑✶ k) →
                  ∀ k t → t     /✶₁ σs₁ ↑✶ k ≡ t     /✶₁ σs₂ ↑✶ k

  termApplication : Application T₁ T₂
  termApplication = record { _/_ = app termLift }

  varApplication : Application T₁ Fin
  varApplication = record { _/_ = app varLift }

  field /✶-↑✶₂ : let open Application varApplication
                       using () renaming (_/✶_ to _/✶₁₃_)
                     open Application termApplication
                       using () renaming (_/✶_ to _/✶₁₂_)
                     open Lifted varLift
                       using () renaming (_↑✶_ to _↑✶₃_; _/✶_ to _/✶₂₃_)
                     open TermSubst termSubst
                       using () renaming (_↑✶_ to _↑✶₂_; _/✶_ to _/✶₂₂_)
                 in
                 ∀ {m n} (σs₁ : Subs Fin m n) (σs₂ : Subs T₂ m n) →
                 (∀ k x → var x /✶₂₃ σs₁ ↑✶₃ k ≡ var x /✶₂₂ σs₂ ↑✶₂ k) →
                  ∀ k t → t     /✶₁₃ σs₁ ↑✶₃ k ≡ t     /✶₁₂ σs₂ ↑✶₂ k

  -- An instantiation of the above lemmas for T₂ substitutions in T₁s.
  termLiftAppLemmas : LiftAppLemmas T₁ T₂ T₂
  termLiftAppLemmas = record
    { lift          = Lift.lift termLift
    ; application₁₃ = termApplication
    ; application₂₃ = TermLemmas.application termLemmas
    ; lemmas₂       = TermLemmas.lemmas₄ termLemmas
    ; lemmas₃       = TermLemmas.lemmas₄ termLemmas
    ; lift-/        = λ _ → refl
    ; lift-var      = λ _ → refl
    ; /✶-↑✶         = /✶-↑✶₁
    }

  open LiftAppLemmas termLiftAppLemmas public hiding (/-wk)

  -- An instantiation of the above lemmas for variable substitutions
  -- (renamings) in T₁s.
  varLiftSubLemmas : LiftSubLemmas T₁ T₂ Fin
  varLiftSubLemmas = record
    { application₁₂ = termApplication
    ; liftAppLemmas = record
      { lift          = Lift.lift varLift
      ; application₁₃ = varApplication
      ; application₂₃ = Lifted.application varLift
      ; lemmas₂       = TermLemmas.lemmas₄ termLemmas
      ; lemmas₃       = VarLemmas.lemmas₄
      ; lift-/        = λ _ → sym (TermLemmas.app-var termLemmas)
      ; lift-var      = λ _ → refl
      ; /✶-↑✶         = /✶-↑✶₁
      }
    ; weaken-lift = λ _ → TermLemmas.weaken-var termLemmas
    ; /-⊙₂        = AppLemmas./-⊙ appLemmas
    ; /✶-↑✶₁      = /✶-↑✶₂
    ; /✶-↑✶₂      = TermLemmas./✶-↑✶ termLemmas
    }

  open Application   varApplication   public using () renaming (_/_ to _/Var_)
  open LiftSubLemmas varLiftSubLemmas public hiding (/✶-↑✶₁; /✶-↑✶₂)
    renaming (liftAppLemmas to varLiftAppLemmas)

  -- Weakening of T₁s.
  weaken : ∀ {n} → T₁ n → T₁ (suc n)
  weaken t = t /Var VarSubst.wk

  -- A version of wk-sub-vanishes for T₁s.
  weaken-sub : ∀ {n t′} → (t : T₁ n) → weaken t / sub t′ ≡ t
  weaken-sub t = begin
    weaken t / sub _   ≡⟨ cong₂ _/_ /-wk refl ⟩
    t / wk / sub _     ≡⟨ wk-sub-vanishes t ⟩
    t                  ∎


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.HITs.Sn.Properties where

open import Cubical.Data.Int hiding (_+_)
open import Cubical.Data.Bool
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Univalence
open import Cubical.HITs.S1 hiding (inv)
open import Cubical.Data.Nat
open import Cubical.Data.Prod
open import Cubical.HITs.Sn.Base
open import Cubical.HITs.Susp
open import Cubical.Data.Unit
open import Cubical.HITs.Join
open import Cubical.HITs.Pushout
open import Cubical.HITs.SmashProduct
open import Cubical.HITs.PropositionalTruncation

open Iso

private
  variable
    ℓ ℓ' : Level
    A : Type ℓ
    B : Type ℓ'

--- Some silly lemmas on S1 ---

S¹≡S1 : S₊ 1 ≡ S¹
S¹≡S1 = cong Susp (sym (ua Bool≃Susp⊥)) ∙ sym S¹≡SuspBool

isOfHLevelS1 : isOfHLevel 3 (S₊ 1)
isOfHLevelS1 = transport (λ i → isOfHLevel 3 (S¹≡S1 (~ i)))
                          λ x y → J (λ y p → (q : x ≡ y) → isProp (p ≡ q))
                                     (transport (λ i → isSet (basedΩS¹≡Int x (~ i))) isSetInt refl)

isGroupoidS1 : isGroupoid (S₊ 1)
isGroupoidS1 = transport (λ i → isGroupoid (S¹≡S1 (~ i))) isGroupoidS¹

isConnectedS1 : (x : S₊ 1) → ∥ north ≡ x ∥
isConnectedS1 x = rec propTruncIsProp
                       (λ p → ∣ cong (transport (sym (S¹≡S1))) p ∙ transport⁻Transport (S¹≡S1) x ∣)
                       (isConnectedS¹ (transport S¹≡S1 x))

SuspBool→S1 : Susp Bool → S₊ 1
SuspBool→S1 north           = north
SuspBool→S1 south           = south
SuspBool→S1 (merid false i) = merid south i
SuspBool→S1 (merid true i)  = merid north i

S1→SuspBool : S₊ 1 → Susp Bool
S1→SuspBool north           = north
S1→SuspBool south           = south
S1→SuspBool (merid north i) = merid true i
S1→SuspBool (merid south i) = merid false i

SuspBool→S1-sect : section SuspBool→S1 S1→SuspBool
SuspBool→S1-sect north = refl
SuspBool→S1-sect south = refl
SuspBool→S1-sect (merid north i) = refl
SuspBool→S1-sect (merid south i) = refl

SuspBool→S1-retr : retract SuspBool→S1 S1→SuspBool
SuspBool→S1-retr north = refl
SuspBool→S1-retr south = refl
SuspBool→S1-retr (merid false i) = refl
SuspBool→S1-retr (merid true i) = refl

S1→S¹ : S₊ 1 → S¹
S1→S¹ x = SuspBool→S¹ (S1→SuspBool x)

S¹→S1 : S¹ → S₊ 1
S¹→S1 x = SuspBool→S1 (S¹→SuspBool x)

S1→S¹-sect : section S1→S¹ S¹→S1
S1→S¹-sect x =
    cong SuspBool→S¹ (SuspBool→S1-retr (S¹→SuspBool x))
  ∙ S¹→SuspBool→S¹ x

S1→S¹-retr : retract S1→S¹ S¹→S1
S1→S¹-retr x =
    cong SuspBool→S1 (SuspBool→S¹→SuspBool (S1→SuspBool x))
  ∙ SuspBool→S1-sect x

SuspBoolIsoS1 : Iso (Susp Bool) (S₊ 1)
fun SuspBoolIsoS1                      = SuspBool→S1
inv SuspBoolIsoS1                      = S1→SuspBool
rightInv SuspBoolIsoS1 north           = refl
rightInv SuspBoolIsoS1 south           = refl
rightInv SuspBoolIsoS1 (merid north i) = refl
rightInv SuspBoolIsoS1 (merid south i) = refl
leftInv SuspBoolIsoS1 north            = refl
leftInv SuspBoolIsoS1 south            = refl
leftInv SuspBoolIsoS1 (merid false i)  = refl
leftInv SuspBoolIsoS1 (merid true i)   = refl

SuspBool≃S1 : Susp Bool ≃ S₊ 1
SuspBool≃S1 = isoToEquiv SuspBoolIsoS1

-- map between S¹ ∧ A and Susp A.
private
  f' : {a : A} → A → S₊ 1 → Susp A
  f' {a = pt} A north = north
  f' {a = pt} A south = north
  f' {a = pt} a (merid p i) = ((merid a) ∙ sym (merid pt)) i

  proj' : {A : Pointed ℓ} {B : Pointed ℓ'} → typ A → typ B → A ⋀ B
  proj' a b = inr (a , b)

module smashS1→susp {(A , pt) : Pointed ℓ} where
  f : (S₊ 1 , north) ⋀ (A , pt) → (Susp A)
  f (inl tt)                    = north
  f (inr (x , x₁))              = f' {a = pt} x₁ x
  f  (push (inl north) i)       = north
  f (push (inl south) i)        = north
  f (push (inl (merid a i₁)) i) = rCancel (merid pt) (~ i) i₁
  f (push (inr x) i)            = north
  f (push (push tt i₁) i)       = north

  f⁻ : Susp A → (S₊ 1 , north) ⋀ (A , pt)
  f⁻ north = inl tt
  f⁻ south = inl tt
  f⁻ (merid a i) =
    (push (inr a) ∙∙ cong (λ x → proj' {A = S₊ 1 , north} {B = A , pt} x a) (merid south ∙ sym (merid north)) ∙∙ sym (push (inr a))) i

  {- TODO : Prove that they cancel out -}

{- Map used in definition of cup product. Maybe mover there later -}
sphereSmashMap : (n m : ℕ) → (S₊ (suc n) , north) ⋀ (S₊ (suc m) , north) → S₊ (2 + n + m)
sphereSmashMap zero m = smashS1→susp.f
sphereSmashMap (suc n) m =
  smashS1→susp.f ∘
  (idfun∙ _ ⋀→ (sphereSmashMap n m , refl)) ∘
  ⋀-associate ∘
  ((smashS1→susp.f⁻ , refl) ⋀→ idfun∙ _)


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

module Dodo.Unary.Equality where

-- Stdlib imports
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open import Level using (Level; _⊔_)
open import Function using (_∘_)
open import Relation.Unary using (Pred)

-- # Definitions

infix 4 _⊆₁'_ _⊆₁_ _⇔₁_

-- Binary relation subset helper. Generally, use `_⊆₁_` (below).
_⊆₁'_ : {a ℓ₁ ℓ₂ : Level} {A : Set a}
  → (P : Pred A ℓ₁)
  → (R : Pred A ℓ₂)
  → Set (a ⊔ ℓ₁ ⊔ ℓ₂)
_⊆₁'_ {A = A} P R = ∀ (x : A) → P x → R x

-- Somehow, Agda cannot infer P and R from `P ⇒ R`, and requires them explicitly passed.
-- For proof convenience, wrap the proof in this structure, which explicitly conveys P and R
-- to the type-checker.
data _⊆₁_ {a ℓ₁ ℓ₂ : Level} {A : Set a} (P : Pred A ℓ₁) (R : Pred A ℓ₂) : Set (a ⊔ ℓ₁ ⊔ ℓ₂) where
  ⊆: : P ⊆₁' R → P ⊆₁ R

data _⇔₁_ {a ℓ₁ ℓ₂ : Level} {A : Set a} (P : Pred A ℓ₁) (R : Pred A ℓ₂) : Set (a ⊔ ℓ₁ ⊔ ℓ₂) where
  ⇔: : P ⊆₁' R → R ⊆₁' P → P ⇔₁ R

-- # Helpers

⇔₁-intro : {a ℓ₁ ℓ₂ : Level} {A : Set a}
  → {P : Pred A ℓ₁} {R : Pred A ℓ₂}
  → P ⊆₁ R
  → R ⊆₁ P
  → P ⇔₁ R
⇔₁-intro (⊆: P⊆R) (⊆: R⊆P) = ⇔: P⊆R R⊆P

⇔₁-compose : ∀ {a b ℓ₁ ℓ₂ ℓ₃ ℓ₄ : Level} {A : Set a} {B : Set b}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred B ℓ₃} {S : Pred B ℓ₄}
  → ( P ⊆₁ Q → R ⊆₁ S )
  → ( Q ⊆₁ P → S ⊆₁ R )
  → P ⇔₁ Q
  → R ⇔₁ S
⇔₁-compose ⊆-proof ⊇-proof (⇔: P⊆Q R⊆S) = ⇔₁-intro (⊆-proof (⊆: P⊆Q)) (⊇-proof (⊆: R⊆S))

-- # Properties

-- ## Properties: ⊆₁

module _ {a ℓ : Level} {A : Set a} {R : Pred A ℓ} where

  ⊆₁-refl : R ⊆₁ R
  ⊆₁-refl = ⊆: (λ _ Rx → Rx)

module _ {a ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a} {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred A ℓ₃} where

  ⊆₁-trans : P ⊆₁ Q → Q ⊆₁ R → P ⊆₁ R
  ⊆₁-trans (⊆: P⊆Q) (⊆: Q⊆R) = ⊆: (λ x Qx → Q⊆R x (P⊆Q x Qx))

-- ## Properties: ⇔₁

module _ {a ℓ : Level} {A : Set a} {R : Pred A ℓ} where

  ⇔₁-refl : R ⇔₁ R
  ⇔₁-refl = ⇔₁-intro ⊆₁-refl ⊆₁-refl

module _ {a ℓ₁ ℓ₂ : Level} {A : Set a} {Q : Pred A ℓ₁} {R : Pred A ℓ₂} where

  ⇔₁-sym : Q ⇔₁ R → R ⇔₁ Q
  -- WARNING: Do *NOT* use `Symmetric _⇔_`. It messes up the universe levels.
  ⇔₁-sym (⇔: Q⊆R R⊆Q) = ⇔: R⊆Q Q⊆R

module _ {a ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred A ℓ₃} where

  ⇔₁-trans : P ⇔₁ Q → Q ⇔₁ R → P ⇔₁ R
  ⇔₁-trans (⇔: P⊆Q Q⊆P) (⇔: Q⊆R R⊆Q) = ⇔₁-intro (⊆₁-trans (⊆: P⊆Q) (⊆: Q⊆R)) (⊆₁-trans (⊆: R⊆Q) (⊆: Q⊆P))

-- # Operations

-- ## Operations: ⇔₁ and ⊆₁ conversion

un-⊆₁ : ∀ {a ℓ₁ ℓ₂ : Level} {A : Set a} {P : Pred A ℓ₁} {R : Pred A ℓ₂}
  → P ⊆₁ R
  → P ⊆₁' R
un-⊆₁ (⊆: P⊆R) = P⊆R

unlift-⊆₁ : ∀ {a b ℓ₁ ℓ₂ ℓ₃ ℓ₄ : Level} {A : Set a} {B : Set b}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred B ℓ₃} {S : Pred B ℓ₄}
  → ( P ⊆₁ Q → R ⊆₁ S )
  → ( P ⊆₁' Q → R ⊆₁' S )
unlift-⊆₁ f P⊆Q = un-⊆₁ (f (⊆: P⊆Q))

lift-⊆₁ : ∀ {a b ℓ₁ ℓ₂ ℓ₃ ℓ₄ : Level} {A : Set a} {B : Set b}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred B ℓ₃} {S : Pred B ℓ₄}
  → ( P ⊆₁' Q → R ⊆₁' S )
  → ( P ⊆₁ Q → R ⊆₁ S )
lift-⊆₁ f (⊆: P⊆Q) = ⊆: (f P⊆Q)

⇔₁-to-⊆₁ : {a ℓ₁ ℓ₂ : Level} {A : Set a} {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
  → P ⇔₁ Q
    ------
  → P ⊆₁ Q
⇔₁-to-⊆₁ (⇔: P⊆Q _) = ⊆: P⊆Q

⇔₁-to-⊇₁ : {a ℓ₁ ℓ₂ : Level} {A : Set a} {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
  → P ⇔₁ Q
    ------
  → Q ⊆₁ P
⇔₁-to-⊇₁ (⇔: _ Q⊆P) = ⊆: Q⊆P

-- ## Operations: ⊆₁

⊆₁-apply : ∀ {a ℓ₁ ℓ₂ : Level} {A : Set a}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
  → P ⊆₁ Q
  → {x : A}
  → P x
    -------
  → Q x
⊆₁-apply (⊆: P⊆Q) {x} = P⊆Q x

⊆₁-substˡ : ∀ {a ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred A ℓ₃} {x : A}
  → P ⇔₁ Q
  → P ⊆₁ R
    ------
  → Q ⊆₁ R
⊆₁-substˡ (⇔: _ Q⊆P) P⊆R = ⊆₁-trans (⊆: Q⊆P) P⊆R

⊆₁-substʳ : ∀ {a ℓ₁ ℓ₂ ℓ₃ : Level} {A : Set a}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂} {R : Pred A ℓ₃}
  → Q ⇔₁ R
  → P ⊆₁ Q
    ------
  → P ⊆₁ R
⊆₁-substʳ (⇔: Q⊆R _) P⊆Q = ⊆₁-trans P⊆Q (⊆: Q⊆R)

≡-to-⊆₁ : {a b ℓ : Level} {A : Set a}
    {P : Pred A ℓ} {Q : Pred A ℓ}
  → P ≡ Q
    ------
  → P ⊆₁ Q
≡-to-⊆₁ refl = ⊆: (λ _ Px → Px)

-- ## Operations: ⇔₁

⇔₁-apply-⊆₁ : {a ℓ₁ ℓ₂ : Level} {A : Set a}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
  → P ⇔₁ Q
  → {x : A}
  → P x
    -------
  → Q x
⇔₁-apply-⊆₁ = ⊆₁-apply ∘ ⇔₁-to-⊆₁

⇔₁-apply-⊇₁ : {a ℓ₁ ℓ₂ : Level} {A : Set a}
    {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
  → P ⇔₁ Q
  → {x : A}
  → Q x
    -------
  → P x
⇔₁-apply-⊇₁ = ⊆₁-apply ∘ ⇔₁-to-⊇₁

≡-to-⇔₁ : {a ℓ : Level} {A : Set a}
    {P : Pred A ℓ} {Q : Pred A ℓ}
  → P ≡ Q
    ------
  → P ⇔₁ Q
≡-to-⇔₁ refl = ⇔₁-intro ⊆₁-refl ⊆₁-refl

-- # Reasoning

-- ## Reasoning: ⊆₁

module ⊆₁-Reasoning {a ℓ₁ : Level} {A : Set a} where

  infix  3 _⊆₁∎
  infixr 2 step-⊆₁
  infix  1 begin⊆₁_

  begin⊆₁_ : {ℓ₂ : Level} {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
    → P ⊆₁ Q
      ------
    → P ⊆₁ Q
  begin⊆₁_ P⊆Q = P⊆Q

  step-⊆₁ : ∀ {ℓ₂ ℓ₃ : Level}
    → (P : Pred A ℓ₁)
    → {Q : Pred A ℓ₂}
    → {R : Pred A ℓ₃}
    → Q ⊆₁ R
    → P ⊆₁ Q
      ------
    → P ⊆₁ R
  step-⊆₁ P Q⊆R P⊆Q = ⊆₁-trans P⊆Q Q⊆R

  _⊆₁∎ : ∀ (P : Pred A ℓ₁) → P ⊆₁ P
  _⊆₁∎ _ = ⊆₁-refl

  syntax step-⊆₁ P Q⊆R P⊆Q = P ⊆₁⟨ P⊆Q ⟩ Q⊆R

-- ## Reasoning: ⇔₁

module ⇔₁-Reasoning {a ℓ₁ : Level} {A : Set a} where

  infix  3 _⇔₁∎
  infixr 2 _⇔₁⟨⟩_ step-⇔₁
  infix  1 begin⇔₁_

  begin⇔₁_ : {ℓ₂ : Level} {P : Pred A ℓ₁} {Q : Pred A ℓ₂}
    → P ⇔₁ Q
      ------
    → P ⇔₁ Q
  begin⇔₁_ P⇔Q = P⇔Q

  _⇔₁⟨⟩_ : {ℓ₂ : Level}
      (P : Pred A ℓ₁)
    → {Q : Pred A ℓ₂}
    → P ⇔₁ Q
      ---------------
    → P ⇔₁ Q
  _ ⇔₁⟨⟩ x≡y = x≡y

  step-⇔₁ : ∀ {ℓ₂ ℓ₃ : Level}
    → (P : Pred A ℓ₁)
    → {Q : Pred A ℓ₂}
    → {R : Pred A ℓ₃}
    → Q ⇔₁ R
    → P ⇔₁ Q
      ---------------
    → P ⇔₁ R
  step-⇔₁ _ Q⇔R P⇔Q = ⇔₁-trans P⇔Q Q⇔R

  _⇔₁∎ : ∀ (P : Pred A ℓ₁) → P ⇔₁ P
  _⇔₁∎ _ = ⇔₁-refl

  syntax step-⇔₁ P Q⇔R P⇔Q = P ⇔₁⟨ P⇔Q ⟩ Q⇔R


open import Data.Product using ( ∃ ; _×_ ; _,_ )
open import Data.Sum using ( inj₁ ; inj₂ )
open import Relation.Nullary using ( Dec ; yes ; no )
open import Relation.Unary using ( _∈_ ; _∉_ ; ∅ ; U ; _∪_ ; _∩_ ; ∁ )
open import Web.Semantic.DL.Concept using ( Concept ; ⟨_⟩ ; ¬⟨_⟩ ; ⊤ ; ⊥ ; _⊓_ ; _⊔_ ; ∀[_]_ ; ∃⟨_⟩_ ; ≤1 ; >1 ; neg )
open import Web.Semantic.DL.Role.Model using ( _⟦_⟧₂ ; ⟦⟧₂-resp-≈ ; ⟦⟧₂-resp-≲ ; ⟦⟧₂-galois )
open import Web.Semantic.DL.Signature using ( Signature )
open import Web.Semantic.DL.TBox.Interp using ( Interp ; Δ ; _⊨_≈_ ; _⊨_≉_ ; con ; con-≈ ; ≈-refl ; ≈-sym ; ≈-trans )
open import Web.Semantic.DL.TBox.Interp.Morphism using ( _≲_ ; _≃_ ; ≲-resp-con ; ≲-resp-≈ ; ≃-impl-≲ ; ≃-impl-≳ ; ≃-iso ; ≃-iso⁻¹ ; ≃-image ; ≃-image⁻¹ ; ≃-refl-≈ ; ≃-sym )
open import Web.Semantic.Util using ( Subset ; tt ; _∘_ ; ExclMiddle ; elim )

module Web.Semantic.DL.Concept.Model {Σ : Signature} where

_⟦_⟧₁ : ∀ (I : Interp Σ) → Concept Σ → Subset (Δ I)
I ⟦ ⟨ c ⟩    ⟧₁ = con I c
I ⟦ ¬⟨ c ⟩   ⟧₁ = ∁ (con I c)
I ⟦ ⊤        ⟧₁ = U
I ⟦ ⊥        ⟧₁ = ∅
I ⟦ C ⊓ D    ⟧₁ = I ⟦ C ⟧₁ ∩ I ⟦ D ⟧₁
I ⟦ C ⊔ D    ⟧₁ = I ⟦ C ⟧₁ ∪ I ⟦ D ⟧₁
I ⟦ ∀[ R ] C ⟧₁ = λ x → ∀   y → ((x , y) ∈ I ⟦ R ⟧₂) → (y ∈ I ⟦ C ⟧₁)
I ⟦ ∃⟨ R ⟩ C ⟧₁ = λ x → ∃ λ y → ((x , y) ∈ I ⟦ R ⟧₂) × (y ∈ I ⟦ C ⟧₁)
I ⟦ ≤1 R     ⟧₁ = λ x → ∀ y z → ((x , y) ∈ I ⟦ R ⟧₂) → ((x , z) ∈ I ⟦ R ⟧₂) → (I ⊨ y ≈ z)
I ⟦ >1 R     ⟧₁ = λ x → ∃ λ y → ∃ λ z → ((x , y) ∈ I ⟦ R ⟧₂) × ((x , z) ∈ I ⟦ R ⟧₂) × (I ⊨ y ≉ z)

⟦⟧₁-resp-≈ : ∀ I C {x y} → (x ∈ I ⟦ C ⟧₁) → (I ⊨ x ≈ y) → (y ∈ I ⟦ C ⟧₁)
⟦⟧₁-resp-≈ I ⟨ c ⟩      x∈⟦c⟧                           x≈y =
  con-≈ I c x∈⟦c⟧ x≈y
⟦⟧₁-resp-≈ I ¬⟨ c ⟩     x∉⟦c⟧                           x≈y =
  λ y∈⟦c⟧ → x∉⟦c⟧ (con-≈ I c y∈⟦c⟧ (≈-sym I x≈y))
⟦⟧₁-resp-≈ I ⊤          x∈⊤                             x≈y =
  tt
⟦⟧₁-resp-≈ I (C ⊓ D)    (x∈⟦C⟧ , x∈⟦D⟧)                 x≈y =
  (⟦⟧₁-resp-≈ I C x∈⟦C⟧ x≈y , ⟦⟧₁-resp-≈ I D x∈⟦D⟧ x≈y)
⟦⟧₁-resp-≈ I (C ⊔ D)    (inj₁ x∈⟦C⟧)                    x≈y =
  inj₁ (⟦⟧₁-resp-≈ I C x∈⟦C⟧ x≈y)
⟦⟧₁-resp-≈ I (C ⊔ D)    (inj₂ x∈⟦D⟧)                    x≈y =
  inj₂ (⟦⟧₁-resp-≈ I D x∈⟦D⟧ x≈y)
⟦⟧₁-resp-≈ I (∀[ R ] C) x∈⟦∀RC⟧                         x≈y =
  λ z yz∈⟦R⟧ → x∈⟦∀RC⟧ z (⟦⟧₂-resp-≈ I R x≈y yz∈⟦R⟧ (≈-refl I))
⟦⟧₁-resp-≈ I (∃⟨ R ⟩ C) (z , xz∈⟦R⟧ , z∈⟦C⟧)            x≈y =
  (z , ⟦⟧₂-resp-≈ I R (≈-sym I x≈y) xz∈⟦R⟧ (≈-refl I) , z∈⟦C⟧)
⟦⟧₁-resp-≈ I (≤1 R)     x∈⟦≤1R⟧                         x≈y =
  λ w z yw∈⟦R⟧ yz∈⟦R⟧ → x∈⟦≤1R⟧ w z
    (⟦⟧₂-resp-≈ I R x≈y yw∈⟦R⟧ (≈-refl I))
    (⟦⟧₂-resp-≈ I R x≈y yz∈⟦R⟧ (≈-refl I))
⟦⟧₁-resp-≈ I (>1 R)     (w , z , xw∈⟦R⟧ , xz∈⟦R⟧ , w≉z) x≈y =
  ( w , z
  , ⟦⟧₂-resp-≈ I R (≈-sym I x≈y) xw∈⟦R⟧ (≈-refl I)
  , ⟦⟧₂-resp-≈ I R (≈-sym I x≈y) xz∈⟦R⟧ (≈-refl I)
  , w≉z)
⟦⟧₁-resp-≈ I ⊥          ()                              x≈y

mutual

  ⟦⟧₁-refl-≃ : ∀ {I J : Interp Σ} → (I≃J : I ≃ J) → ∀ {x} C → (≃-image⁻¹ I≃J x ∈ I ⟦ C ⟧₁) → (x ∈ J ⟦ C ⟧₁)
  ⟦⟧₁-refl-≃ {I} {J} I≃J {x} C x∈⟦C⟧ = ⟦⟧₁-resp-≈ J C (⟦⟧₁-resp-≃ I≃J C x∈⟦C⟧) (≃-iso⁻¹ I≃J x)

  ⟦⟧₁-resp-≃ : ∀ {I J : Interp Σ} → (I≃J : I ≃ J) → ∀ {x} C → (x ∈ I ⟦ C ⟧₁) → (≃-image I≃J x ∈ J ⟦ C ⟧₁)
  ⟦⟧₁-resp-≃ {I} {J} I≃J ⟨ c ⟩ x∈⟦c⟧ =
    ≲-resp-con (≃-impl-≲ I≃J) x∈⟦c⟧
  ⟦⟧₁-resp-≃ {I} {J} I≃J {x} ¬⟨ c ⟩ x∉⟦c⟧ =
    λ x∈⟦c⟧ → x∉⟦c⟧ (con-≈ I c (≲-resp-con (≃-impl-≳ I≃J) x∈⟦c⟧) (≃-iso I≃J x))
  ⟦⟧₁-resp-≃ {I} {J} I≃J ⊤ _ =
    tt
  ⟦⟧₁-resp-≃ {I} {J} I≃J ⊥ ()
  ⟦⟧₁-resp-≃ {I} {J} I≃J (C ⊓ D) (x∈⟦C⟧ , x∈⟦D⟧) =
    (⟦⟧₁-resp-≃ I≃J C x∈⟦C⟧ , ⟦⟧₁-resp-≃ I≃J D x∈⟦D⟧)
  ⟦⟧₁-resp-≃ {I} {J} I≃J (C ⊔ D) (inj₁ x∈⟦C⟧) =
    inj₁ (⟦⟧₁-resp-≃ I≃J C x∈⟦C⟧)
  ⟦⟧₁-resp-≃ {I} {J} I≃J (C ⊔ D) (inj₂ x∈⟦D⟧) =
    inj₂ (⟦⟧₁-resp-≃ I≃J D x∈⟦D⟧)
  ⟦⟧₁-resp-≃ {I} {J} I≃J (∀[ R ] C) x∈⟦∀RC⟧ =
    λ y xy∈⟦R⟧ → ⟦⟧₁-refl-≃ I≃J C
      (x∈⟦∀RC⟧ (≃-image⁻¹ I≃J y) (⟦⟧₂-galois (≃-sym I≃J) R xy∈⟦R⟧))
  ⟦⟧₁-resp-≃ {I} {J} I≃J (∃⟨ R ⟩ C) (y , xy∈⟦R⟧ , y∈⟦C⟧) =
    ( ≃-image I≃J y
    , ⟦⟧₂-resp-≲ (≃-impl-≲ I≃J) R xy∈⟦R⟧
    , ⟦⟧₁-resp-≃ I≃J C y∈⟦C⟧ )
  ⟦⟧₁-resp-≃ {I} {J} I≃J (≤1 R) x∈⟦≤1R⟧ =
    λ y z xy∈⟦R⟧ xz∈⟦R⟧ → ≃-refl-≈ I≃J
      (x∈⟦≤1R⟧ (≃-image⁻¹ I≃J y) (≃-image⁻¹ I≃J z)
      (⟦⟧₂-galois (≃-sym I≃J) R xy∈⟦R⟧) (⟦⟧₂-galois (≃-sym I≃J) R xz∈⟦R⟧))
  ⟦⟧₁-resp-≃ {I} {J} I≃J (>1 R) (y , z , xy∈⟦R⟧ , xz∈⟦R⟧ , y≉z) =
    ( ≃-image I≃J y , ≃-image I≃J z
    , ⟦⟧₂-resp-≲ (≃-impl-≲ I≃J) R xy∈⟦R⟧
    , ⟦⟧₂-resp-≲ (≃-impl-≲ I≃J) R xz∈⟦R⟧
    , y≉z ∘ ≃-refl-≈ (≃-sym I≃J) )

neg-sound : ∀ I {x} C → (x ∈ I ⟦ neg C ⟧₁) → (x ∉ I ⟦ C ⟧₁)
neg-sound I ⟨ c ⟩ x∉⟦c⟧ x∈⟦c⟧ = x∉⟦c⟧ x∈⟦c⟧
neg-sound I ¬⟨ c ⟩ x∈⟦c⟧ x∉⟦c⟧ = x∉⟦c⟧ x∈⟦c⟧
neg-sound I ⊤ () _
neg-sound I ⊥ _ ()
neg-sound I (C ⊓ D) (inj₁ x∈⟦¬C⟧) (x∈⟦C⟧ , x∈⟦D⟧) = neg-sound I C x∈⟦¬C⟧ x∈⟦C⟧
neg-sound I (C ⊓ D) (inj₂ x∈⟦¬D⟧) (x∈⟦C⟧ , x∈⟦D⟧) = neg-sound I D x∈⟦¬D⟧ x∈⟦D⟧
neg-sound I (C ⊔ D) (x∈⟦¬C⟧ , x∈⟦¬D⟧) (inj₁ x∈⟦C⟧) = neg-sound I C x∈⟦¬C⟧ x∈⟦C⟧
neg-sound I (C ⊔ D) (x∈⟦¬C⟧ , x∈⟦¬D⟧) (inj₂ x∈⟦D⟧) = neg-sound I D x∈⟦¬D⟧ x∈⟦D⟧
neg-sound I (∀[ R ] C) (y , xy∈⟦R⟧ , y∈⟦¬C⟧) x∈⟦∀RC⟧ = neg-sound I C y∈⟦¬C⟧ (x∈⟦∀RC⟧ y xy∈⟦R⟧)
neg-sound I (∃⟨ R ⟩ C) x∈⟦∀R¬C⟧ (y , xy∈⟦R⟧ , y∈⟦C⟧) = neg-sound I C (x∈⟦∀R¬C⟧ y xy∈⟦R⟧) y∈⟦C⟧
neg-sound I (≤1 R) (y , z , xy∈⟦R⟧ , xz∈⟦R⟧ , y≉z) x∈⟦≤1R⟧ = y≉z (x∈⟦≤1R⟧ y z xy∈⟦R⟧ xz∈⟦R⟧)
neg-sound I (>1 R) x∈⟦≤1R⟧ (y , z , xy∈⟦R⟧ , xz∈⟦R⟧ , y≉z) = y≉z (x∈⟦≤1R⟧ y z xy∈⟦R⟧ xz∈⟦R⟧)

neg-complete : ExclMiddle → ∀ I {x} C → (x ∉ I ⟦ C ⟧₁) → (x ∈ I ⟦ neg C ⟧₁)
neg-complete excl-middle I ⟨ c ⟩ x∉⟦c⟧ = x∉⟦c⟧
neg-complete excl-middle I {x} ¬⟨ c ⟩ ¬x∉⟦c⟧ with excl-middle (x ∈ con I c)
neg-complete excl-middle I ¬⟨ c ⟩ ¬x∉⟦c⟧ | yes x∈⟦c⟧ = x∈⟦c⟧
neg-complete excl-middle I ¬⟨ c ⟩ ¬x∉⟦c⟧ | no  x∉⟦c⟧ = elim (¬x∉⟦c⟧ x∉⟦c⟧)
neg-complete excl-middle I ⊤ x∉⟦⊤⟧ = x∉⟦⊤⟧ tt
neg-complete excl-middle I ⊥ x∉⟦⊥⟧ = tt
neg-complete excl-middle I {x} (C ⊓ D) x∉⟦C⊓D⟧ with excl-middle (x ∈ I ⟦ C ⟧₁)
neg-complete excl-middle I (C ⊓ D) x∉⟦C⊓D⟧ | yes x∈⟦C⟧ =
  inj₂ (neg-complete excl-middle I D (λ x∈⟦D⟧ → x∉⟦C⊓D⟧ (x∈⟦C⟧ , x∈⟦D⟧)))
neg-complete excl-middle I (C ⊓ D) x∉⟦C⊓D⟧ | no x∉⟦C⟧ =
  inj₁ (neg-complete excl-middle I C x∉⟦C⟧)
neg-complete excl-middle I (C ⊔ D) x∉⟦C⊔D⟧ =
  ( neg-complete excl-middle I C (x∉⟦C⊔D⟧ ∘ inj₁)
  , neg-complete excl-middle I D (x∉⟦C⊔D⟧ ∘ inj₂))
neg-complete excl-middle I {x} (∀[ R ] C) x∉⟦∀RC⟧
  with excl-middle (∃ λ y → ((x , y) ∈ I ⟦ R ⟧₂) × (y ∉ I ⟦ C ⟧₁))
neg-complete excl-middle I {x} (∀[ R ] C) x∉⟦∀RC⟧ | yes (y , xy∈⟦R⟧ , y∉⟦C⟧) =
  (y , xy∈⟦R⟧ , neg-complete excl-middle I C y∉⟦C⟧)
neg-complete excl-middle I {x} (∀[ R ] C) x∉⟦∀RC⟧ | no ∄xy∈⟦R⟧∙y∉⟦C⟧ =
  elim (x∉⟦∀RC⟧ lemma) where
  lemma : x ∈ I ⟦ ∀[ R ] C ⟧₁
  lemma y xy∈⟦R⟧ with excl-middle (y ∈ I ⟦ C ⟧₁)
  lemma y xy∈⟦R⟧ | yes y∈⟦C⟧ = y∈⟦C⟧
  lemma y xy∈⟦R⟧ | no  y∉⟦C⟧ = elim (∄xy∈⟦R⟧∙y∉⟦C⟧ (y , xy∈⟦R⟧ , y∉⟦C⟧))
neg-complete excl-middle I (∃⟨ R ⟩ C) x∉⟦∃RC⟧ =
  λ y xy∈⟦R⟧ → neg-complete excl-middle I C
    (λ y∈⟦C⟧ → x∉⟦∃RC⟧ (y , xy∈⟦R⟧ , y∈⟦C⟧))
neg-complete excl-middle I {x} (≤1 R) x∉⟦≤1R⟧ = lemma where
  lemma : x ∈ I ⟦ >1 R ⟧₁
  lemma with excl-middle (x ∈ I ⟦ >1 R ⟧₁)
  lemma | yes x∈⟦>1R⟧ = x∈⟦>1R⟧
  lemma | no x∉⟦>1R⟧ = elim (x∉⟦≤1R⟧ lemma′) where
    lemma′ : x ∈ I ⟦ ≤1 R ⟧₁
    lemma′ y z xy∈⟦R⟧ xz∈⟦R⟧ with excl-middle (I ⊨ y ≈ z)
    lemma′ y z xy∈⟦R⟧ xz∈⟦R⟧ | yes y≈z = y≈z
    lemma′ y z xy∈⟦R⟧ xz∈⟦R⟧ | no y≉z =
      elim (x∉⟦>1R⟧ (y , z , xy∈⟦R⟧ , xz∈⟦R⟧ , y≉z))
neg-complete excl-middle I {x} (>1 R) x∉⟦>1R⟧ = lemma where
  lemma : x ∈ I ⟦ ≤1 R ⟧₁
  lemma y z xy∈⟦R⟧ xz∈⟦R⟧ with excl-middle (I ⊨ y ≈ z)
  lemma y z xy∈⟦R⟧ xz∈⟦R⟧ | yes y≈z = y≈z
  lemma y z xy∈⟦R⟧ xz∈⟦R⟧ | no y≉z =
    elim (x∉⟦>1R⟧ (y , z , xy∈⟦R⟧ , xz∈⟦R⟧ , y≉z))


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

open import lib.Basics
open import lib.cubical.Square
open import lib.types.Bool
open import lib.types.Cofiber
open import lib.types.Lift
open import lib.types.Paths
open import lib.types.Pointed
open import lib.types.PushoutFmap
open import lib.types.Sigma
open import lib.types.Span
open import lib.types.Suspension
open import lib.types.Wedge

module lib.types.BigWedge where

module _ {i j} {A : Type i} where

  {- the function for cofiber -}
  bigwedge-f : (X : A → Ptd j) → A → Σ A (de⊙ ∘ X)
  bigwedge-f X a = a , pt (X a)

  bigwedge-span : (A → Ptd j) → Span
  bigwedge-span X = cofiber-span (bigwedge-f X)

  BigWedge : (A → Ptd j) → Type (lmax i j)
  BigWedge X = Cofiber (bigwedge-f X)

  bwbase : {X : A → Ptd j} → BigWedge X
  bwbase = cfbase

  bwin : {X : A → Ptd j} → (a : A) → de⊙ (X a) → BigWedge X
  bwin = curry cfcod

  ⊙BigWedge : (A → Ptd j) → Ptd (lmax i j)
  ⊙BigWedge X = ⊙[ BigWedge X , bwbase ]

  bwglue : {X : A → Ptd j} → (a : A) → bwbase {X} == bwin a (pt (X a))
  bwglue = cfglue

  ⊙bwin : {X : A → Ptd j} → (a : A) → X a ⊙→ ⊙BigWedge X
  ⊙bwin a = (bwin a , ! (bwglue a))

  module BigWedgeElim {X : A → Ptd j} {k} {P : BigWedge X → Type k}
    (base* : P bwbase) (in* : (a : A) (x : de⊙ (X a)) → P (bwin a x))
    (glue* : (a : A) → base* == in* a (pt (X a)) [ P ↓ bwglue a ])
    = CofiberElim {f = bigwedge-f X} {P = P} base* (uncurry in*) glue*

  BigWedge-elim = BigWedgeElim.f

  module BigWedgeRec {X : A → Ptd j} {k} {C : Type k}
    (base* : C) (in* : (a : A) → de⊙ (X a) → C)
    (glue* : (a : A) → base* == in* a (pt (X a)))
    = CofiberRec {f = bigwedge-f X} {C = C} base* (uncurry in*) glue*

module _ {i j₀ j₁} {A : Type i} {X₀ : A → Ptd j₀} {X₁ : A → Ptd j₁}
  (Xeq : ∀ a → X₀ a ⊙≃ X₁ a) where

  bigwedge-span-emap-r : SpanEquiv (cofiber-span (bigwedge-f X₀)) (cofiber-span (bigwedge-f X₁))
  bigwedge-span-emap-r = span-map (idf _) (Σ-fmap-r λ a → fst (⊙–> (Xeq a))) (idf _)
    (comm-sqr λ _ → idp) (comm-sqr λ a → pair= idp (⊙–>-pt (Xeq a))) ,
    idf-is-equiv _ , Σ-isemap-r (λ a → snd (Xeq a)) , idf-is-equiv _

  BigWedge-emap-r : BigWedge X₀ ≃ BigWedge X₁
  BigWedge-emap-r = Pushout-emap bigwedge-span-emap-r

  ⊙BigWedge-emap-r : ⊙BigWedge X₀ ⊙≃ ⊙BigWedge X₁
  ⊙BigWedge-emap-r = ≃-to-⊙≃ BigWedge-emap-r idp

module _ {i₀ i₁ j} {A₀ : Type i₀} {A₁ : Type i₁}
  (X : A₁ → Ptd j) (Aeq : A₀ ≃ A₁) where

  bigwedge-span-emap-l : SpanEquiv (cofiber-span (bigwedge-f (X ∘ –> Aeq))) (cofiber-span (bigwedge-f X))
  bigwedge-span-emap-l = span-map (idf _) (Σ-fmap-l (de⊙ ∘ X) (–> Aeq)) (–> Aeq)
    (comm-sqr λ _ → idp) (comm-sqr λ _ → idp) ,
    idf-is-equiv _ , Σ-isemap-l (de⊙ ∘ X) (snd Aeq) , snd Aeq

  BigWedge-emap-l : BigWedge (X ∘ –> Aeq) ≃ BigWedge X
  BigWedge-emap-l = Pushout-emap bigwedge-span-emap-l

  ⊙BigWedge-emap-l : ⊙BigWedge (X ∘ –> Aeq) ⊙≃ ⊙BigWedge X
  ⊙BigWedge-emap-l = ≃-to-⊙≃ BigWedge-emap-l idp

module _ {i j} {A : Type i} (X : A → Ptd j) where

  extract-glue-from-BigWedge-is-const :
    ∀ bw → extract-glue {s = bigwedge-span X} bw == north
  extract-glue-from-BigWedge-is-const = BigWedge-elim
    idp
    (λ x y → ! (merid x))
    (↓-='-from-square ∘ λ x →
      ExtractGlue.glue-β x ∙v⊡
        tr-square (merid x)
      ⊡v∙ ! (ap-cst north (cfglue x)))

{- A BigWedge indexed by Bool is just a binary Wedge -}
module _ {i} (Pick : Bool → Ptd i) where

  BigWedge-Bool-equiv-Wedge : BigWedge Pick ≃ Wedge (Pick true) (Pick false)
  BigWedge-Bool-equiv-Wedge = equiv f g f-g g-f
    where
    module F = BigWedgeRec {X = Pick}
      {C = Wedge (Pick true) (Pick false)}
      (winl (pt (Pick true)))
      (λ {true → winl; false → winr})
      (λ {true → idp; false → wglue})

    module G = WedgeRec {X = Pick true} {Y = Pick false}
      {C = BigWedge Pick}
      (bwin true)
      (bwin false)
      (! (bwglue true) ∙ bwglue false)

    f = F.f
    g = G.f

    abstract
      f-g : ∀ w → f (g w) == w
      f-g = Wedge-elim
        (λ _ → idp)
        (λ _ → idp)
        (↓-∘=idf-in' f g $
          ap f (ap g wglue)
            =⟨ ap (ap f) G.glue-β ⟩
          ap f (! (bwglue true) ∙ bwglue false)
            =⟨ ap-∙ f (! (bwglue true)) (bwglue false) ⟩
          ap f (! (bwglue true)) ∙ ap f (bwglue false)
            =⟨ ap-! f (bwglue true)
               |in-ctx (λ w → w ∙ ap f (bwglue false)) ⟩
          ! (ap f (bwglue true)) ∙ ap f (bwglue false)
            =⟨ F.glue-β true
               |in-ctx (λ w → ! w ∙ ap f (bwglue false)) ⟩
          ap f (bwglue false)
            =⟨ F.glue-β false ⟩
          wglue =∎)

      g-f : ∀ bw → g (f bw) == bw
      g-f = BigWedge-elim
        (! (bwglue true))
        (λ {true → λ _ → idp; false → λ _ → idp})
        (λ {true → ↓-∘=idf-from-square g f $
              ap (ap g) (F.glue-β true) ∙v⊡
              bl-square (bwglue true);
            false → ↓-∘=idf-from-square g f $
              (ap (ap g) (F.glue-β false) ∙ G.glue-β) ∙v⊡
              lt-square (! (bwglue true)) ⊡h vid-square})

module _ {i j} {A : Type i} (dec : has-dec-eq A) {X : Ptd j} where

  {- The dependent version increases the complexity significantly
     and we do not need it. -}

  ⊙bwproj-in : A → A → X ⊙→ X
  ⊙bwproj-in a a' with dec a a'
  ... | inl _ = ⊙idf _
  ... | inr _ = ⊙cst

  module BigWedgeProj (a : A) = BigWedgeRec
    {X = λ _ → X}
    (pt X)
    (λ a' → fst (⊙bwproj-in a a'))
    (λ a' → ! (snd (⊙bwproj-in a a')))

  bwproj : A → BigWedge (λ _ → X) → de⊙ X
  bwproj = BigWedgeProj.f

  ⊙bwproj : A → ⊙BigWedge (λ _ → X) ⊙→ X
  ⊙bwproj a = bwproj a , idp

  abstract
    bwproj-bwin-diag : (a : A) → bwproj a ∘ bwin a ∼ idf (de⊙ X)
    bwproj-bwin-diag a x with dec a a
    ... | inl _ = idp
    ... | inr a≠a with a≠a idp
    ...           | ()

    bwproj-bwin-≠ : {a a' : A} → a ≠ a' → bwproj a ∘ bwin a' ∼ cst (pt X)
    bwproj-bwin-≠ {a} {a'} a≠a' x with dec a a'
    ... | inr _ = idp
    ... | inl a=a' with a≠a' a=a'
    ...            | ()

module _ {i j k} {A : Type i} (dec : has-dec-eq A) {X : Ptd j} (a : A) where

  abstract
    private
      bwproj-BigWedge-emap-r-lift-in : ∀ a'
        → bwproj dec {X = ⊙Lift {j = k} X} a ∘ bwin a' ∘ lift
        ∼ lift {j = k} ∘ bwproj dec {X = X} a ∘ bwin a'
      bwproj-BigWedge-emap-r-lift-in a' with dec a a'
      ... | inl _ = λ _ → idp
      ... | inr _ = λ _ → idp

      bwproj-BigWedge-emap-r-lift-glue' : ∀ (a' : A)
        → ap (lift {j = k}) (! (snd (⊙bwproj-in dec {X = X} a a')))
        == ! (snd (⊙bwproj-in dec {X = ⊙Lift {j = k} X} a a')) ∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)
      bwproj-BigWedge-emap-r-lift-glue' a' with dec a a'
      ... | inl _ = idp
      ... | inr _ = idp

    bwproj-BigWedge-emap-r-lift-glue : ∀ (a' : A)
      →  idp == bwproj-BigWedge-emap-r-lift-in a' (pt X)
      [ (λ x → bwproj dec {X = ⊙Lift {j = k} X} a (–> (BigWedge-emap-r (λ _ → ⊙lift-equiv {j = k})) x)
            == lift {j = k} (bwproj dec {X = X} a x)) ↓ bwglue a' ]
    bwproj-BigWedge-emap-r-lift-glue a' = ↓-='-in' $
      ap (lift {j = k} ∘ bwproj dec a) (bwglue a')
        =⟨ ap-∘ (lift {j = k}) (bwproj dec a) (bwglue a') ⟩
      ap (lift {j = k}) (ap (bwproj dec a) (bwglue a'))
        =⟨ ap (ap (lift {j = k})) $ BigWedgeProj.glue-β dec a a' ⟩
      ap (lift {j = k}) (! (snd (⊙bwproj-in dec a a')))
        =⟨ bwproj-BigWedge-emap-r-lift-glue' a' ⟩
      ! (snd (⊙bwproj-in dec a a')) ∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)
        =⟨ ap (_∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)) $
            ( ! $ BigWedgeProj.glue-β dec a a') ⟩
      ap (bwproj dec a) (bwglue a') ∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)
        =⟨ ap (λ p → ap (bwproj dec a) p
                  ∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)) $
            (! $ PushoutFmap.glue-β (fst (bigwedge-span-emap-r (λ _ → ⊙lift-equiv {j = k}))) a') ⟩
      ap (bwproj dec a) (ap (–> (BigWedge-emap-r (λ _ → ⊙lift-equiv {j = k}))) (bwglue a'))
      ∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)
        =⟨ ap (_∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)) $
            (∘-ap (bwproj dec a) (–> (BigWedge-emap-r (λ _ → ⊙lift-equiv {j = k}))) (bwglue a')) ⟩
      ap (bwproj dec a ∘ –> (BigWedge-emap-r (λ _ → ⊙lift-equiv {j = k}))) (bwglue a')
      ∙' bwproj-BigWedge-emap-r-lift-in a' (pt X)
        =∎

    bwproj-BigWedge-emap-r-lift :
        bwproj dec {X = ⊙Lift {j = k} X} a ∘ –> (BigWedge-emap-r (λ _ → ⊙lift-equiv {j = k}))
      ∼ lift {j = k} ∘ bwproj dec a
    bwproj-BigWedge-emap-r-lift =
      BigWedge-elim {X = λ _ → X} idp
        bwproj-BigWedge-emap-r-lift-in
        bwproj-BigWedge-emap-r-lift-glue


------------------------------------------------------------------------
-- The Agda standard library
--
-- Products
------------------------------------------------------------------------

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

module Data.Product where

open import Function
open import Level
open import Relation.Nullary
open import Agda.Builtin.Equality

infixr 4 _,′_
infix  4 -,_
infixr 2 _×_ _-×-_ _-,-_

------------------------------------------------------------------------
-- Definition

open import Agda.Builtin.Sigma hiding (module Σ) public renaming (fst to proj₁; snd to proj₂)

module Σ = Agda.Builtin.Sigma.Σ renaming (fst to proj₁; snd to proj₂)

-- The syntax declaration below is attached to Σ-syntax, to make it
-- easy to import Σ without the special syntax.

infix 2 Σ-syntax

Σ-syntax : ∀ {a b} (A : Set a) → (A → Set b) → Set (a ⊔ b)
Σ-syntax = Σ

syntax Σ-syntax A (λ x → B) = Σ[ x ∈ A ] B

∃ : ∀ {a b} {A : Set a} → (A → Set b) → Set (a ⊔ b)
∃ = Σ _

∃-syntax : ∀ {a b} {A : Set a} → (A → Set b) → Set (a ⊔ b)
∃-syntax = ∃

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

∄ : ∀ {a b} {A : Set a} → (A → Set b) → Set (a ⊔ b)
∄ P = ¬ ∃ P

∄-syntax : ∀ {a b} {A : Set a} → (A → Set b) → Set (a ⊔ b)
∄-syntax = ∄

syntax ∄-syntax (λ x → B) = ∄[ x ] B

∃₂ : ∀ {a b c} {A : Set a} {B : A → Set b}
     (C : (x : A) → B x → Set c) → Set (a ⊔ b ⊔ c)
∃₂ C = ∃ λ a → ∃ λ b → C a b

_×_ : ∀ {a b} (A : Set a) (B : Set b) → Set (a ⊔ b)
A × B = Σ[ x ∈ A ] B

_,′_ : ∀ {a b} {A : Set a} {B : Set b} → A → B → A × B
_,′_ = _,_

------------------------------------------------------------------------
-- Unique existence

-- Parametrised on the underlying equality.

∃! : ∀ {a b ℓ} {A : Set a} →
     (A → A → Set ℓ) → (A → Set b) → Set (a ⊔ b ⊔ ℓ)
∃! _≈_ B = ∃ λ x → B x × (∀ {y} → B y → x ≈ y)

------------------------------------------------------------------------
-- Functions

-- Sometimes the first component can be inferred.

-,_ : ∀ {a b} {A : Set a} {B : A → Set b} {x} → B x → ∃ B
-, y = _ , y

<_,_> : ∀ {a b c} {A : Set a} {B : A → Set b} {C : ∀ {x} → B x → Set c}
        (f : (x : A) → B x) → ((x : A) → C (f x)) →
        ((x : A) → Σ (B x) C)
< f , g > x = (f x , g x)

map : ∀ {a b p q}
      {A : Set a} {B : Set b} {P : A → Set p} {Q : B → Set q} →
      (f : A → B) → (∀ {x} → P x → Q (f x)) →
      Σ A P → Σ B Q
map f g (x , y) = (f x , g y)

map₁ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} →
       (A → B) → A × C → B × C
map₁ f = map f id

map₂ : ∀ {a b c} {A : Set a} {B : A → Set b} {C : A → Set c} →
       (∀ {x} → B x → C x) → Σ A B → Σ A C
map₂ f = map id f

zip : ∀ {a b c p q r}
        {A : Set a} {B : Set b} {C : Set c}
        {P : A → Set p} {Q : B → Set q} {R : C → Set r} →
      (_∙_ : A → B → C) →
      (∀ {x y} → P x → Q y → R (x ∙ y)) →
      Σ A P → Σ B Q → Σ C R
zip _∙_ _∘_ (a , p) (b , q) = ((a ∙ b) , (p ∘ q))

swap : ∀ {a b} {A : Set a} {B : Set b} → A × B → B × A
swap (x , y) = (y , x)

_-×-_ : ∀ {a b i j} {A : Set a} {B : Set b} →
        (A → B → Set i) → (A → B → Set j) → (A → B → Set _)
f -×- g = f -[ _×_ ]- g

_-,-_ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
        (A → B → C) → (A → B → D) → (A → B → C × D)
f -,- g = f -[ _,_ ]- g

curry : ∀ {a b c} {A : Set a} {B : A → Set b} {C : Σ A B → Set c} →
        ((p : Σ A B) → C p) →
        ((x : A) → (y : B x) → C (x , y))
curry f x y = f (x , y)

curry′ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} →
         (A × B → C) → (A → B → C)
curry′ = curry

uncurry : ∀ {a b c} {A : Set a} {B : A → Set b} {C : Σ A B → Set c} →
          ((x : A) → (y : B x) → C (x , y)) →
          ((p : Σ A B) → C p)
uncurry f (x , y) = f x y

uncurry′ : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c} →
           (A → B → C) → (A × B → C)
uncurry′ = uncurry


module Sets.IterativeSet.Oper.Proofs where

import      Lvl
open import Data
open import Data.Boolean
open import Data.Boolean.Stmt
open import Data.Either as Either using (_‖_)
open import Data.Tuple as Tuple using ()
open import Functional
open import Logic
open import Logic.Propositional
open import Logic.Predicate
open import Relator.Equals using () renaming (_≡_ to Id ; [≡]-intro to intro)
open import Sets.IterativeSet.Oper
open import Sets.IterativeSet.Relator
open import Sets.IterativeSet.Relator.Proofs
open import Sets.IterativeSet
open import Structure.Function.Domain
open import Structure.Function
open import Structure.Relator.Properties
open import Structure.Relator
open import Syntax.Function
open import Syntax.Transitivity
open import Type.Dependent
open import Type.Properties.Decidable
open import Type.Properties.Decidable.Proofs

module _ where
  private variable {ℓ ℓ₁ ℓ₂} : Lvl.Level
  open Iset

  -- If there is an element in the empty set, then there exists an instance of the empty type by definition, and that is false by definition.
  ∅-membership : ∀{x : Iset{ℓ₁}} → (x ∉ ∅ {ℓ₂})
  ∅-membership ()

  -- There is a bijection between (A ‖ B) and ∃{Lvl.Up Bool}(\{(Lvl.up 𝐹) → A ; (Lvl.up 𝑇) → B}).
  pair-membership : ∀{a b x : Iset{ℓ}} → (x ∈ pair a b) ↔ (x ≡ a)∨(x ≡ b)
  Tuple.left (pair-membership {a = a} {x}) ([∨]-introₗ p) = [∃]-intro (Lvl.up 𝐹) ⦃ p ⦄
  Tuple.left (pair-membership {a = a} {x}) ([∨]-introᵣ p) = [∃]-intro (Lvl.up 𝑇) ⦃ p ⦄
  Tuple.right (pair-membership {a = a} {x}) ([∃]-intro (Lvl.up 𝐹) ⦃ eq ⦄) = [∨]-introₗ eq
  Tuple.right (pair-membership {a = a} {x}) ([∃]-intro (Lvl.up 𝑇) ⦃ eq ⦄) = [∨]-introᵣ eq

  -- There is a bijection between (A) and ∃{Unit}(\{<> → A}).
  singleton-membership : ∀{a x : Iset{ℓ}} → (x ∈ singleton(a)) ↔ (x ≡ a)
  Tuple.left (singleton-membership {a = a} {x}) xin = [∃]-intro <> ⦃ xin ⦄
  Tuple.right (singleton-membership {a = a} {x}) ([∃]-intro i ⦃ eq ⦄ ) = eq

  [∪]-membership : ∀{A B x : Iset{ℓ}} → (x ∈ (A ∪ B)) ↔ (x ∈ A)∨(x ∈ B)
  Tuple.left ([∪]-membership {A = A} {B} {x}) ([∨]-introₗ ([∃]-intro ia)) = [∃]-intro (Either.Left  ia)
  Tuple.left ([∪]-membership {A = A} {B} {x}) ([∨]-introᵣ ([∃]-intro ib)) = [∃]-intro (Either.Right ib)
  Tuple.right ([∪]-membership {A = A} {B} {x}) ([∃]-intro ([∨]-introₗ ia)) = [∨]-introₗ ([∃]-intro ia)
  Tuple.right ([∪]-membership {A = A} {B} {x}) ([∃]-intro ([∨]-introᵣ ib)) = [∨]-introᵣ ([∃]-intro ib)

  [⋃]-membership : ∀{A x : Iset{ℓ}} → (x ∈ (⋃ A)) ↔ ∃(a ↦ (a ∈ A) ∧ (x ∈ a))
  Σ.left  (∃.witness (Tuple.left ([⋃]-membership {A = A} {x}) ([∃]-intro a ⦃ [∧]-intro ([∃]-intro iA) _ ⦄))) = iA
  Σ.right (∃.witness (Tuple.left ([⋃]-membership {A = A} {x}) ([∃]-intro a ⦃ [∧]-intro ([∃]-intro iA ⦃ aA ⦄) ([∃]-intro ia) ⦄))) = _⊆_.map (Tuple.right aA) ia
  ∃.proof (Tuple.left ([⋃]-membership {A = A} {x}) ([∃]-intro a ⦃ [∧]-intro ([∃]-intro iA ⦃ aA ⦄) ([∃]-intro ia ⦃ xa ⦄) ⦄)) = [≡]-transitivity-raw xa ([⊆]-with-elem (sub₂(_≡_)(_⊆_) aA) {ia})
  ∃.witness (Tuple.right ([⋃]-membership {A = A} {x}) ([∃]-intro (intro iA ia) ⦃ proof ⦄)) = elem(A)(iA)
  Tuple.left (∃.proof (Tuple.right ([⋃]-membership {A = A} {x}) ([∃]-intro (intro iA ia) ⦃ proof ⦄))) = [∈]-of-elem {A = A}
  ∃.witness (Tuple.right (∃.proof (Tuple.right ([⋃]-membership {A = A} {x}) ([∃]-intro (intro iA ia) ⦃ proof ⦄)))) = ia
  ∃.proof (Tuple.right (∃.proof (Tuple.right ([⋃]-membership {A = A} {x}) ([∃]-intro (intro iA ia) ⦃ proof ⦄)))) = proof

  module _ {A : Iset{ℓ}} where
    open import Relator.Equals.Proofs.Equivalence using ([≡]-equiv)
    instance _ = [≡]-equiv {T = Index(A)}

    module _ {P : Index(A) → Stmt{ℓ}} where
      indexFilter-elem-membershipₗ : ∀{i : Index(A)} → (elem(A)(i) ∈ indexFilter A P) ← P(i)
      Σ.left (∃.witness ((indexFilter-elem-membershipₗ {i = i}) pi)) = i
      Σ.right(∃.witness ((indexFilter-elem-membershipₗ {i = i}) pi)) = pi
      Tuple.left  (∃.proof (indexFilter-elem-membershipₗ pi)) = intro id [≡]-reflexivity-raw
      Tuple.right (∃.proof (indexFilter-elem-membershipₗ pi)) = intro id [≡]-reflexivity-raw

    module _
      ⦃ _ : Injective(elem A) ⦄ -- TODO: Is this satisfiable?
      {P : Index(A) → Stmt{ℓ}}
      ⦃ unaryRelator-P : UnaryRelator(P) ⦄
      where

      indexFilter-elem-membership : ∀{i : Index(A)} → (elem(A)(i) ∈ indexFilter A P) ↔ P(i)
      Tuple.left   indexFilter-elem-membership = indexFilter-elem-membershipₗ
      Tuple.right (indexFilter-elem-membership {i = i}) ([∃]-intro (intro iA PiA) ⦃ pp ⦄) = substitute₁(P) (injective(elem A) (symmetry(_≡_) pp)) PiA

  filter-elem-membership : ∀{A : Iset{ℓ}}{P} ⦃ _ : UnaryRelator(P) ⦄ {i : Index(A)} → (elem(A)(i) ∈ filter A P) ↔ P(elem(A)(i))
  Tuple.left  (filter-elem-membership {A = A}{P = P}) = indexFilter-elem-membershipₗ {A = A}{P = P ∘ elem(A)}
  Tuple.right (filter-elem-membership {P = P}) ([∃]-intro (intro iA PiA) ⦃ pp ⦄) = substitute₁(P) (symmetry(_≡_) pp) PiA

  filter-membership : ∀{A : Iset{ℓ}}{P} ⦃ _ : UnaryRelator(P) ⦄ {x : Iset{ℓ}} → (x ∈ filter A P) ↔ ((x ∈ A) ∧ P(x))
  ∃.witness (Tuple.left(filter-membership {P = P}) ([∧]-intro ([∃]-intro i ⦃ p ⦄) pb)) = intro i (substitute₁(P) p pb)
  ∃.proof   (Tuple.left(filter-membership) ([∧]-intro ([∃]-intro i ⦃ p ⦄) pb)) = p
  ∃.witness (Tuple.left (Tuple.right(filter-membership) ([∃]-intro (intro iA PiA) ⦃ pp ⦄))) = iA
  ∃.proof (Tuple.left (Tuple.right(filter-membership) ([∃]-intro (intro iA PiA) ⦃ pp ⦄))) = pp
  Tuple.right (Tuple.right(filter-membership {P = P}) ([∃]-intro (intro iA PiA) ⦃ pp ⦄)) = substitute₁(P) (symmetry(_≡_) pp) PiA

  mapSet-membership : ∀{A : Iset{ℓ}}{f} ⦃ _ : Function(f) ⦄ {y : Iset{ℓ}} → (y ∈ mapSet f(A)) ↔ ∃(x ↦ (x ∈ A) ∧ (y ≡ f(x)))
  ∃.witness (Tuple.left  (mapSet-membership)                         ([∃]-intro x ⦃ [∧]-intro xA fxy ⦄)) = [∃]-witness xA
  ∃.proof   (Tuple.left  (mapSet-membership {A = A} {f = f} {y = y}) ([∃]-intro x ⦃ [∧]-intro xA fxy ⦄)) =
    y                                   🝖[ _≡_ ]-[ fxy ]
    f(x)                                🝖[ _≡_ ]-[ congruence₁(f) ([∃]-proof xA) ]
    f(elem(A) ([∃]-witness xA))         🝖[ _≡_ ]-[]
    elem (mapSet f(A)) ([∃]-witness xA) 🝖[ _≡_ ]-end
  ∃.witness (Tuple.right (mapSet-membership {A = A}) ([∃]-intro iA)) = elem(A) iA
  Tuple.left  (∃.proof (Tuple.right (mapSet-membership {A = A}) ([∃]-intro iA ⦃ p ⦄))) = [∈]-of-elem {A = A} {ia = iA}
  Tuple.right (∃.proof (Tuple.right  mapSet-membership          ([∃]-intro iA ⦃ p ⦄))) = p

  open import Logic.Classical
  module _ ⦃ classical : ∀{ℓ}{P} → Classical{ℓ}(P) ⦄ where
    instance _ = classical-to-decidable
    instance _ = classical-to-decider

    indexFilterBool-subset : ∀{A : Iset{ℓ}}{P} → (indexFilterBool A P ⊆ A)
    _⊇_.map indexFilterBool-subset (intro iA _) = iA
    _⊇_.proof (indexFilterBool-subset {ℓ = ℓ}{A = A}{P = P}) {intro iA (Lvl.up PiA)} =
      elem (indexFilterBool A P) (intro iA (Lvl.up PiA))                   🝖[ _≡_ ]-[]
      elem (indexFilter A (Lvl.Up ∘ IsTrue ∘ P)) (intro iA (Lvl.up PiA))   🝖[ _≡_ ]-[]
      elem A (Σ.left {B = Lvl.Up{ℓ} ∘ IsTrue ∘ P} (intro iA (Lvl.up PiA))) 🝖[ _≡_ ]-[]
      elem A iA                                                            🝖[ _≡_ ]-end

    ℘-membershipₗ : ∀{A : Iset{ℓ}}{B : Iset{ℓ}} → (B ∈ ℘(A)) ← (B ⊆ A)
    ∃.witness (℘-membershipₗ {A = A}{B = B} BA) iA = decide(0)(∃(iB ↦ Id(_⊆_.map BA iB) iA))
    _⊇_.map (Tuple.left (∃.proof (℘-membershipₗ {A = A} {B = B} _))) (intro iA (Lvl.up mapiBiA)) = [∃]-witness([↔]-to-[←] decider-true mapiBiA)
    _⊇_.proof (Tuple.left (∃.proof (℘-membershipₗ {ℓ = ℓ} {A = A} {B = B} BA))) {intro iA (Lvl.up mapiBiA)} =
      elem (elem (℘ A) f) (intro iA (Lvl.up mapiBiA))                          🝖[ _≡_ ]-[]
      elem (indexFilterBool A f) (intro iA (Lvl.up mapiBiA))                   🝖[ _≡_ ]-[]
      elem (indexFilter A (Lvl.Up ∘ IsTrue ∘ f)) (intro iA (Lvl.up mapiBiA))   🝖[ _≡_ ]-[]
      elem A (Σ.left {B = Lvl.Up{ℓ} ∘ IsTrue ∘ f} (intro iA (Lvl.up mapiBiA))) 🝖[ _≡_ ]-[]
      elem A iA                                                                🝖[ _≡_ ]-[ [≡]-to-equivalence(congruence₁(elem A) ([∃]-proof emapiBiA)) ]-sym
      elem A (_⊆_.map BA ([∃]-witness emapiBiA))                               🝖[ _≡_ ]-[ _⊆_.proof BA {[∃]-witness emapiBiA} ]-sym
      elem B ([∃]-witness emapiBiA)                                            🝖[ _≡_ ]-end
      where
        f = \iA → decide(0)(∃(iB ↦ Id(_⊆_.map BA iB) iA))
        emapiBiA = [↔]-to-[←] decider-true mapiBiA
        open import Relator.Equals.Proofs.Equiv using ([≡]-to-equivalence)
    _⊇_.map (Tuple.right (∃.proof (℘-membershipₗ {A = A} {B = B} BA))) iB = intro (_⊆_.map BA iB) (Lvl.up ([↔]-to-[→] decider-true ([∃]-intro iB ⦃ intro ⦄)))
    _⊇_.proof (Tuple.right (∃.proof (℘-membershipₗ {A = A} {B = B} BA))) = _⊆_.proof BA

    ℘-membershipᵣ : ∀{A : Iset{ℓ}}{B : Iset{ℓ}} → (B ∈ ℘(A)) → (B ⊆ A)
    ℘-membershipᵣ ([∃]-intro witness ⦃ b≡indexFilterBool ⦄) = substitute₂ₗ(_⊆_) (symmetry(_≡_) b≡indexFilterBool) indexFilterBool-subset

    ℘-membership : ∀{A : Iset{ℓ}}{x : Iset{ℓ}} → (x ∈ ℘(A)) ↔ (x ⊆ A)
    ℘-membership = [↔]-intro ℘-membershipₗ ℘-membershipᵣ


module Structure.Categorical.Proofs where

open import Data
open import Functional
open import Logic.Propositional
import      Lvl
open import Structure.Setoid
open import Structure.Categorical.Properties
import      Structure.Relator.Names as Names
open import Structure.Relator.Properties
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓ₃ ℓ₄ ℓₘ ℓₑ : Lvl.Level
private variable Obj A B : Type{ℓ}
private variable _▫_ : Obj → Obj → Type{ℓ}
private variable f : A → B

empty-morphism : ∀{ℓₘ ℓ} → Empty{ℓ} → Empty{ℓ} → Type{ℓₘ}
empty-morphism{ℓₘ}{ℓ} = empty{T = Empty{ℓ} → Type{ℓₘ}}

empty-id : Names.Reflexivity(empty-morphism{ℓₘ}{ℓ})
empty-id {x = ()}

empty-comp : Names.SwappedTransitivity(empty-morphism{ℓₘ}{ℓ})
empty-comp {x = ()}

empty-inv : Names.Symmetry(empty-morphism{ℓₘ}{ℓ})
empty-inv {x = ()}

module _ ⦃ empty-equiv : ∀{x y} → Equiv{ℓₑ}(empty-morphism{ℓₘ}{ℓ} x y) ⦄ where
  instance
    empty-associativity : Morphism.Associativity{Morphism = empty-morphism} ⦃ empty-equiv ⦄ empty-comp
    empty-associativity = Morphism.intro \{}

  instance
    empty-identityₗ : Morphism.Identityₗ{Morphism = empty-morphism} ⦃ empty-equiv ⦄ empty-comp empty-id
    empty-identityₗ = Morphism.intro \{}

  instance
    empty-identityᵣ : Morphism.Identityᵣ{Morphism = empty-morphism} ⦃ empty-equiv ⦄ empty-comp empty-id
    empty-identityᵣ = Morphism.intro \{}

  empty-identity : Morphism.Identity empty-comp empty-id
  empty-identity = [∧]-intro empty-identityₗ empty-identityᵣ

  instance
    empty-inverterₗ : Polymorphism.Inverterₗ {Morphism = empty-morphism} ⦃ empty-equiv ⦄ empty-comp empty-id empty-inv
    empty-inverterₗ = Polymorphism.intro \{}

  instance
    empty-inverterᵣ : Polymorphism.Inverterᵣ {Morphism = empty-morphism} ⦃ empty-equiv ⦄ empty-comp empty-id empty-inv
    empty-inverterᵣ = Polymorphism.intro \{}

  empty-inverter : Polymorphism.Inverter empty-comp empty-id empty-inv
  empty-inverter = [∧]-intro empty-inverterₗ empty-inverterᵣ

single-morphism : ∀{ℓₘ ℓ} → Unit{ℓ} → Unit{ℓ} → Type{ℓₘ}
single-morphism <> <> = Unit

single-id : Names.Reflexivity(single-morphism{ℓₘ}{ℓ})
single-id = <>

single-comp : Names.SwappedTransitivity(single-morphism{ℓₘ}{ℓ})
single-comp <> <> = <>

single-inv : Names.Symmetry(single-morphism{ℓₘ}{ℓ})
single-inv <> = <>

module _ ⦃ single-equiv : ∀{x y} → Equiv{ℓₑ}(single-morphism{ℓₘ}{ℓ} x y) ⦄ where
  instance
    single-associativity : Morphism.Associativity{Morphism = single-morphism{ℓₘ}{ℓ₁}} ⦃ single-equiv ⦄ (single-comp{ℓ = ℓ₂})
    Morphism.Associativity.proof single-associativity {<>}{<>}{<>}{<>}{<>}{<>}{<>} = Reflexivity.proof(Equiv.reflexivity (single-equiv {<>}{<>}))

  instance
    single-identityₗ : Morphism.Identityₗ{Morphism = single-morphism{ℓₘ}{ℓ₁}} ⦃ single-equiv ⦄ (single-comp{ℓ = ℓ₂}) (single-id{ℓ = ℓ₃})
    Morphism.Identityₗ.proof single-identityₗ {<>}{<>}{<>} = Reflexivity.proof(Equiv.reflexivity (single-equiv {<>}{<>}))

  instance
    single-identityᵣ : Morphism.Identityᵣ{Morphism = single-morphism{ℓₘ}{ℓ₁}} ⦃ single-equiv ⦄ (single-comp{ℓ = ℓ₂}) (single-id{ℓ = ℓ₃})
    Morphism.Identityᵣ.proof single-identityᵣ {<>}{<>}{<>} = Reflexivity.proof(Equiv.reflexivity (single-equiv {<>}{<>}))

  single-identity : Morphism.Identity{Morphism = single-morphism{ℓₘ}{ℓ₁}} (single-comp{ℓ = ℓ₂}) (single-id{ℓ = ℓ₃})
  single-identity{ℓ₁}{ℓ₂}{ℓ₃} = [∧]-intro (single-identityₗ{ℓ₁}{ℓ₂}{ℓ₃}) (single-identityᵣ{ℓ₁}{ℓ₂}{ℓ₃})

  instance
    single-inverterₗ : Polymorphism.Inverterₗ {Morphism = single-morphism{ℓₘ}{ℓ₁}} ⦃ single-equiv ⦄ (single-comp{ℓ = ℓ₂}) (single-id{ℓ = ℓ₃}) (single-inv{ℓ = ℓ₄})
    Polymorphism.Inverterₗ.proof single-inverterₗ {<>}{<>}{<>} = Reflexivity.proof(Equiv.reflexivity (single-equiv {<>}{<>}))

  instance
    single-inverterᵣ : Polymorphism.Inverterᵣ {Morphism = single-morphism{ℓₘ}{ℓ₁}} ⦃ single-equiv ⦄ (single-comp{ℓ = ℓ₂}) (single-id{ℓ = ℓ₃}) (single-inv{ℓ = ℓ₄})
    Polymorphism.Inverterᵣ.proof single-inverterᵣ {<>}{<>}{<>} = Reflexivity.proof(Equiv.reflexivity (single-equiv {<>}{<>}))

  single-inverter : Polymorphism.Inverter {Morphism = single-morphism{ℓₘ}{ℓ₁}} ⦃ single-equiv ⦄ (single-comp{ℓ = ℓ₂}) (single-id{ℓ = ℓ₃}) (single-inv{ℓ = ℓ₄})
  single-inverter{ℓ₁}{ℓ₂}{ℓ₃}{ℓ₄} = [∧]-intro (single-inverterₗ{ℓ₁}{ℓ₂}{ℓ₃}{ℓ₄}) (single-inverterᵣ{ℓ₁}{ℓ₂}{ℓ₃}{ℓ₄})

module _ {Morphism : B → B → Type{ℓₘ}} ⦃ morphism-equiv : ∀{x y} → Equiv{ℓₑ}(Morphism x y) ⦄ (f : A → B) where
  module _ {comp : Names.SwappedTransitivity(Morphism)} where
    on₂-associativity : Morphism.Associativity{Morphism = Morphism} ⦃ morphism-equiv ⦄ comp → Morphism.Associativity{Morphism = Morphism on₂ f} comp
    Morphism.Associativity.proof (on₂-associativity p) = Morphism.Associativity.proof p

    module _ {id : Names.Reflexivity(Morphism)} where
      on₂-identityₗ : Morphism.Identityₗ{Morphism = Morphism} ⦃ morphism-equiv ⦄ comp id → Morphism.Identityₗ{Morphism = Morphism on₂ f} comp id
      Morphism.Identityₗ.proof (on₂-identityₗ p) = Morphism.Identityₗ.proof p

      on₂-identityᵣ : Morphism.Identityᵣ{Morphism = Morphism} ⦃ morphism-equiv ⦄ comp id → Morphism.Identityᵣ{Morphism = Morphism on₂ f} comp id
      Morphism.Identityᵣ.proof (on₂-identityᵣ p) = Morphism.Identityᵣ.proof p

      on₂-identity : Morphism.Identity{Morphism = Morphism} ⦃ morphism-equiv ⦄ comp id → Morphism.Identity{Morphism = Morphism on₂ f} comp id
      on₂-identity ([∧]-intro l r) = [∧]-intro (on₂-identityₗ l) (on₂-identityᵣ r)

      module _ {inv : Names.Symmetry(Morphism)} where
        on₂-inverterₗ : Polymorphism.Inverterₗ {Morphism = Morphism} ⦃ morphism-equiv ⦄ comp id inv → Polymorphism.Inverterₗ {Morphism = Morphism on₂ f} comp id inv
        Polymorphism.Inverterₗ.proof (on₂-inverterₗ p) = Polymorphism.Inverterₗ.proof p

        on₂-inverterᵣ : Polymorphism.Inverterᵣ {Morphism = Morphism} ⦃ morphism-equiv ⦄ comp id inv → Polymorphism.Inverterᵣ {Morphism = Morphism on₂ f} comp id inv
        Polymorphism.Inverterᵣ.proof (on₂-inverterᵣ p) = Polymorphism.Inverterᵣ.proof p

        on₂-inverter : Polymorphism.Inverter{Morphism = Morphism} ⦃ morphism-equiv ⦄ comp id inv → Polymorphism.Inverter{Morphism = Morphism on₂ f} comp id inv
        on₂-inverter ([∧]-intro l r) = [∧]-intro (on₂-inverterₗ l) (on₂-inverterᵣ r)


open import Prelude

module Implicits.Substitutions.Context where

open import Implicits.Syntax.Type
open import Implicits.Syntax.Context
open import Implicits.Substitutions.Type as TS using ()

open import Data.Fin.Substitution
open import Data.Star as Star hiding (map)
open import Data.Star.Properties
open import Data.Vec
open import Data.List as List using ()

ktx-map : ∀ {ν μ n} → (Type ν → Type μ) →  Ktx ν n → Ktx μ n
ktx-map f (Γ , Δ) = map f Γ , List.map f Δ

_/_ : ∀ {ν μ n} → Ktx ν n → Sub Type ν μ → Ktx μ n
K / σ = ktx-map (λ t → t TS./ σ) K

_/Var_ : ∀ {ν m n} → Sub Fin n m → Ktx ν m → Ktx ν n
σ /Var (Γ , Δ) = map (λ x → lookup x Γ) σ , Δ

ictx-weaken : ∀ {ν} → ICtx ν → ICtx (suc ν)
ictx-weaken Δ = List.map (flip TS._/_ TS.wk) Δ

ctx-weaken : ∀ {ν n} → Ctx ν n → Ctx (suc ν) n
ctx-weaken Γ = map (flip TS._/_ TS.wk) Γ

weaken : ∀ {ν n} → Ktx ν n → Ktx (suc ν) n
weaken K = K / TS.wk


open import Nat
open import Prelude
open import List
open import Hazelnut-core

module Hazelnut-checks where
  -----------------------------------------------------------------------------

  -- these theorems aren't listed in the draft, but have been discussed
  -- since submission. broadly speaking, they act as sanity checks on the
  -- rules. if these properties can't be proven for any extensions to the
  -- language, then the rules are not good.

  -----------------------------------------------------------------------------

  -- movement doesn't change the term other than moving the focus around.
  moveerase : {e e' : ê} {δ : direction} {t : τ̇} →
            (e + move δ +>e e') →
            (e ◆e) == (e' ◆e)
  moveerase EMAscFirstChild = refl
  moveerase EMAscParent1 = refl
  moveerase EMAscParent2 = refl
  moveerase EMAscNextSib = refl
  moveerase EMAscPrevSib = refl
  moveerase EMLamFirstChild = refl
  moveerase EMLamParent = refl
  moveerase EMPlusFirstChild = refl
  moveerase EMPlusParent1 = refl
  moveerase EMPlusParent2 = refl
  moveerase EMPlusNextSib = refl
  moveerase EMPlusPrevSib = refl
  moveerase EMApFirstChild = refl
  moveerase EMApParent1 = refl
  moveerase EMApParent2 = refl
  moveerase EMApNextSib = refl
  moveerase EMApPrevSib = refl
  moveerase EMFHoleFirstChild = refl
  moveerase EMFHoleParent = refl

  -- describes lists of action whose semantics are composable
  data iterok : Set where

  -- iterates a list of actions that can be composed
  data iterate : iterok → ê → ê → Set where

  --constructability
  mutual
    constructable1 : {Γ : ·ctx} {e : ê} {t : τ̇} →
                     (wt : Γ ⊢ (e ◆e) => t) →
                      Σ[ αs ∈ iterok ]
                        (Σ[ e' ∈ ê ] ((iterate αs (▹ <||> ◃) e') ×
                                       ((e ◆e) == (e' ◆e))))
    constructable1 = {!!}

    constructable2 : {Γ : ·ctx} {e : ê} {t : τ̇} →
                     (wt : Γ ⊢ (e ◆e) <= t) →
                      Σ[ αs ∈ iterok ]
                        (Σ[ e' ∈ ê ] ((iterate αs (▹ <||> ◃) e') ×
                                       ((e ◆e) == (e' ◆e))))
    constructable2 = {!!}

  --reachability
  mutual
    reachable1 : {Γ : ·ctx} {e e' : ê} {t : τ̇}
                 (wt  : Γ ⊢ (e ◆e) <= t) →
                 (wt' : Γ ⊢ (e' ◆e) <= t) →
                 (p : (e ◆e) == (e' ◆e)) →
                 Σ[ αs ∈ iterok ] (iterate αs e e')
    reachable1 = {!!}

    reachable2 : {Γ : ·ctx} {e e' : ê} {t : τ̇}
                 (wt  : Γ ⊢ (e ◆e) => t) →
                 (wt' : Γ ⊢ (e' ◆e) => t) →
                 (p : (e ◆e) == (e' ◆e)) →
                 Σ[ αs ∈ iterok ] (iterate αs e e')
    reachable2 = {!!}


{-

This file contains:
  - The equivalence "James X ≃ Ω Σ X" for any connected pointed type X.
    (KANG Rongji, Feb. 2022)

-}
{-# OPTIONS --safe #-}
module Cubical.HITs.James.LoopSuspEquiv where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.Fiberwise
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Pointed

open import Cubical.Data.Unit
open import Cubical.Data.Sigma
open import Cubical.HITs.Pushout
open import Cubical.HITs.Pushout.Flattening
open import Cubical.HITs.Susp
open import Cubical.HITs.James.Base
  renaming (James to JamesContruction ; James∙ to JamesContruction∙)
open import Cubical.HITs.Truncation

open import Cubical.Homotopy.Connected
open import Cubical.Homotopy.Loopspace

private
  variable
    ℓ : Level

module _
  ((X , x₀) : Pointed ℓ) where

  private
    James  =  JamesContruction  (X , x₀)
    James∙ =  JamesContruction∙ (X , x₀)

  Total : Type ℓ
  Total = Pushout {A = X × James} snd (λ (x , xs) → x ∷ xs)

  private
    flipSquare : (xs : James)(i j : I) → James
    flipSquare xs i j =
      hcomp (λ k → λ
        { (i = i0) → unit xs (j ∨ k)
        ; (i = i1) → unit (x₀ ∷ xs) j
        ; (j = i0) → unit xs (i ∨ k)
        ; (j = i1) → x₀ ∷ (unit xs i) })
      (unit (unit xs i) j)

    square1 : (x : X)(xs : James)(i j : I) → Total
    square1 x xs i j =
      hfill (λ j → λ
        { (i = i0) → push (x , xs) (~ j)
        ; (i = i1) → push (x₀ , x ∷ xs) (~ j) })
      (inS (inr (unit (x ∷ xs) i))) j

    square2 : (xs : James)(i j : I) → Total
    square2 xs i j =
      hcomp (λ k → λ
        { (i = i0) → push (x₀ , xs) (~ k)
        ; (i = i1) → square1 x₀ xs j k
        ; (j = i0) → push (x₀ , xs) (~ k)
        ; (j = i1) → push (x₀ , unit xs i) (~ k) })
      (inr (flipSquare xs i j))

    center : Total
    center = inl []

    pathL : (xs : James) → center ≡ inl xs
    pathL [] = refl
    pathL (x ∷ xs) = pathL xs ∙ (λ i → square1 x xs i i1)
    pathL (unit xs i) j =
      hcomp (λ k → λ
        { (i = i0) → pathL xs j
        ; (i = i1) → compPath-filler (pathL xs) (λ i → square1 x₀ xs i i1) k j
        ; (j = i0) → inl []
        ; (j = i1) → square2 xs i k })
      (pathL xs j)

  isContrTotal : isContr Total
  isContrTotal .fst = center
  isContrTotal .snd (inl xs) = pathL xs
  isContrTotal .snd (inr xs) = pathL xs ∙∙ push (x₀ , xs) ∙∙ (λ i → inr (unit xs (~ i)))
  isContrTotal .snd (push (x , xs) i) j =
    hcomp (λ k → λ
      { (i = i0) → compPath-filler' (pathL xs) (λ i → square1 x xs i i1) (~ j) (~ k)
      ; (i = i1) → doubleCompPath-filler (pathL (x ∷ xs)) (push (x₀ , x ∷ xs)) (λ i → inr (unit (x ∷ xs) (~ i))) k j
      ; (j = i0) → pathL (x ∷ xs) (~ k)
      ; (j = i1) → square1 x xs (~ k) (~ i) })
    (push (x₀ , x ∷ xs) (i ∧ j))

  module _
    (conn : isConnected 2 X) where

    private
      isEquivx₀∷ : isEquiv {A = James} (x₀ ∷_)
      isEquivx₀∷ = subst isEquiv (λ i xs → unit xs i) (idIsEquiv _)

      ∣isEquiv∣ : hLevelTrunc 2 X → Type ℓ
      ∣isEquiv∣ x = rec isSetHProp (λ x → isEquiv {A = James} (x ∷_) , isPropIsEquiv _) x .fst

    isEquiv∷ : (x : X) → isEquiv {A = James} (x ∷_)
    isEquiv∷ x = subst ∣isEquiv∣ (sym (conn .snd ∣ x₀ ∣) ∙ (conn .snd ∣ x ∣)) isEquivx₀∷

    Code : Susp X → Type ℓ
    Code north = James
    Code south = James
    Code (merid x i) = ua (_ , isEquiv∷ x) i

    private
      open FlatteningLemma (λ _ → tt) (λ _ → tt) (λ tt → James) (λ tt → James) (λ x → _ , isEquiv∷ x)

      Total≃ : Pushout Σf Σg ≃ Total
      Total≃ = pushoutEquiv _ _ _ _ (idEquiv _) (ΣUnit _) (ΣUnit _) refl refl

      PushoutSuspCode : (x : PushoutSusp X) → E x ≃ Code (PushoutSusp→Susp x)
      PushoutSuspCode (inl tt) = idEquiv _
      PushoutSuspCode (inr tt) = idEquiv _
      PushoutSuspCode (push x i) = idEquiv _

      ΣCode≃' : _ ≃ Σ _ Code
      ΣCode≃' = Σ-cong-equiv PushoutSusp≃Susp PushoutSuspCode

    ΣCode≃ : Total ≃ Σ _ Code
    ΣCode≃ = compEquiv (invEquiv Total≃) (compEquiv (invEquiv flatten) ΣCode≃')

    isContrΣCode : isContr (Σ _ Code)
    isContrΣCode = isOfHLevelRespectEquiv _ ΣCode≃ isContrTotal

    ΩΣ≃J : Ω (Susp∙ X) .fst ≃ James
    ΩΣ≃J = recognizeId Code [] isContrΣCode _

    ΩΣ≃∙J : Ω (Susp∙ X) ≃∙ James∙
    ΩΣ≃∙J = ΩΣ≃J , refl

    J≃ΩΣ : James ≃ Ω (Susp∙ X) .fst
    J≃ΩΣ = invEquiv ΩΣ≃J

    J≃∙ΩΣ : James∙ ≃∙ Ω (Susp∙ X)
    J≃∙ΩΣ = invEquiv∙ ΩΣ≃∙J


open import Agda.Builtin.List
open import Agda.Builtin.Unit
open import Agda.Builtin.Nat
open import Agda.Builtin.String
  renaming (primShowNat to show)
open import Agda.Builtin.Reflection
  renaming (bindTC to _>>=_; returnTC to return)

pattern vArg t = arg (arg-info visible relevant) t
pattern var₀ x = var x []

infixr 10 _++_
_++_ = primStringAppend

postulate
  whatever : ∀ {a} {A : Set a} → A

macro
  test1 : Nat → Term → TC _
  test1 n _ =
    extendContext (vArg (quoteTerm Nat)) do
      var₀ i ← quoteTC n where _ → whatever
      m ← unquoteTC {A = Nat} (var₀ 0)
      var₀ j ← quoteTC m where _ → whatever
      extendContext (vArg (quoteTerm Nat)) do
        var₀ k ← quoteTC n where _ → whatever
        var₀ l ← quoteTC m where _ → whatever
        typeError (strErr (show i ++ show k ++ show j ++ show l) ∷ [])

  test2 : Term → TC _
  test2 hole = do
    st ← quoteTC Set
    t ← extendContext (vArg st) do
      v ← unquoteTC {A = Set} (var₀ zero)
      extendContext (vArg (var₀ zero)) do
        _ ← unquoteTC {A = v} (var₀ zero)
        return tt
    u ← quoteTC t
    unify hole u

  test3 : Nat → Term → TC _
  test3 n _ = do
    m      ← extendContext (vArg (quoteTerm Nat)) (return n)
    var₀ i ← quoteTC m where _ → whatever
    typeError (strErr (show i) ∷ [])

  localvar : Term → TC _
  localvar _ = do
    m ← extendContext (vArg (quoteTerm Nat)) (unquoteTC {A = Nat} (var₀ 0))
    typeError (strErr (show m) ∷ [])


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

{-
  This file lists some basic facts about equivalences
  that have to be put in a separate file due to dependency.
-}

open import Types
open import Functions
open import Paths
open import HLevel
open import Equivalences
open import Univalence
open import HLevelBis

module EquivalenceLemmas where

  equiv-eq : ∀ {i} {A B : Set i} {f g : A ≃ B} → π₁ f ≡ π₁ g → f ≡ g
  equiv-eq p = Σ-eq p $ prop-has-all-paths (is-equiv-is-prop _) _ _


-- Andreas, 2020-09-26, issue #4946.
-- More liberal type signatures for constructors of sized types.

-- {-# OPTIONS -v tc.polarity:20 #-}

open import Agda.Builtin.Size

variable
  i : Size
  A : Set

data T : Size → Set → Set where
  c : A → T i A → T (↑ i) A

-- The type of the constructor c is elaborated to
--
--   c : {A : Set} {i : Set} → A → T i A → T (↑ i) A
--
-- Thus, the size argument i is not the first.
-- Nevertheless, Agda recognize the first argument of T
-- as covariant.

test : T i A → T ∞ A
test x = x

-- Should pass.


module _ where

open import Common.Prelude hiding (_>>=_)
open import Common.Reflection
open import Agda.Builtin.Sigma

infix -100 This:_ this:_
data This:_ {a} {A : Set a} : A → Set where
  this:_ : ∀ x → This: x

macro
  runT : Tactic → Tactic
  runT m = m

  evalT : ∀ {a} {A : Set a} → TC A → Tactic
  evalT m hole = m >>= quoteTC >>= unify hole

-- The context on the rhs of each of the two functions below is the same, a single String

Γ : Telescope
Γ = ("s" , vArg (def (quote String) [])) ∷ []

context-Γ₀ : String → This: Γ
context-Γ₀ s = this: evalT getContext

module _ (S : String) where
  Γ' : Telescope
  Γ' = ("S" , vArg (def (quote String) [])) ∷ []

  context-Γ₁ : This: Γ'
  context-Γ₁ = this: evalT getContext

downMap : {A : Set} → (Nat → A) → Nat → List A
downMap f zero    = []
downMap f (suc n) = f n ∷ downMap f n

f-type : Term
f-type = def (quote String) []

f-tel : Nat → List (Σ String λ _ → Arg Type)
f-tel n = downMap (λ _ → "_" , vArg unknown) n

f-pats : Nat → List (Arg Pattern)
f-pats n = downMap (λ x → vArg (var x)) n

f-term : Nat → Term
f-term n = var n []

defineFresh : Nat → Nat → TC QName
defineFresh #pats #term =
  freshName "f" >>= λ f →
  define (vArg f) (funDef f-type (clause (f-tel #pats) (f-pats #pats) (f-term #term) ∷ [])) >>= λ _ →
  returnTC f

freshFun : Nat → Nat → TC Bool
freshFun #pats #term =
  catchTC (defineFresh #pats #term >>= λ _ → returnTC true)
          (returnTC false)

-- Check that the pattern list must be of length 0
-- and the context features 1 available variable.

define-Γ₀-0-0 : String → This: true
define-Γ₀-0-0 s = this: evalT (freshFun 0 0)

define-Γ₀-1-0 : String → This: false
define-Γ₀-1-0 s = this: evalT (freshFun 1 0)

define-Γ₀-1-1 : String → This: false
define-Γ₀-1-1 s = this: evalT (freshFun 0 1)

module _ (S : String) where
  define-Γ₁-0-0 : This: true
  define-Γ₁-0-0 = this: evalT (freshFun 0 0)

  define-Γ₁-0-1 : This: false
  define-Γ₁-0-1 = this: evalT (freshFun 0 1)

  define-Γ₁-1-0 : This: false
  define-Γ₁-1-0 = this: evalT (freshFun 1 0)

f₀ : String → String
f₀ s = runT λ hole → defineFresh 0 0 >>= λ f → unify hole (def f [])

f₁ : String → String
f₁ = λ s → runT λ hole → defineFresh 0 0 >>= λ f → unify hole (def f [])

f₂ : String → String
f₂ s = runT λ hole → defineFresh 0 0 >>= λ f → unify hole (def f [])
  where x = 0


open import Categories
open import Functors
open import RMonads

module RMonads.CatofRAdj {a b c d}{C : Cat {a}{b}}{D : Cat {c}{d}}{J : Fun C D}
                         (M : RMonad J) where

open import Library

open import RAdjunctions

open Fun
open Cat

record ObjAdj {e f} : Set ((a ⊔ b ⊔ c ⊔ d ⊔ lsuc e ⊔ lsuc f)) where
  open RMonad M
  field E   : Cat {e}{f}
        adj : RAdj J E
  open RAdj adj
  field law : R ○ L ≅ TFun M
        ηlaw : ∀{X} → left (iden E {OMap L X}) ≅ η {X}
        bindlaw : ∀{X Y}{f : Hom D (OMap J X) (T Y)} →
          HMap R (right (subst (Hom D (OMap J X))
                               (fcong Y (cong OMap (sym law))) f))
          ≅ bind f
open ObjAdj

record HomAdj {e f}(A B : ObjAdj {e}{f}) : Set (a ⊔ b ⊔ c ⊔ d ⊔ lsuc (e ⊔ f))
  where
  constructor homadj
  open RAdj
  field K : Fun (E A) (E B)
        Llaw : K ○ L (adj A) ≅ L (adj B)
        Rlaw : R (adj A) ≅ R (adj B) ○ K
        rightlaw : {X : Obj C}{Y : Obj (E A)}
                   {f : Hom D (OMap J X) (OMap (R (adj A)) Y)} →
                   HMap K (right (adj A) {X}{Y} f)
                   ≅
                   right (adj B)
                         {X}
                         {OMap K Y}
                         (subst (Hom D (OMap J X))
                                (fcong Y (cong OMap Rlaw))
                                f)

open HomAdj

HomAdjEq : ∀{a b}{A B : ObjAdj {a}{b}}
           (f g : HomAdj A B) → K f ≅ K g → f ≅ g
HomAdjEq {A = A}{B} (homadj K _ _ _) (homadj .K _ _ _) refl =
  cong₃ (homadj K)
        (ir _ _)
        (ir _ _)
        (iext λ _ → iext λ _ → iext λ _ → hir refl)

rightlawlem : ∀{e f}{E : Cat {e}{f}}(R : Fun E D)(L : Fun C E) →
  (p : OMap R ≅ OMap (R ○ (IdF E))) →
  (right : {X : Obj C} {Y : Obj E} →
           Hom D (OMap J X) (OMap R Y) → Hom E (OMap L X) Y) →
  {X : Obj C}{Y : Obj E}{f : Hom D (OMap J X) (OMap R Y)} →
  right f ≅ right (subst (Hom D (OMap J X)) (fcong Y p) f)
rightlawlem R L refl right = refl

idLlaw : ∀{e f}(X : ObjAdj {e}{f}) →
         IdF (E X) ○ RAdj.L (adj X) ≅ RAdj.L (adj X)
idLlaw X = FunctorEq _ _ refl refl

idRlaw : ∀{e f}(X : ObjAdj {e}{f}) →
         RAdj.R (adj X) ≅ RAdj.R (adj X) ○ IdF (E X)
idRlaw X  = FunctorEq _ _ refl refl

idrightlaw : ∀{e f}
             (X : ObjAdj {e}{f}) →
             {X₁ : Obj C} {Y : Obj (E X)}
             {f : Hom D (OMap J X₁) (OMap (RAdj.R (adj X)) Y)} →
             HMap (IdF (E X)) (RAdj.right (adj X) f)
             ≅
             RAdj.right (adj X)
                        (subst (Hom D (OMap J X₁))
                               (fcong Y (cong OMap (idRlaw X)))
                               f)
idrightlaw X =
  rightlawlem (R (adj X))
              (L (adj X))
              (cong OMap
                    (FunctorEq (R (adj X))
                               (R (adj X) ○ IdF (E X))
                               refl
                               refl))
              (right (adj X))
  where open RAdj

idHomAdj : ∀{e f}{X : ObjAdj {e}{f}} → HomAdj X X
idHomAdj {X = X} = record {
  K        = IdF (E X);
  Llaw     = idLlaw X;
  Rlaw     = idRlaw X;
  rightlaw = idrightlaw X }

HMaplem : ∀{a b c d}{C : Cat {a}{b}}{D : Cat {c}{d}}{X X' Y Y' : Obj C} →
          X ≅ X' → Y ≅ Y' →
          {f : Hom C X Y}{f' : Hom C X' Y'} → f ≅ f' → (F : Fun C D) →
          HMap F {X}{Y} f ≅ HMap F {X'}{Y'} f'
HMaplem refl refl refl F = refl

rightlawlem2 : ∀{e f g h i j}{E : Cat {e}{f}}{F : Cat {g}{h}}{G : Cat {i}{j}}
  (J : Fun C D)
  (RX : Fun E D)(LX : Fun C E)
  (RY : Fun F D)(LY : Fun C F)
  (RZ : Fun G D)(LZ : Fun C G)
  (KA : Fun E F)(KB : Fun F G) →
  (rightX : {X : Obj C}{Y : Obj E} →
            Hom D (OMap J X) (OMap RX Y) → Hom E (OMap LX X) Y)
  (rightY : {X : Obj C}{Y : Obj F} →
            Hom D (OMap J X) (OMap RY Y) → Hom F (OMap LY X) Y)
  (rightZ : {X : Obj C}{Y : Obj G} →
            Hom D (OMap J X) (OMap RZ Y) → Hom G (OMap LZ X) Y) →
  (p : RY ≅ RZ ○ KB)(q : RX ≅ RY ○ KA) →
  (p' : KA ○ LX ≅ LY) →
  (r : {X : Obj C}{Y : Obj F}{f : Hom D (OMap J X) (OMap RY Y)} →
       HMap KB (rightY f)
       ≅
       rightZ (subst (Hom D (OMap J X))
                     (fcong Y (cong OMap p))
                     f)) →
  (s : {X : Obj C}{Y : Obj E}{f : Hom D (OMap J X) (OMap RX Y)} →
       HMap KA (rightX f)
       ≅
       rightY (subst (Hom D (OMap J X))
                     (fcong Y (cong OMap q))
                     f)) →
  ∀{A B}(h : Hom D (OMap J A) (OMap RX B)) →
  HMap (KB ○ KA) (rightX h) ≅
  rightZ
  (subst (Hom D (OMap J A))
         (fcong B (cong OMap (trans q (cong (λ F₁ → F₁ ○ KA) p))))
         h)
rightlawlem2 J .((RZ ○ KB) ○ KA) LX .(RZ ○ KB) .(KA ○ LX) RZ LZ KA KB
  rightX rightY rightZ refl refl refl r s h = trans (cong (HMap KB) s) r

compLlaw : ∀{e f}{X Y Z : ObjAdj {e}{f}} →
            (f : HomAdj Y Z)(g : HomAdj X Y) →
            (K f ○ K g) ○ RAdj.L (adj X) ≅ RAdj.L (adj Z)
compLlaw f g = FunctorEq
  _
  _
  (ext (λ A → trans
    (cong (OMap (K f)) (cong (λ F → OMap F A) (Llaw g)))
    (cong (λ F → OMap F A) (Llaw f))))
  (iext λ A → iext λ B → ext λ h → trans
    (HMaplem
      (cong (λ F → OMap F A) (Llaw g))
      (cong (λ F → OMap F B) (Llaw g))
      (cong (λ F → HMap F h) (Llaw g))
      (K f))
    (cong (λ F → HMap F h) (Llaw f)))

compRlaw : ∀{e f}{X Y Z : ObjAdj {e}{f}} →
            (f : HomAdj Y Z)(g : HomAdj X Y) →
            RAdj.R (adj X) ≅ RAdj.R (adj Z) ○ (K f ○ K g)
compRlaw f g = FunctorEq
  _
  _
  (ext (λ A → trans
    (cong (λ F → OMap F A) (Rlaw g))
    (cong (λ F → OMap F (OMap (K g) A)) (Rlaw f))))
  (iext λ A → iext λ B → ext λ h → trans
    (cong (λ F → HMap F h) (Rlaw g))
    (cong (λ F → HMap F (HMap (K g) h)) (Rlaw f)))

comprightlaw : ∀{e f}{X Y Z : ObjAdj {e}{f}} →
            (f : HomAdj Y Z)(g : HomAdj X Y) →
            {A : Obj C} {B : Obj (E X)}
            {h : Hom D (OMap J A) (OMap (RAdj.R (adj X)) B)} →
            HMap (K f ○ K g) (RAdj.right (adj X) h)
            ≅
            RAdj.right (adj Z)
                       (subst (Hom D (OMap J A))
                              (fcong B (cong OMap (compRlaw f g)))
                              h)
comprightlaw {X = X}{Y}{Z} f g {A}{B}{h} = trans
  (rightlawlem2 J (R (adj X)) (L (adj X)) (R (adj Y)) (L (adj Y))
   (R (adj Z)) (L (adj Z)) (K g) (K f) (right (adj X)) (right (adj Y))
   (right (adj Z)) (Rlaw f) (Rlaw g) (Llaw g) (rightlaw f)
   (rightlaw g) h) (cong (right (adj Z)) (trans (stripsubst (Hom D (OMap J A)) h (fcong B (cong OMap (trans (Rlaw g) (cong (λ F → F ○ K g) (Rlaw f)))))) (sym (stripsubst (Hom D (OMap J A)) h (fcong B (cong OMap (compRlaw f g))))))) where open RAdj

compHomAdj : ∀{e f}{X Y Z : ObjAdj {e}{f}} →
             HomAdj Y Z → HomAdj X Y → HomAdj X Z
compHomAdj f g = record {
  K        = K f ○ K g;
  Llaw     = compLlaw f g;
  Rlaw     = compRlaw f g;
  rightlaw = comprightlaw f g }

idlHomAdj : ∀{e f}{X Y : ObjAdj {e}{f}}
            {f : HomAdj X Y} → compHomAdj idHomAdj f ≅ f
idlHomAdj = HomAdjEq _ _ (FunctorEq _ _ refl refl)

idrHomAdj : ∀{e f}{X Y : ObjAdj {e}{f}}
            {f : HomAdj X Y} → compHomAdj f idHomAdj ≅ f
idrHomAdj  = HomAdjEq _ _ (FunctorEq _ _ refl refl)

assHomAdj : ∀{a b}{W X Y Z : ObjAdj {a}{b}}
            {f : HomAdj Y Z} {g : HomAdj X Y} {h : HomAdj W X} →
            compHomAdj (compHomAdj f g) h ≅ compHomAdj f (compHomAdj g h)
assHomAdj = HomAdjEq _ _ (FunctorEq _ _ refl refl)

CatofAdj : ∀{a b} → Cat
CatofAdj {a}{b} = record{
  Obj  = ObjAdj {a}{b};
  Hom  = HomAdj;
  iden = idHomAdj;
  comp = compHomAdj;
  idl  = idlHomAdj;
  idr  = idrHomAdj;
  ass  = λ{_ _ _ _ f g h} →
    assHomAdj {f = f}{g}{h}}


module _ where

data Unit : Set where
  unit : Unit

module M (_ : Unit) where

  record R₁ (A : Set) : Set₁ where
    no-eta-equality

    postulate
      x : A

  open R₁ ⦃ … ⦄ public

  record R₂ (A : Set) : Set₁ where
    field
      instance
        r₁ : R₁ A

  open R₂ ⦃ … ⦄

open M unit

postulate
  A : Set

instance
  postulate
    m : R₁ A

a : A
a = x


{-
This second-order term syntax was created from the following second-order syntax description:

syntax Ring | R

type
  * : 0-ary

term
  zero : * | 𝟘
  add  : *  *  ->  * | _⊕_ l20
  one  : * | 𝟙
  mult : *  *  ->  * | _⊗_ l30
  neg  : *  ->  * | ⊖_ r50

theory
  (𝟘U⊕ᴸ) a |> add (zero, a) = a
  (𝟘U⊕ᴿ) a |> add (a, zero) = a
  (⊕A) a b c |> add (add(a, b), c) = add (a, add(b, c))
  (⊕C) a b |> add(a, b) = add(b, a)
  (𝟙U⊗ᴸ) a |> mult (one, a) = a
  (𝟙U⊗ᴿ) a |> mult (a, one) = a
  (⊗A) a b c |> mult (mult(a, b), c) = mult (a, mult(b, c))
  (⊗D⊕ᴸ) a b c |> mult (a, add (b, c)) = add (mult(a, b), mult(a, c))
  (⊗D⊕ᴿ) a b c |> mult (add (a, b), c) = add (mult(a, c), mult(b, c))
  (𝟘X⊗ᴸ) a |> mult (zero, a) = zero
  (𝟘X⊗ᴿ) a |> mult (a, zero) = zero
  (⊖N⊕ᴸ) a |> add (neg (a), a) = zero
  (⊖N⊕ᴿ) a |> add (a, neg (a)) = zero
-}

module Ring.Syntax where

open import SOAS.Common
open import SOAS.Context
open import SOAS.Variable
open import SOAS.Families.Core
open import SOAS.Construction.Structure
open import SOAS.ContextMaps.Inductive

open import SOAS.Metatheory.Syntax

open import Ring.Signature

private
  variable
    Γ Δ Π : Ctx
    α : *T
    𝔛 : Familyₛ

-- Inductive term declaration
module R:Terms (𝔛 : Familyₛ) where

  data R : Familyₛ where
    var  : ℐ ⇾̣ R
    mvar : 𝔛 α Π → Sub R Π Γ → R α Γ

    𝟘   : R * Γ
    _⊕_ : R * Γ → R * Γ → R * Γ
    𝟙   : R * Γ
    _⊗_ : R * Γ → R * Γ → R * Γ
    ⊖_  : R * Γ → R * Γ

  infixl 20 _⊕_
  infixl 30 _⊗_
  infixr 50 ⊖_

  open import SOAS.Metatheory.MetaAlgebra ⅀F 𝔛

  Rᵃ : MetaAlg R
  Rᵃ = record
    { 𝑎𝑙𝑔 = λ where
      (zeroₒ ⋮ _)     → 𝟘
      (addₒ  ⋮ a , b) → _⊕_ a b
      (oneₒ  ⋮ _)     → 𝟙
      (multₒ ⋮ a , b) → _⊗_ a b
      (negₒ  ⋮ a)     → ⊖_  a
    ; 𝑣𝑎𝑟 = var ; 𝑚𝑣𝑎𝑟 = λ 𝔪 mε → mvar 𝔪 (tabulate mε) }

  module Rᵃ = MetaAlg Rᵃ

  module _ {𝒜 : Familyₛ}(𝒜ᵃ : MetaAlg 𝒜) where

    open MetaAlg 𝒜ᵃ

    𝕤𝕖𝕞 : R ⇾̣ 𝒜
    𝕊 : Sub R Π Γ → Π ~[ 𝒜 ]↝ Γ
    𝕊 (t ◂ σ) new = 𝕤𝕖𝕞 t
    𝕊 (t ◂ σ) (old v) = 𝕊 σ v
    𝕤𝕖𝕞 (mvar 𝔪 mε) = 𝑚𝑣𝑎𝑟 𝔪 (𝕊 mε)
    𝕤𝕖𝕞 (var v) = 𝑣𝑎𝑟 v

    𝕤𝕖𝕞  𝟘        = 𝑎𝑙𝑔 (zeroₒ ⋮ tt)
    𝕤𝕖𝕞 (_⊕_ a b) = 𝑎𝑙𝑔 (addₒ  ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞  𝟙        = 𝑎𝑙𝑔 (oneₒ  ⋮ tt)
    𝕤𝕖𝕞 (_⊗_ a b) = 𝑎𝑙𝑔 (multₒ ⋮ 𝕤𝕖𝕞 a , 𝕤𝕖𝕞 b)
    𝕤𝕖𝕞 (⊖_  a)   = 𝑎𝑙𝑔 (negₒ  ⋮ 𝕤𝕖𝕞 a)

    𝕤𝕖𝕞ᵃ⇒ : MetaAlg⇒ Rᵃ 𝒜ᵃ 𝕤𝕖𝕞
    𝕤𝕖𝕞ᵃ⇒ = record
      { ⟨𝑎𝑙𝑔⟩ = λ{ {t = t} → ⟨𝑎𝑙𝑔⟩ t }
      ; ⟨𝑣𝑎𝑟⟩ = refl
      ; ⟨𝑚𝑣𝑎𝑟⟩ = λ{ {𝔪 = 𝔪}{mε} → cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-tab mε)) }  }
      where
      open ≡-Reasoning
      ⟨𝑎𝑙𝑔⟩ : (t : ⅀ R α Γ) → 𝕤𝕖𝕞 (Rᵃ.𝑎𝑙𝑔 t) ≡ 𝑎𝑙𝑔 (⅀₁ 𝕤𝕖𝕞 t)
      ⟨𝑎𝑙𝑔⟩ (zeroₒ ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (addₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (oneₒ  ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (multₒ ⋮ _) = refl
      ⟨𝑎𝑙𝑔⟩ (negₒ  ⋮ _) = refl

      𝕊-tab : (mε : Π ~[ R ]↝ Γ)(v : ℐ α Π) → 𝕊 (tabulate mε) v ≡ 𝕤𝕖𝕞 (mε v)
      𝕊-tab mε new = refl
      𝕊-tab mε (old v) = 𝕊-tab (mε ∘ old) v

    module _ (g : R ⇾̣ 𝒜)(gᵃ⇒ : MetaAlg⇒ Rᵃ 𝒜ᵃ g) where

      open MetaAlg⇒ gᵃ⇒

      𝕤𝕖𝕞! : (t : R α Γ) → 𝕤𝕖𝕞 t ≡ g t
      𝕊-ix : (mε : Sub R Π Γ)(v : ℐ α Π) → 𝕊 mε v ≡ g (index mε v)
      𝕊-ix (x ◂ mε) new = 𝕤𝕖𝕞! x
      𝕊-ix (x ◂ mε) (old v) = 𝕊-ix mε v
      𝕤𝕖𝕞! (mvar 𝔪 mε) rewrite cong (𝑚𝑣𝑎𝑟 𝔪) (dext (𝕊-ix mε))
        = trans (sym ⟨𝑚𝑣𝑎𝑟⟩) (cong (g ∘ mvar 𝔪) (tab∘ix≈id mε))
      𝕤𝕖𝕞! (var v) = sym ⟨𝑣𝑎𝑟⟩

      𝕤𝕖𝕞! 𝟘 = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (_⊕_ a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! 𝟙 = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (_⊗_ a b) rewrite 𝕤𝕖𝕞! a | 𝕤𝕖𝕞! b = sym ⟨𝑎𝑙𝑔⟩
      𝕤𝕖𝕞! (⊖_ a) rewrite 𝕤𝕖𝕞! a = sym ⟨𝑎𝑙𝑔⟩

-- Syntax instance for the signature
R:Syn : Syntax
R:Syn = record
  { ⅀F = ⅀F
  ; ⅀:CS = ⅀:CompatStr
  ; mvarᵢ = R:Terms.mvar
  ; 𝕋:Init = λ 𝔛 → let open R:Terms 𝔛 in record
    { ⊥ = R ⋉ Rᵃ
    ; ⊥-is-initial = record { ! = λ{ {𝒜 ⋉ 𝒜ᵃ} → 𝕤𝕖𝕞 𝒜ᵃ ⋉ 𝕤𝕖𝕞ᵃ⇒ 𝒜ᵃ }
    ; !-unique = λ{ {𝒜 ⋉ 𝒜ᵃ} (f ⋉ fᵃ⇒) {x = t} → 𝕤𝕖𝕞! 𝒜ᵃ f fᵃ⇒ t } } } }

-- Instantiation of the syntax and metatheory
open Syntax R:Syn public
open R:Terms public
open import SOAS.Families.Build public
open import SOAS.Syntax.Shorthands Rᵃ public
open import SOAS.Metatheory R:Syn public


module Luau.OpSem where

open import Agda.Builtin.Equality using (_≡_)
open import FFI.Data.Maybe using (just)
open import Luau.Heap using (Heap; _≡_⊕_↦_; lookup; function_⟨_⟩_end)
open import Luau.Substitution using (_[_/_]ᴮ)
open import Luau.Syntax using (Expr; Stat; Block; nil; addr; var; function⟨_⟩_end; _$_; block_is_end; local_←_; _∙_; done; function_⟨_⟩_end; return)
open import Luau.Value using (addr; val)

data _⊢_⟶ᴮ_⊣_ : Heap → Block → Block → Heap → Set
data _⊢_⟶ᴱ_⊣_ : Heap → Expr → Expr → Heap → Set

data _⊢_⟶ᴱ_⊣_  where

  nil : ∀ {H} →

    -------------------
    H ⊢ nil ⟶ᴱ nil ⊣ H

  function : ∀ {H H′ a x B} →

    H′ ≡ H ⊕ a ↦ (function "anon" ⟨ x ⟩ B end) →
    -------------------------------------------
    H ⊢ (function⟨ x ⟩ B end) ⟶ᴱ (addr a) ⊣ H′

  app : ∀ {H H′ M M′ N} →

    H ⊢ M ⟶ᴱ M′ ⊣ H′ →
    -----------------------------
    H ⊢ (M $ N) ⟶ᴱ (M′ $ N) ⊣ H′

  beta : ∀ {H M a f x B} →

    (lookup H a) ≡ just(function f ⟨ x ⟩ B end) →
    -----------------------------------------------------
    H ⊢ (addr a $ M) ⟶ᴱ (block f is local x ← M ∙ B end) ⊣ H

  block : ∀ {H H′ B B′ b} →

    H ⊢ B ⟶ᴮ B′ ⊣ H′ →
    ----------------------------------------------------
    H ⊢ (block b is B end) ⟶ᴱ (block b is B′ end) ⊣ H′

  return : ∀ {H V B b} →

    --------------------------------------------------------
    H ⊢ (block b is return (val V) ∙ B end) ⟶ᴱ (val V) ⊣ H

  done : ∀ {H b} →

    ---------------------------------
    H ⊢ (block b is done end) ⟶ᴱ nil ⊣ H

data _⊢_⟶ᴮ_⊣_  where

  local : ∀ {H H′ x M M′ B} →

    H ⊢ M ⟶ᴱ M′ ⊣ H′ →
    -------------------------------------------------
    H ⊢ (local x ← M ∙ B) ⟶ᴮ (local x ← M′ ∙ B) ⊣ H′

  subst : ∀ {H x v B} →

    -------------------------------------------------
    H ⊢ (local x ← val v ∙ B) ⟶ᴮ (B [ v / x ]ᴮ) ⊣ H

  function : ∀ {H H′ a f x B C} →

    H′ ≡ H ⊕ a ↦ (function f ⟨ x ⟩ C end) →
    --------------------------------------------------------------
    H ⊢ (function f ⟨ x ⟩ C end ∙ B) ⟶ᴮ (B [ addr a / f ]ᴮ) ⊣ H′

  return : ∀ {H H′ M M′ B} →

    H ⊢ M ⟶ᴱ M′ ⊣ H′ →
    --------------------------------------------
    H ⊢ (return M ∙ B) ⟶ᴮ (return M′ ∙ B) ⊣ H′

data _⊢_⟶*_⊣_ : Heap → Block → Block → Heap → Set where

  refl : ∀ {H B} →

    ----------------
    H ⊢ B ⟶* B ⊣ H

  step : ∀ {H H′ H″ B B′ B″} →
    H ⊢ B ⟶ᴮ B′ ⊣ H′ →
    H′ ⊢ B′ ⟶* B″ ⊣ H″ →
    ------------------
    H ⊢ B ⟶* B″ ⊣ H″