------------------------------------------------------------------------
-- Least upper bounds
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

module Delay-monad.Least-upper-bound where

open import Equality.Propositional
open import Prelude hiding (_⊔_)
open import Prelude.Size

open import Nat equality-with-J

open import Delay-monad
open import Delay-monad.Partial-order

module _ {a} {A : Type a} where

  -- Least upper bounds (if they exist).

  infix 10 _⊔_

  _⊔_ : ∀ {i} → Delay A i → Delay A i → Delay A i
  now x   ⊔ _       = now x
  later x ⊔ now y   = now y
  later x ⊔ later y = later λ { .force → force x ⊔ force y }

  -- The computation x ⊔ y is smaller than or equal to every upper
  -- bound of x and y.

  ⊔-least-upper-bound : ∀ {i x y z} →
                        [ i ] x ⊑ z → [ i ] y ⊑ z → [ i ] x ⊔ y ⊑ z
  ⊔-least-upper-bound               now        _ = now
  ⊔-least-upper-bound               (laterʳ p) q = laterʳ (⊔-least-upper-bound p (laterʳ⁻¹ q))
  ⊔-least-upper-bound {y = now y}   (laterˡ _) q = q
  ⊔-least-upper-bound {y = later y} (laterˡ p) q = laterˡ λ { .force →
                                                     ⊔-least-upper-bound (force p) (laterˡ⁻¹ q) }

  -- If x and y have a lower bound, then this is also a lower bound of
  -- x ⊔ y.

  ⊔-lower-bound : ∀ {i x y z} → [ i ] z ⊑ x → [ i ] z ⊑ y → [ i ] z ⊑ x ⊔ y
  ⊔-lower-bound               now        _ = now
  ⊔-lower-bound               (laterˡ p) q = laterˡ λ { .force →
                                               ⊔-lower-bound (force p) (laterˡ⁻¹ q) }
  ⊔-lower-bound {y = now y}   (laterʳ _) q = q
  ⊔-lower-bound {y = later y} (laterʳ p) q = laterʳ (⊔-lower-bound p (laterʳ⁻¹ q))

  -- If x and y have an upper bound, then x ⊔ y is an upper bound
  -- of y.

  ⊔-upper-boundʳ : ∀ {i x y z} →
                   [ i ] x ⊑ z → [ i ] y ⊑ z → [ i ] y ⊑ x ⊔ y
  ⊔-upper-boundʳ               now        q = q
  ⊔-upper-boundʳ               (laterʳ p) q = ⊔-upper-boundʳ p (laterʳ⁻¹ q)
  ⊔-upper-boundʳ {y = now y}   (laterˡ _) _ = now
  ⊔-upper-boundʳ {y = later y} (laterˡ p) q =
    later-cong λ { .force → ⊔-upper-boundʳ (force p) (laterˡ⁻¹ q) }

  -- If x and y have an upper bound, then x ⊔ y is an upper bound
  -- of x.

  ⊔-upper-boundˡ : ∀ {i x y z} →
                   [ i ] x ⊑ z → [ i ] y ⊑ z → [ i ] x ⊑ x ⊔ y
  ⊔-upper-boundˡ               now        q = now
  ⊔-upper-boundˡ               (laterʳ p) q = ⊔-upper-boundˡ p (laterʳ⁻¹ q)
  ⊔-upper-boundˡ {y = later y} (laterˡ p) q = later-cong λ { .force →
                                                ⊔-upper-boundˡ (force p) (laterˡ⁻¹ q) }
  ⊔-upper-boundˡ {y = now y}   (laterˡ p) q = laterˡ λ { .force →
                                                ⊔-upper-boundʳ q (force p) }

  -- A variant of the lower/upper bound lemmas.

  ⊔-lower-upper-bound : ∀ {i x y z} →
                        x ⊑ y → [ i ] y ⊑ z → [ i ] y ⊑ x ⊔ z
  ⊔-lower-upper-bound               now        _            = now
  ⊔-lower-upper-bound               (laterʳ p) q            = laterˡ λ { .force →
                                                                ⊔-lower-upper-bound p (laterˡ⁻¹ q) }
  ⊔-lower-upper-bound               (laterˡ p) (laterʳ q)   = laterʳ (⊔-lower-upper-bound (force p) q)
  ⊔-lower-upper-bound {z = now z}   (laterˡ _) q            = q
  ⊔-lower-upper-bound {z = later z} (laterˡ p) (laterˡ q)   = later-cong λ { .force →
                                                                ⊔-lower-upper-bound
                                                                  (laterʳ⁻¹ (force p))
                                                                  (laterʳ⁻¹ (force q)) }

  -- If x terminates, then x ⊔ y also terminates.

  ⊔-⇓ˡ : ∀ {i x y} {z : A} →
         Terminates i x z → ∃ λ z′ → Terminates i (x ⊔ y) z′
  ⊔-⇓ˡ               now        = _ , now
  ⊔-⇓ˡ {y = now y}   (laterʳ p) = _ , now
  ⊔-⇓ˡ {y = later y} (laterʳ p) = Σ-map id laterʳ (⊔-⇓ˡ p)

  -- If y terminates, then x ⊔ y also terminates.

  ⊔-⇓ʳ : ∀ {i} x {y} {z : A} →
         Terminates i y z → ∃ λ z′ → Terminates i (x ⊔ y) z′
  ⊔-⇓ʳ (now x)   _          = _ , now
  ⊔-⇓ʳ (later x) now        = _ , now
  ⊔-⇓ʳ (later x) (laterʳ p) = Σ-map id laterʳ (⊔-⇓ʳ (force x) p)

-- Increasing sequences.

Increasing-sequence : ∀ {a} → Type a → Type a
Increasing-sequence A =
  ∃ λ (f : ℕ → Delay A ∞) → ∀ n → f n ⊑ f (suc n)

module _ {a} {A : Type a} where

  -- The tail of an increasing sequence.

  tailˢ : Increasing-sequence A → Increasing-sequence A
  tailˢ = Σ-map (_∘ suc) (_∘ suc)

  -- Later sequence elements are larger.

  later-larger : (s : Increasing-sequence A) →
                 ∀ {m n} → m ≤ n → proj₁ s m ⊑ proj₁ s n
  later-larger s (≤-refl′ refl)   = reflexive _
  later-larger s (≤-step′ p refl) =
    transitive (later-larger s p) (proj₂ s _)

  -- Least upper bounds of increasing sequences.

  ⨆ : ∀ {i} → Increasing-sequence A → Delay A i
  ⨆ s = proj₁ s 0 ⊔ later λ { .force → ⨆ (tailˢ s) }

  -- ⨆ s is smaller than or equal to every upper bound of s.

  ⨆-least-upper-bound :
    ∀ {i} (s : Increasing-sequence A) ub →
    (∀ n → [ i ] proj₁ s n ⊑ ub) → [ i ] ⨆ s ⊑ ub
  ⨆-least-upper-bound s ub is-ub =
    ⊔-least-upper-bound
      (is-ub 0)
      (laterˡ λ { .force →
         ⨆-least-upper-bound (tailˢ s) ub (is-ub ∘ suc) })

  -- If every element in s terminates with a given value, then ⨆ s
  -- terminates with the same value.

  ⨆-⇓ : ∀ {i x} (s : Increasing-sequence A) →
        (∀ n → proj₁ s n ⇓ x) → Terminates i (⨆ s) x
  ⨆-⇓ s s-⇓ =
    ⊑now→⇓→⇓ (⨆-least-upper-bound s _ (λ n → symmetric (s-⇓ n)))
             (proj₂ (⊔-⇓ˡ (s-⇓ 0)))

  -- If s has a lower bound, then this is also a lower bound of ⨆ s.

  ⨆-lower-bound : ∀ {i} (s : Increasing-sequence A) lb →
                  (∀ n → lb ⊑ proj₁ s n) → [ i ] lb ⊑ ⨆ s
  ⨆-lower-bound s (now x)    is-lb = ⨆-⇓ s is-lb
  ⨆-lower-bound s (later lb) is-lb =
    ⊔-lower-bound
      (is-lb 0)
      (later-cong λ { .force →
         ⨆-lower-bound (tailˢ s) (force lb) λ n →
           laterˡ⁻¹ (transitive (is-lb n) (proj₂ s n)) })

  -- ⨆ s is an upper bound of s.

  ⨆-upper-bound : ∀ {i} (s : Increasing-sequence A) →
                  ∀ n → [ i ] proj₁ s n ⊑ ⨆ s
  ⨆-upper-bound s zero    = ⨆-lower-bound s (proj₁ s 0)
                              (λ n → later-larger s (zero≤ n))
  ⨆-upper-bound s (suc n) = ⊔-lower-upper-bound
                              (later-larger s (zero≤ (suc n)))
                              (laterʳ (⨆-upper-bound (tailˢ s) n))


------------------------------------------------------------------------
-- The two definitions of context extensions are isomorphic
------------------------------------------------------------------------

open import Data.Universe.Indexed

module deBruijn.Context.Extension.Isomorphic
  {i u e} (Uni : IndexedUniverse i u e) where

import deBruijn.Context.Basics          as Basics
import deBruijn.Context.Extension.Left  as Left
import deBruijn.Context.Extension.Right as Right
open import Function.Base
open import Function.Inverse using (_↔_)
import Relation.Binary.PropositionalEquality as P

open Basics Uni
open Left   Uni
open Right  Uni
open P.≡-Reasoning

-- Ctxt₊-s can be turned into Ctxt⁺-s.

₊-to-⁺ : ∀ {Γ} → Ctxt₊ Γ → Ctxt⁺ Γ
₊-to-⁺ ε        = ε
₊-to-⁺ (σ ◅ Γ₊) = ε ▻ σ ⁺++⁺ ₊-to-⁺ Γ₊

abstract

  -- The semantics is preserved.

  ++⁺-₊-to-⁺ : ∀ {Γ} (Γ₊ : Ctxt₊ Γ) → Γ ++₊ Γ₊ ≅-Ctxt Γ ++⁺ ₊-to-⁺ Γ₊
  ++⁺-₊-to-⁺     ε        = P.refl
  ++⁺-₊-to-⁺ {Γ} (σ ◅ Γ₊) = begin
    Γ ▻ σ ++₊ Γ₊                  ≡⟨ ++⁺-₊-to-⁺ Γ₊ ⟩
    Γ ▻ σ ++⁺ ₊-to-⁺ Γ₊           ≡⟨ ++⁺-++⁺ (ε ▻ σ) (₊-to-⁺ Γ₊) ⟩
    Γ ++⁺ (ε ▻ σ ⁺++⁺ ₊-to-⁺ Γ₊)  ∎

mutual

  -- Ctxt⁺-s can be turned into Ctxt₊-s.

  ⁺-to-₊ : ∀ {Γ} → Ctxt⁺ Γ → Ctxt₊ Γ
  ⁺-to-₊ ε        = ε
  ⁺-to-₊ (Γ⁺ ▻ σ) =
    ⁺-to-₊ Γ⁺ ₊++₊ P.subst Ctxt₊ (++₊-⁺-to-₊ Γ⁺) (σ ◅ ε)

  abstract

    -- The semantics is preserved.

    ++₊-⁺-to-₊ : ∀ {Γ} (Γ⁺ : Ctxt⁺ Γ) → Γ ++⁺ Γ⁺ ≅-Ctxt Γ ++₊ ⁺-to-₊ Γ⁺
    ++₊-⁺-to-₊     ε        = P.refl
    ++₊-⁺-to-₊ {Γ} (Γ⁺ ▻ σ) =
      let σ◅ε = P.subst Ctxt₊ (++₊-⁺-to-₊ Γ⁺) (σ ◅ ε)

      in begin
      Γ ++⁺ Γ⁺ ▻ σ                ≡⟨ P.refl ⟩
      Γ ++⁺ Γ⁺ ++₊ (σ ◅ ε)        ≡⟨ P.sym $ ++₊-cong $ drop-subst-Ctxt₊ id (++₊-⁺-to-₊ Γ⁺) ⟩
      Γ ++₊ ⁺-to-₊ Γ⁺ ++₊ σ◅ε     ≡⟨ ++₊-++₊ (⁺-to-₊ Γ⁺) σ◅ε ⟩
      Γ ++₊ (⁺-to-₊ Γ⁺ ₊++₊ σ◅ε)  ∎

-- Some congruence lemmas.

₊-to-⁺-cong : ∀ {Γ₁} {Γ₊₁ : Ctxt₊ Γ₁}
                {Γ₂} {Γ₊₂ : Ctxt₊ Γ₂} →
              Γ₊₁ ≅-Ctxt₊ Γ₊₂ → ₊-to-⁺ Γ₊₁ ≅-Ctxt⁺ ₊-to-⁺ Γ₊₂
₊-to-⁺-cong P.refl = P.refl

⁺-to-₊-cong : ∀ {Γ₁} {Γ⁺₁ : Ctxt⁺ Γ₁}
                {Γ₂} {Γ⁺₂ : Ctxt⁺ Γ₂} →
              Γ⁺₁ ≅-Ctxt⁺ Γ⁺₂ → ⁺-to-₊ Γ⁺₁ ≅-Ctxt₊ ⁺-to-₊ Γ⁺₂
⁺-to-₊-cong P.refl = P.refl

-- Ctxt⁺ and Ctxt₊ are isomorphic.

Ctxt⁺↔Ctxt₊ : ∀ Γ → Ctxt⁺ Γ ↔ Ctxt₊ Γ
Ctxt⁺↔Ctxt₊ Γ = record
  { to         = P.→-to-⟶ ⁺-to-₊
  ; from       = P.→-to-⟶ ₊-to-⁺
  ; inverse-of = record
    { left-inverse-of  = λ Γ⁺ → ≅-Ctxt⁺-⇒-≡ $ ₊-to-⁺-⁺-to-₊ Γ⁺
    ; right-inverse-of = λ Γ₊ → ≅-Ctxt₊-⇒-≡ $ ⁺-to-₊-₊-to-⁺ Γ₊
    }
  }
  where
  abstract
    ₊-to-⁺-⁺-to-₊ : (Γ⁺ : Ctxt⁺ Γ) → ₊-to-⁺ (⁺-to-₊ Γ⁺) ≅-Ctxt⁺ Γ⁺
    ₊-to-⁺-⁺-to-₊ Γ⁺ = cancel-++⁺-left _ _ (begin
      Γ ++⁺ ₊-to-⁺ (⁺-to-₊ Γ⁺)  ≡⟨ P.sym $ ++⁺-₊-to-⁺ (⁺-to-₊ Γ⁺) ⟩
      Γ ++₊ (⁺-to-₊ Γ⁺)         ≡⟨ P.sym $ ++₊-⁺-to-₊ Γ⁺ ⟩
      Γ ++⁺ Γ⁺                  ∎)

    ⁺-to-₊-₊-to-⁺ : (Γ₊ : Ctxt₊ Γ) → ⁺-to-₊ (₊-to-⁺ Γ₊) ≅-Ctxt₊ Γ₊
    ⁺-to-₊-₊-to-⁺ Γ₊ = cancel-++₊-left _ _ (begin
      Γ ++₊ ⁺-to-₊ (₊-to-⁺ Γ₊)  ≡⟨ P.sym $ ++₊-⁺-to-₊ (₊-to-⁺ Γ₊) ⟩
      Γ ++⁺ (₊-to-⁺ Γ₊)         ≡⟨ P.sym $ ++⁺-₊-to-⁺ Γ₊ ⟩
      Γ ++₊ Γ₊                  ∎)


------------------------------------------------------------------------
-- Code related to the paper "Bag Equivalence via a Proof-Relevant
-- Membership Relation"
--
-- Nils Anders Danielsson
------------------------------------------------------------------------

-- Note that the code does not follow the paper exactly. For instance,
-- many definitions are universe-polymorphic, and in some cases where
-- the paper contains both a specialised and a more general definition
-- the code only contains the more general one. The code has also been
-- changed after the paper was published.

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

module README.Bag-equivalence where

------------------------------------------------------------------------
-- 2: Brief Introduction to Agda

-- The prelude, containing List, ℕ, Fin, ⊥, ⊤, _⊎_ (written as _+_ in
-- the paper), ∃, and _×_.

import Prelude

-- Some list functions: length and index (called lookup in the paper).

import List

-- Logical equivalences: _⇔_.

import Logical-equivalence

-- Bijections: _↔_.

import Bijection

-- Equality: _≡_.

import Equality.Propositional

-- The K rule, and a proof showing that it implies proof-irrelevance.

import Equality

-- Bijectional reasoning combinators (more general than those in the
-- paper): inverse (written as sym in the paper), _□, and _↔⟨_⟩_.

import Function-universe

------------------------------------------------------------------------
-- 3: Bag Equivalence for Lists

-- Any, _∈_, and the two definitions of bag equivalence.

import Bag-equivalence

------------------------------------------------------------------------
-- 4: Bijectional Reasoning

-- Definitions of map, concat and _>>=_ (the latter as part of a monad
-- instance).

import List

-- Algebraic properties of type constructors (like ⊥ ⊎ A ↔ A).

import Function-universe
import Fin

-- All the main lemmas from this section, including
-- >>=-left-distributive.

import Bag-equivalence

------------------------------------------------------------------------
-- 5: The Definitions Are Equivalent

-- The equivalence proof.

import Bag-equivalence

-- There are infinitely many proofs of ℕ ≡ ℕ in homotopy type theory.

import Univalence-axiom

------------------------------------------------------------------------
-- 6: Bag Equivalence for Arbitrary Containers

-- Containers, including Any, _∈_, the two definitions of bag
-- equivalence, and a proof showing that the two definitions are
-- logically equivalent.
--
-- There is also a proof which shows that the definitions are
-- isomorphic (assuming extensionality), if "bijection" is replaced by
-- the logically equivalent concept of "(weak) equivalence" in the
-- definitions of bag equivalence.

import Container
import Equivalence

-- The List, Stream and Tree containers. It is shown that the general
-- definition of bag equivalence for containers, instantiated with the
-- List container, is equivalent (in a certain sense) to the list-only
-- definition given above.

import Container.List
import Container.Stream
import Container.Tree

------------------------------------------------------------------------
-- 7: More Bijectional Reasoning

-- Three implementations of tree sort are provided.

-- 1) An implementation of tree sort, formally proved to return a
--    permutation of the input.

import Tree-sort.Partial
import Tree-sort.Examples

-- 2) An implementation of tree sort, formally proved to return a
--    /sorted/ permutation of the input.

import Tree-sort.Full
import Tree-sort.Examples

-- 3) An implementation of tree sort which uses containers to
--    represent trees and lists.
--
--    In the module Tree-sort.Full indexed types are used to enforce
--    sortedness, but this development uses non-indexed containers, so
--    sortedness is not enforced.
--
--    The implementation using containers has the advantage of uniform
--    definitions of Any/membership/bag equivalence, but the other
--    implementations use more direct definitions and are perhaps a
--    bit "leaner".

import Container.Tree
import Container.Tree-sort
import Container.Tree-sort.Example

------------------------------------------------------------------------
-- 8: Set Equivalence, Subsets and Subbags

-- Injections: _↣_.

import Injection

-- The general definition of set and bag equivalence and the subset
-- and subbag preorders, as well as preservation lemmas such as
-- >>=-cong.

import Bag-equivalence

------------------------------------------------------------------------
-- 9: Related Work

-- One of the definitions of bag equivalence from Coq's standard
-- library has been replicated, and shown to be sound with respect to
-- the other ones.

import Bag-equivalence

------------------------------------------------------------------------
-- 10: Conclusions

-- Two proofs showing that cons is left cancellative, using different
-- definitions of bag equivalence.

import Bag-equivalence


module SN where

open import Relation.Unary using (_∈_; _⊆_)
open import Library
open import Terms
open import Substitution
open import TermShape public

-- Inductive definition of strong normalization.

infix 7 _⟨_⟩⇒_ _⇒ˢ_

mutual

  -- Strongly normalizing evaluation contexts

  SNhole : ∀ {i : Size} {Γ : Cxt} {a b : Ty} → Tm Γ b → ECxt Γ a b → Tm Γ a → Set
  SNhole {i} = PCxt (SN {i})

  -- Strongly neutral terms.

  SNe : ∀ {i : Size} {Γ} {b} → Tm Γ b → Set
  SNe {i} = PNe (SN {i})

  -- Strongly normalizing terms.

  data SN {i : Size}{Γ} : ∀ {a} → Tm Γ a → Set where

    ne   : ∀ {j : Size< i} {a t}
           → (𝒏 : SNe {j} t)
           → SN {a = a} t

    abs  : ∀ {j : Size< i} {a b}{t : Tm (a ∷ Γ) b}
           → (𝒕 : SN {j} t)
           → SN (abs t)

    exp  : ∀ {j₁ j₂ : Size< i} {a t t′}
           → (t⇒ :  t ⟨ j₁ ⟩⇒ t′) (𝒕′ : SN {j₂} t′)
           → SN {a = a} t

  _⟨_⟩⇒_ : ∀ {Γ a} → Tm Γ a → Size → Tm Γ a → Set
  t ⟨ i ⟩⇒ t′ = SN {i} / t ⇒ t′

  -- Strong head reduction

  _⇒ˢ_ : ∀ {i : Size} {Γ} {a} → Tm Γ a → Tm Γ a → Set
  _⇒ˢ_ {i} t t' = (SN {i}) / t ⇒ t'

-- -- Inductive definition of strong normalization.

-- mutual

--   -- Strongly normalizing evaluation contexts

--   data SNhole {i : Size} (n : ℕ) {Γ : Cxt} : {a b : Ty} → Tm Γ b → ECxt Γ a b → Tm Γ a → Set where

--     appl  : ∀ {a b t u}
--             → (𝒖 : SN {i} n u)
--             → SNhole n (app t u) (appl u) (t ∶ (a →̂ b))

--   -- Strongly neutral terms.

--   data SNe {i : Size} (n : ℕ) {Γ} {b} : Tm Γ b → Set where

--     var  : ∀ x                              → SNe n (var x)

--     elim : ∀ {a} {t : Tm Γ a} {E Et}
--            → (𝒏 : SNe {i} n t) (𝑬𝒕 : SNhole {i} n Et E t) → SNe n Et
--     -- elim : ∀ {j₁ j₂ : Size< i}{a} {t : Tm Γ a} {E Et}
--     --        → (𝒏 : SNe {j₁} n t) (𝑬𝒕 : SNhole {j₂} n Et E t) → SNe n Et

--   -- Strongly normalizing terms.

--   data SN {i : Size}{Γ} : ℕ → ∀ {a} → Tm Γ a → Set where

--     ne   : ∀ {j : Size< i} {a n t}
--            → (𝒏 : SNe {j} n t)
--            → SN n {a} t

--     abs  : ∀ {j : Size< i} {a b n}{t : Tm (a ∷ Γ) b}
--            → (𝒕 : SN {j} n t)
--            → SN n (abs t)

--     exp  : ∀ {j₁ j₂ : Size< i} {a n t t′}
--            → (t⇒ : j₁ size t ⟨ n ⟩⇒ t′) (𝒕′ : SN {j₂} n t′)
--            → SN n {a} t

--   _size_⟨_⟩⇒_ : ∀ (i : Size) {Γ}{a} → Tm Γ a → ℕ → Tm Γ a → Set
--   i size t ⟨ n ⟩⇒ t′ = _⟨_⟩⇒_ {i} t n t′

--   -- Strong head reduction

--   data _⟨_⟩⇒_ {i : Size} {Γ} : ∀ {a} → Tm Γ a → ℕ → Tm Γ a → Set where

--     β     : ∀  {a b}{t : Tm (a ∷ Γ) b}{u}
--             → (𝒖 : SN {i} n u)
--             → (app (abs t) u) ⟨ n ⟩⇒ subst0 u t

--     cong  : ∀  {a b t t' Et Et'}{E : ECxt Γ a b}
--             → (𝑬𝒕 : Ehole Et E t)
--             → (𝑬𝒕' : Ehole Et' E t')
--             → (t⇒ : i size t ⟨ n ⟩⇒ t')
--             → Et ⟨ n ⟩⇒ Et'

    -- β     : ∀ {j : Size< i} {a b}{t : Tm (a ∷ Γ) b}{u}
    --         → (𝒖 : SN {j} n u)
    --         → (app (abs t) u) ⟨ n ⟩⇒ subst0 u t

    -- cong  : ∀ {j : Size< i} {a b t t' Et Et'}{E : ECxt Γ a b}
    --         → (𝑬𝒕 : Ehole Et E t)
    --         → (𝑬𝒕' : Ehole Et' E t')
    --         → (t⇒ : j size t ⟨ n ⟩⇒ t')
    --         → Et ⟨ n ⟩⇒ Et'

-- Strong head reduction is deterministic.

det⇒ : ∀ {a Γ} {t t₁ t₂ : Tm Γ a}
       → (t⇒₁ : t ⟨ _ ⟩⇒ t₁) (t⇒₂ : t ⟨ _ ⟩⇒ t₂) → t₁ ≡ t₂
det⇒ (β _) (β _)                                              = ≡.refl
det⇒ (β _) (cong (appl u) (appl .u) (cong () _ _))
det⇒ (cong (appl u) (appl .u) (cong () _ _)) (β _)
det⇒ (cong (appl u) (appl .u) x) (cong (appl .u) (appl .u) y) = ≡.cong (λ t → app t u) (det⇒ x y)

-- Strongly neutrals are closed under application.

sneApp : ∀{Γ a b}{t : Tm Γ (a →̂ b)}{u : Tm Γ a} →
  SNe t → SN u → SNe (app t u)
sneApp 𝒏 𝒖 = elim 𝒏 (appl 𝒖)

-- Substituting strongly neutral terms

record RenSubSNe {i} (vt : VarTm i) (Γ Δ : Cxt) : Set where
  constructor _,_
  field theSubst : RenSub vt Γ Δ
        isSNe    : ∀ {a} (x : Var Γ a) → SNe (vt2tm _ (theSubst x))
open RenSubSNe

RenSN    = RenSubSNe `Var
SubstSNe = RenSubSNe `Tm

-- The singleton SNe substitution.
-- Replaces the first variable by another variable.

sgs-varSNe : ∀ {Γ a} → Var Γ a → SubstSNe (a ∷ Γ) Γ
theSubst (sgs-varSNe x)         = sgs (var x)
isSNe    (sgs-varSNe x) (zero)  = (var x)
isSNe    (sgs-varSNe x) (suc y) = var y

-- The SN-notions are closed under SNe substitution.

mutual
  substSNh : ∀ {i vt Γ Δ a b} → (σ : RenSubSNe {i} vt Γ Δ) → ∀ {E : ECxt Γ a b}{Et t} → (SNh : SNhole Et E t)
                                → SNhole (subst (theSubst σ) Et) (substEC (theSubst σ) E) (subst (theSubst σ) t)
  substSNh σ (appl u) = appl (substSN σ u)

  subst⇒ : ∀ {i vt Γ Δ a} (σ : RenSubSNe {i} vt Γ Δ) {t t' : Tm Γ a} → t ⟨ _ ⟩⇒ t' → subst (theSubst σ) t ⟨ _ ⟩⇒ subst (theSubst σ) t'
  subst⇒ (σ , σ∈Ne) (β {t = t} {u = u} x) = ≡.subst (λ t' → app (abs (subst (lifts σ) t)) (subst σ u) ⟨ _ ⟩⇒ t')
                                                   (sgs-lifts-term {σ = σ} {u} {t})
                                                   (β {t = subst (lifts σ) t} (substSN (σ , σ∈Ne) x))

  subst⇒ σ (cong Eh Eh' t→t')    = cong (substEh (theSubst σ) Eh) (substEh (theSubst σ) Eh') (subst⇒ σ t→t')

  -- Lifting a SNe substitution.

  liftsSNe : ∀ {i vt Γ Δ a} → RenSubSNe {i} vt Γ Δ → RenSubSNe {i} vt (a ∷ Γ) (a ∷ Δ)
  theSubst (liftsSNe σ)                   = lifts (theSubst σ)
  isSNe    (liftsSNe {vt = `Var} (σ , σ∈SNe)) (zero)    = var (zero)
  isSNe    (liftsSNe {vt = `Var} (σ , σ∈SNe)) (suc y) = var (suc (σ y))
  isSNe    (liftsSNe {vt = `Tm } (σ , σ∈SNe)) (zero)    = var (zero)
  isSNe    (liftsSNe {vt = `Tm } (σ , σ∈SNe)) (suc y) = substSNe {vt = `Var} (suc , (λ x → var (suc x))) (σ∈SNe y)

  substSNe : ∀ {i vt Γ Δ τ} → (σ : RenSubSNe {i} vt Γ Δ) → ∀ {t : Tm Γ τ} → SNe t → SNe (subst (theSubst σ) t)
  substSNe σ (var x)            = isSNe σ x
  substSNe σ (elim t∈SNe E∈SNh) = elim (substSNe σ t∈SNe) (substSNh σ E∈SNh)

  substSN : ∀ {i vt Γ Δ τ} → (σ : RenSubSNe {i} vt Γ Δ) → ∀ {t : Tm Γ τ} → SN t → SN (subst (theSubst σ) t)
  substSN σ (ne t∈SNe)         = ne (substSNe σ t∈SNe)
  substSN σ (abs t∈SN)         = abs (substSN (liftsSNe σ) t∈SN)
  substSN σ (exp t→t' t'∈SN)   = exp (subst⇒ σ t→t') (substSN σ t'∈SN)

-- SN is closed under renaming.

renSN :  ∀{Γ Δ} (ρ : Γ ≤ Δ) → RenSN Δ Γ
renSN ρ = (ρ , λ x → var (ρ x))

renameSNe : ∀{a Γ Δ} (ρ : Γ ≤ Δ) {t : Tm Δ a} →
  SNe t → SNe (rename ρ t)
renameSNe ρ = substSNe (renSN ρ)

renameSN : ∀{a Γ Δ} (ρ : Γ ≤ Δ) {t : Tm Δ a} →
  SN t → SN (rename ρ t)
renameSN ρ = substSN (renSN ρ)

-- Variables are SN.

varSN : ∀{Γ a x} → var x ∈ SN {Γ = Γ} {a}
varSN = ne (var _)

-- SN is closed under application to variables.

appVarSN : ∀{Γ a b}{t : Tm Γ (a →̂ b)}{x} → t ∈ SN → app t (var x) ∈ SN
appVarSN (ne t∈SNe)       = ne (elim t∈SNe (appl varSN))
appVarSN (abs t∈SN)       = exp (β varSN) (substSN (sgs-varSNe _) t∈SN)
appVarSN (exp t→t' t'∈SN) = exp (cong (appl (var _)) (appl (var _)) t→t') (appVarSN t'∈SN)

-- Subterm properties of SN

-- If app t u ∈ SN then u ∈ SN.

apprSN : ∀{i a b Γ}{t : Tm Γ (a →̂ b)}{u : Tm Γ a} → SN {i} (app t u) → SN {i} u
apprSN (ne (elim 𝒏 (appl 𝒖)))               = 𝒖
apprSN (exp (β 𝒖) 𝒕)                        = 𝒖
apprSN (exp (cong (appl u) (appl .u) t⇒) 𝒕) = apprSN 𝒕


module Data.QuadTree.FoldableProofs.FoldableFunctorProof where

open import Haskell.Prelude renaming (zero to Z; suc to S)
open import Data.Logic
open import Data.QuadTree.Implementation.Definition
open import Data.QuadTree.Implementation.ValidTypes
open import Data.QuadTree.Implementation.QuadrantLenses
open import Data.QuadTree.Implementation.SafeFunctions
open import Data.QuadTree.Implementation.PropDepthRelation
open import Data.QuadTree.Implementation.Foldable
open import Data.QuadTree.Implementation.Functors
open import Data.QuadTree.FoldableProofs.FoldableProof
open import Data.QuadTree.FunctorProofs.Valid-QuadrantFunctor

proof-foldfmap-list : {t s : Set} {{monT : Monoid t}} {{monS : Monoid s}} -> (l : List t )
    -> (f : t -> s)
    -> foldMap f l ≡ foldMap id (fmap f l)
proof-foldfmap-list [] f = refl
proof-foldfmap-list (x ∷ xs) f =
    begin
        f x <> foldMap f xs
    =⟨ cong (_<>_ (f x)) (proof-foldfmap-list xs f) ⟩
        f x <> foldMap id (fmap f xs)
    end

fmap-replicate : {A B : Set} -> (f : A -> B) -> (v : A) -> (n : Nat)
    -> fmap f (replicateₙ n v) ≡ replicateₙ n (f v)
fmap-replicate f v Z = refl
fmap-replicate f v (S n) = cong (λ z → f v ∷ z) (fmap-replicate f v n)

fmap-concat : {A B : Set} -> (f : A -> B) -> (l1 l2 : List A)
    -> fmap f (l1 ++ l2) ≡ fmap f l1 ++ fmap f l2
fmap-concat f [] l2 = refl
fmap-concat f (x ∷ l1) l2 = cong (λ z → f x ∷ z) (fmap-concat f l1 l2)

fmap-concat4 : {A B : Set} -> (f : A -> B) -> (l1 l2 l3 l4 : List A)
    -> fmap f (l1 ++ l2 ++ l3 ++ l4) ≡ fmap f l1 ++ fmap f l2 ++ fmap f l3 ++ fmap f l4
fmap-concat4 f l1 l2 l3 l4 =
    begin
        fmap f (l1 ++ l2 ++ l3 ++ l4)
    =⟨ fmap-concat f l1 (l2 ++ l3 ++ l4) ⟩
        fmap f l1 ++ fmap f (l2 ++ l3 ++ l4)
    =⟨ cong (_++_ (map f l1)) (fmap-concat f l2 (l3 ++ l4)) ⟩
        fmap f l1 ++ fmap f l2 ++ fmap f (l3 ++ l4)
    =⟨ cong (λ z → map f l1 ++ map f l2 ++ z) (fmap-concat f l3 l4) ⟩
        fmap f l1 ++ fmap f l2 ++ fmap f l3 ++ fmap f l4
    end

fmap-expand : {A B : Set} {{eqA : Eq A}} {{eqB : Eq B}} -> (f : A -> B) -> (t : Tile A)
    -> fmap f (expand t) ≡ expand (fmap f t)
fmap-expand f t@(TileC v (RegionC (x1 , y1) (x2 , y2))) = fmap-replicate f v (diff x2 x1 * diff y2 y1)

fmap-concat-expand : {A B : Set} {{eqA : Eq A}} {{eqB : Eq B}} -> (f : A -> B) -> (l : List (Tile A))
    -> (fmap f $ concat $ map expand l) ≡ (concat $ map expand $ fmap (fmap f) l)
fmap-concat-expand f [] = refl
fmap-concat-expand f (x ∷ xs) =
    begin
        fmap f (expand x ++ concat (map expand xs))
    =⟨ fmap-concat f (expand x) _ ⟩
        fmap f (expand x) ++ fmap f (concat (map expand xs))
    =⟨ cong (_++_ (fmap f (expand x))) (fmap-concat-expand f xs) ⟩
        fmap f (expand x) ++ concat (map expand $ fmap (fmap f) xs)
    =⟨ cong (λ q -> q ++ concat (map expand $ fmap (fmap f) xs)) (fmap-expand f x) ⟩
        expand (fmap f x) ++ concat (map expand $ fmap (fmap f) xs)
    end

replicate-concat : {t : Set} -> (a b : Nat) -> (v : t)
    -> replicateₙ a v ++ replicateₙ b v ≡ replicateₙ (a + b) v
replicate-concat Z b v = refl
replicate-concat (S a) b v = cong (λ z → v ∷ z) (replicate-concat a b v)

replicate-concat4 : {t : Set} -> (a b c d : Nat) -> (v : t)
    -> replicateₙ a v ++ replicateₙ b v ++ replicateₙ c v ++ replicateₙ d v ≡ replicateₙ (a + b + c + d) v
replicate-concat4 a b c d v =
    begin
        replicateₙ a v ++ replicateₙ b v ++ (replicateₙ c v ++ replicateₙ d v)
    =⟨ cong (λ z → replicateₙ a v ++ replicateₙ b v ++ z) (replicate-concat c d v) ⟩
        replicateₙ a v ++ replicateₙ b v ++ replicateₙ (c + d) v
    =⟨ cong (_++_ (replicateₙ a v)) (replicate-concat b (c + d) v) ⟩
        replicateₙ a v ++ replicateₙ (b + (c + d)) v
    =⟨ replicate-concat a (b + (c + d)) v ⟩
        replicateₙ (a + (b + (c + d))) v
    =⟨ sym (cong (λ z → replicateₙ z v) (add-assoc a b (c + d))) ⟩
        replicateₙ (a + b + (c + d)) v
    =⟨ sym (cong (λ z → replicateₙ z v) (add-assoc (a + b) c d)) ⟩
        replicateₙ (a + b + c + d) v
    end

++-assoc : {t : Set} -> (l1 l2 l3 : List t)
    -> (l1 ++ l2) ++ l3 ≡ l1 ++ (l2 ++ l3)
++-assoc [] l2 l3 = refl
++-assoc (x ∷ xs) l2 l3 = cong (λ z → x ∷ z) (++-assoc xs l2 l3)

concat-cme : {t : Set} -> (l1 l2 : List (Tile t))
    -> concat (map expand (l1 ++ l2)) ≡ (concat $ map expand l1) ++ (concat $ map expand l2)
concat-cme [] l2 = refl
concat-cme l1@(x ∷ xs) l2 =
    begin
        expand x ++ concat (map expand (xs ++ l2))
    =⟨ cong (_++_ (expand x)) (concat-cme xs l2) ⟩
        expand x ++ ((concat $ map expand xs) ++ (concat $ map expand l2))
    =⟨ sym $ ++-assoc (expand x) (concat $ map expand xs) (concat $ map expand l2) ⟩
        (expand x ++ concat (map expand xs)) ++ concat (map expand l2)
    end

concat-cme4 : {t : Set} -> (l1 l2 l3 l4 : List (Tile t))
    -> concat (map expand (l1 ++ l2 ++ l3 ++ l4)) ≡ (concat (map expand l1)) ++ (concat (map expand l2)) ++ (concat (map expand l3)) ++ (concat (map expand l4))
concat-cme4 l1 l2 l3 l4 =
    begin
        concat (map expand (l1 ++ (l2 ++ (l3 ++ l4))))
    =⟨ concat-cme l1 (l2 ++ (l3 ++ l4)) ⟩
        concat (map expand l1) ++ concat (map expand (l2 ++ (l3 ++ l4)))
    =⟨ cong (_++_ (concat (map expand l1))) (concat-cme l2 (l3 ++ l4)) ⟩
        concat (map expand l1) ++ concat (map expand l2) ++ concat (map expand (l3 ++ l4))
    =⟨ cong (λ q -> concat (map expand l1) ++ concat (map expand l2) ++ q) (concat-cme l3 l4) ⟩
        concat (map expand l1) ++ concat (map expand l2) ++ concat (map expand l3) ++ concat (map expand l4)
    end

tilesQd-concat : {A : Set} {{eqA : Eq A}} -> (deps : Nat) -> (a b c d : VQuadrant A {deps}) -> (x1 y1 x2 y2 : Nat)
    -> IsTrue (x1 <= x2) -> IsTrue (y1 <= y2)
    -> ({a b : A} -> IsTrue (a == b) -> a ≡ b)
    -> (concat $ map expand $ (tilesQd deps a (RegionC (x1 , y1) (min x2 (pow 2 deps + x1) , min y2 (pow 2 deps + y1)))
        ++ tilesQd deps b (RegionC (min x2 (pow 2 deps + x1) , y1) (x2 , min y2 (pow 2 deps + y1)))
        ++ tilesQd deps c (RegionC (x1 , min y2 (pow 2 deps + y1)) (min x2 (pow 2 deps + x1) , y2) )
        ++ tilesQd deps d (RegionC (min x2 (pow 2 deps + x1) , min y2 (pow 2 deps + y1)) (x2 , y2) )))
    ≡ (concat $ map expand $ tilesQd (S deps) (combine a b c d) (RegionC (x1 , y1) (x2 , y2)))
tilesQd-concat {A} deps a@(CVQuadrant (Leaf va)) b@(CVQuadrant (Leaf vb)) c@(CVQuadrant (Leaf vc)) d@(CVQuadrant (Leaf vd)) x1 y1 x2 y2 xp yp peq =
    ifc (va == vb && vb == vc && vc == vd)
    then (λ {{pc}} ->
        begin
            (cme $ TileC va rA ∷ TileC vb rB ∷ TileC vc rC ∷ TileC vd rD ∷ [])
        =⟨⟩
            replicateₙ (area rA) va ++ replicateₙ (area rB) vb ++ replicateₙ (area rC) vc ++ replicateₙ (area rD) vd ++ []
        =⟨ cong (λ q -> replicateₙ (area rA) va ++ replicateₙ (area rB) vb ++ replicateₙ (area rC) vc ++ q) (concat-nothing _) ⟩
            replicateₙ (area rA) (va) ++ replicateₙ (area rB) (vb) ++ replicateₙ (area rC) (vc) ++ replicateₙ (area rD) vd
        =⟨ cong (λ v -> replicateₙ (area rA) (va) ++ replicateₙ (area rB) (vb) ++ replicateₙ (area rC) (vc) ++ replicateₙ (area rD) v)
            (sym $ peq $ andSnd {vb == vc} $ andSnd {va == vb} pc) ⟩
            replicateₙ (area rA) (va) ++ replicateₙ (area rB) (vb) ++ replicateₙ (area rC) (vc) ++ replicateₙ (area rD) (vc)
        =⟨ cong (λ v -> replicateₙ (area rA) (va) ++ replicateₙ (area rB) (vb) ++ replicateₙ (area rC) (v) ++ replicateₙ (area rD) (v))
            (sym $ peq $ andFst {vb == vc} $ andSnd {va == vb} pc) ⟩
            replicateₙ (area rA) (va) ++ replicateₙ (area rB) (vb) ++ replicateₙ (area rC) (vb) ++ replicateₙ (area rD) (vb)
        =⟨ cong (λ v -> replicateₙ (area rA) (va) ++ replicateₙ (area rB) (v) ++ replicateₙ (area rC) (v) ++ replicateₙ (area rD) (v))
            (sym $ peq $ andFst {va == vb} pc) ⟩
            replicateₙ (area rA) (va) ++ replicateₙ (area rB) (va) ++ replicateₙ (area rC) (va) ++ replicateₙ (area rD) (va)
        =⟨ replicate-concat4 (area rA) (area rB) (area rC) (area rD) (va) ⟩
            replicateₙ (area rA + area rB + area rC + area rD) (va)
        =⟨ cong (λ q -> replicateₙ q (va)) (square-split x1 y1 (min x2 (mid + x1)) (min y2 (mid + y1)) x2 y2
            (min-rel-1 x1 mid x2 xp) (min-rel-2 x2 (mid + x1)) (min-rel-1 y1 mid y2 yp) (min-rel-2 y2 (mid + y1))) ⟩
            replicateₙ (diff x2 x1 * diff y2 y1) (va)
        =⟨ sym $ concat-nothing (replicateₙ (diff x2 x1 * diff y2 y1) (va)) ⟩
            replicateₙ (diff x2 x1 * diff y2 y1) va ++ []
        =⟨⟩
            (cme $ TileC va (RegionC (x1 , y1) (x2 , y2)) ∷ [])
        =⟨ cong (λ q -> cme $ tilesQd (S deps) q (RegionC (x1 , y1) (x2 , y2))) (sym $ ifcTrue (va == vb && vb == vc && vc == vd) pc) ⟩
            (cme $ tilesQd (S deps) (ifc (va == vb && vb == vc && vc == vd) then CVQuadrant (Leaf va) else CVQuadrant (Node (Leaf va) (Leaf vb) (Leaf vc) (Leaf vd))) (RegionC (x1 , y1) (x2 , y2)))
        end)
    else (λ {{pc}} ->
        begin
            (concat $ map expand $ tilesQd (S deps) (CVQuadrant (Node (Leaf va) (Leaf vb) (Leaf vc) (Leaf vd)) {andCombine (zeroLteAny deps) (falseToNotTrue pc)}) (RegionC (x1 , y1) (x2 , y2)))
        =⟨ cong {x = CVQuadrant (Node (Leaf va) (Leaf vb) (Leaf vc) (Leaf vd)) {andCombine (zeroLteAny deps) (falseToNotTrue pc)}} (λ q -> concat $ map expand $  tilesQd (S deps) q (RegionC (x1 , y1) (x2 , y2))) (sym $ ifcFalse (va == vb && vb == vc && vc == vd) pc) ⟩
            (concat $ map expand $ tilesQd (S deps) (ifc (va == vb && vb == vc && vc == vd) then CVQuadrant (Leaf va) else CVQuadrant (Node (Leaf va) (Leaf vb) (Leaf vc) (Leaf vd))) (RegionC (x1 , y1) (x2 , y2)))
        end) where
        mid = pow 2 deps
        rA = (RegionC (x1 , y1) (min x2 (mid + x1) , min y2 (mid + y1)))
        rB = (RegionC (min x2 (mid + x1) , y1) (x2 , min y2 (mid + y1)))
        rC = (RegionC (x1 , min y2 (mid + y1)) (min x2 (mid + x1) , y2) )
        rD = (RegionC (min x2 (mid + x1) , min y2 (mid + y1)) (x2 , y2) )
        cme : List (Tile A) -> List A
        cme v = concat $ map expand $ v
        area : Region -> Nat
        area (RegionC (x1 , y1) (x2 , y2)) = diff x2 x1 * diff y2 y1
tilesQd-concat deps (CVQuadrant (Leaf x)) (CVQuadrant (Leaf x₁)) (CVQuadrant (Leaf x₂)) (CVQuadrant (Node d d₁ d₂ d₃)) x1 y1 x2 y2 xp yp peq = refl
tilesQd-concat deps (CVQuadrant (Leaf x)) (CVQuadrant (Leaf x₁)) (CVQuadrant (Node c c₁ c₂ c₃)) (CVQuadrant d) x1 y1 x2 y2 xp yp peq = refl
tilesQd-concat deps (CVQuadrant (Leaf x)) (CVQuadrant (Node b b₁ b₂ b₃)) (CVQuadrant c) (CVQuadrant d) x1 y1 x2 y2 xp yp peq = refl
tilesQd-concat deps (CVQuadrant (Node a a₁ a₂ a₃)) (CVQuadrant b) (CVQuadrant c) (CVQuadrant d) x1 y1 x2 y2 xp yp peq = refl

fmap-tilesQd : {A B : Set} {{eqA : Eq A}} {{eqB : Eq B}} -> (f : A -> B) -> (dep : Nat) -> (vqd : VQuadrant A {dep}) -> (x1 y1 x2 y2 : Nat)
    -> IsTrue (x1 <= x2) -> IsTrue (y1 <= y2)
    -> ({a b : A} -> IsTrue (a == b) -> a ≡ b)
    -> ({a b : B} -> IsTrue (a == b) -> a ≡ b)
    -> (concat $ map expand $ fmap (fmap f) $ tilesQd dep vqd (RegionC (x1 , y1) (x2 , y2))) ≡ (concat $ map expand $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) (RegionC (x1 , y1) (x2 , y2)))
fmap-tilesQd f dep (CVQuadrant (Leaf v) {p1}) x1 y1 x2 y2 px py peqA peqB = refl
fmap-tilesQd {A} {B} f dep@(S deps) vqd@(CVQuadrant (Node a@(Leaf va) b@(Leaf vb) c@(Leaf vc) d@(Leaf vd)) {p1}) x1 y1 x2 y2 xp yp peqA peqB =
    ifc ((va == vb) && (vb == vc) && (vc == vd))
    then (λ {{pc}} ->
        (begin
            (cme $ fmap (fmap f) $ tilesQd dep vqd reg)
        =⟨⟩
            (cme $ TileC (f va) rA ∷ TileC (f vb) rB ∷ TileC (f vc) rC ∷ TileC (f vd) rD ∷ [])
        =⟨⟩
            replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f vc) ++ replicateₙ (area rD) (f vd) ++ []
        =⟨ cong (λ q -> replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f vc) ++ q) (concat-nothing _) ⟩
            replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f vc) ++ replicateₙ (area rD) (f vd)
        =⟨ cong (λ v -> replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f vc) ++ replicateₙ (area rD) (f v))
            (sym $ peqA $ andSnd {vb == vc} $ andSnd {va == vb} pc) ⟩
            replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f vc) ++ replicateₙ (area rD) (f vc)
        =⟨ cong (λ v -> replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f v) ++ replicateₙ (area rD) (f v))
            (sym $ peqA $ andFst {vb == vc} $ andSnd {va == vb} pc) ⟩
            replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f vb) ++ replicateₙ (area rC) (f vb) ++ replicateₙ (area rD) (f vb)
        =⟨ cong (λ v -> replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f v) ++ replicateₙ (area rC) (f v) ++ replicateₙ (area rD) (f v))
            (sym $ peqA $ andFst {va == vb} pc) ⟩
            replicateₙ (area rA) (f va) ++ replicateₙ (area rB) (f va) ++ replicateₙ (area rC) (f va) ++ replicateₙ (area rD) (f va)
        =⟨ replicate-concat4 (area rA) (area rB) (area rC) (area rD) (f va) ⟩
            replicateₙ (area rA + area rB + area rC + area rD) (f va)
        =⟨ cong (λ q -> replicateₙ q (f va)) (square-split x1 y1 (min x2 (mid + x1)) (min y2 (mid + y1)) x2 y2
            (min-rel-1 x1 mid x2 xp) (min-rel-2 x2 (mid + x1)) (min-rel-1 y1 mid y2 yp) (min-rel-2 y2 (mid + y1))) ⟩
            replicateₙ (diff x2 x1 * diff y2 y1) (f va)
        =⟨ sym $ concat-nothing (replicateₙ (diff x2 x1 * diff y2 y1) (f va)) ⟩
            replicateₙ (diff x2 x1 * diff y2 y1) (f va) ++ []
        =⟨ cong (λ q -> cme $ tilesQd dep q reg)
                (sym $ ifcTrue ((f va == f vb) && (f vb == f vc) && (f vc == f vd))
                    (useEq (cong3 (λ e1 e2 e3 -> e1 && e2 && e3) (eq-subst f va vb) (eq-subst f vb vc) (eq-subst f vc vd)) pc)) ⟩
            (cme $ tilesQd dep (combine
                (fmapₑ (quadrantFunctor deps) f sA)
                (fmapₑ (quadrantFunctor deps) f sB)
                (fmapₑ (quadrantFunctor deps) f sC)
                (fmapₑ (quadrantFunctor deps) f sD)) reg)
        =⟨⟩
            (cme $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) reg)
        end))
    else (λ {{pc}} ->
        (begin
            (cme $ fmap (fmap f) $ tilesQd dep vqd reg)
        =⟨⟩
            (cme $ TileC (f va) rA ∷ TileC (f vb) rB ∷ TileC (f vc) rC ∷ TileC (f vd) rD ∷ [])
        =⟨⟩
            (cme $ tilesQd dep (CVQuadrant (Node (Leaf (f va)) (Leaf (f vb)) (Leaf (f vc)) (Leaf (f vd)))
                {andCombine (zeroLteAny deps) (falseToNotTrue $ useEqFalse (cong3 (λ e1 e2 e3 -> e1 && e2 && e3) (eq-subst f va vb) (eq-subst f vb vc) (eq-subst f vc vd)) pc)}) reg)
        =⟨ cong {x = CVQuadrant (Node (Leaf (f va)) (Leaf (f vb)) (Leaf (f vc)) (Leaf (f vd)))
                {andCombine (zeroLteAny deps) (falseToNotTrue $ useEqFalse (cong3 (λ e1 e2 e3 -> e1 && e2 && e3) (eq-subst f va vb) (eq-subst f vb vc) (eq-subst f vc vd)) pc)}}
             (λ q -> cme $ tilesQd dep q reg) (sym $ ifcFalse ((f va == f vb) && (f vb == f vc) && (f vc == f vd))
             (useEqFalse (cong3 (λ e1 e2 e3 -> e1 && e2 && e3) (eq-subst f va vb) (eq-subst f vb vc) (eq-subst f vc vd)) pc)) ⟩
            (cme $ tilesQd dep (combine
                (fmapₑ (quadrantFunctor deps) f sA)
                (fmapₑ (quadrantFunctor deps) f sB)
                (fmapₑ (quadrantFunctor deps) f sC)
                (fmapₑ (quadrantFunctor deps) f sD)) reg)
        =⟨⟩
            (cme $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) reg)
        end))

    where
        mid = pow 2 deps
        rA = (RegionC (x1 , y1) (min x2 (mid + x1) , min y2 (mid + y1)))
        rB = (RegionC (min x2 (mid + x1) , y1) (x2 , min y2 (mid + y1)))
        rC = (RegionC (x1 , min y2 (mid + y1)) (min x2 (mid + x1) , y2) )
        rD = (RegionC (min x2 (mid + x1) , min y2 (mid + y1)) (x2 , y2) )
        reg = (RegionC (x1 , y1) (x2 , y2))
        sA = CVQuadrant {dep = deps} a {aSub {dep = deps} a b c d p1}
        sB = CVQuadrant {dep = deps} b {bSub {dep = deps} a b c d p1}
        sC = CVQuadrant {dep = deps} c {cSub {dep = deps} a b c d p1}
        sD = CVQuadrant {dep = deps} d {dSub {dep = deps} a b c d p1}
        cme : List (Tile B) -> List B
        cme v = concat $ map expand $ v
        area : Region -> Nat
        area (RegionC (x1 , y1) (x2 , y2)) = diff x2 x1 * diff y2 y1
fmap-tilesQd {A} {B} f dep@(S deps) vqd@(CVQuadrant (Node a@(Leaf _) b@(Leaf _) c@(Leaf _) d@(Node _ _ _ _)) {p1}) x1 y1 x2 y2 xp yp peqA peqB =
    begin
        (cme $ fmap (fmap f) $ tilesQd dep vqd reg)
    =⟨⟩
        (cme $ fmap (fmap f) $ (
            tilesQd deps sA rA ++ tilesQd deps sB rB ++ tilesQd deps sC rC ++ tilesQd deps sD rD
        ))
    =⟨ cong cme $ fmap-concat4 (fmap f) (tilesQd deps sA rA) (tilesQd deps sB rB) (tilesQd deps sC rC) (tilesQd deps sD rD) ⟩
        (cme (
            fmap (fmap f) (tilesQd deps sA rA)
            ++ fmap (fmap f) (tilesQd deps sB rB)
            ++ fmap (fmap f) (tilesQd deps sC rC)
            ++ fmap (fmap f) (tilesQd deps sD rD)
        ))
    =⟨ concat-cme4 (fmap (fmap f) (tilesQd deps sA rA)) (fmap (fmap f) (tilesQd deps sB rB)) (fmap (fmap f) (tilesQd deps sC rC)) (fmap (fmap f) (tilesQd deps sD rD)) ⟩
        (cme $ fmap (fmap f) (tilesQd deps sA rA))
        ++ (cme $ fmap (fmap f) (tilesQd deps sB rB))
        ++ (cme $ fmap (fmap f) (tilesQd deps sC rC))
        ++ (cme $ fmap (fmap f) (tilesQd deps sD rD))
    =⟨ cong4 (λ l1 l2 l3 l4 -> l1 ++ l2 ++ l3 ++ l4)
        (fmap-tilesQd f deps sA x1 y1 (min x2 (mid + x1)) (min y2 (mid + y1)) (min-rel-1 x1 mid x2 xp) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sB (min x2 (mid + x1)) y1 x2 (min y2 (mid + y1)) (min-rel-2 x2 (mid + x1)) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sC x1 (min y2 (mid + y1)) (min x2 (mid + x1)) y2 (min-rel-1 x1 mid x2 xp) (min-rel-2 y2 (mid + y1)) peqA peqB)
        (fmap-tilesQd f deps sD (min x2 (mid + x1)) (min y2 (mid + y1)) x2 y2 (min-rel-2 x2 (mid + x1)) (min-rel-2 y2 (mid + y1)) peqA peqB) ⟩

        (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD))
    =⟨ sym $ concat-cme4 (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD) ⟩
        (cme (
            tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD
        ))
    =⟨ tilesQd-concat deps (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD) x1 y1 x2 y2 xp yp peqB ⟩
        (cme $ tilesQd dep
            (combine (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD)) reg)
    =⟨⟩
        (cme $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) reg)
    end where
        cme : List (Tile B) -> List B
        cme v = concat $ map expand $ v
        reg = (RegionC (x1 , y1) (x2 , y2))
        mid = pow 2 deps
        rA = (RegionC (x1 , y1) (min x2 (mid + x1) , min y2 (mid + y1)))
        rB = (RegionC (min x2 (mid + x1) , y1) (x2 , min y2 (mid + y1)))
        rC = (RegionC (x1 , min y2 (mid + y1)) (min x2 (mid + x1) , y2) )
        rD = (RegionC (min x2 (mid + x1) , min y2 (mid + y1)) (x2 , y2) )
        sA = CVQuadrant {dep = deps} a {aSub {dep = deps} a b c d p1}
        sB = CVQuadrant {dep = deps} b {bSub {dep = deps} a b c d p1}
        sC = CVQuadrant {dep = deps} c {cSub {dep = deps} a b c d p1}
        sD = CVQuadrant {dep = deps} d {dSub {dep = deps} a b c d p1}
fmap-tilesQd {A} {B} f dep@(S deps) vqd@(CVQuadrant (Node a@(Leaf _) b@(Leaf _) c@(Node _ _ _ _) d) {p1}) x1 y1 x2 y2 xp yp peqA peqB =
    begin
        (cme $ fmap (fmap f) $ tilesQd dep vqd reg)
    =⟨⟩
        (cme $ fmap (fmap f) $ (
            tilesQd deps sA rA ++ tilesQd deps sB rB ++ tilesQd deps sC rC ++ tilesQd deps sD rD
        ))
    =⟨ cong cme $ fmap-concat4 (fmap f) (tilesQd deps sA rA) (tilesQd deps sB rB) (tilesQd deps sC rC) (tilesQd deps sD rD) ⟩
        (cme (
            fmap (fmap f) (tilesQd deps sA rA)
            ++ fmap (fmap f) (tilesQd deps sB rB)
            ++ fmap (fmap f) (tilesQd deps sC rC)
            ++ fmap (fmap f) (tilesQd deps sD rD)
        ))
    =⟨ concat-cme4 (fmap (fmap f) (tilesQd deps sA rA)) (fmap (fmap f) (tilesQd deps sB rB)) (fmap (fmap f) (tilesQd deps sC rC)) _ ⟩
        (cme $ fmap (fmap f) (tilesQd deps sA rA))
        ++ (cme $ fmap (fmap f) (tilesQd deps sB rB))
        ++ (cme $ fmap (fmap f) (tilesQd deps sC rC))
        ++ (cme $ fmap (fmap f) (tilesQd deps sD rD))
    =⟨ cong4 (λ l1 l2 l3 l4 -> l1 ++ l2 ++ l3 ++ l4)
        (fmap-tilesQd f deps sA x1 y1 (min x2 (mid + x1)) (min y2 (mid + y1)) (min-rel-1 x1 mid x2 xp) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sB (min x2 (mid + x1)) y1 x2 (min y2 (mid + y1)) (min-rel-2 x2 (mid + x1)) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sC x1 (min y2 (mid + y1)) (min x2 (mid + x1)) y2 (min-rel-1 x1 mid x2 xp) (min-rel-2 y2 (mid + y1)) peqA peqB)
        (fmap-tilesQd f deps sD (min x2 (mid + x1)) (min y2 (mid + y1)) x2 y2 (min-rel-2 x2 (mid + x1)) (min-rel-2 y2 (mid + y1)) peqA peqB) ⟩

        (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD))
    =⟨ sym $ concat-cme4 (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC) _ ⟩
        (cme (
            tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD
        ))
    =⟨ tilesQd-concat deps (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD) x1 y1 x2 y2 xp yp peqB ⟩
        (cme $ tilesQd dep
            (combine (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD)) reg)
    =⟨⟩
        (cme $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) reg)
    end where
        cme : List (Tile B) -> List B
        cme v = concat $ map expand $ v
        reg = (RegionC (x1 , y1) (x2 , y2))
        mid = pow 2 deps
        rA = (RegionC (x1 , y1) (min x2 (mid + x1) , min y2 (mid + y1)))
        rB = (RegionC (min x2 (mid + x1) , y1) (x2 , min y2 (mid + y1)))
        rC = (RegionC (x1 , min y2 (mid + y1)) (min x2 (mid + x1) , y2) )
        rD = (RegionC (min x2 (mid + x1) , min y2 (mid + y1)) (x2 , y2) )
        sA = CVQuadrant {dep = deps} a {aSub {dep = deps} a b c d p1}
        sB = CVQuadrant {dep = deps} b {bSub {dep = deps} a b c d p1}
        sC = CVQuadrant {dep = deps} c {cSub {dep = deps} a b c d p1}
        sD = CVQuadrant {dep = deps} d {dSub {dep = deps} a b c d p1}
fmap-tilesQd {A} {B} f dep@(S deps) vqd@(CVQuadrant (Node a@(Leaf _) b@(Node _ _ _ _) c d) {p1}) x1 y1 x2 y2 xp yp peqA peqB =
    begin
        (cme $ fmap (fmap f) $ tilesQd dep vqd reg)
    =⟨⟩
        (cme $ fmap (fmap f) $ (
            tilesQd deps sA rA ++ tilesQd deps sB rB ++ tilesQd deps sC rC ++ tilesQd deps sD rD
        ))
    =⟨ cong cme $ fmap-concat4 (fmap f) (tilesQd deps sA rA) (tilesQd deps sB rB) (tilesQd deps sC rC) (tilesQd deps sD rD) ⟩
        (cme (
            fmap (fmap f) (tilesQd deps sA rA)
            ++ fmap (fmap f) (tilesQd deps sB rB)
            ++ fmap (fmap f) (tilesQd deps sC rC)
            ++ fmap (fmap f) (tilesQd deps sD rD)
        ))
    =⟨ concat-cme4 (fmap (fmap f) (tilesQd deps sA rA)) (fmap (fmap f) (tilesQd deps sB rB)) (fmap (fmap f) (tilesQd deps sC rC)) _ ⟩
        (cme $ fmap (fmap f) (tilesQd deps sA rA))
        ++ (cme $ fmap (fmap f) (tilesQd deps sB rB))
        ++ (cme $ fmap (fmap f) (tilesQd deps sC rC))
        ++ (cme $ fmap (fmap f) (tilesQd deps sD rD))
    =⟨ cong4 (λ l1 l2 l3 l4 -> l1 ++ l2 ++ l3 ++ l4)
        (fmap-tilesQd f deps sA x1 y1 (min x2 (mid + x1)) (min y2 (mid + y1)) (min-rel-1 x1 mid x2 xp) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sB (min x2 (mid + x1)) y1 x2 (min y2 (mid + y1)) (min-rel-2 x2 (mid + x1)) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sC x1 (min y2 (mid + y1)) (min x2 (mid + x1)) y2 (min-rel-1 x1 mid x2 xp) (min-rel-2 y2 (mid + y1)) peqA peqB)
        (fmap-tilesQd f deps sD (min x2 (mid + x1)) (min y2 (mid + y1)) x2 y2 (min-rel-2 x2 (mid + x1)) (min-rel-2 y2 (mid + y1)) peqA peqB) ⟩

        (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD))
    =⟨ sym $ concat-cme4 (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC) _ ⟩
        (cme (
            tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD
        ))
    =⟨ tilesQd-concat deps (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD) x1 y1 x2 y2 xp yp peqB ⟩
        (cme $ tilesQd dep
            (combine (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD)) reg)
    =⟨⟩
        (cme $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) reg)
    end where
        cme : List (Tile B) -> List B
        cme v = concat $ map expand $ v
        reg = (RegionC (x1 , y1) (x2 , y2))
        mid = pow 2 deps
        rA = (RegionC (x1 , y1) (min x2 (mid + x1) , min y2 (mid + y1)))
        rB = (RegionC (min x2 (mid + x1) , y1) (x2 , min y2 (mid + y1)))
        rC = (RegionC (x1 , min y2 (mid + y1)) (min x2 (mid + x1) , y2) )
        rD = (RegionC (min x2 (mid + x1) , min y2 (mid + y1)) (x2 , y2) )
        sA = CVQuadrant {dep = deps} a {aSub {dep = deps} a b c d p1}
        sB = CVQuadrant {dep = deps} b {bSub {dep = deps} a b c d p1}
        sC = CVQuadrant {dep = deps} c {cSub {dep = deps} a b c d p1}
        sD = CVQuadrant {dep = deps} d {dSub {dep = deps} a b c d p1}
fmap-tilesQd {A} {B} f dep@(S deps) vqd@(CVQuadrant (Node a@(Node _ _ _ _) b c d) {p1}) x1 y1 x2 y2 xp yp peqA peqB =
    begin
        (cme $ fmap (fmap f) $ tilesQd dep vqd reg)
    =⟨⟩
        (cme $ fmap (fmap f) $ (
            tilesQd deps sA rA ++ tilesQd deps sB rB ++ tilesQd deps sC rC ++ tilesQd deps sD rD
        ))
    =⟨ cong cme $ fmap-concat4 (fmap f) (tilesQd deps sA rA) (tilesQd deps sB rB) (tilesQd deps sC rC) (tilesQd deps sD rD) ⟩
        (cme (
            fmap (fmap f) (tilesQd deps sA rA)
            ++ fmap (fmap f) (tilesQd deps sB rB)
            ++ fmap (fmap f) (tilesQd deps sC rC)
            ++ fmap (fmap f) (tilesQd deps sD rD)
        ))
    =⟨ concat-cme4 (fmap (fmap f) (tilesQd deps sA rA)) (fmap (fmap f) (tilesQd deps sB rB)) (fmap (fmap f) (tilesQd deps sC rC)) _ ⟩
        (cme $ fmap (fmap f) (tilesQd deps sA rA))
        ++ (cme $ fmap (fmap f) (tilesQd deps sB rB))
        ++ (cme $ fmap (fmap f) (tilesQd deps sC rC))
        ++ (cme $ fmap (fmap f) (tilesQd deps sD rD))
    =⟨ cong4 (λ l1 l2 l3 l4 -> l1 ++ l2 ++ l3 ++ l4)
        (fmap-tilesQd f deps sA x1 y1 (min x2 (mid + x1)) (min y2 (mid + y1)) (min-rel-1 x1 mid x2 xp) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sB (min x2 (mid + x1)) y1 x2 (min y2 (mid + y1)) (min-rel-2 x2 (mid + x1)) (min-rel-1 y1 mid y2 yp) peqA peqB)
        (fmap-tilesQd f deps sC x1 (min y2 (mid + y1)) (min x2 (mid + x1)) y2 (min-rel-1 x1 mid x2 xp) (min-rel-2 y2 (mid + y1)) peqA peqB)
        (fmap-tilesQd f deps sD (min x2 (mid + x1)) (min y2 (mid + y1)) x2 y2 (min-rel-2 x2 (mid + x1)) (min-rel-2 y2 (mid + y1)) peqA peqB) ⟩

        (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC))
        ++ (cme $ (tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD))
    =⟨ sym $ concat-cme4 (tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB) (tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC) _ ⟩
        (cme (
            tilesQd deps (fmapₑ (quadrantFunctor deps) f sA) rA
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sB) rB
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sC) rC
            ++ tilesQd deps (fmapₑ (quadrantFunctor deps) f sD) rD
        ))
    =⟨ tilesQd-concat deps (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD) x1 y1 x2 y2 xp yp peqB ⟩
        (cme $ tilesQd dep
            (combine (fmapₑ (quadrantFunctor deps) f sA) (fmapₑ (quadrantFunctor deps) f sB) (fmapₑ (quadrantFunctor deps) f sC) (fmapₑ (quadrantFunctor deps) f sD)) reg)
    =⟨⟩
        (cme $ tilesQd dep (fmapₑ (quadrantFunctor dep) f vqd) reg)
    end where
        cme : List (Tile B) -> List B
        cme v = concat $ map expand $ v
        reg = (RegionC (x1 , y1) (x2 , y2))
        mid = pow 2 deps
        rA = (RegionC (x1 , y1) (min x2 (mid + x1) , min y2 (mid + y1)))
        rB = (RegionC (min x2 (mid + x1) , y1) (x2 , min y2 (mid + y1)))
        rC = (RegionC (x1 , min y2 (mid + y1)) (min x2 (mid + x1) , y2) )
        rD = (RegionC (min x2 (mid + x1) , min y2 (mid + y1)) (x2 , y2) )
        sA = CVQuadrant {dep = deps} a {aSub {dep = deps} a b c d p1}
        sB = CVQuadrant {dep = deps} b {bSub {dep = deps} a b c d p1}
        sC = CVQuadrant {dep = deps} c {cSub {dep = deps} a b c d p1}
        sD = CVQuadrant {dep = deps} d {dSub {dep = deps} a b c d p1}

qdToQt : {A : Set} {{eqA : Eq A}} -> (dep w h : Nat) -> (vqd : VQuadrant A {dep}) -> .(q : IsTrue (dep == log2up (if w < h then h else w)))
    -> tilesQd dep vqd (RegionC (0 , 0) (w , h)) ≡ tilesQt dep (toQt dep w h q vqd)
qdToQt dep w h (CVQuadrant qd {p}) q = refl

fmap-tilesQt : {A B : Set} {{eqA : Eq A}} {{eqB : Eq B}} -> (f : A -> B) -> (dep : Nat) -> (vqt : VQuadTree A {dep})
    -> ({a b : A} -> IsTrue (a == b) -> a ≡ b)
    -> ({a b : B} -> IsTrue (a == b) -> a ≡ b)
    -> (concat $ map expand $ fmap (fmap f) $ tilesQt dep vqt) ≡ (concat $ map expand $ tilesQt dep $ fmapₑ (quadtreeFunctor dep) f vqt)
fmap-tilesQt f dep (CVQuadTree (Wrapper (w , h) (Leaf v))) peqA peqB = refl
fmap-tilesQt {A} {B} f dep@(S deps) vqt@(CVQuadTree (Wrapper (w , h) qd@(Node a b c d)) {p1} {p2}) peqA peqB =
    begin
        (concat $ map expand $ fmap (fmap f) $ tilesQt dep vqt)
    =⟨⟩
        (concat $ map expand $ fmap (fmap f) $ tilesQd dep (CVQuadrant qd {p1}) (RegionC (0 , 0) (w , h)))
    =⟨ fmap-tilesQd f dep (CVQuadrant qd {p1}) 0 0 w h (zeroLteAny w) (zeroLteAny h) peqA peqB ⟩
        (concat $ map expand $ tilesQd dep (fmapₑ (quadrantFunctor dep) f (CVQuadrant qd {p1})) (RegionC (0 , 0) (w , h)))
    =⟨ cong (λ l -> concat $ map expand l) (qdToQt dep w h (combine (fmapₑ (quadrantFunctor deps) f (CVQuadrant a)) _ _ _) p2) ⟩
        (concat $ map expand $ tilesQt dep (fmapₑ (quadtreeFunctor dep) f vqt))
    end

proof-foldfmap-qt : {t s : Set} {{eqT : Eq t}} {{eqS : Eq s}} {{monS : Monoid s}} {{monT : Monoid t}} (dep : Nat) -> (vqt : VQuadTree t {dep})
    -> (f : t -> s)
    -> ({a b : t} -> IsTrue (a == b) -> a ≡ b)
    -> ({a b : s} -> IsTrue (a == b) -> a ≡ b)
    -> foldMapₑ (quadtreeFoldable dep) f vqt ≡ foldMapₑ (quadtreeFoldable dep) id (fmapₑ (quadtreeFunctor dep) f vqt)
proof-foldfmap-qt {t} {s} dep vqt@(CVQuadTree (Wrapper (w , h) (Leaf v)) {p} {q}) f peqA peqB =
    begin
        foldMapₑ (quadtreeFoldable dep) f vqt
    =⟨⟩
        foldMap f (replicateₙ (w * h) v ++ [])
    =⟨ cong (foldMap f) (concat-nothing (replicateₙ (w * h) v)) ⟩
        foldMap f (replicateₙ (w * h) v)
    =⟨ proof-foldfmap-list (replicateₙ (w * h) v) f ⟩
        foldMap id (fmap f (replicateₙ (w * h) v))
    =⟨ cong (foldMap id) (fmap-replicate f v (w * h)) ⟩
        foldMap id (replicateₙ (w * h) (f v))
    =⟨ cong (foldMap id) (sym $ concat-nothing (replicateₙ (w * h) (f v))) ⟩
        foldMap id (replicateₙ (w * h) (f v) ++ [])
    =⟨⟩
        foldMapₑ (quadtreeFoldable dep) id (fmapₑ (quadtreeFunctor dep) f vqt)
    end
proof-foldfmap-qt {t} {s} dep vqt@(CVQuadTree (Wrapper (w , h) (Node qd qd₁ qd₂ qd₃)) {p} {q}) f peqA peqB =
    begin
        foldMapₑ (quadtreeFoldable dep) f vqt
    =⟨⟩
        (foldMap f $ concat $ map expand $ tilesQt dep vqt)
    =⟨ proof-foldfmap-list (concat $ map expand $ tilesQt dep vqt) f ⟩
        (foldMap id $ fmap f $ concat $ map expand $ tilesQt dep vqt)
    =⟨ cong (foldMap id) (fmap-concat-expand f (tilesQt dep vqt)) ⟩
        (foldMap id $ concat $ map expand $ fmap (fmap f) $ tilesQt dep vqt)
    =⟨ cong (foldMap id) (fmap-tilesQt f dep vqt peqA peqB) ⟩
        (foldMap id $ concat $ map expand $ tilesQt dep $ fmapₑ (quadtreeFunctor dep) f vqt)
    =⟨⟩
        foldMapₑ (quadtreeFoldable dep) id (fmapₑ (quadtreeFunctor dep) f vqt)
    end


------------------------------------------------------------------------
-- The Agda standard library
--
-- Boring lemmas used in Data.Nat.GCD and Data.Nat.Coprimality
------------------------------------------------------------------------

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

module Data.Nat.GCD.Lemmas where

open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.Solver
open import Function
open import Relation.Binary.PropositionalEquality

open +-*-Solver
open ≡-Reasoning

private
  distrib-comm : ∀ x k n → x * k + x * n ≡ x * (n + k)
  distrib-comm =
    solve 3 (λ x k n → x :* k :+ x :* n  :=  x :* (n :+ k)) refl

  distrib-comm₂ : ∀ d x k n → d + x * (n + k) ≡ d + x * k + x * n
  distrib-comm₂ =
    solve 4 (λ d x k n → d :+ x :* (n :+ k) :=  d :+ x :* k :+ x :* n) refl

-- Other properties
-- TODO: Can this proof be simplified? An automatic solver which can
-- handle ∸ would be nice...
lem₀ : ∀ i j m n → i * m ≡ j * m + n → (i ∸ j) * m ≡ n
lem₀ i j m n eq = begin
  (i ∸ j) * m            ≡⟨ *-distribʳ-∸ m i j ⟩
  (i * m) ∸ (j * m)      ≡⟨ cong (_∸ j * m) eq ⟩
  (j * m + n) ∸ (j * m)  ≡⟨ cong (_∸ j * m) (+-comm (j * m) n) ⟩
  (n + j * m) ∸ (j * m)  ≡⟨ m+n∸n≡m n (j * m) ⟩
  n                      ∎

lem₁ : ∀ i j → 2 + i ≤′ 2 + j + i
lem₁ i j = ≤⇒≤′ $ s≤s $ s≤s $ n≤m+n j i

lem₂ : ∀ d x {k n} →
       d + x * k ≡ x * n → d + x * (n + k) ≡ 2 * x * n
lem₂ d x {k} {n} eq = begin
  d + x * (n + k)    ≡⟨ distrib-comm₂ d x k n ⟩
  d + x * k + x * n  ≡⟨ cong₂ _+_ eq refl ⟩
  x * n + x * n      ≡⟨ solve 3 (λ x n k → x :* n :+ x :* n
                                       :=  con 2 :* x :* n)
                                 refl x n k ⟩
  2 * x * n          ∎

lem₃ : ∀ d x {i k n} →
       d + (1 + x + i) * k ≡ x * n →
       d + (1 + x + i) * (n + k) ≡ (1 + 2 * x + i) * n
lem₃ d x {i} {k} {n} eq = begin
  d + y * (n + k)      ≡⟨ distrib-comm₂ d y k n ⟩
  d + y * k + y * n    ≡⟨ cong₂ _+_ eq refl ⟩
  x * n + y * n        ≡⟨ solve 3 (λ x n i → x :* n :+ (con 1 :+ x :+ i) :* n
                                         :=  (con 1 :+ con 2 :* x :+ i) :* n)
                                  refl x n i ⟩
  (1 + 2 * x + i) * n  ∎
  where y = 1 + x + i

lem₄ : ∀ d y {k i} n →
       d + y * k ≡ (1 + y + i) * n →
       d + y * (n + k) ≡ (1 + 2 * y + i) * n
lem₄ d y {k} {i} n eq = begin
  d + y * (n + k)          ≡⟨ distrib-comm₂ d y k n ⟩
  d + y * k + y * n        ≡⟨ cong₂ _+_ eq refl ⟩
  (1 + y + i) * n + y * n  ≡⟨ solve 3 (λ y i n → (con 1 :+ y :+ i) :* n :+ y :* n
                                             :=  (con 1 :+ con 2 :* y :+ i) :* n)
                                      refl y i n ⟩
  (1 + 2 * y + i) * n      ∎

lem₅ : ∀ d x {n k} →
       d + x * n ≡ x * k →
       d + 2 * x * n ≡ x * (n + k)
lem₅ d x {n} {k} eq = begin
  d + 2 * x * n      ≡⟨ solve 3 (λ d x n → d :+ con 2 :* x :* n
                                       :=  d :+ x :* n :+ x :* n)
                                refl d x n ⟩
  d + x * n + x * n  ≡⟨ cong₂ _+_ eq refl ⟩
  x * k + x * n      ≡⟨ distrib-comm x k n ⟩
  x * (n + k)        ∎

lem₆ : ∀ d x {n i k} →
       d + x * n ≡ (1 + x + i) * k →
       d + (1 + 2 * x + i) * n ≡ (1 + x + i) * (n + k)
lem₆ d x {n} {i} {k} eq = begin
  d + (1 + 2 * x + i) * n  ≡⟨ solve 4 (λ d x i n → d :+ (con 1 :+ con 2 :* x :+ i) :* n
                                               :=  d :+ x :* n :+ (con 1 :+ x :+ i) :* n)
                                      refl d x i n ⟩
  d + x * n + y * n        ≡⟨ cong₂ _+_ eq refl ⟩
  y * k + y * n            ≡⟨ distrib-comm y k n ⟩
  y * (n + k)              ∎
  where y = 1 + x + i

lem₇ : ∀ d y {i} n {k} →
       d + (1 + y + i) * n ≡ y * k →
       d + (1 + 2 * y + i) * n ≡ y * (n + k)
lem₇ d y {i} n {k} eq = begin
  d + (1 + 2 * y + i) * n      ≡⟨ solve 4 (λ d y i n → d :+ (con 1 :+ con 2 :* y :+ i) :* n
                                                   :=  d :+ (con 1 :+ y :+ i) :* n :+ y :* n)
                                          refl d y i n ⟩
  d + (1 + y + i) * n + y * n  ≡⟨ cong₂ _+_ eq refl ⟩
  y * k + y * n                ≡⟨ distrib-comm y k n ⟩
  y * (n + k)                  ∎

lem₈ : ∀ {i j k q} x y →
       1 + y * j ≡ x * i → j * k ≡ q * i →
       k ≡ (x * k ∸ y * q) * i
lem₈ {i} {j} {k} {q} x y eq eq′ =
  sym (lem₀ (x * k) (y * q) i k lemma)
  where
  lemma = begin
    x * k * i        ≡⟨ solve 3 (λ x k i → x :* k :* i
                                       :=  x :* i :* k)
                                refl x k i ⟩
    x * i * k        ≡⟨ cong (_* k) (sym eq) ⟩
    (1 + y * j) * k  ≡⟨ solve 3 (λ y j k → (con 1 :+ y :* j) :* k
                                       :=  y :* (j :* k) :+ k)
                                refl y j k ⟩
    y * (j * k) + k  ≡⟨ cong (λ n → y * n + k) eq′ ⟩
    y * (q * i) + k  ≡⟨ solve 4 (λ y q i k → y :* (q :* i) :+ k
                                         :=  y :*  q :* i  :+ k)
                                refl y q i k ⟩
    y *  q * i  + k  ∎

lem₉ : ∀ {i j k q} x y →
       1 + x * i ≡ y * j → j * k ≡ q * i →
       k ≡ (y * q ∸ x * k) * i
lem₉ {i} {j} {k} {q} x y eq eq′ =
  sym (lem₀ (y * q) (x * k) i k lemma)
  where
  lem   = solve 3 (λ a b c → a :* b :* c  :=  b :* c :* a) refl
  lemma = begin
    y * q * i        ≡⟨ lem y q i ⟩
    q * i * y        ≡⟨ cong (λ n → n * y) (sym eq′) ⟩
    j * k * y        ≡⟨ sym (lem y j k) ⟩
    y * j * k        ≡⟨ cong (λ n → n * k) (sym eq) ⟩
    (1 + x * i) * k  ≡⟨ solve 3 (λ x i k → (con 1 :+ x :* i) :* k
                                       :=  x :* k :* i :+ k)
                                refl x i k ⟩
    x * k * i + k    ∎

lem₁₀ : ∀ {a′} b c {d} e f → let a = suc a′ in
        a + b * (c * d * a) ≡ e * (f * d * a) →
        d ≡ 1
lem₁₀ {a′} b c {d} e f eq =
  i*j≡1⇒j≡1 (e * f ∸ b * c) d
    (lem₀ (e * f) (b * c) d 1
       (*-cancelʳ-≡ (e * f * d) (b * c * d + 1) (begin
          e * f * d * a        ≡⟨ solve 4 (λ e f d a → e :* f :* d :* a
                                                   :=  e :* (f :* d :* a))
                                          refl e f d a ⟩
          e * (f * d * a)      ≡⟨ sym eq ⟩
          a + b * (c * d * a)  ≡⟨ solve 4 (λ a b c d → a :+ b :* (c :* d :* a)
                                                   :=  (b :* c :* d :+ con 1) :* a)
                                          refl a b c d ⟩
          (b * c * d + 1) * a  ∎)))
  where a = suc a′

lem₁₁ : ∀ {i j m n k d} x y →
       1 + y * j ≡ x * i → i * k ≡ m * d → j * k ≡ n * d →
       k ≡ (x * m ∸ y * n) * d
lem₁₁ {i} {j} {m} {n} {k} {d} x y eq eq₁ eq₂ =
  sym (lem₀ (x * m) (y * n) d k (begin
    x * m * d        ≡⟨ *-assoc x m d ⟩
    x * (m * d)      ≡⟨ cong (x *_) (sym eq₁) ⟩
    x * (i * k)      ≡⟨ sym (*-assoc x i k) ⟩
    x * i * k        ≡⟨ cong₂ _*_ (sym eq) refl ⟩
    (1 + y * j) * k  ≡⟨ solve 3 (λ y j k → (con 1 :+ y :* j) :* k
                                       :=  y :* (j :* k) :+ k)
                                refl y j k ⟩
    y * (j * k) + k  ≡⟨ cong (λ p → y * p + k) eq₂ ⟩
    y * (n * d) + k  ≡⟨ cong₂ _+_ (sym $ *-assoc y n d) refl ⟩
    y * n * d + k    ∎))


{-# OPTIONS --without-K --safe #-}
module Cats.Category.Presheaves.Facts.Exponential where

open import Data.Product using (_,_)
open import Relation.Binary using (Setoid)

open import Cats.Category
open import Cats.Category.Fun using (Fun ; ≈-intro ; ≈-elim)
open import Cats.Category.Presheaves using (Presheaves)
open import Cats.Category.Setoids as Setoids using (≈-intro ; ≈-elim)
open import Cats.Functor using (Functor)
open import Cats.Trans using (Trans ; component ; natural)

import Cats.Category.Fun.Facts.Product as Product
import Cats.Category.Setoids.Facts

open Functor
open HasBinaryProducts {{...}}
open Setoids._⇒_

module _ {l} {ℂ : Category l l l} where

  private
    module ℂ = Category ℂ
    open Category (Presheaves ℂ l l)
    module $ (F : Obj) {x : Category.Obj ℂ} = Setoid (fobj F x)

  -- Could replace this with transposeBifunctor₂ HomF, but that would introduce
  -- even more redundant compositions with the identity.
  y : ℂ.Obj → Obj
  y d = record
    { fobj = λ c → ℂ.Hom c d
    ; fmap = λ f → record
      { arr = (ℂ._∘ f)
      ; resp = ℂ.∘-resp-l
      }
    ; fmap-resp = λ eq₁ → ≈-intro λ eq₂ → ℂ.∘-resp eq₂ eq₁
    ; fmap-id = ≈-intro λ eq → ℂ.≈.trans ℂ.id-r eq
    ; fmap-∘ = ≈-intro λ eq → ℂ.≈.trans ℂ.assoc (ℂ.∘-resp-l eq)
    }

  _↝_ : (F G : Obj) → Obj
  F ↝ G = record
    { fobj = λ d → Hom (y d × F) G
    ; fmap = λ {d} {d′} f → record
      { arr = λ α → record
        { component = λ d → record
          { arr = λ where
              (g , c) → arr (component α d) (f ℂ.∘ g , c)
          ; resp = λ where
              (eq₁ , eq₂) → resp (component α d) (ℂ.∘-resp-r eq₁ , eq₂)
          }
        ; natural = λ {e} {e′} {g} → ≈-intro λ where
            {h , c} {h′ , c′} (h≈h′ , c≈c′) →
              $.trans G (resp (component α e′) (ℂ.unassoc , $.refl F))
                (≈-elim (natural α) (ℂ.∘-resp-r h≈h′ , c≈c′))
        }
      ; resp = λ α≈β → ≈-intro (≈-intro λ where
          (f≈g , h≈i) → ≈-elim (≈-elim α≈β) (ℂ.∘-resp-r f≈g , h≈i))
      }
    ; fmap-resp = λ f≈g → ≈-intro λ α≈β → ≈-intro (≈-intro λ where
        (c≈d , h≈i) → ≈-elim (≈-elim α≈β) ((ℂ.∘-resp f≈g c≈d) , h≈i))
    ; fmap-id = ≈-intro λ α≈β → ≈-intro (≈-intro λ where
        (c≈d , h≈i) → ≈-elim (≈-elim α≈β) ((ℂ.≈.trans ℂ.id-l c≈d) , h≈i))
    ; fmap-∘ = ≈-intro λ α≈β → ≈-intro (≈-intro λ where
        (c≈d , h≈i) → ≈-elim (≈-elim α≈β) ((ℂ.≈.trans ℂ.unassoc (ℂ.∘-resp-r c≈d)) , h≈i))
    }

  Eval : ∀ F G → F ↝ G × F ⇒ G
  Eval F G = record
    { component = λ c → record
      { arr = λ where
          (α , x) → arr (component α c) (ℂ.id , x)
      ; resp = λ where
          (α≈β , f≈g) → ≈-elim (≈-elim α≈β) (ℂ.≈.refl , f≈g)
      }
    ; natural = λ {c} {d} → ≈-intro λ where
        {α , f} {β , g} (α≈β , f≈g) →
          $.trans G
            (≈-elim (≈-elim α≈β)
              (ℂ.≈.trans ℂ.id-r (ℂ.≈.sym ℂ.id-l) , resp (fmap F _) f≈g))
            (≈-elim (natural β) (ℂ.≈.refl , $.refl F))
    }

  Curry : ∀ E F G → E × F ⇒ G → E ⇒ F ↝ G
  Curry E F G α = record
    { component = λ d → record
      { arr = λ x → record
        { component = λ c → record
          { arr = λ where
              (f , y) → arr (component α c) (arr (fmap E f) x , y)
          ; resp = λ where
              (c≈d , x≈y) → resp (component α c)
                (≈-elim (fmap-resp E c≈d) ($.refl E) , x≈y)
          }
        ; natural = ≈-intro λ where
            (f≈g , x≈y) → $.trans G
              (resp (component α _)
                (($.sym E (≈-elim (fmap-∘ E) ($.refl E))) , ($.refl F)))
              (≈-elim (natural α) (≈-elim (fmap-resp E f≈g) ($.refl E) , x≈y))
        }
      ; resp = λ x≈y → ≈-intro (≈-intro λ where
          (f≈g , c≈d) → resp (component α _) ((≈-elim (fmap-resp E f≈g) x≈y) , c≈d))
      }
    ; natural = ≈-intro λ x≈y → ≈-intro (≈-intro λ where
        (f≈g , c≈d) → resp (component α _)
          ( $.trans E (≈-elim (fmap-∘ E) x≈y)
              (≈-elim (fmap-resp E (ℂ.∘-resp-r f≈g)) ($.refl E))
          , c≈d ))
    }

  instance
    hasExponentials : HasExponentials (Presheaves ℂ l l)
    hasExponentials = record
      { hasBinaryProducts = Product.hasBinaryProducts
      ; _↝′_ = λ F G → record
        { Cᴮ = F ↝ G
        ; eval = Eval F G
        ; curry′ = λ {E} α → record
          { arr = Curry E F G α
          ; prop = ≈-intro (≈-intro λ where
              (x≈y , u≈v) → resp (component α _) (≈-elim (fmap-id E) x≈y , u≈v))
          ; unique = λ {α} α≈ → ≈-intro (≈-intro (λ x≈y → ≈-intro (≈-intro (λ where
              (f≈g , u≈v) → $.trans G
                (≈-elim (≈-elim (≈.sym α≈)) (≈-elim (fmap-resp E f≈g) ($.refl E) , u≈v))
                ($.trans G
                  (≈-elim (≈-elim (≈-elim (natural α) x≈y)) (ℂ.≈.refl , ($.refl F)))
                  (resp (component (arr (component α _) _) _) (ℂ.id-r , ($.refl F))))))))
          }
        }
      }


open import Agda.Primitive using (_⊔_)

import Categories.Category as Category
import Categories.Category.Cartesian as Cartesian
import MultiSorted.Interpretation as Interpretation

open import MultiSorted.AlgebraicTheory
open import MultiSorted.Substitution
import MultiSorted.Product as Product

module MultiSorted.Model {o ℓ e ℓt}
          {𝓈 ℴ}
          {Σ : Signature {𝓈} {ℴ}}
          (T : Theory ℓt Σ)
          {𝒞 : Category.Category o ℓ e}
          {cartesian-𝒞 : Cartesian.Cartesian 𝒞} where

  -- Model of a theory

  record Is-Model (I : Interpretation.Interpretation Σ cartesian-𝒞) : Set (ℓt ⊔ o ⊔ ℓ ⊔ e) where

    open Theory T
    open Category.Category 𝒞
    open Interpretation.Interpretation I
    open HomReasoning

    field
      model-eq : ∀ (ε : ax) → ⊨ ax-eq (ε)

    -- Soundness of semantics
    module _ where
      open Product.Producted interp-ctx

      -- first we show that substitution preserves validity
      model-resp-[]s : ∀ {Γ Δ} {A} {u v : Term Γ A} {σ : Δ ⇒s Γ} →
                       interp-term u ≈ interp-term v → interp-term (u [ σ ]s) ≈ interp-term (v [ σ ]s)
      model-resp-[]s {u = u} {v = v} {σ = σ} ξ =
        begin
          interp-term (u [ σ ]s) ≈⟨  interp-[]s {t = u} ⟩
          (interp-term u ∘ interp-subst σ)  ≈⟨ ξ ⟩∘⟨refl ⟩
          (interp-term v ∘ interp-subst σ) ≈˘⟨ interp-[]s {t = v} ⟩
          interp-term (v [ σ ]s) ∎

      -- the soundness statement
      ⊢-⊨ : ∀ {ε : Equation Σ} → ⊢ ε → ⊨ ε
      ⊢-⊨ eq-refl =  Equiv.refl
      ⊢-⊨ (eq-symm ξ) = ⟺ (⊢-⊨ ξ)
      ⊢-⊨ (eq-tran ξ θ) = ⊢-⊨ ξ ○ ⊢-⊨ θ
      ⊢-⊨ (eq-congr ξ) = ∘-resp-≈ʳ (unique λ i → project ○ ⟺ (⊢-⊨ (ξ i)))
      ⊢-⊨ (eq-axiom ε σ) = model-resp-[]s {u = ax-lhs ε} {v = ax-rhs ε} (model-eq ε)

  -- Every theory has the trivial model, whose carrier is the terminal object
  Trivial : Is-Model (Interpretation.Trivial Σ cartesian-𝒞)
  Trivial =
    let open Cartesian.Cartesian cartesian-𝒞 in
    record { model-eq = λ ε → !-unique₂ }


{-# OPTIONS --no-positivity-check #-}

{- This file gives a standard example showing that if arguments to
   constructors can use the datatype in a negative position (to the
   left of one or an odd number of arrows), then termination and
   logical consistency is lost. -}

module neg-datatype-nonterm where

open import empty

data Bad : Set where
  bad : (Bad → ⊥) → Bad

badFunc : Bad → ⊥
badFunc (bad f) = f (bad f)

-- if you try to normalize the following, it will diverge
inconsistency : ⊥
inconsistency = badFunc (bad badFunc)


-- Idiom bracket notation.
module Syntax.Idiom where

import      Lvl
open import Type

private variable ℓ ℓ₁ ℓ₂ : Lvl.Level
private variable A B : Type{ℓ}
private variable F : Type{ℓ₁} → Type{ℓ₂}

-- The notation `⦇ f x₁ x₂ x₃ ⦈` will automatically be translated to `((pure f <*> x₁) <*> x₂) <*> x₃`.
record IdiomBrackets (F : Type{ℓ₁} → Type{ℓ₂}) : Type{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂} where
  constructor intro
  field
    pure : (A → F(A))
    _<*>_ : F(A → B) → (F(A) → F(B))
open IdiomBrackets ⦃ … ⦄ using (pure ; _<*>_) public

-- The notation `⦇⦈` will automatically be translated to `empty`.
record IdiomBrackets₀ (F : Type{ℓ₁} → Type{ℓ₂}) : Type{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂} where
  constructor intro
  field
    empty : F(A)
open IdiomBrackets₀ ⦃ … ⦄ using (empty) public

-- The notation `⦇ f₁ x₁ x₂ x₃ | f₂ y₁ y₂ | f₃ z₁ ⦈` will automatically be translated to `(((pure f <*> x₁) <*> x₂) <*> x₃) <|> (((pure f₂ <*> y₁) <*> y₂) <|> (pure f₃ <*> z₁))`.
record IdiomBrackets₊ (F : Type{ℓ₁} → Type{ℓ₂}) ⦃ _ : IdiomBrackets(F) ⦄ : Type{Lvl.𝐒(ℓ₁) Lvl.⊔ ℓ₂} where
  constructor intro
  field
    _<|>_ : F(A) → F(A) → F(A)
open IdiomBrackets₊ ⦃ … ⦄ using (_<|>_) public


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

open import Base
open import Homotopy.PullbackDef

module Homotopy.PullbackIsPullback {i} (d : pullback-diag i) where

open pullback-diag d

import Homotopy.PullbackUP as PullbackUP
open PullbackUP d (λ _ → unit)

pullback-cone : cone (pullback d)
pullback-cone = (pullback.a d , pullback.b d , pullback.h d)

factor-pullback : (E : Set i) → (cone E → (E → pullback d))
factor-pullback E (top→A , top→B , h) x = (top→A x , top→B x , h x)

pullback-is-pullback : is-pullback (pullback d) pullback-cone
pullback-is-pullback E = iso-is-eq _
  (factor-pullback E)
  (λ y → refl)
  (λ f → refl)


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

{- Remember to keep CodeBP.agda in sync. -}

open import HoTT
import homotopy.RelativelyConstantToSetExtendsViaSurjection as SurjExt

module homotopy.vankampen.CodeAP {i j k l}
  (span : Span {i} {j} {k})
  {D : Type l} (h : D → Span.C span) (h-is-surj : is-surj h) where

  open Span span

  data precodeAA (a₀ : A) : A → Type (lmax (lmax (lmax i j) k) l)
  data precodeAB (a₀ : A) (b₁ : B) : Type (lmax (lmax (lmax i j) k) l)

  data precodeAA a₀ where
    pc-a : ∀ {a₁} (pA : a₀ =₀ a₁) → precodeAA a₀ a₁
    pc-aba : ∀ d {a₁} (pc : precodeAB a₀ (g (h d))) (pA : f (h d) =₀ a₁) → precodeAA a₀ a₁

  infix 66 pc-a
  syntax pc-a p = ⟧a p
  infixl 65 pc-aba
  syntax pc-aba d pcAB pA = pcAB ab⟦ d ⟧a pA

  data precodeAB a₀ b₁ where
    pc-aab : ∀ d (pc : precodeAA a₀ (f (h d))) (pB : g (h d) =₀ b₁) → precodeAB a₀ b₁

  infixl 65 pc-aab
  syntax pc-aab d pcAA pB = pcAA aa⟦ d ⟧b pB

  data precodeAA-rel {a₀ : A} : {a₁ : A}
    → precodeAA a₀ a₁ → precodeAA a₀ a₁ → Type (lmax (lmax (lmax i j) k) l)
  data precodeAB-rel {a₀ : A} : {b₁ : B}
    → precodeAB a₀ b₁ → precodeAB a₀ b₁ → Type (lmax (lmax (lmax i j) k) l)
  data precodeAA-rel {a₀} where
    pcAAr-idp₀-idp₀ : ∀ {d} pcAA → precodeAA-rel (pcAA aa⟦ d ⟧b idp₀ ab⟦ d ⟧a idp₀) pcAA
    pcAAr-cong : ∀ {d a₁ pcAB₁ pcAB₂} (r : precodeAB-rel pcAB₁ pcAB₂) (pA : f (h d) =₀ a₁)
      → precodeAA-rel (pcAB₁ ab⟦ d ⟧a pA) (pcAB₂ ab⟦ d ⟧a pA)
  data precodeAB-rel {a₀} where
    pcABr-idp₀-idp₀ : ∀ {d} pcAB → precodeAB-rel (pcAB ab⟦ d ⟧a idp₀ aa⟦ d ⟧b idp₀) pcAB
    pcABr-switch : ∀ {d₀ d₁ : D} pcAB (pC : h d₀ =₀ h d₁)
      → precodeAB-rel (pcAB ab⟦ d₀ ⟧a ap₀ f pC aa⟦ d₁ ⟧b idp₀) (pcAB ab⟦ d₀ ⟧a idp₀ aa⟦ d₀ ⟧b ap₀ g pC)
    pcABr-cong : ∀ {d b₁ pcAA₁ pcAA₂} (r : precodeAA-rel pcAA₁ pcAA₂) (pB : g (h d) =₀ b₁)
      → precodeAB-rel (pcAA₁ aa⟦ d ⟧b pB) (pcAA₂ aa⟦ d ⟧b pB)

  codeAA : A → A → Type (lmax (lmax (lmax i j) k) l)
  codeAA a₀ a₁ = SetQuot (precodeAA-rel {a₀} {a₁})

  codeAB : A → B → Type (lmax (lmax (lmax i j) k) l)
  codeAB a₀ b₁ = SetQuot (precodeAB-rel {a₀} {b₁})

  c-aba : ∀ {a₀} d {a₁} (pc : codeAB a₀ (g (h d))) (pA : f (h d) =₀ a₁) → codeAA a₀ a₁
  c-aba d {a₁} c pA = SetQuot-rec SetQuot-is-set
    (λ pc → q[ pc-aba d pc pA ])
    (λ r → quot-rel $ pcAAr-cong r pA) c

  c-aab : ∀ {a₀} d {b₁} (pc : codeAA a₀ (f (h d))) (pB : g (h d) =₀ b₁) → codeAB a₀ b₁
  c-aab d {a₁} c pB = SetQuot-rec SetQuot-is-set
    (λ pc → q[ pc-aab d pc pB ])
    (λ r → quot-rel $ pcABr-cong r pB) c

-- codeAP

  abstract
    pcAA-idp₀-idp₀-head : ∀ {d₀ a} (pA : f (h d₀) =₀ a)
      → q[ ⟧a idp₀ aa⟦ d₀ ⟧b idp₀ ab⟦ d₀ ⟧a pA ] == q[ ⟧a pA ] :> codeAA _ a
    pcAA-idp₀-idp₀-head {d₀} = Trunc-elim (λ _ → =-preserves-set SetQuot-is-set) lemma where
      lemma : ∀ {a} (pA : f (h d₀) == a)
        → q[ ⟧a idp₀ aa⟦ d₀ ⟧b idp₀ ab⟦ d₀ ⟧a [ pA ] ] == q[ ⟧a [ pA ] ] :> codeAA _ a
      lemma idp = quot-rel $ pcAAr-idp₀-idp₀ (⟧a idp₀)

  pcAA-prepend : ∀ {a₀} d₁ {a₂} → a₀ =₀ f (h d₁) → precodeAA (f (h d₁)) a₂ → precodeAA a₀ a₂
  pcAB-prepend : ∀ {a₀} d₁ {b₂} → a₀ =₀ f (h d₁) → precodeAB (f (h d₁)) b₂ → precodeAB a₀ b₂
  pcAA-prepend d₁ pA (pc-a pA₁) = pc-aba d₁ (pc-aab d₁ (pc-a pA) idp₀) pA₁
  pcAA-prepend d₁ pA (pc-aba d pc pA₁) = pc-aba d (pcAB-prepend d₁ pA pc) pA₁
  pcAB-prepend d₁ pA (pc-aab d pc pB) = pc-aab d (pcAA-prepend d₁ pA pc) pB

  abstract
    pcAA-prepend-idp₀ : ∀ {d₀ a₁} (pcAA : precodeAA (f (h d₀)) a₁)
      → q[ pcAA-prepend d₀ idp₀ pcAA ] == q[ pcAA ] :> codeAA (f (h d₀)) a₁
    pcAB-prepend-idp₀ : ∀ {d₀ b₁} (pcAB : precodeAB (f (h d₀)) b₁)
      → q[ pcAB-prepend d₀ idp₀ pcAB ] == q[ pcAB ] :> codeAB (f (h d₀)) b₁
    pcAA-prepend-idp₀ (pc-a pA) = pcAA-idp₀-idp₀-head pA
    pcAA-prepend-idp₀ (pc-aba d pc pA) = pcAB-prepend-idp₀ pc |in-ctx λ c → c-aba d c pA
    pcAB-prepend-idp₀ (pc-aab d pc pA) = pcAA-prepend-idp₀ pc |in-ctx λ c → c-aab d c pA

    transp-cAA-l : ∀ d {a₀ a₁} (p : f (h d) == a₀) (pcAA : precodeAA (f (h d)) a₁)
      → transport (λ x → codeAA x a₁) p q[ pcAA ] == q[ pcAA-prepend d [ ! p ] pcAA ]
    transp-cAA-l d idp pcAA = ! $ pcAA-prepend-idp₀ pcAA

    transp-cAB-l : ∀ d {a₀ b₁} (p : f (h d) == a₀) (pcAB : precodeAB (f (h d)) b₁)
      → transport (λ x → codeAB x b₁) p q[ pcAB ] == q[ pcAB-prepend d [ ! p ] pcAB ]
    transp-cAB-l d idp pcAB = ! $ pcAB-prepend-idp₀ pcAB

    transp-cAA-r : ∀ d {a₀ a₁} (p : f (h d) == a₁) (pcAA : precodeAA a₀ (f (h d)))
      → transport (λ x → codeAA a₀ x) p q[ pcAA ] == q[ pcAA aa⟦ d ⟧b idp₀ ab⟦ d ⟧a [ p ] ]
    transp-cAA-r d idp pcAA = ! $ quot-rel $ pcAAr-idp₀-idp₀ pcAA

    transp-cAB-r : ∀ d {a₀ b₁} (p : g (h d) == b₁) (pcAB : precodeAB a₀ (g (h d)))
      → transport (λ x → codeAB a₀ x) p q[ pcAB ] == q[ pcAB ab⟦ d ⟧a idp₀ aa⟦ d ⟧b [ p ] ]
    transp-cAB-r d idp pcAB = ! $ quot-rel $ pcABr-idp₀-idp₀ pcAB

  module CodeAAEquivCodeAB (a₀ : A) where

    eqv-on-image : (d : D) → codeAA a₀ (f (h d)) ≃ codeAB a₀ (g (h d))
    eqv-on-image d = equiv to from to-from from-to where
      to = λ c → c-aab d c idp₀
      from = λ c → c-aba d c idp₀

      abstract
        from-to : ∀ cAA → from (to cAA) == cAA
        from-to = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (λ pcAA → quot-rel (pcAAr-idp₀-idp₀ pcAA))
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

        to-from : ∀ cAB → to (from cAB) == cAB
        to-from = SetQuot-elim
          (λ _ → =-preserves-set SetQuot-is-set)
          (λ pcAB → quot-rel (pcABr-idp₀-idp₀ pcAB))
          (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    abstract
      eqv-is-const : ∀ d₁ d₂ (p : h d₁ == h d₂)
        → eqv-on-image d₁ == eqv-on-image d₂
        [ (λ c → codeAA a₀ (f c) ≃ codeAB a₀ (g c)) ↓ p ]
      eqv-is-const d₁ d₂ p = ↓-Subtype-in (λ d → is-equiv-prop) $
        ↓-→-from-transp $ λ= $
          SetQuot-elim (λ _ → =-preserves-set SetQuot-is-set)
            (λ pcAA →
              transport (λ c → codeAB a₀ (g c)) p q[ pcAA aa⟦ d₁ ⟧b idp₀ ]
                =⟨ ap-∘ (codeAB a₀) g p |in-ctx (λ p → coe p q[ pcAA aa⟦ d₁ ⟧b idp₀ ]) ⟩
              transport (codeAB a₀) (ap g p) q[ pcAA aa⟦ d₁ ⟧b idp₀ ]
                =⟨ transp-cAB-r d₁ (ap g p) (pcAA aa⟦ d₁ ⟧b idp₀) ⟩
              q[ pcAA aa⟦ d₁ ⟧b idp₀ ab⟦ d₁ ⟧a idp₀ aa⟦ d₁ ⟧b [ ap g p ] ]
                =⟨ ! $ quot-rel $ pcABr-switch (pcAA aa⟦ d₁ ⟧b idp₀) [ p ] ⟩
              q[ pcAA aa⟦ d₁ ⟧b idp₀ ab⟦ d₁ ⟧a [ ap f p ] aa⟦ d₂ ⟧b idp₀ ]
                =⟨ ! $ transp-cAA-r d₁ (ap f p) pcAA |in-ctx (λ c → c-aab d₂ c idp₀) ⟩
              c-aab d₂ (transport (codeAA a₀) (ap f p) q[ pcAA ]) idp₀
                =⟨ ∘-ap (codeAA a₀) f p |in-ctx (λ p → coe p q[ pcAA ]) |in-ctx (λ c → c-aab d₂ c idp₀) ⟩
              c-aab d₂ (transport (λ c → codeAA a₀ (f c)) p q[ pcAA ]) idp₀
                =∎)
            (λ _ → prop-has-all-paths-↓ (SetQuot-is-set _ _))

    module SE = SurjExt
      (λ c → ≃-is-set SetQuot-is-set SetQuot-is-set)
      h h-is-surj
      eqv-on-image
      eqv-is-const

    abstract
      eqv : ∀ c → codeAA a₀ (f c) ≃ codeAB a₀ (g c)
      eqv = SE.ext

      eqv-β : ∀ d → eqv (h d) == eqv-on-image d
      eqv-β = SE.β

  module CodeAP (a₀ : A) = PushoutRec (codeAA a₀) (codeAB a₀)
    (ua ∘ CodeAAEquivCodeAB.eqv a₀)

  codeAP : A → Pushout span → Type (lmax (lmax (lmax i j) k) l)
  codeAP = CodeAP.f

  abstract
    codeAP-level : ∀ {a₀ p₁} → is-set (codeAP a₀ p₁)
    codeAP-level {a₀} {p₁} = Pushout-elim
      {P = λ p₁ → is-set (codeAP a₀ p₁)}
      (λ a₁ → SetQuot-is-set)
      (λ b₁ → SetQuot-is-set)
      (λ c₁ → prop-has-all-paths-↓ is-set-is-prop)
      p₁
  codeAP-is-set = codeAP-level

  abstract
    transp-cAP-glue : ∀ {a₀} d₁ (pcAA : precodeAA a₀ (f (h d₁)))
      → transport (codeAP a₀) (glue (h d₁)) q[ pcAA ] == q[ pcAA aa⟦ d₁ ⟧b idp₀ ]
    transp-cAP-glue {a₀} d₁ pcAA =
      transport (codeAP a₀) (glue (h d₁)) q[ pcAA ]
        =⟨ ap (λ e → coe e q[ pcAA ]) (CodeAP.glue-β a₀ (h d₁) ∙ ap ua (CodeAAEquivCodeAB.eqv-β a₀ d₁)) ⟩
      coe (ua (CodeAAEquivCodeAB.eqv-on-image a₀ d₁)) q[ pcAA ]
        =⟨ coe-β (CodeAAEquivCodeAB.eqv-on-image a₀ d₁) q[ pcAA ] ⟩
      q[ pcAA aa⟦ d₁ ⟧b idp₀ ]
        =∎

    transp-cAP-!glue : ∀ {a₀} d₁ (pcAB : precodeAB a₀ (g (h d₁)))
      → transport (codeAP a₀) (! (glue (h d₁))) q[ pcAB ] == q[ pcAB ab⟦ d₁ ⟧a idp₀ ]
    transp-cAP-!glue {a₀} d₁ pcAB =
      transport (codeAP a₀) (! (glue (h d₁))) q[ pcAB ]
        =⟨ ap (λ e → coe e q[ pcAB ]) (ap-! (codeAP a₀) (glue (h d₁)))
         ∙ coe-! (ap (codeAP a₀) (glue (h d₁))) q[ pcAB ] ⟩
      transport! (codeAP a₀) (glue (h d₁)) q[ pcAB ]
        =⟨ ap (λ e → coe! e q[ pcAB ]) (CodeAP.glue-β a₀ (h d₁) ∙ ap ua (CodeAAEquivCodeAB.eqv-β a₀ d₁)) ⟩
      coe! (ua (CodeAAEquivCodeAB.eqv-on-image a₀ d₁)) q[ pcAB ]
        =⟨ coe!-β (CodeAAEquivCodeAB.eqv-on-image a₀ d₁) q[ pcAB ] ⟩
      q[ pcAB ab⟦ d₁ ⟧a idp₀ ]
        =∎

  -- code to path
  pcAA-to-path : ∀ {a₀ a₁} → precodeAA a₀ a₁ → left a₀ =₀ left a₁ :> Pushout span
  pcAB-to-path : ∀ {a₀ b₁} → precodeAB a₀ b₁ → left a₀ =₀ right b₁ :> Pushout span
  pcAA-to-path (pc-a pA) = ap₀ left pA
  pcAA-to-path (pc-aba d pc pA) = pcAB-to-path pc ∙₀' !₀ [ glue (h d) ] ∙₀' ap₀ left pA
  pcAB-to-path (pc-aab d pc pB) = pcAA-to-path pc ∙₀' [ glue (h d) ] ∙₀' ap₀ right pB

  abstract
    pcAA-to-path-rel : ∀ {a₀ a₁} {pcAA₀ pcAA₁ : precodeAA a₀ a₁}
      → precodeAA-rel pcAA₀ pcAA₁ → pcAA-to-path pcAA₀ == pcAA-to-path pcAA₁
    pcAB-to-path-rel : ∀ {a₀ b₁} {pcAB₀ pcAB₁ : precodeAB a₀ b₁}
      → precodeAB-rel pcAB₀ pcAB₁ → pcAB-to-path pcAB₀ == pcAB-to-path pcAB₁
    pcAA-to-path-rel (pcAAr-idp₀-idp₀ pcAA) =
        ∙₀'-assoc (pcAA-to-path pcAA) [ glue (h _) ] [ ! (glue (h _)) ]
      ∙ ap (λ p → pcAA-to-path pcAA ∙₀' [ p ]) (!-inv'-r (glue (h _)))
      ∙ ∙₀'-unit-r (pcAA-to-path pcAA)
    pcAA-to-path-rel (pcAAr-cong pcAB pA) = pcAB-to-path-rel pcAB |in-ctx _∙₀' !₀ [ glue (h _) ] ∙₀' ap₀ left pA
    pcAB-to-path-rel (pcABr-idp₀-idp₀ pcAB) =
        ∙₀'-assoc (pcAB-to-path pcAB) [ ! (glue (h _)) ] [ glue (h _) ]
      ∙ ap (λ p → pcAB-to-path pcAB ∙₀' [ p ]) (!-inv'-l (glue (h _)))
      ∙ ∙₀'-unit-r (pcAB-to-path pcAB)
    pcAB-to-path-rel (pcABr-switch pcAB pC) =
        ap (_∙₀' [ glue (h _) ]) (! (∙₀'-assoc (pcAB-to-path pcAB) [ ! (glue (h _)) ] (ap₀ left (ap₀ f pC))))
      ∙ ∙₀'-assoc (pcAB-to-path pcAB ∙₀' [ ! (glue (h _)) ]) (ap₀ left (ap₀ f pC)) [ glue (h _) ]
      ∙ ap ((pcAB-to-path pcAB ∙₀' [ ! (glue (h _)) ]) ∙₀'_) (natural₀ pC)
      where
        natural : ∀ {c₀ c₁} (p : c₀ == c₁)
          → (ap left (ap f p) ∙' glue c₁) == (glue c₀ ∙' ap right (ap g p))
          :> (left (f c₀) == right (g c₁) :> Pushout span)
        natural idp = ∙'-unit-l (glue _)

        natural₀ : ∀ {c₀ c₁} (p : c₀ =₀ c₁)
          → (ap₀ left (ap₀ f p) ∙₀' [ glue c₁ ]) == ([ glue c₀ ] ∙₀' ap₀ right (ap₀ g p))
          :> (left (f c₀) =₀ right (g c₁) :> Pushout span)
        natural₀ = Trunc-elim (λ _ → =-preserves-set Trunc-level) (ap [_] ∘ natural)
    pcAB-to-path-rel (pcABr-cong pcAA pB) = pcAA-to-path-rel pcAA |in-ctx _∙₀' [ glue (h _) ] ∙₀' ap₀ right pB

  decodeAA : ∀ {a₀ a₁} → codeAA a₀ a₁ → left a₀ =₀ left a₁ :> Pushout span
  decodeAB : ∀ {a₀ b₁} → codeAB a₀ b₁ → left a₀ =₀ right b₁ :> Pushout span
  decodeAA = SetQuot-rec Trunc-level pcAA-to-path pcAA-to-path-rel
  decodeAB = SetQuot-rec Trunc-level pcAB-to-path pcAB-to-path-rel

  abstract
    decodeAA-is-decodeAB : ∀ {a₀} c₁ →
      decodeAA {a₀} {f c₁} == decodeAB {a₀} {g c₁}
      [ (λ p₁ → codeAP a₀ p₁ → left a₀ =₀ p₁) ↓ glue c₁ ]
    decodeAA-is-decodeAB {a₀ = a₀} = SurjExt.ext
      (λ _ → ↓-preserves-level $ Π-is-set λ _ → Trunc-level) h h-is-surj
      (λ d₁ → ↓-→-from-transp $ λ= $ SetQuot-elim
        {P = λ cAA → transport (left a₀ =₀_) (glue (h d₁)) (decodeAA cAA)
                  == decodeAB (transport (codeAP a₀) (glue (h d₁)) cAA)}
        (λ _ → =-preserves-set Trunc-level)
        (λ pcAA →
          transport (left a₀ =₀_) (glue (h d₁)) (pcAA-to-path pcAA)
            =⟨ transp₀-cst=₀idf [ glue (h d₁) ] (pcAA-to-path pcAA) ⟩
          pcAA-to-path pcAA ∙₀' [ glue (h d₁) ]
            =⟨ ! $ ap (λ e → decodeAB (–> e q[ pcAA ])) (CodeAAEquivCodeAB.eqv-β a₀ d₁) ⟩
          decodeAB (–> (CodeAAEquivCodeAB.eqv a₀ (h d₁)) q[ pcAA ])
            =⟨ ! $ ap decodeAB (coe-β (CodeAAEquivCodeAB.eqv a₀ (h d₁)) q[ pcAA ]) ⟩
          decodeAB (coe (ua (CodeAAEquivCodeAB.eqv a₀ (h d₁))) q[ pcAA ])
            =⟨ ! $ ap (λ p → decodeAB (coe p q[ pcAA ])) (CodeAP.glue-β a₀ (h d₁)) ⟩
          decodeAB (transport (codeAP a₀) (glue (h d₁)) q[ pcAA ])
            =∎)
        (λ _ → prop-has-all-paths-↓ $ Trunc-level {n = 0} _ _))
      (λ _ _ _ → prop-has-all-paths-↓ $ ↓-level $ Π-is-set λ _ → Trunc-level)

  decodeAP : ∀ {a₀ p₁} → codeAP a₀ p₁ → left a₀ =₀ p₁
  decodeAP {p₁ = p₁} = Pushout-elim
    (λ a₁ → decodeAA) (λ b₁ → decodeAB)
    decodeAA-is-decodeAB
    p₁


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

open import HoTT

{- Useful lemmas for computing the effect of transporting a function
 - across an equivalence in the domain or codomain.
 - TODO move these lemmas into lib.types.Pi or lib.types.PointedPi -}

-- XXX Naming convensions?

module stash.homotopy.FunctionOver where

{- transporting a ptd function along a equivalence or path in the domain -}
module _ {i} {j} {Y : Ptd i} {Z : Ptd j} (g : Y ⊙→ Z) where

  domain-over-⊙path : {X : Ptd i} (p : de⊙ X == de⊙ Y)
    (q : coe p (pt X) == pt Y)
    → g ⊙∘ (coe p , q) == g [ (λ W → W ⊙→ Z) ↓ ptd= p (↓-idf-in p q) ]
  domain-over-⊙path idp idp = idp

  domain-over-⊙equiv : {X : Ptd i} (e : X ⊙≃ Y)
    → g ⊙∘ ⊙–> e == g [ (λ W → W ⊙→ Z) ↓ ⊙ua e ]
  domain-over-⊙equiv {X = X} e =
    ap (λ w → g ⊙∘ w) (! $ ⊙λ= (coe-β (⊙≃-to-≃ e)) (↓-idf=cst-in idp))
    ◃ domain-over-⊙path (ua (⊙≃-to-≃ e))
                        (coe-β (⊙≃-to-≃ e) (pt X) ∙ snd (⊙–> e))

module _ {i} {j} {X : Ptd i} {Z : Ptd j} (f : X ⊙→ Z) where

  domain!-over-⊙path : {Y : Ptd i} (p : de⊙ X == de⊙ Y)
    (q : coe p (pt X) == pt Y)
    → f == f ⊙∘ (coe! p , ap (coe! p) (! q) ∙ coe!-inv-l p (pt X))
      [ (λ W → W ⊙→ Z) ↓ ptd= p (↓-idf-in p q) ]
  domain!-over-⊙path idp idp = idp

  domain!-over-⊙equiv : {Y : Ptd i} (e : X ⊙≃ Y)
    → f == f ⊙∘ (⊙<– e) [ (λ W → W ⊙→ Z) ↓ ⊙ua e ]
  domain!-over-⊙equiv {Y = Y} e =
    (! (ap (λ w → f ⊙∘ w) (⊙<–-inv-l e)) ∙ ! (⊙∘-assoc f _ (⊙–> e)))
    ◃ domain-over-⊙equiv (f ⊙∘ (⊙<– e)) e

{- transporting a ptd function along a equivalence or path in the codomain -}
module _ {i} {j} {X : Ptd i} {Y : Ptd j} (f : X ⊙→ Y) where

  codomain-over-⊙path : {Z : Ptd j} (p : de⊙ Y == de⊙ Z)
    (q : coe p (pt Y) == pt Z)
    → f == (coe p , q) ⊙∘ f [ (λ W → X ⊙→ W) ↓ ptd= p (↓-idf-in p q) ]
  codomain-over-⊙path idp idp = pair= idp (! (∙-unit-r _ ∙ ap-idf (snd f)))

  codomain-over-⊙equiv : {Z : Ptd j} (e : Y ⊙≃ Z)
    → f == (⊙–> e) ⊙∘ f [ (λ W → X ⊙→ W) ↓ ⊙ua e ]
  codomain-over-⊙equiv {Z = Z} e =
    codomain-over-⊙path (ua (⊙≃-to-≃ e))
      (coe-β (⊙≃-to-≃ e) (pt Y) ∙ snd (⊙–> e))
    ▹ ap (λ w → w ⊙∘ f) (⊙λ= (coe-β (⊙≃-to-≃ e)) (↓-idf=cst-in idp))

module _ {i} {j} {X : Ptd i} {Z : Ptd j} (g : X ⊙→ Z) where

  codomain!-over-⊙path : {Y : Ptd j} (p : de⊙ Y == de⊙ Z)
    (q : coe p (pt Y) == pt Z)
    → (coe! p , ap (coe! p) (! q) ∙ coe!-inv-l p (pt Y)) ⊙∘ g == g
      [ (λ W → X ⊙→ W) ↓ ptd= p (↓-idf-in p q) ]
  codomain!-over-⊙path idp idp = pair= idp (∙-unit-r _ ∙ ap-idf (snd g))

  codomain!-over-⊙equiv : {Y : Ptd j} (e : Y ⊙≃ Z)
    → (⊙<– e) ⊙∘ g == g [ (λ W → X ⊙→ W) ↓ ⊙ua e ]
  codomain!-over-⊙equiv {Y = Y} e =
    codomain-over-⊙equiv (⊙<– e ⊙∘ g) e
    ▹ ! (⊙∘-assoc (⊙–> e) _ g) ∙ ap (λ w → w ⊙∘ g) (⊙<–-inv-r e) ∙ ⊙∘-unit-l g

{- transporting a group homomorphism along an isomorphism -}

domain-over-iso : ∀ {i j} {G H : Group i} {K : Group j}
  {φ : G →ᴳ H} {ie : is-equiv (GroupHom.f φ)} {ψ : G →ᴳ K} {χ : H →ᴳ K}
  → GroupHom.f ψ == GroupHom.f χ
    [ (λ A → A → Group.El K) ↓ ua (GroupHom.f φ , ie) ]
  → ψ == χ [ (λ J → J →ᴳ K) ↓ uaᴳ (φ , ie) ]
domain-over-iso {K = K} {φ = φ} {ie} {ψ} {χ} p =
  group-hom=-↓ $ ↓-ap-out _ Group.El _ $
    transport
      (λ q → GroupHom.f ψ == GroupHom.f χ [ (λ A → A → Group.El K) ↓ q ])
      (! (El=-β (φ , ie)))
      p

codomain-over-iso : ∀ {i j} {G : Group i} {H K : Group j}
  {φ : H →ᴳ K} {ie : is-equiv (GroupHom.f φ)} {ψ : G →ᴳ H} {χ : G →ᴳ K}
  → GroupHom.f ψ == GroupHom.f χ
    [ (λ A → Group.El G → A) ↓ ua (GroupHom.f φ , ie) ]
  → ψ == χ [ (λ J → G →ᴳ J) ↓ uaᴳ (φ , ie) ]
codomain-over-iso {G = G} {φ = φ} {ie} {ψ} {χ} p =
  group-hom=-↓ $ ↓-ap-out _ Group.El _ $
    transport
      (λ q → GroupHom.f ψ == GroupHom.f χ [ (λ A → Group.El G → A) ↓ q ])
      (! (El=-β (φ , ie)))
      p


open import Coinduction using ( ∞ ; ♯_ ; ♭ )
open import Data.Product using ( _,_ )
open import FRP.LTL.ISet.Core using ( ISet ; M⟦_⟧ ; splitM⟦_⟧ ; ⟦_⟧ ; ⌈_⌉ ; [_] )
open import FRP.LTL.ISet.Product using ( _∧_ )
open import FRP.LTL.ISet.Stateless using ( _⇒_ )
open import FRP.LTL.Time.Bound using ( Time∞ ; fin ; +∞ ; _≼_ ; _≺_ ; ≼-refl ; ≺-impl-≼ ; ≺-impl-⋡ ; _≺-trans_ ; _≼-case_ ; lt ; eq ; gt ; t≺+∞ )
open import FRP.LTL.Time.Interval using ( [_⟩ )
open import FRP.LTL.Util using ( ⊥-elim ; ≡-relevant )
open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl )

module FRP.LTL.ISet.Decoupled where

data _∙_⊷_∙_ (A : ISet) (s : Time∞) (B : ISet) (u : Time∞) : Set where
  inp : .(s ≺ u) → .(u ≺ +∞) →
    (∀ {t} .(s≺t : s ≺ t) → M⟦ A ⟧ [ s≺t ⟩ → ∞ (A ∙ t ⊷ B ∙ u)) →
      (A ∙ s ⊷ B ∙ u)
  out : ∀ {v} .(u≺v : u ≺ v) →
    M⟦ B ⟧ [ u≺v ⟩ → ∞ (A ∙ s ⊷ B ∙ v) →
      (A ∙ s ⊷ B ∙ u)
  done : .(u ≡ +∞) →
    (A ∙ s ⊷ B ∙ u)

_▹_ : ISet → ISet → ISet
A ▹ B = ⌈ (λ t → A ∙ fin t ⊷ B ∙ fin t) ⌉

_/_/_ : ∀ {A B s t u} → (A ∙ s ⊷ B ∙ u) → .(s≺t : s ≺ t) → M⟦ A ⟧ [ s≺t ⟩ → (A ∙ t ⊷ B ∙ u)
inp s≺u u≺∞ P / s≺t / σ = ♭ (P s≺t σ)
out u≺v τ P   / s≺t / σ = out u≺v τ (♯ (♭ P / s≺t / σ))
done u≡∞      / s≺t / σ = done u≡∞

mutual

  ≻-tr : ∀ {A B C s u} .(s≻u : u ≺ s) → M⟦ B ⟧ [ s≻u ⟩ →
    ((A ∧ B) ∙ u ⊷ (A ∧ C) ∙ u) → (B ∙ s ⊷ C ∙ u)
  ≻-tr s≻u σ (inp u≺u u≺∞ P) = ⊥-elim (≺-impl-⋡ u≺u ≼-refl)
  ≻-tr {A} {B} {C} {s} {u} s≻u σ (out {v} u≺v [ ρ , τ ] P)
    with s ≼-case v
  ≻-tr {A} {B} {C} s≻u σ (out u≺v [ ρ , τ ] P) | lt s≺v
    with splitM⟦ A ⟧ [ s≻u ⟩ [ s≺v ⟩ refl ρ
  ≻-tr {A} {B} {C} s≻u σ (out u≺v [ ρ , τ ] P) | lt s≺v | (ρ₁ , ρ₂) =
    out u≺v τ (♯ ≺-tr s≺v ρ₂ (♭ P / s≻u / [ ρ₁ , σ ]))
  ≻-tr {A} {B} {C} s≻u σ (out u≺v [ ρ , τ ] P) | eq s≡v
    with ≡-relevant s≡v
  ≻-tr {A} {B} {C} s≻u σ (out u≺v [ ρ , τ ] P) | eq s≡v | refl =
    out u≺v τ (♯ tr (♭ P / s≻u / [ ρ , σ ]))
  ≻-tr {A} {B} {C} s≻u σ (out u≺v [ ρ , τ ] P) | gt s≻v
    with splitM⟦ B ⟧ [ u≺v ⟩ [ s≻v ⟩ refl σ
  ≻-tr {A} {B} {C} s≻u σ (out u≺v [ ρ , τ ] P) | gt s≻v | (σ₁ , σ₂) =
    out u≺v τ (♯ ≻-tr s≻v σ₂ (♭ P / u≺v / [ ρ , σ₁ ]))
  ≻-tr s≻u σ (done u≡∞) = done u≡∞

  ≺-tr : ∀ {A B C s u} .(s≺u : s ≺ u) → M⟦ A ⟧ [ s≺u ⟩ →
    ((A ∧ B) ∙ s ⊷ (A ∧ C) ∙ u) → (B ∙ s ⊷ C ∙ u)
  ≺-tr {A} {B} {C} {s} {+∞}    s≺u ρ P = done refl
  ≺-tr {A} {B} {C} {s} {fin u} s≺u ρ P = inp s≺u t≺+∞ Q where

    Q : ∀ {t} .(s≺t : s ≺ t) → M⟦ B ⟧ [ s≺t ⟩ → ∞ (B ∙ t ⊷ C ∙ fin u)
    Q {t} s≺t σ  with t ≼-case (fin u)
    Q s≺t σ | lt t≺u with splitM⟦ _ ⟧ [ s≺t ⟩ [ t≺u ⟩ refl ρ
    Q s≺t σ | lt t≺u | (ρ₁ , ρ₂) = ♯ ≺-tr t≺u ρ₂ (P / s≺t / [ ρ₁ , σ ])
    Q s≺t σ | eq t≡u with ≡-relevant t≡u
    Q s≺t σ | eq t≡u | refl = ♯ tr (P / s≺u / [ ρ , σ ])
    Q s≺t σ | gt t≻u with splitM⟦ _ ⟧ [ s≺u ⟩ [ t≻u ⟩ refl σ
    Q s≺t σ | gt t≻u | (σ₁ , σ₂) = ♯ (≻-tr t≻u σ₂ (P / s≺u / [ ρ , σ₁ ]))

  tr : ∀ {A B C s} → ((A ∧ B) ∙ s ⊷ (A ∧ C) ∙ s) → (B ∙ s ⊷ C ∙ s)
  tr (inp s≺s s≺∞ P)       = ⊥-elim (≺-impl-⋡ s≺s ≼-refl)
  tr (out s≺u [ ρ , τ ] P) = out s≺u τ (♯ ≺-tr s≺u ρ (♭ P))
  tr (done s≡∞)            = done s≡∞

loop : ∀ {A B C} → ⟦ ((A ∧ B) ▹ (A ∧ C)) ⇒ (B ▹ C) ⟧
loop [ [ f ] ] = [ (λ t t∈i → tr (f t t∈i)) ]


module Syntax where

open import Stack public

-- Abstract symbols, or atoms.

abstract
  Atom : Set
  Atom = Nat

-- Types, or propositions in constructive logic.

infixl 9 _⩕_
infixr 7 _⇒_
data Type : Set where
  α_   : Atom → Type
  _⇒_ : Type → Type → Type
  _⩕_  : Type → Type → Type
  ⫪   : Type

-- Contexts, or stacks of types.

Context : Set
Context = Stack Type

-- Derivations, or syntactic entailment.

infix 3 _⊢_
data _⊢_ : Context → Type → Set where
  var   : ∀ {A Γ}   → A ∈ Γ → Γ ⊢ A
  lam   : ∀ {A B Γ} → Γ , A ⊢ B → Γ ⊢ A ⇒ B
  app   : ∀ {A B Γ} → Γ ⊢ A ⇒ B → Γ ⊢ A → Γ ⊢ B
  pair  : ∀ {A B Γ} → Γ ⊢ A → Γ ⊢ B → Γ ⊢ A ⩕ B
  fst   : ∀ {A B Γ} → Γ ⊢ A ⩕ B → Γ ⊢ A
  snd   : ∀ {A B Γ} → Γ ⊢ A ⩕ B → Γ ⊢ B
  unit  : ∀ {Γ}     → Γ ⊢ ⫪

-- Shorthand for variables.

v₀ : ∀ {A Γ} → Γ , A ⊢ A
v₀ = var i₀

v₁ : ∀ {A B Γ} → Γ , A , B ⊢ A
v₁ = var i₁

v₂ : ∀ {A B C Γ} → Γ , A , B , C ⊢ A
v₂ = var i₂

-- Stacks of derivations, or simultaneous syntactic entailment.

infix 3 _⊢⋆_
_⊢⋆_ : Context → Stack Type → Set
Γ ⊢⋆ ∅     = ⊤
Γ ⊢⋆ Ξ , A = Γ ⊢⋆ Ξ ∧ Γ ⊢ A

-- Monotonicity of syntactic entailment with respect to context inclusion.

mono⊢ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ A → Γ′ ⊢ A
mono⊢ p (var i)      = var (mono∈ p i)
mono⊢ p (lam d)      = lam (mono⊢ (keep p) d)
mono⊢ p (app d e)    = app (mono⊢ p d) (mono⊢ p e)
mono⊢ p (pair d e)   = pair (mono⊢ p d) (mono⊢ p e)
mono⊢ p (fst d)      = fst (mono⊢ p d)
mono⊢ p (snd d)      = snd (mono⊢ p d)
mono⊢ p unit         = unit

mono⊢⋆ : ∀ {Ξ Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢⋆ Ξ → Γ′ ⊢⋆ Ξ
mono⊢⋆ {∅}     p ∙       = ∙
mono⊢⋆ {Ξ , A} p (ζ , d) = mono⊢⋆ p ζ , mono⊢ p d

-- TODO: Naming things.

idmono⊢ : ∀ {A Γ} → (d : Γ ⊢ A) → mono⊢ refl⊆ d ≡ d
idmono⊢ (var {A} i) = cong var (idmono∈ i)
idmono⊢ (lam d)     = cong lam (idmono⊢ d)
idmono⊢ (app d e)   = cong² app (idmono⊢ d) (idmono⊢ e)
idmono⊢ (pair d e)  = cong² pair (idmono⊢ d) (idmono⊢ e)
idmono⊢ (fst d)     = cong fst (idmono⊢ d)
idmono⊢ (snd d)     = cong snd (idmono⊢ d)
idmono⊢ unit        = refl

idmono⊢⋆ : ∀ {Ξ Γ} → (ζ : Γ ⊢⋆ Ξ) → mono⊢⋆ refl⊆ ζ ≡ ζ
idmono⊢⋆ {∅}     ∙       = refl
idmono⊢⋆ {Ξ , A} (ζ , d) = cong² _,_ (idmono⊢⋆ ζ) (idmono⊢ d)

-- Reflexivity of simultaneous syntactic entailment.

refl⊢⋆ : ∀ {Γ} → Γ ⊢⋆ Γ
refl⊢⋆ {∅}     = ∙
refl⊢⋆ {Γ , A} = mono⊢⋆ weak⊆ refl⊢⋆ , v₀

-- Substitution.

[_≔_]∈_ : ∀ {A C Γ} → (i : C ∈ Γ) → Γ ∖ i ⊢ C → A ∈ Γ → Γ ∖ i ⊢ A
[ i ≔ c ]∈  j      with i ≟∈ j
[ i ≔ c ]∈ .i      | same   = c
[ i ≔ c ]∈ ._      | diff j = var j

[_≔_]_ : ∀ {A C Γ} → (i : C ∈ Γ) → Γ ∖ i ⊢ C → Γ ⊢ A → Γ ∖ i ⊢ A
[ i ≔ c ] var j    = [ i ≔ c ]∈ j
[ i ≔ c ] lam d    = lam ([ pop i ≔ mono⊢ weak⊆ c ] d)
[ i ≔ c ] app d e  = app ([ i ≔ c ] d) ([ i ≔ c ] e)
[ i ≔ c ] pair d e = pair ([ i ≔ c ] d) ([ i ≔ c ] e)
[ i ≔ c ] fst d    = fst ([ i ≔ c ] d)
[ i ≔ c ] snd d    = snd ([ i ≔ c ] d)
[ i ≔ c ] unit     = unit

-- Grafting of derivation trees, or simultaneous substitution, or cut.

graft∈ : ∀ {A Ξ Γ} → Γ ⊢⋆ Ξ → A ∈ Ξ → Γ ⊢ A
graft∈ (ζ , d) top     = d
graft∈ (ζ , d) (pop i) = graft∈ ζ i

graft⊢ : ∀ {A Ξ Γ} → Γ ⊢⋆ Ξ → Ξ ⊢ A → Γ ⊢ A
graft⊢ ζ (var i)      = graft∈ ζ i
graft⊢ ζ (lam d)      = lam (graft⊢ (mono⊢⋆ weak⊆ ζ , v₀) d)
graft⊢ ζ (app d e)    = app (graft⊢ ζ d) (graft⊢ ζ e)
graft⊢ ζ (pair d e)   = pair (graft⊢ ζ d) (graft⊢ ζ e)
graft⊢ ζ (fst d)      = fst (graft⊢ ζ d)
graft⊢ ζ (snd d)      = snd (graft⊢ ζ d)
graft⊢ ζ unit         = unit

-- Derivations, or syntactic entailment, in normal form.

mutual
  infix 3 _⊢ⁿᶠ_
  data _⊢ⁿᶠ_ : Context → Type → Set where
    neⁿᶠ   : ∀ {A Γ}   → Γ ⊢ⁿᵉ A → Γ ⊢ⁿᶠ A
    lamⁿᶠ  : ∀ {A B Γ} → Γ , A ⊢ⁿᶠ B → Γ ⊢ⁿᶠ A ⇒ B
    pairⁿᶠ : ∀ {A B Γ} → Γ ⊢ⁿᶠ A → Γ ⊢ⁿᶠ B → Γ ⊢ⁿᶠ A ⩕ B
    unitⁿᶠ : ∀ {Γ}     → Γ ⊢ⁿᶠ ⫪

  infix 3 _⊢ⁿᵉ_
  data _⊢ⁿᵉ_ : Context → Type → Set where
    varⁿᵉ  : ∀ {A Γ}   → A ∈ Γ → Γ ⊢ⁿᵉ A
    appⁿᵉ  : ∀ {A B Γ} → Γ ⊢ⁿᵉ A ⇒ B → Γ ⊢ⁿᶠ A → Γ ⊢ⁿᵉ B
    fstⁿᵉ  : ∀ {A B Γ} → Γ ⊢ⁿᵉ A ⩕ B → Γ ⊢ⁿᵉ A
    sndⁿᵉ  : ∀ {A B Γ} → Γ ⊢ⁿᵉ A ⩕ B → Γ ⊢ⁿᵉ B

-- Shorthand for variables.

v₀ⁿᵉ : ∀ {A Γ} → Γ , A ⊢ⁿᵉ A
v₀ⁿᵉ = varⁿᵉ top

-- Stacks of derivations, or simultaneous syntactic entailment, in normal form.

infix 3 _⊢⋆ⁿᵉ_
_⊢⋆ⁿᵉ_ : Context → Stack Type → Set
Γ ⊢⋆ⁿᵉ ∅     = ⊤
Γ ⊢⋆ⁿᵉ Ξ , A = Γ ⊢⋆ⁿᵉ Ξ ∧ Γ ⊢ⁿᵉ A

-- Translation from normal form to arbitrary form.

mutual
  nf→af : ∀ {A Γ} → Γ ⊢ⁿᶠ A → Γ ⊢ A
  nf→af (neⁿᶠ d)     = ne→af d
  nf→af (lamⁿᶠ d)    = lam (nf→af d)
  nf→af (pairⁿᶠ d e) = pair (nf→af d) (nf→af e)
  nf→af unitⁿᶠ       = unit

  ne→af : ∀ {A Γ} → Γ ⊢ⁿᵉ A → Γ ⊢ A
  ne→af (varⁿᵉ i)    = var i
  ne→af (appⁿᵉ d e)  = app (ne→af d) (nf→af e)
  ne→af (fstⁿᵉ d)    = fst (ne→af d)
  ne→af (sndⁿᵉ d)    = snd (ne→af d)

-- Monotonicity of syntactic entailment with respect to context inclusion, in normal form.

mutual
  mono⊢ⁿᶠ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ⁿᶠ A → Γ′ ⊢ⁿᶠ A
  mono⊢ⁿᶠ p (neⁿᶠ d)     = neⁿᶠ (mono⊢ⁿᵉ p d)
  mono⊢ⁿᶠ p (lamⁿᶠ d)    = lamⁿᶠ (mono⊢ⁿᶠ (keep p) d)
  mono⊢ⁿᶠ p (pairⁿᶠ d e) = pairⁿᶠ (mono⊢ⁿᶠ p d) (mono⊢ⁿᶠ p e)
  mono⊢ⁿᶠ p unitⁿᶠ       = unitⁿᶠ

  mono⊢ⁿᵉ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ⁿᵉ A → Γ′ ⊢ⁿᵉ A
  mono⊢ⁿᵉ p (varⁿᵉ i)    = varⁿᵉ (mono∈ p i)
  mono⊢ⁿᵉ p (appⁿᵉ d e)  = appⁿᵉ (mono⊢ⁿᵉ p d) (mono⊢ⁿᶠ p e)
  mono⊢ⁿᵉ p (fstⁿᵉ d)    = fstⁿᵉ (mono⊢ⁿᵉ p d)
  mono⊢ⁿᵉ p (sndⁿᵉ d)    = sndⁿᵉ (mono⊢ⁿᵉ p d)

mono⊢⋆ⁿᵉ : ∀ {Ξ Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢⋆ⁿᵉ Ξ → Γ′ ⊢⋆ⁿᵉ Ξ
mono⊢⋆ⁿᵉ {∅}     p ∙       = ∙
mono⊢⋆ⁿᵉ {Ξ , A} p (ζ , d) = mono⊢⋆ⁿᵉ p ζ , mono⊢ⁿᵉ p d

-- TODO: Naming things.

mutual
  idmono⊢ⁿᶠ : ∀ {A Γ} → (d : Γ ⊢ⁿᶠ A) → mono⊢ⁿᶠ refl⊆ d ≡ d
  idmono⊢ⁿᶠ (neⁿᶠ d)     = cong neⁿᶠ (idmono⊢ⁿᵉ d)
  idmono⊢ⁿᶠ (lamⁿᶠ d)    = cong lamⁿᶠ (idmono⊢ⁿᶠ d)
  idmono⊢ⁿᶠ (pairⁿᶠ d e) = cong² pairⁿᶠ (idmono⊢ⁿᶠ d) (idmono⊢ⁿᶠ e)
  idmono⊢ⁿᶠ unitⁿᶠ       = refl

  idmono⊢ⁿᵉ : ∀ {A Γ} → (d : Γ ⊢ⁿᵉ A) → mono⊢ⁿᵉ refl⊆ d ≡ d
  idmono⊢ⁿᵉ (varⁿᵉ i)    = cong varⁿᵉ (idmono∈ i)
  idmono⊢ⁿᵉ (appⁿᵉ d e)  = cong² appⁿᵉ (idmono⊢ⁿᵉ d) (idmono⊢ⁿᶠ e)
  idmono⊢ⁿᵉ (fstⁿᵉ d)    = cong fstⁿᵉ (idmono⊢ⁿᵉ d)
  idmono⊢ⁿᵉ (sndⁿᵉ d)    = cong sndⁿᵉ (idmono⊢ⁿᵉ d)

idmono⊢⋆ⁿᵉ : ∀ {Ξ Γ} → (ζ : Γ ⊢⋆ⁿᵉ Ξ) → mono⊢⋆ⁿᵉ refl⊆ ζ ≡ ζ
idmono⊢⋆ⁿᵉ {∅}     ∙       = refl
idmono⊢⋆ⁿᵉ {Ξ , A} (ζ , d) = cong² _,_ (idmono⊢⋆ⁿᵉ ζ) (idmono⊢ⁿᵉ d)

-- Reflexivity of simultaneous syntactic entailment, in normal form.

refl⊢⋆ⁿᵉ : ∀ {Γ} → Γ ⊢⋆ⁿᵉ Γ
refl⊢⋆ⁿᵉ {∅}     = ∙
refl⊢⋆ⁿᵉ {Γ , A} = mono⊢⋆ⁿᵉ weak⊆ refl⊢⋆ⁿᵉ , v₀ⁿᵉ