------------------------------------------------------------------------
-- Many properties which hold for _∼_ also hold for flip₁ _∼_
------------------------------------------------------------------------

open import Relation.Binary

module Relation.Binary.Flip where

open import Data.Function
open import Data.Product

implies : ∀ {A} (≈ ∼ : Rel A) → ≈ ⇒ ∼ → flip₁ ≈ ⇒ flip₁ ∼
implies _ _ impl = impl

reflexive : ∀ {A} (∼ : Rel A) → Reflexive ∼ → Reflexive (flip₁ ∼)
reflexive _ refl = refl

irreflexive : ∀ {A} (≈ ∼ : Rel A) →
              Irreflexive ≈ ∼ → Irreflexive (flip₁ ≈) (flip₁ ∼)
irreflexive _ _ irrefl = irrefl

symmetric : ∀ {A} (∼ : Rel A) → Symmetric ∼ → Symmetric (flip₁ ∼)
symmetric _ sym = sym

transitive : ∀ {A} (∼ : Rel A) → Transitive ∼ → Transitive (flip₁ ∼)
transitive _ trans = flip trans

antisymmetric : ∀ {A} (≈ ≤ : Rel A) →
                Antisymmetric ≈ ≤ → Antisymmetric (flip₁ ≈) (flip₁ ≤)
antisymmetric _ _ antisym = antisym

asymmetric : ∀ {A} (< : Rel A) → Asymmetric < → Asymmetric (flip₁ <)
asymmetric _ asym = asym

respects : ∀ {A} (∼ : Rel A) P →
           Symmetric ∼ → P Respects ∼ → P Respects flip₁ ∼
respects _ _ sym resp ∼ = resp (sym ∼)

respects₂ : ∀ {A} (∼₁ ∼₂ : Rel A) →
            Symmetric ∼₂ → ∼₁ Respects₂ ∼₂ → flip₁ ∼₁ Respects₂ flip₁ ∼₂
respects₂ _ _ sym (resp₁ , resp₂) =
  ((λ {_} {_} {_} ∼ → resp₂ (sym ∼)) , λ {_} {_} {_} ∼ → resp₁ (sym ∼))

decidable : ∀ {A} (∼ : Rel A) → Decidable ∼ → Decidable (flip₁ ∼)
decidable _ dec x y = dec y x

total : ∀ {A} (∼ : Rel A) → Total ∼ → Total (flip₁ ∼)
total _ tot x y = tot y x

trichotomous : ∀ {A} (≈ < : Rel A) →
               Trichotomous ≈ < → Trichotomous (flip₁ ≈) (flip₁ <)
trichotomous _ _ compare x y = compare y x

isEquivalence : ∀ {A} {≈ : Rel A} →
                IsEquivalence ≈ → IsEquivalence (flip₁ ≈)
isEquivalence {≈ = ≈} eq = record
  { refl  = reflexive  ≈ Eq.refl
  ; sym   = symmetric  ≈ Eq.sym
  ; trans = transitive ≈ Eq.trans
  }
  where module Eq = IsEquivalence eq

setoid : Setoid → Setoid
setoid S = record
  { _≈_           = flip₁ S._≈_
  ; isEquivalence = isEquivalence S.isEquivalence
  } where module S = Setoid S

isPreorder : ∀ {A} {≈ ∼ : Rel A} →
             IsPreorder ≈ ∼ → IsPreorder (flip₁ ≈) (flip₁ ∼)
isPreorder {≈ = ≈} {∼} pre = record
  { isEquivalence = isEquivalence Pre.isEquivalence
  ; reflexive     = implies ≈ ∼ Pre.reflexive
  ; trans         = transitive ∼ Pre.trans
  ; ∼-resp-≈      = respects₂ ∼ ≈ Pre.Eq.sym Pre.∼-resp-≈
  }
  where module Pre = IsPreorder pre

preorder : Preorder → Preorder
preorder P = record
  { _∼_        = flip₁ P._∼_
  ; _≈_        = flip₁ P._≈_
  ; isPreorder = isPreorder P.isPreorder
  } where module P = Preorder P

isDecEquivalence : ∀ {A} {≈ : Rel A} →
                   IsDecEquivalence ≈ → IsDecEquivalence (flip₁ ≈)
isDecEquivalence {≈ = ≈} dec = record
  { isEquivalence = isEquivalence Dec.isEquivalence
  ; _≟_           = decidable ≈ Dec._≟_
  }
  where module Dec = IsDecEquivalence dec

decSetoid : DecSetoid → DecSetoid
decSetoid S = record
  { _≈_              = flip₁ S._≈_
  ; isDecEquivalence = isDecEquivalence S.isDecEquivalence
  } where module S = DecSetoid S

isPartialOrder : ∀ {A} {≈ ≤ : Rel A} →
                 IsPartialOrder ≈ ≤ →
                 IsPartialOrder (flip₁ ≈) (flip₁ ≤)
isPartialOrder {≈ = ≈} {≤} po = record
  { isPreorder = isPreorder Po.isPreorder
  ; antisym    = antisymmetric ≈ ≤ Po.antisym
  }
  where module Po = IsPartialOrder po

poset : Poset → Poset
poset O = record
  { _≈_            = flip₁ O._≈_
  ; _≤_            = flip₁ O._≤_
  ; isPartialOrder = isPartialOrder O.isPartialOrder
  } where module O = Poset O

isStrictPartialOrder : ∀ {A} {≈ < : Rel A} →
                       IsStrictPartialOrder ≈ < →
                       IsStrictPartialOrder (flip₁ ≈) (flip₁ <)
isStrictPartialOrder {≈ = ≈} {<} spo = record
  { isEquivalence = isEquivalence Spo.isEquivalence
  ; irrefl        = irreflexive ≈ < Spo.irrefl
  ; trans         = transitive < Spo.trans
  ; <-resp-≈      = respects₂ < ≈ Spo.Eq.sym Spo.<-resp-≈
  }
  where module Spo = IsStrictPartialOrder spo

strictPartialOrder : StrictPartialOrder → StrictPartialOrder
strictPartialOrder O = record
  { _≈_                  = flip₁ O._≈_
  ; _<_                  = flip₁ O._<_
  ; isStrictPartialOrder = isStrictPartialOrder O.isStrictPartialOrder
  } where module O = StrictPartialOrder O

isTotalOrder : ∀ {A} {≈ ≤ : Rel A} →
               IsTotalOrder ≈ ≤ →
               IsTotalOrder (flip₁ ≈) (flip₁ ≤)
isTotalOrder {≈ = ≈} {≤} to = record
  { isPartialOrder = isPartialOrder To.isPartialOrder
  ; total          = total ≤ To.total
  }
  where module To = IsTotalOrder to

totalOrder : TotalOrder → TotalOrder
totalOrder O = record
  { _≈_          = flip₁ O._≈_
  ; _≤_          = flip₁ O._≤_
  ; isTotalOrder = isTotalOrder O.isTotalOrder
  } where module O = TotalOrder O

isDecTotalOrder : ∀ {A} {≈ ≤ : Rel A} →
                  IsDecTotalOrder ≈ ≤ →
                  IsDecTotalOrder (flip₁ ≈) (flip₁ ≤)
isDecTotalOrder {≈ = ≈} {≤} dec = record
  { isTotalOrder = isTotalOrder Dec.isTotalOrder
  ; _≟_          = decidable ≈ Dec._≟_
  ; _≤?_         = decidable ≤ Dec._≤?_
  }
  where module Dec = IsDecTotalOrder dec

decTotalOrder : DecTotalOrder → DecTotalOrder
decTotalOrder O = record
  { _≈_             = flip₁ O._≈_
  ; _≤_             = flip₁ O._≤_
  ; isDecTotalOrder = isDecTotalOrder O.isDecTotalOrder
  } where module O = DecTotalOrder O

isStrictTotalOrder : ∀ {A} {≈ < : Rel A} →
                     IsStrictTotalOrder ≈ < →
                       IsStrictTotalOrder (flip₁ ≈) (flip₁ <)
isStrictTotalOrder {≈ = ≈} {<} sto = record
  { isEquivalence = isEquivalence Sto.isEquivalence
  ; trans         = transitive < Sto.trans
  ; compare       = trichotomous ≈ < Sto.compare
  ; <-resp-≈      = respects₂ < ≈ Sto.Eq.sym Sto.<-resp-≈
  }
  where module Sto = IsStrictTotalOrder sto

strictTotalOrder : StrictTotalOrder → StrictTotalOrder
strictTotalOrder O = record
  { _≈_                = flip₁ O._≈_
  ; _<_                = flip₁ O._<_
  ; isStrictTotalOrder = isStrictTotalOrder O.isStrictTotalOrder
  } where module O = StrictTotalOrder O


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

open import Categories.Category using (Category; module Commutation)
open import Categories.Category.Monoidal.Core using (Monoidal)

-- The "four middle interchange" for monoidal categories.
--
-- Aka the "interchange law" or "exchange law" (though those terms are
-- more comonly used in the more general context of composition in
-- 2-categories).

module Categories.Category.Monoidal.Interchange
  {o ℓ e} {C : Category o ℓ e} where

open import Level using (_⊔_)
open import Data.Product using (_,_)

import Categories.Category.Monoidal.Construction.Product as MonoidalProduct
open import Categories.Category.Monoidal.Braided using (Braided)
open import Categories.Category.Monoidal.Braided.Properties
  using (braiding-coherence; inv-Braided; inv-braiding-coherence)
open import Categories.Category.Monoidal.Properties using (module Kelly's)
import Categories.Category.Monoidal.Reasoning as MonoidalReasoning
open import Categories.Category.Monoidal.Symmetric using (Symmetric)
import Categories.Category.Monoidal.Utilities as MonoidalUtilities
open import Categories.Category.Product using (_⁂_; assocˡ)
open import Categories.Functor using (_∘F_)
open import Categories.NaturalTransformation.NaturalIsomorphism
  using (_≃_; niHelper)
open import Categories.Morphism C using (_≅_; module ≅)
open import Categories.Morphism.IsoEquiv C using (module _≃_)
open import Categories.Morphism.Reasoning C

private module C = Category C
open C
open Commutation C

private
  variable
    W W₁ W₂ X X₁ X₂ Y Y₁ Y₂ Z Z₁ Z₂ : Obj
    f g h i : X ⇒ Y

-- An abstract definition of an interchange map with the minimal set
-- of coherence laws required to make the tensor product ⊗ of C a
-- monoidal functor.  (See also Categories.Functor.Monoidal.Tensor.)

record HasInterchange (M : Monoidal C) : Set (o ⊔ ℓ ⊔ e) where
  open Monoidal M
  open MonoidalUtilities.Shorthands M

  -- The "four middle interchange" for tensor products.

  field swapInner : ∀ {W X Y Z} → (W ⊗₀ X) ⊗₀ (Y ⊗₀ Z) ≅ (W ⊗₀ Y) ⊗₀ (X ⊗₀ Z)

  module swapInner {W X Y Z} = _≅_ (swapInner {W} {X} {Y} {Z})
  private
    i⇒ = swapInner.from
    i⇐ = swapInner.to

  -- Naturality and coherence laws of the interchange.

  field

    natural : i⇒ ∘ (f ⊗₁ g) ⊗₁ (h ⊗₁ i) ≈ (f ⊗₁ h) ⊗₁ (g ⊗₁ i) ∘ i⇒

    assoc : [ ((X₁ ⊗₀ X₂) ⊗₀ (Y₁ ⊗₀ Y₂)) ⊗₀ (Z₁ ⊗₀ Z₂) ⇒
              (X₁ ⊗₀ (Y₁ ⊗₀ Z₁)) ⊗₀ (X₂ ⊗₀ (Y₂ ⊗₀ Z₂)) ]⟨
              i⇒ ⊗₁ id   ⇒⟨ ((X₁ ⊗₀ Y₁) ⊗₀ (X₂ ⊗₀ Y₂)) ⊗₀ (Z₁ ⊗₀ Z₂) ⟩
              i⇒         ⇒⟨ ((X₁ ⊗₀ Y₁) ⊗₀ Z₁) ⊗₀ ((X₂ ⊗₀ Y₂) ⊗₀ Z₂) ⟩
              α⇒ ⊗₁ α⇒
            ≈ α⇒         ⇒⟨ (X₁ ⊗₀ X₂) ⊗₀ ((Y₁ ⊗₀ Y₂) ⊗₀ (Z₁ ⊗₀ Z₂)) ⟩
              id ⊗₁ i⇒   ⇒⟨ (X₁ ⊗₀ X₂) ⊗₀ ((Y₁ ⊗₀ Z₁) ⊗₀ (Y₂ ⊗₀ Z₂)) ⟩
              i⇒
            ⟩

    unitˡ : [ unit ⊗₀ (X ⊗₀ Y) ⇒ (X ⊗₀ Y) ]⟨
              λ⇐ ⊗₁ id   ⇒⟨ (unit ⊗₀ unit) ⊗₀ (X ⊗₀ Y) ⟩
              i⇒         ⇒⟨ (unit ⊗₀ X) ⊗₀ (unit ⊗₀ Y) ⟩
              λ⇒ ⊗₁ λ⇒
            ≈ λ⇒
            ⟩

    unitʳ : [ (X ⊗₀ Y) ⊗₀ unit ⇒ (X ⊗₀ Y) ]⟨
              id ⊗₁ λ⇐   ⇒⟨ (X ⊗₀ Y) ⊗₀ (unit ⊗₀ unit) ⟩
              i⇒         ⇒⟨ (X ⊗₀ unit) ⊗₀ (Y ⊗₀ unit) ⟩
              ρ⇒ ⊗₁ ρ⇒
            ≈ ρ⇒
            ⟩

  -- The interchange is a natural isomorphism.

  naturalIso : ⊗ ∘F (⊗ ⁂ ⊗) ≃ ⊗ ∘F MonoidalProduct.⊗ M M
  naturalIso = niHelper (record
    { η       = λ _ → i⇒
    ; η⁻¹     = λ _ → i⇐
    ; commute = λ _ → natural
    ; iso     = λ _ → swapInner.iso
    })

-- Shorthands for composing and inverting isomorphisms.

open ≅ using () renaming (refl to idᵢ; sym to _⁻¹)
private
  infixr 9 _∘ᵢ_
  _∘ᵢ_ = λ {X Y Z} f g → ≅.trans {X} {Y} {Z} g f

-- Braided monoidal categories have an interchange map.

module BraidedInterchange {M : Monoidal C} (B : Braided M) where
  open MonoidalReasoning M
  open MonoidalUtilities M
  open Braided B renaming (associator to α)
  open Shorthands

  -- Shorthands for braiding.

  private
    σ : X ⊗₀ Y ≅ Y ⊗₀ X
    σ = braiding.FX≅GX
    module σ  {X Y} = _≅_ (σ {X} {Y})
    σ⇒ = σ.from
    σ⇐ = σ.to

  -- The "four middle interchange" for braided tensor products.

  swapInner : (W ⊗₀ X) ⊗₀ (Y ⊗₀ Z) ≅ (W ⊗₀ Y) ⊗₀ (X ⊗₀ Z)
  swapInner = α ⁻¹ ∘ᵢ idᵢ ⊗ᵢ (α ∘ᵢ σ ⊗ᵢ idᵢ ∘ᵢ α ⁻¹) ∘ᵢ α

  module swapInner {W X Y Z} = _≅_ (swapInner {W} {X} {Y} {Z})
  private
    i⇒ = swapInner.from
    i⇐ = swapInner.to

  -- The interchange is a natural isomorphism.

  swapInner-natural : i⇒ ∘ (f ⊗₁ g) ⊗₁ (h ⊗₁ i) ≈ (f ⊗₁ h) ⊗₁ (g ⊗₁ i) ∘ i⇒
  swapInner-natural {f = f} {g = g} {h = h} {i = i} = begin
      (α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘ (f ⊗₁ g) ⊗₁ (h ⊗₁ i)
    ≈⟨ pullʳ (pullʳ assoc-commute-from) ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ f ⊗₁ g ⊗₁ (h ⊗₁ i) ∘ α⇒
    ≈⟨ refl⟩∘⟨ extendʳ (parallel id-comm-sym (begin
          (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ g ⊗₁ (h ⊗₁ i)
        ≈⟨ pullʳ (pullʳ assoc-commute-to) ⟩
          α⇒ ∘ σ⇒ ⊗₁ id ∘ (g ⊗₁ h) ⊗₁ i ∘ α⇐
        ≈⟨ refl⟩∘⟨ extendʳ (parallel (braiding.⇒.commute _) id-comm-sym) ⟩
          α⇒ ∘ (h ⊗₁ g) ⊗₁ i ∘ σ⇒ ⊗₁ id ∘ α⇐
        ≈⟨ extendʳ assoc-commute-from ⟩
          h ⊗₁ (g ⊗₁ i) ∘ α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐
        ∎)) ⟩
      α⇐ ∘ f ⊗₁ h ⊗₁ (g ⊗₁ i) ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ extendʳ assoc-commute-to ⟩
      (f ⊗₁ h) ⊗₁ (g ⊗₁ i) ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ∎

  swapInner-naturalIsomorphism : ⊗ ∘F (⊗ ⁂ ⊗) ≃ ⊗ ∘F MonoidalProduct.⊗ M M
  swapInner-naturalIsomorphism = niHelper (record
    { η       = λ _ → i⇒
    ; η⁻¹     = λ _ → i⇐
    ; commute = λ _ → swapInner-natural
    ; iso     = λ _ → swapInner.iso
    })

  -- Another version of the interchange that associates differently.
  --
  -- Why are there two versions and what's the difference? The domain
  -- (X₁ ⊗ X₂) ⊗₀ (Y₁ ⊗ Y₂) and codomain (X₁ ⊗ Y₁) ⊗₀ (X₁ ⊗ Y₂) of the
  -- interchange map are perfectly symmetric/balanced. But in order to
  -- apply the braiding to the middle X₂ and Y₁, we need to
  -- re-associate and that breaks the symmetry. We must first
  -- re-associate the whole expression in one direction and then the
  -- larger subterm in the other. This can be done in two ways,
  -- associate to the right first, then to the left, resulting in X₁ ⊗
  -- ((Y₂ ⊗₀ X₁) ⊗ Y₂), or vice versa, resulting in (X₁ ⊗ (Y₂ ⊗₀ X₁))
  -- ⊗ Y₂. The choice is arbitrary and results in two distinct
  -- interchange maps that behave the same way (as witnessed by
  -- swapInner-coherent below).
  --
  -- Why define both? Because the proofs of some coherence laws become
  -- easier when the core of the expression is associated in one
  -- direction vs. the other. For example swapInner-unitˡ is easier to
  -- prove for the second definition, while swapInner-unitʳ is easier
  -- to prove for the first; swapInner-assoc uses both.

  swapInner′ : (W ⊗₀ X) ⊗₀ (Y ⊗₀ Z) ≅ (W ⊗₀ Y) ⊗₀ (X ⊗₀ Z)
  swapInner′ = α ∘ᵢ (α ⁻¹ ∘ᵢ idᵢ ⊗ᵢ σ ∘ᵢ α) ⊗ᵢ idᵢ ∘ᵢ α ⁻¹

  module swapInner′ {W X Y Z} = _≅_ (swapInner′ {W} {X} {Y} {Z})
  private
    j⇒ = swapInner′.from
    j⇐ = swapInner′.to

  -- Derived coherence laws.

  swapInner-coherent : [ (X₁ ⊗₀ X₂) ⊗₀ (Y₁ ⊗₀ Y₂) ⇒ (X₁ ⊗₀ Y₁) ⊗₀ (X₂ ⊗₀ Y₂) ]⟨
                         i⇒
                       ≈ j⇒
                       ⟩
  swapInner-coherent = begin
      α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
      α⇐ ∘ id ⊗₁ α⇒ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ refl⟩∘⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
      α⇐ ∘ id ⊗₁ α⇒ ∘ id ⊗₁ (σ⇒ ⊗₁ id) ∘ id ⊗₁ α⇐ ∘ α⇒
    ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ conjugate-from α (idᵢ ⊗ᵢ α) (⟺ pentagon) ⟩
      α⇐ ∘ id ⊗₁ α⇒ ∘ id ⊗₁ (σ⇒ ⊗₁ id) ∘ (α⇒ ∘ α⇒ ⊗₁ id) ∘ α⇐
    ≈˘⟨ pullʳ (pullʳ (extendʳ (pushˡ assoc-commute-from))) ⟩
      (α⇐ ∘ id ⊗₁ α⇒ ∘ α⇒ ∘ (id ⊗₁ σ⇒) ⊗₁ id) ∘ α⇒ ⊗₁ id ∘ α⇐
    ≈⟨ pushʳ sym-assoc ⟩∘⟨refl ⟩
      ((α⇐ ∘ id ⊗₁ α⇒ ∘ α⇒) ∘ (id ⊗₁ σ⇒) ⊗₁ id) ∘ α⇒ ⊗₁ id ∘ α⇐
    ≈⟨ pushˡ (conjugate-from (α ⊗ᵢ idᵢ) α (assoc ○ pentagon)) ⟩∘⟨refl ⟩
      (α⇒ ∘ α⇐ ⊗₁ id ∘ (id ⊗₁ σ⇒) ⊗₁ id) ∘ α⇒ ⊗₁ id ∘ α⇐
    ≈˘⟨ pushʳ (pushʳ (pushˡ split₁ˡ)) ⟩
      α⇒ ∘ α⇐ ⊗₁ id ∘ (id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
    ≈˘⟨ refl⟩∘⟨ pushˡ split₁ˡ ⟩
      α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
    ∎

  swapInner-assoc : [ ((X₁ ⊗₀ X₂) ⊗₀ (Y₁ ⊗₀ Y₂)) ⊗₀ (Z₁ ⊗₀ Z₂) ⇒
                      (X₁ ⊗₀ (Y₁ ⊗₀ Z₁)) ⊗₀ (X₂ ⊗₀ (Y₂ ⊗₀ Z₂)) ]⟨
                      i⇒ ⊗₁ id    ⇒⟨ ((X₁ ⊗₀ Y₁) ⊗₀ (X₂ ⊗₀ Y₂)) ⊗₀ (Z₁ ⊗₀ Z₂) ⟩
                      i⇒          ⇒⟨ ((X₁ ⊗₀ Y₁) ⊗₀ Z₁) ⊗₀ ((X₂ ⊗₀ Y₂) ⊗₀ Z₂) ⟩
                      α⇒ ⊗₁ α⇒
                    ≈ α⇒          ⇒⟨ (X₁ ⊗₀ X₂) ⊗₀ ((Y₁ ⊗₀ Y₂) ⊗₀ (Z₁ ⊗₀ Z₂)) ⟩
                      id ⊗₁ i⇒    ⇒⟨ (X₁ ⊗₀ X₂) ⊗₀ ((Y₁ ⊗₀ Z₁) ⊗₀ (Y₂ ⊗₀ Z₂)) ⟩
                      i⇒
                    ⟩
  swapInner-assoc = begin
      α⇒ ⊗₁ α⇒ ∘ i⇒ ∘ i⇒ ⊗₁ id
    ≈⟨ (begin
        α⇒ ⊗₁ α⇒
      ≈⟨ serialize₂₁ ⟩
        id ⊗₁ α⇒ ∘ α⇒ ⊗₁ id
      ≈⟨ refl⟩∘⟨ switch-fromtoˡ α (switch-fromtoˡ (idᵢ ⊗ᵢ α) pentagon) ⟩
        id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ α⇐ ∘ α⇒ ∘ α⇒
      ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩⊗⟨ cancelˡ α.isoʳ ⟩∘⟨refl ⟩
        id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇐ ∘ α⇐) ∘ α⇒ ∘ α⇒
      ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩⊗⟨ pullʳ inv-pentagon ⟩∘⟨refl ⟩
        id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ ((α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ id ⊗₁ α⇐) ∘ α⇒ ∘ α⇒
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
        id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘
        id ⊗₁ (id ⊗₁ α⇐) ∘ α⇒ ∘ α⇒
      ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ extendʳ assoc-commute-from ⟩
        id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘
        α⇒ ∘ (id ⊗₁ id) ⊗₁ α⇐ ∘ α⇒
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ ⊗.identity ⟩⊗⟨refl ⟩∘⟨refl ⟩
        id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘ id ⊗₁ α⇐ ∘ α⇒
      ∎) ⟩∘⟨refl ⟩
      (id ⊗₁ α⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘ id ⊗₁ α⇐ ∘ α⇒) ∘
      i⇒ ∘ i⇒ ⊗₁ id
    ≈˘⟨ pullˡ (pushˡ (⊗.identity ⟩⊗⟨refl ⟩∘⟨refl)) ⟩
      ((id ⊗₁ id) ⊗₁ α⇒ ∘ α⇐) ∘
      (id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘ id ⊗₁ α⇐ ∘ α⇒) ∘
      (α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘
      i⇒ ⊗₁ id
    ≈⟨ ⟺ assoc-commute-to ⟩∘⟨
       pullʳ (pullʳ (pushʳ (pushˡ (pushʳ (pushˡ split₂ˡ))))) ⟩
      (α⇐ ∘ id ⊗₁ (id ⊗₁ α⇒)) ∘ id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘
      ((id ⊗₁ α⇐ ∘ α⇒) ∘ α⇐ ∘ id ⊗₁ α⇒) ∘
      (id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘
      (α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ⊗₁ id
    ≈⟨ pullʳ (refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ elimˡ (_≅_.isoˡ (α ⁻¹ ∘ᵢ idᵢ ⊗ᵢ α))) ⟩
      α⇐ ∘ id ⊗₁ (id ⊗₁ α⇒) ∘ id ⊗₁ (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘
      (id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘
      (α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ⊗₁ id
    ≈⟨ refl⟩∘⟨ merge₂ sym-assoc ⟩∘⟨
         pushʳ ((⟺ ⊗.identity ⟩⊗⟨refl) ⟩∘⟨refl ⟩∘⟨ split₁ˡ) ⟩
      α⇐ ∘ id ⊗₁ ((id ⊗₁ α⇒ ∘ α⇒) ∘ α⇐ ⊗₁ id ∘ α⇐) ∘
      (α⇒ ∘ (id ⊗₁ id) ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘
      α⇐ ⊗₁ id ∘ (id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ⊗₁ id
    ≈⟨ refl⟩∘⟨ refl⟩⊗⟨
         extendʳ (pushˡ (switch-fromtoʳ (α ⊗ᵢ idᵢ) (assoc ○ pentagon))) ⟩∘⟨
         extendʳ assoc-commute-from ⟩∘⟨refl ⟩
      α⇐ ∘ id ⊗₁ ((α⇒ ∘ α⇒) ∘ (α⇐ ⊗₁ id ∘ α⇐ ⊗₁ id) ∘ α⇐) ∘
      (id ⊗₁ (id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐)) ∘ (α⇒ ∘ α⇒)) ∘ α⇐ ⊗₁ id ∘
      (id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ⊗₁ id
    ≈˘⟨ refl⟩∘⟨ refl⟩⊗⟨ pushʳ (refl⟩∘⟨ split₁ˡ ⟩∘⟨refl) ⟩∘⟨
         pushʳ (extendʳ (switch-fromtoʳ (α ⊗ᵢ idᵢ) (assoc ○ pentagon))) ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐) ∘
      id ⊗₁ (id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐)) ∘ id ⊗₁ α⇒ ∘ α⇒ ∘
      (id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ⊗₁ id
    ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ split₁ˡ ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐) ∘
      id ⊗₁ (id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐)) ∘ id ⊗₁ α⇒ ∘ α⇒ ∘
      (id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐)) ⊗₁ id ∘ α⇒ ⊗₁ id
    ≈⟨ refl⟩∘⟨ merge₂ assoc²' ○ (refl⟩∘⟨ refl⟩∘⟨ assoc) ⟩∘⟨
         refl⟩∘⟨ extendʳ assoc-commute-from ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐)) ∘
      id ⊗₁ α⇒ ∘
      id ⊗₁ ((α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id) ∘ α⇒ ∘ α⇒ ⊗₁ id
    ≈⟨ refl⟩∘⟨ merge₂ (assoc²' ○ (refl⟩∘⟨ refl⟩∘⟨ assoc²')) ⟩∘⟨
         refl⟩∘⟨ switch-fromtoˡ (idᵢ ⊗ᵢ α) pentagon ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘
      id ⊗₁ ((α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id) ∘ id ⊗₁ α⇐ ∘ α⇒ ∘ α⇒
    ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
      α⇐ ∘ id ⊗₁
        (α⇒ ∘ α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘
      id ⊗₁ ((α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐) ∘ α⇒ ∘ α⇒
    ≈⟨ refl⟩∘⟨ merge₂ assoc²' ○ (refl⟩∘⟨ refl⟩∘⟨ (assoc²' ○
        (refl⟩∘⟨ refl⟩∘⟨ assoc))) ⟩∘⟨ Equiv.refl ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘
        α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘
        (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐) ∘ α⇒ ∘ α⇒
    ≈⟨ refl⟩∘⟨ refl⟩⊗⟨ pushʳ (begin
        α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘
        (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ extendʳ (pushˡ split₂ˡ) ⟩
        α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id) ∘ (id ⊗₁ α⇐ ∘ α⇒) ∘
        (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ (extendʳ assoc-commute-to ○ (refl⟩∘⟨ sym-assoc)) ⟩
        α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ (id ⊗₁ σ⇒) ⊗₁ id ∘ (α⇐ ∘ (id ⊗₁ α⇐ ∘ α⇒)) ∘
        (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
      ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ (sym-assoc ○
           (conjugate-to α (α ⊗ᵢ idᵢ) (sym-assoc ○ inv-pentagon))) ⟩∘⟨refl ⟩
        α⇒ ∘ (α⇐ ∘ α⇐) ⊗₁ id ∘ (id ⊗₁ σ⇒) ⊗₁ id ∘ (α⇒ ⊗₁ id ∘ α⇐) ∘
        (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
      ≈˘⟨ refl⟩∘⟨ split₁ Equiv.refl ⟩∘⟨
            pushʳ (refl⟩∘⟨ refl⟩⊗⟨ ⊗.identity ⟩∘⟨refl) ⟩
        α⇒ ∘ ((α⇐ ∘ α⇐) ∘ id ⊗₁ σ⇒) ⊗₁ id ∘ α⇒ ⊗₁ id ∘ α⇐ ∘
        (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ (id ⊗₁ id) ∘ α⇐
      ≈⟨ refl⟩∘⟨ merge₁ assoc² ⟩∘⟨ extendʳ assoc-commute-to ⟩
        α⇒ ∘ (α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘
        ((α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id) ⊗₁ id ∘ α⇐ ∘ α⇐
      ≈˘⟨ refl⟩∘⟨ pushˡ split₁ˡ ⟩
        α⇒ ∘ ((α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ∘ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id) ⊗₁ id ∘
        α⇐ ∘ α⇐
      ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ (sym-assoc ○ inv-pentagon) ⟩
        α⇒ ∘ ((α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ∘ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id) ⊗₁ id ∘
        α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇐
      ≈⟨ refl⟩∘⟨ pullˡ (⟺ split₁ˡ ○ (assoc ⟩⊗⟨refl)) ⟩
        α⇒ ∘
        ((α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ∘ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐) ⊗₁ id ∘
        α⇐ ∘ id ⊗₁ α⇐
      ≈⟨ refl⟩∘⟨ (begin
          (α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ∘ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
        ≈⟨ pushˡ (pushʳ sym-assoc) ⟩
          (α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒) ∘ α⇒ ∘ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
        ≈⟨ (refl⟩∘⟨ extendʳ (pushʳ split₁ˡ)) ⟩
          (α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒) ∘ (α⇒ ∘ α⇒ ⊗₁ id) ∘ (σ⇒ ⊗₁ id ∘ α⇐) ⊗₁ id ∘ α⇐
        ≈⟨ pushʳ (switch-fromtoˡ (idᵢ ⊗ᵢ α) pentagon ⟩∘⟨ pushˡ split₁ˡ) ⟩
          ((α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒) ∘ (id ⊗₁ α⇐ ∘ α⇒ ∘ α⇒)) ∘
          (σ⇒ ⊗₁ id) ⊗₁ id ∘ α⇐ ⊗₁ id ∘ α⇐
        ≈⟨ pushˡ (sym-assoc ○ (pullˡ (pullʳ assoc ⟩∘⟨refl))) ⟩
          ((α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ id ⊗₁ α⇐) ∘ α⇒) ∘
          α⇒ ∘ (σ⇒ ⊗₁ id) ⊗₁ id ∘ α⇐ ⊗₁ id ∘ α⇐
        ≈⟨ pullʳ (refl⟩∘⟨ extendʳ assoc-commute-from) ⟩
          (α⇐ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ id ⊗₁ α⇐) ∘ α⇒ ∘
          σ⇒ ⊗₁ (id ⊗₁ id) ∘ α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐
        ≈⟨ (refl⟩∘⟨ refl⟩∘⟨ ⟺ split₂ˡ) ⟩∘⟨ refl⟩∘⟨ (refl⟩⊗⟨ ⊗.identity ⟩∘⟨
             conjugate-from (idᵢ ⊗ᵢ (α ⁻¹)) (α ⁻¹) inv-pentagon) ⟩
          (α⇐ ∘ α⇐ ∘ id ⊗₁ (σ⇒ ∘ α⇐)) ∘ α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈˘⟨ extendʳ (sym-assoc ○ inv-pentagon) ⟩∘⟨refl ⟩
          (α⇐ ⊗₁ id ∘ (α⇐ ∘ id ⊗₁ α⇐) ∘ id ⊗₁ (σ⇒ ∘ α⇐)) ∘
          α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈˘⟨ (refl⟩∘⟨ pushʳ split₂ˡ) ⟩∘⟨refl ⟩
          (α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (α⇐ ∘ σ⇒ ∘ α⇐)) ∘
          α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈˘⟨ pushˡ ((refl⟩∘⟨ refl⟩∘⟨ refl⟩⊗⟨
              (sym-assoc ○ hexagon₂ ○ assoc)) ⟩∘⟨refl) ⟩
          ((α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ σ⇒)) ∘ α⇒) ∘
          σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ pushˡ (pushʳ (pushʳ split₂ˡ)) ⟩∘⟨refl ⟩
          ((α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ σ⇒ ⊗₁ id) ∘ id ⊗₁ (α⇐ ∘ id ⊗₁ σ⇒) ∘ α⇒) ∘
          σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ pushʳ (pushˡ split₂ˡ) ⟩∘⟨refl ⟩
          (((α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ σ⇒ ⊗₁ id) ∘ id ⊗₁ α⇐) ∘
            id ⊗₁ id ⊗₁ σ⇒ ∘ α⇒) ∘ σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ ((pushˡ (pushʳ assoc-commute-to) ⟩∘⟨
             ⟺ assoc-commute-from) ○ (pullʳ sym-assoc)) ⟩∘⟨refl ⟩
          ((α⇐ ⊗₁ id ∘ (id ⊗₁ σ⇒) ⊗₁ id) ∘ ((α⇐ ∘ id ⊗₁ α⇐) ∘ α⇒) ∘
            (id ⊗₁ id) ⊗₁ σ⇒) ∘ σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ pullˡ (⟺ split₁ˡ ⟩∘⟨
             conjugate-from α (idᵢ ⊗ᵢ α ∘ᵢ α) (⟺ (assoc ○ pentagon)))
             ⟩∘⟨refl ⟩
          (((α⇐ ∘ id ⊗₁ σ⇒) ⊗₁ id ∘ α⇒ ⊗₁ id ∘ α⇐) ∘ (id ⊗₁ id) ⊗₁ σ⇒) ∘
          σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ (pullˡ (⟺ split₁ˡ) ⟩∘⟨ ⊗.identity ⟩⊗⟨refl) ⟩∘⟨refl ⟩
          ((((α⇐ ∘ id ⊗₁ σ⇒) ∘ α⇒) ⊗₁ id ∘ α⇐) ∘ id ⊗₁ σ⇒) ∘
          σ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ extend² (⟺ serialize₂₁ ○ serialize₁₂) ⟩
          ((((α⇐ ∘ id ⊗₁ σ⇒) ∘ α⇒) ⊗₁ id ∘ α⇐) ∘ σ⇒ ⊗₁ id) ∘
          id ⊗₁ σ⇒ ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈˘⟨ pushʳ (refl⟩∘⟨ refl⟩⊗⟨ ⊗.identity) ⟩∘⟨ ⊗.identity ⟩⊗⟨refl ⟩∘⟨refl ⟩
          (((α⇐ ∘ id ⊗₁ σ⇒) ∘ α⇒) ⊗₁ id ∘ α⇐ ∘ σ⇒ ⊗₁ id ⊗₁ id) ∘
          (id ⊗₁ id) ⊗₁ σ⇒ ∘ α⇐ ∘ id ⊗₁ α⇒
        ≈⟨ pushʳ assoc-commute-to ⟩∘⟨ extendʳ (⟺ assoc-commute-to) ⟩
          ((((α⇐ ∘ id ⊗₁ σ⇒) ∘ α⇒) ⊗₁ id ∘ (σ⇒ ⊗₁ id) ⊗₁ id) ∘ α⇐) ∘
          α⇐ ∘ id ⊗₁ id ⊗₁ σ⇒ ∘ id ⊗₁ α⇒
        ≈˘⟨ ((sym-assoc ○ sym-assoc) ⟩⊗⟨refl ○ split₁ˡ) ⟩∘⟨refl ⟩∘⟨
            refl⟩∘⟨ split₂ˡ ⟩
          (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ⊗₁ id) ∘ α⇐) ∘
          α⇐ ∘ id ⊗₁ (id ⊗₁ σ⇒ ∘ α⇒)
        ≈˘⟨ extend² (sym-assoc ○ inv-pentagon) ⟩
          (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ⊗₁ id) ∘ α⇐ ⊗₁ id) ∘
          (α⇐ ∘ id ⊗₁ α⇐) ∘ id ⊗₁ (id ⊗₁ σ⇒ ∘ α⇒)
        ≈˘⟨ split₁ˡ ⟩∘⟨ pushʳ split₂ˡ ⟩
          ((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ id ∘
          α⇐ ∘ id ⊗₁ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒)
        ∎) ⟩⊗⟨refl ⟩∘⟨refl ⟩
        α⇒ ∘ (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ id ∘ α⇐ ∘
          id ⊗₁ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒)) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇐
      ≈⟨ (refl⟩∘⟨ pushˡ ((sym-assoc ⟩⊗⟨refl) ○ split₁ˡ)) ⟩
        α⇒ ∘ (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ id ∘ α⇐) ⊗₁ id ∘
        (id ⊗₁ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒)) ⊗₁ id ∘ α⇐ ∘ id ⊗₁ α⇐
      ≈⟨ (refl⟩∘⟨ split₁ˡ ⟩∘⟨ extendʳ (⟺ assoc-commute-to)) ○ pushʳ assoc ⟩
        (α⇒ ∘ (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ id) ⊗₁ id) ∘
        α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ id ⊗₁ α⇐
      ≈⟨ pushˡ assoc-commute-from ⟩
        ((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ (id ⊗₁ id) ∘ α⇒ ∘
        α⇐ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ id ⊗₁ α⇐
      ≈˘⟨ refl⟩∘⟨ pullʳ (pullʳ (refl⟩∘⟨ split₂ˡ)) ⟩
        ((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ (id ⊗₁ id) ∘
        (α⇒ ∘ α⇐ ⊗₁ id ∘ α⇐) ∘ id ⊗₁ ((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐)
      ≈˘⟨ pullʳ (pullˡ (conjugate-from (α ∘ᵢ α ⊗ᵢ idᵢ) α pentagon)) ⟩
        (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ (id ⊗₁ id) ∘ α⇐) ∘
        id ⊗₁ α⇒ ∘ id ⊗₁ ((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐)
      ≈⟨ refl⟩⊗⟨ ⊗.identity ⟩∘⟨refl ⟩∘⟨ ⟺ split₂ˡ ⟩
        (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ id ∘ α⇐) ∘
        id ⊗₁ (α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐)
      ≡⟨⟩
        (((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐) ⊗₁ id ∘ α⇐) ∘ id ⊗₁ j⇒
      ≈˘⟨ switch-fromtoʳ α (switch-fromtoˡ α (⟺ hexagon₁))
            ⟩⊗⟨refl ⟩∘⟨refl ⟩∘⟨refl ⟩
        (σ⇒ ⊗₁ id ∘ α⇐) ∘ id ⊗₁ j⇒
      ∎) ⟩∘⟨refl ⟩
      α⇐ ∘ id ⊗₁ ((α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ id ⊗₁ j⇒) ∘ α⇒ ∘ α⇒
    ≈⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ id ⊗₁ id ⊗₁ j⇒ ∘ α⇒ ∘ α⇒
    ≈˘⟨ pullʳ (pullʳ (extendʳ assoc-commute-from)) ⟩
      (α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘ (id ⊗₁ id) ⊗₁ j⇒ ∘ α⇒
    ≡⟨⟩
      i⇒ ∘ (id ⊗₁ id) ⊗₁ j⇒ ∘ α⇒
    ≈⟨ refl⟩∘⟨ ⊗.identity ⟩⊗⟨ ⟺ swapInner-coherent ⟩∘⟨refl ⟩
      i⇒ ∘ id ⊗₁ i⇒ ∘ α⇒
    ∎
    where inv-pentagon = λ {W X Y Z} → _≃_.to-≈ (pentagon-iso {W} {X} {Y} {Z})

  swapInner-unitˡ : [ unit ⊗₀ (X ⊗₀ Y) ⇒ (X ⊗₀ Y) ]⟨
                      λ⇐ ⊗₁ id         ⇒⟨ (unit ⊗₀ unit) ⊗₀ (X ⊗₀ Y) ⟩
                      i⇒               ⇒⟨ (unit ⊗₀ X) ⊗₀ (unit ⊗₀ Y) ⟩
                      λ⇒ ⊗₁ λ⇒
                    ≈ λ⇒
                    ⟩
  swapInner-unitˡ = begin
      λ⇒ ⊗₁ λ⇒ ∘ i⇒ ∘ λ⇐ ⊗₁ id
    ≈⟨ refl⟩∘⟨ swapInner-coherent ⟩∘⟨ (Kelly₃′ ⟩⊗⟨refl) ⟩
      λ⇒ ⊗₁ λ⇒ ∘ j⇒ ∘ ρ⇐ ⊗₁ id
    ≡⟨⟩
      λ⇒ ⊗₁ λ⇒ ∘ (α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐) ∘ ρ⇐ ⊗₁ id
    ≈⟨ pullˡ (pushˡ serialize₁₂) ⟩
      (λ⇒ ⊗₁ id ∘ id ⊗₁ λ⇒ ∘ α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐) ∘ ρ⇐ ⊗₁ id
    ≈⟨ (refl⟩∘⟨ (begin
        id ⊗₁ λ⇒ ∘ α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
      ≈⟨ pullˡ triangle ⟩
        ρ⇒ ⊗₁ id ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
      ≈˘⟨ pushˡ split₁ˡ ⟩
        (ρ⇒ ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
      ≈⟨ (pullˡ Kelly₂′ ⟩⊗⟨refl ⟩∘⟨refl) ⟩
        (id ⊗₁ ρ⇒ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
      ≈˘⟨ pushˡ split₂ˡ ⟩⊗⟨refl ⟩∘⟨refl ⟩
        (id ⊗₁ (ρ⇒ ∘ σ⇒) ∘ α⇒) ⊗₁ id ∘ α⇐
      ≈⟨ (refl⟩⊗⟨ σ⁻¹-coherence ⟩∘⟨refl) ⟩⊗⟨refl ⟩∘⟨refl ⟩
        (id ⊗₁ λ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐
      ≈⟨ triangle ⟩⊗⟨refl ⟩∘⟨refl ⟩
        (ρ⇒ ⊗₁ id) ⊗₁ id ∘ α⇐
      ≈˘⟨ assoc-commute-to ⟩
        α⇐ ∘ ρ⇒ ⊗₁ (id ⊗₁ id)
      ∎)) ⟩∘⟨refl ⟩
      (λ⇒ ⊗₁ id ∘ α⇐ ∘ ρ⇒ ⊗₁ (id ⊗₁ id)) ∘ ρ⇐ ⊗₁ id
    ≈⟨ (sym-assoc ○ (Kelly₁′ ⟩∘⟨ refl⟩⊗⟨ ⊗.identity)) ⟩∘⟨refl ⟩
      (λ⇒ ∘ ρ⇒ ⊗₁ id) ∘ ρ⇐ ⊗₁ id
    ≈⟨ cancelʳ (_≅_.isoʳ (unitorʳ ⊗ᵢ idᵢ)) ⟩
      λ⇒
    ∎
    where
      Kelly₁′       = ⟺ (switch-fromtoʳ α (Kelly's.coherence₁ M))
      Kelly₂′       = ⟺ (switch-fromtoʳ α (Kelly's.coherence₂ M))
      Kelly₃′       = _≃_.to-≈ (Kelly's.coherence-iso₃ M)
      σ⁻¹-coherence = inv-braiding-coherence (inv-Braided B)

  swapInner-unitʳ : [ (X ⊗₀ Y) ⊗₀ unit ⇒ (X ⊗₀ Y) ]⟨
                      id ⊗₁ λ⇐         ⇒⟨ (X ⊗₀ Y) ⊗₀ (unit ⊗₀ unit) ⟩
                      i⇒               ⇒⟨ (X ⊗₀ unit) ⊗₀ (Y ⊗₀ unit) ⟩
                      ρ⇒ ⊗₁ ρ⇒
                    ≈ ρ⇒
                    ⟩
  swapInner-unitʳ = begin
      ρ⇒ ⊗₁ ρ⇒ ∘ i⇒ ∘ id ⊗₁ λ⇐
    ≡⟨⟩
      ρ⇒ ⊗₁ ρ⇒ ∘ (α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘ id ⊗₁ λ⇐
    ≈⟨ pullˡ (pushˡ serialize₂₁) ⟩
      (id ⊗₁ ρ⇒ ∘ ρ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒) ∘ id ⊗₁ λ⇐
    ≈⟨ (refl⟩∘⟨ (begin
        ρ⇒ ⊗₁ id ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
      ≈˘⟨ pushˡ (switch-fromtoʳ α triangle) ⟩
        id ⊗₁ λ⇒ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
      ≈˘⟨ pushˡ split₂ˡ ⟩
        id ⊗₁ (λ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
      ≈⟨ refl⟩⊗⟨ pullˡ (Kelly's.coherence₁ M) ⟩∘⟨refl ⟩
        id ⊗₁ (λ⇒ ⊗₁ id ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
      ≈˘⟨ refl⟩⊗⟨ pushˡ split₁ˡ ⟩∘⟨refl ⟩
        id ⊗₁ ((λ⇒ ∘ σ⇒) ⊗₁ id ∘ α⇐) ∘ α⇒
      ≈⟨ refl⟩⊗⟨ (braiding-coherence B ⟩⊗⟨refl ⟩∘⟨refl) ⟩∘⟨refl ⟩
        id ⊗₁ (ρ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
      ≈˘⟨ refl⟩⊗⟨ switch-fromtoʳ α triangle ⟩∘⟨refl ⟩
        id ⊗₁ (id ⊗₁ λ⇒) ∘ α⇒
      ≈˘⟨ assoc-commute-from ⟩
        α⇒ ∘ (id ⊗₁ id) ⊗₁ λ⇒
      ∎)) ⟩∘⟨refl ⟩
      (id ⊗₁ ρ⇒ ∘ α⇒ ∘ (id ⊗₁ id) ⊗₁ λ⇒) ∘ id ⊗₁ λ⇐
    ≈⟨ (sym-assoc ○ (Kelly's.coherence₂ M ⟩∘⟨ ⊗.identity ⟩⊗⟨refl)) ⟩∘⟨refl ⟩
      (ρ⇒ ∘ id ⊗₁ λ⇒) ∘ id ⊗₁ λ⇐
    ≈⟨ cancelʳ (_≅_.isoʳ (idᵢ ⊗ᵢ unitorˡ)) ⟩
      ρ⇒
    ∎

  swapInner-braiding : [ (W ⊗₀ X) ⊗₀ (Y ⊗₀ Z) ⇒ (Y ⊗₀ Z) ⊗₀ (W ⊗₀ X) ]⟨
                         i⇒         ⇒⟨ (W ⊗₀ Y) ⊗₀ (X ⊗₀ Z) ⟩
                         σ⇒ ⊗₁ σ⇒   ⇒⟨ (Y ⊗₀ W) ⊗₀ (Z ⊗₀ X) ⟩
                         i⇒
                       ≈ σ⇒
                       ⟩
  swapInner-braiding = begin
      i⇒ ∘ σ⇒ ⊗₁ σ⇒ ∘ i⇒
    ≡⟨⟩
      i⇒ ∘ σ⇒ ⊗₁ σ⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ swapInner-coherent ⟩∘⟨ pushˡ serialize₁₂ ⟩
      j⇒ ∘ σ⇒ ⊗₁ id ∘ id ⊗₁ σ⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈˘⟨ ((refl⟩∘⟨ refl⟩⊗⟨ ⊗.identity) ⟩∘⟨ ⊗.identity ⟩⊗⟨refl ⟩∘⟨refl) ○ assoc ⟩
      ((α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐) ∘ σ⇒ ⊗₁ (id ⊗₁ id)) ∘
      (id ⊗₁ id) ⊗₁ σ⇒ ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ pullʳ (pullʳ assoc-commute-to) ⟩∘⟨ extendʳ (⟺ assoc-commute-to) ⟩
      (α⇒ ∘ (α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ⊗₁ id ∘ (σ⇒ ⊗₁ id) ⊗₁ id ∘ α⇐) ∘
      α⇐ ∘ id ⊗₁ (id ⊗₁ σ⇒) ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈˘⟨ (refl⟩∘⟨ pushˡ split₁ˡ) ⟩∘⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
      (α⇒ ∘ ((α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒) ∘ σ⇒ ⊗₁ id) ⊗₁ id ∘ α⇐) ∘
      α⇐ ∘ id ⊗₁ (id ⊗₁ σ⇒ ∘ α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ (refl⟩∘⟨ pullʳ (assoc ○ hexagon₁) ⟩⊗⟨refl ⟩∘⟨refl) ⟩∘⟨
        refl⟩∘⟨ refl⟩⊗⟨ (⟺ assoc ○ (pullˡ (assoc ○ hexagon₁))) ⟩∘⟨refl ⟩
      (α⇒ ∘ (α⇐ ∘ α⇒ ∘ σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐) ∘
      α⇐ ∘ id ⊗₁ ((α⇒ ∘ σ⇒ ∘ α⇒) ∘ α⇐) ∘ α⇒
    ≈⟨ (refl⟩∘⟨ cancelˡ α.isoˡ ⟩⊗⟨refl ⟩∘⟨refl) ⟩∘⟨
        refl⟩∘⟨ refl⟩⊗⟨ pullʳ (cancelʳ α.isoʳ) ⟩∘⟨refl ⟩
      (α⇒ ∘ (σ⇒ ∘ α⇒) ⊗₁ id ∘ α⇐) ∘ α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒) ∘ α⇒
    ≈⟨ extendʳ (pushʳ split₁ˡ) ⟩∘⟨ extendʳ (pushʳ split₂ˡ) ⟩
      ((α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇒ ⊗₁ id ∘ α⇐) ∘ (α⇐ ∘ id ⊗₁ α⇒) ∘ id ⊗₁ σ⇒ ∘ α⇒
    ≈˘⟨ extendʳ (pushʳ (pushʳ assoc)) ⟩
      (α⇒ ∘ σ⇒ ⊗₁ id) ∘ (α⇒ ⊗₁ id ∘ (α⇐ ∘ α⇐) ∘ id ⊗₁ α⇒) ∘ id ⊗₁ σ⇒ ∘ α⇒
    ≈˘⟨ refl⟩∘⟨ switch-tofromˡ (α ⊗ᵢ idᵢ)
                  (switch-tofromʳ (idᵢ ⊗ᵢ α) inv-pentagon) ⟩∘⟨refl ⟩
      (α⇒ ∘ σ⇒ ⊗₁ id) ∘ α⇐ ∘ id ⊗₁ σ⇒ ∘ α⇒
    ≈⟨ pullʳ (sym-assoc ○ pullˡ hexagon₂) ⟩
      α⇒ ∘ ((α⇐ ∘ σ⇒) ∘ α⇐) ∘ α⇒
    ≈⟨ (refl⟩∘⟨ cancelʳ α.isoˡ) ⟩
      α⇒ ∘ (α⇐ ∘ σ⇒)
    ≈⟨ cancelˡ α.isoʳ ⟩
      σ⇒
    ∎
    where inv-pentagon = _≃_.to-≈ pentagon-iso

  -- Braided monoidal categories have an interchange.

  hasInterchange : HasInterchange M
  hasInterchange = record
    { swapInner  = swapInner
    ; natural    = swapInner-natural
    ; assoc      = swapInner-assoc
    ; unitˡ      = swapInner-unitˡ
    ; unitʳ      = swapInner-unitʳ
    }
  open HasInterchange hasInterchange public using (naturalIso)

-- Extra identities that hold only for symmetric monoidal categories.

module SymmetricInterchange {M : Monoidal C} (S : Symmetric M) where
  open MonoidalReasoning M
  open MonoidalUtilities M
  open Symmetric S renaming (associator to α)
  open Shorthands
  open BraidedInterchange braided public
  private
    i⇒ = swapInner.from
    i⇐ = swapInner.to
    σ⇒ = λ {X Y} → braiding.⇒.η (X , Y)
    σ⇐ = λ {X Y} → braiding.⇐.η (X , Y)

  swapInner-commutative : [ (X₁ ⊗₀ X₂) ⊗₀ (Y₁ ⊗₀ Y₂) ⇒
                            (X₁ ⊗₀ X₂) ⊗₀ (Y₁ ⊗₀ Y₂) ]⟨
                             i⇒    ⇒⟨ (X₁ ⊗₀ Y₁) ⊗₀ (X₂ ⊗₀ Y₂) ⟩
                             i⇒
                           ≈ id
                           ⟩
  swapInner-commutative = begin
      i⇒ ∘ i⇒
    ≈⟨ pullʳ (cancelInner α.isoʳ) ⟩
      α⇐ ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ id ⊗₁ (α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈˘⟨ refl⟩∘⟨ pushˡ split₂ˡ ⟩
      α⇐ ∘ id ⊗₁ ((α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒ ∘ σ⇒ ⊗₁ id ∘ α⇐) ∘ α⇒
    ≈⟨ refl⟩∘⟨ refl⟩⊗⟨ (∘-resp-≈ʳ sym-assoc ○ α[σ⊗1]α⁻¹.isoʳ) ⟩∘⟨refl ⟩
      α⇐ ∘ id ⊗₁ id ∘ α⇒
    ≈⟨ refl⟩∘⟨ ⊗.identity ⟩∘⟨refl ⟩
      α⇐ ∘ id ∘ α⇒
    ≈⟨ ∘-resp-≈ʳ identityˡ ○ α.isoˡ ⟩
      id
    ∎
    where module α[σ⊗1]α⁻¹ = _≅_ (α ∘ᵢ braided-iso ⊗ᵢ idᵢ ∘ᵢ α ⁻¹)

  swapInner-iso : (W ⊗₀ X) ⊗₀ (Y ⊗₀ Z) ≅ (W ⊗₀ Y) ⊗₀ (X ⊗₀ Z)
  swapInner-iso = record
    { from = i⇒
    ; to   = i⇒
    ; iso  = record
      { isoˡ = swapInner-commutative
      ; isoʳ = swapInner-commutative
      }
    }

  swapInner-braiding′ : [ (W ⊗₀ X) ⊗₀ (Y ⊗₀ Z) ⇒ (Y ⊗₀ W) ⊗₀ (Z ⊗₀ X) ]⟨
                          i⇒         ⇒⟨ (W ⊗₀ Y) ⊗₀ (X ⊗₀ Z) ⟩
                          σ⇒ ⊗₁ σ⇒
                        ≈ σ⇒         ⇒⟨ (Y ⊗₀ Z) ⊗₀ (W ⊗₀ X) ⟩
                          i⇒
                        ⟩
  swapInner-braiding′ = switch-fromtoˡ swapInner-iso swapInner-braiding


{-# OPTIONS --cubical --no-import-sorts #-}

module Number.Prettyprint where

open import Cubical.Data.Sigma.Base
open import Number.Postulates
open import Number.Base

[ℕ]   = Number (isNat     , anyPositivityᵒʳ)
[ℕ⁺⁻] = Number (isNat     , isNonzeroᵒʳ    )
[ℕ₀⁺] = Number (isNat     , isNonnegativeᵒʳ)
[ℕ⁺]  = Number (isNat     , isPositiveᵒʳ   )
[ℕ₀⁻] = Number (isNat     , isNonpositiveᵒʳ)
[ℤ]   = Number (isInt     , anyPositivityᵒʳ)
[ℤ⁺⁻] = Number (isInt     , isNonzeroᵒʳ    )
[ℤ₀⁺] = Number (isInt     , isNonnegativeᵒʳ)
[ℤ⁺]  = Number (isInt     , isPositiveᵒʳ   )
[ℤ⁻]  = Number (isInt     , isNegativeᵒʳ   )
[ℤ₀⁻] = Number (isInt     , isNonpositiveᵒʳ)
[ℚ]   = Number (isRat     , anyPositivityᵒʳ)
[ℚ⁺⁻] = Number (isRat     , isNonzeroᵒʳ    )
[ℚ₀⁺] = Number (isRat     , isNonnegativeᵒʳ)
[ℚ⁺]  = Number (isRat     , isPositiveᵒʳ   )
[ℚ⁻]  = Number (isRat     , isNegativeᵒʳ   )
[ℚ₀⁻] = Number (isRat     , isNonpositiveᵒʳ)
[ℝ]   = Number (isReal    , anyPositivityᵒʳ)
[ℝ⁺⁻] = Number (isReal    , isNonzeroᵒʳ    )
[ℝ₀⁺] = Number (isReal    , isNonnegativeᵒʳ)
[ℝ⁺]  = Number (isReal    , isPositiveᵒʳ   )
[ℝ⁻]  = Number (isReal    , isNegativeᵒʳ   )
[ℝ₀⁻] = Number (isReal    , isNonpositiveᵒʳ)
[ℂ]   = Number (isComplex , anyPositivityᶠ )
[ℂ⁺⁻] = Number (isComplex , isNonzeroᶠ     )

{-# DISPLAY Number (isNat     , anyPositivityᵒʳ) = [ℕ]   #-}
{-# DISPLAY Number (isNat     , isNonzeroᵒʳ    ) = [ℕ⁺⁻] #-}
{-# DISPLAY Number (isNat     , isNonnegativeᵒʳ) = [ℕ₀⁺] #-}
{-# DISPLAY Number (isNat     , isPositiveᵒʳ   ) = [ℕ⁺]  #-}
{-# DISPLAY Number (isNat     , isNonpositiveᵒʳ) = [ℕ₀⁻] #-}
{-# DISPLAY Number (isInt     , anyPositivityᵒʳ) = [ℤ]   #-}
{-# DISPLAY Number (isInt     , isNonzeroᵒʳ    ) = [ℤ⁺⁻] #-}
{-# DISPLAY Number (isInt     , isNonnegativeᵒʳ) = [ℤ₀⁺] #-}
{-# DISPLAY Number (isInt     , isPositiveᵒʳ   ) = [ℤ⁺]  #-}
{-# DISPLAY Number (isInt     , isNegativeᵒʳ   ) = [ℤ⁻]  #-}
{-# DISPLAY Number (isInt     , isNonpositiveᵒʳ) = [ℤ₀⁻] #-}
{-# DISPLAY Number (isRat     , anyPositivityᵒʳ) = [ℚ]   #-}
{-# DISPLAY Number (isRat     , isNonzeroᵒʳ    ) = [ℚ⁺⁻] #-}
{-# DISPLAY Number (isRat     , isNonnegativeᵒʳ) = [ℚ₀⁺] #-}
{-# DISPLAY Number (isRat     , isPositiveᵒʳ   ) = [ℚ⁺]  #-}
{-# DISPLAY Number (isRat     , isNegativeᵒʳ   ) = [ℚ⁻]  #-}
{-# DISPLAY Number (isRat     , isNonpositiveᵒʳ) = [ℚ₀⁻] #-}
{-# DISPLAY Number (isReal    , anyPositivityᵒʳ) = [ℝ]   #-}
{-# DISPLAY Number (isReal    , isNonzeroᵒʳ    ) = [ℝ⁺⁻] #-}
{-# DISPLAY Number (isReal    , isNonnegativeᵒʳ) = [ℝ₀⁺] #-}
{-# DISPLAY Number (isReal    , isPositiveᵒʳ   ) = [ℝ⁺]  #-}
{-# DISPLAY Number (isReal    , isNegativeᵒʳ   ) = [ℝ⁻]  #-}
{-# DISPLAY Number (isReal    , isNonpositiveᵒʳ) = [ℝ₀⁻] #-}
{-# DISPLAY Number (isComplex , anyPositivityᶠ ) = [ℂ]   #-}
{-# DISPLAY Number (isComplex , isNonzeroᶠ     ) = [ℂ⁺⁻] #-}


{-# OPTIONS --cubical #-}

open import Agda.Primitive.Cubical
open import Agda.Builtin.Cubical.Path
open import Agda.Builtin.Cubical.Id

reflPath : ∀ {ℓ} {A : Set ℓ} {x : A} → x ≡ x
reflPath {x = x} = λ i → x

reflId : ∀ {ℓ} {A : Set ℓ} {x : A} → Id x x
reflId {x = x} = conid i1 reflPath

Id-comp-Id : ∀ {ℓ ℓ'} {A : Set ℓ} {x : A} (P : ∀ y → Id x y → Set ℓ')
           → (b : P x reflId) → Id (primIdJ P b reflId) b
Id-comp-Id P b = reflId

Id-comp-Path : ∀ {ℓ ℓ'} {A : Set ℓ} {x : A} (P : ∀ y → Id x y → Set ℓ')
             → (b : P x reflId) → (primIdJ P b reflId) ≡ b
Id-comp-Path P b = λ i → b


{- defining Nat as a kind of List -}
-- the need for ‼ is unfortunate
module Oscar.Data2 where

record ⊤ : Set where
  constructor tt

-- List
data ⟦_⟧ {a} (A : Set a) : Set a where
  ∅ : ⟦ A ⟧
  _∷_ : A → ⟦ A ⟧ → ⟦ A ⟧

-- Nat
⟦⟧ = ⟦ ⊤ ⟧
pattern ‼ ⟦A⟧ = tt ∷ ⟦A⟧

-- Fin
data ⟦⟧[_] : ⟦⟧ → Set where
  ∅ : ∀ {n} → ⟦⟧[ ‼ n ]
  ! : ∀ {n} → ⟦⟧[ n ] → ⟦⟧[ ‼ n ]

test : ⟦⟧[ ‼ (‼ ∅) ]
test = ! ∅


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties satisfied by decidable total orders
------------------------------------------------------------------------

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

open import Relation.Binary

module Relation.Binary.Properties.DecTotalOrder
         {d₁ d₂ d₃} (DT : DecTotalOrder d₁ d₂ d₃) where

open Relation.Binary.DecTotalOrder DT hiding (trans)
open import Relation.Binary.Construct.NonStrictToStrict _≈_ _≤_

strictTotalOrder : StrictTotalOrder _ _ _
strictTotalOrder = record
  { isStrictTotalOrder = <-isStrictTotalOrder₂ isDecTotalOrder
  }

open StrictTotalOrder strictTotalOrder public


module JamesChapman where

infixr 50 _⟶_

data Ty : Set where
   ι : Ty
   _⟶_ : Ty -> Ty -> Ty

data Tm : Ty -> Set where
   _$_ : {σ τ : Ty} -> Tm (σ ⟶ τ) -> Tm σ -> Tm τ

data Nf : Ty -> Set where

data _↓_ : {σ : Ty} -> Tm σ -> Nf σ -> Set where
   r$ : {σ τ : Ty} -> {t : Tm (σ ⟶ τ)} -> {f : Nf (σ ⟶ τ)} -> t ↓ f ->
     {u : Tm σ} -> {a : Nf σ} -> u ↓ a -> {v : Nf τ} ->
     t $ u ↓ v

nf* : {σ : Ty} -> (t : Tm σ) -> {n : Nf σ} -> t ↓ n -> Set
nf* .{τ} (_$_ {σ} {τ} t u) {v} (r$ {f = f} p q) with nf* {σ ⟶ τ} t {f} p
nf* (t $ u) (r$ p q)  |   _ = Ty


module RandomAccessList.Zeroless.Properties where

open import BuildingBlock
open import BuildingBlock.BinaryLeafTree using (BinaryLeafTree; Node; Leaf; split)
open import RandomAccessList.Zeroless.Core
open import RandomAccessList.Zeroless.Core.Properties
open import RandomAccessList.Zeroless

open import Data.Unit using (tt)
open import Data.Nat
open import Data.Nat.Etc
open import Data.Nat.Properties.Simple
open import Data.Product as Prod

open import Function
open import Relation.Nullary using (Dec; yes; no; ¬_)
open import Relation.Nullary.Decidable using (False; True; fromWitnessFalse; fromWitness)
open import Relation.Nullary.Negation using (contradiction; contraposition)
open import Relation.Binary.PropositionalEquality as PropEq
    using (_≡_; _≢_; refl; cong; cong₂; trans; sym; inspect)
open PropEq.≡-Reasoning

tailₙ-suc : ∀ {n A} → (xs : 1-2-RAL A n) → (p : ⟦ xs ⟧ ≢ 0)
           → suc ⟦ tailₙ xs p ⟧ₙ ≡ ⟦ xs ⟧ₙ
tailₙ-suc {n} {A}    [] p = contradiction (⟦[]⟧≡0 ([] {A} {n}) refl) p
tailₙ-suc (x     1∷ xs) p with ⟦ xs ⟧ ≟ 0
tailₙ-suc {n} (x 1∷ xs) p | yes q =
    begin
        suc 0
    ≡⟨ cong suc (sym (*-right-zero 0)) ⟩
        suc (2 * 0)
    ≡⟨ cong (λ w → suc (2 * w)) (sym (no-zero-divisor (2 * 2 ^ n) ⟦ xs ⟧ₙ (m^n≢0 2 (suc n) (λ ())) q)) ⟩
        suc (2 * ⟦ xs ⟧ₙ)
    ∎
tailₙ-suc (x     1∷ xs) p | no ¬q =
    begin
        3 + 2 * ⟦ tailₙ xs ¬q ⟧ₙ
    ≡⟨ cong suc (sym (+-*-suc 2 ⟦ tailₙ xs ¬q ⟧ₙ)) ⟩
        1 + 2 * suc ⟦ tailₙ xs ¬q ⟧ₙ
    ≡⟨ cong (λ w → 1 + 2 * w) (tailₙ-suc xs ¬q) ⟩
        1 + (2 * ⟦ xs ⟧ₙ)
    ∎
tailₙ-suc (x , y 2∷ xs) p = refl

tail-suc : ∀ {A} → (xs : 1-2-RAL A 0) → (p : ⟦ xs ⟧ ≢ 0)
         → suc ⟦ tail xs p ⟧ ≡ ⟦ xs ⟧
tail-suc {A}        [] p = contradiction (⟦[]⟧≡0 ([] {A} {0}) refl) p
tail-suc (x     1∷ xs) p with ⟦ xs ⟧ ≟ 0
tail-suc (x     1∷ xs) p | yes q = cong (λ w → suc (w + 0)) (sym q)
tail-suc (x     1∷ xs) p | no ¬q =
    begin
        3 + 2 * ⟦ tailₙ xs ¬q ⟧ₙ + 0
    ≡⟨ cong (λ w → 1 + w + 0) (sym (+-*-suc 2 ⟦ tailₙ xs ¬q ⟧ₙ)) ⟩
        1 + 2 * suc ⟦ tailₙ xs ¬q ⟧ₙ + 0
    ≡⟨ cong (λ w → 1 + 2 * w + 0) (tailₙ-suc xs ¬q) ⟩
        1 + 2 * ⟦ xs ⟧ₙ + 0
    ∎
tail-suc (x , y 2∷ xs) p = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Indexed monads
------------------------------------------------------------------------

-- Note that currently the monad laws are not included here.

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

module Category.Monad.Indexed where

open import Category.Applicative.Indexed
open import Function
open import Level

record RawIMonad {i f} {I : Set i} (M : IFun I f) :
                 Set (i ⊔ suc f) where
  infixl 1 _>>=_ _>>_ _>=>_
  infixr 1 _=<<_ _<=<_

  field
    return : ∀ {i A} → A → M i i A
    _>>=_  : ∀ {i j k A B} → M i j A → (A → M j k B) → M i k B

  _>>_ : ∀ {i j k A B} → M i j A → M j k B → M i k B
  m₁ >> m₂ = m₁ >>= λ _ → m₂

  _=<<_ : ∀ {i j k A B} → (A → M j k B) → M i j A → M i k B
  f =<< c = c >>= f

  _>=>_ : ∀ {i j k a} {A : Set a} {B C} →
          (A → M i j B) → (B → M j k C) → (A → M i k C)
  f >=> g = _=<<_ g ∘ f

  _<=<_ : ∀ {i j k B C a} {A : Set a} →
          (B → M j k C) → (A → M i j B) → (A → M i k C)
  g <=< f = f >=> g

  join : ∀ {i j k A} → M i j (M j k A) → M i k A
  join m = m >>= id

  rawIApplicative : RawIApplicative M
  rawIApplicative = record
    { pure = return
    ; _⊛_  = λ f x → f >>= λ f' → x >>= λ x' → return (f' x')
    }

  open RawIApplicative rawIApplicative public

RawIMonadT : ∀ {i f} {I : Set i} (T : IFun I f → IFun I f) → Set (i ⊔ suc f)
RawIMonadT T = ∀ {M} → RawIMonad M → RawIMonad (T M)

record RawIMonadZero {i f} {I : Set i} (M : IFun I f) :
                     Set (i ⊔ suc f) where
  field
    monad           : RawIMonad M
    applicativeZero : RawIApplicativeZero M

  open RawIMonad monad public
  open RawIApplicativeZero applicativeZero using (∅) public

record RawIMonadPlus {i f} {I : Set i} (M : IFun I f) :
                     Set (i ⊔ suc f) where
  field
    monad       : RawIMonad M
    alternative : RawIAlternative M

  open RawIMonad monad public
  open RawIAlternative alternative using (∅; _∣_) public

  monadZero : RawIMonadZero M
  monadZero = record
    { monad           = monad
    ; applicativeZero = RawIAlternative.applicativeZero alternative
    }


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

module Categories.Category.Instance.Properties.Posets where

open import Level using (_⊔_; Lift; lift)
open import Data.Unit using (⊤; tt)
open import Data.Product as Prod using (_,_; <_,_>) renaming (_×_ to _|×|_)
open import Function using (flip)
open import Relation.Binary using (IsPartialOrder; Poset)
open import Relation.Binary.OrderMorphism
  using (id; _∘_) renaming (_⇒-Poset_ to _⇒_)
open import Relation.Binary.PropositionalEquality as ≡ using (_≡_)

open import Categories.Category
import Categories.Category.CartesianClosed as CCC
import Categories.Category.CartesianClosed.Canonical as Canonical
open import Categories.Category.Instance.Posets
open import Categories.Functor using (Functor; Endofunctor)
open import Categories.Utils.Product

open Poset renaming (_≈_ to ₍_₎_≈_; _≤_ to ₍_₎_≤_)
open _⇒_

-- The pointwise partial order on order preserving maps.
--
-- (See the Exponential module below for the definition of the
-- exponential/hom object based on this order.)

module Pointwise {a₁ a₂ a₃ b₁ b₂ b₃} {A : Poset a₁ a₂ a₃} {B : Poset b₁ b₂ b₃}
  where

  infix 4 _≤°_

  _≤°_ : (f g : A ⇒ B) → Set (a₁ ⊔ b₃)
  f ≤° g = ∀ {x} → ₍ B ₎ fun f x ≤ fun g x

  ≤°-isPartialOrder : IsPartialOrder _≗_ _≤°_
  ≤°-isPartialOrder = record
    { isPreorder      = record
      { isEquivalence = ≗-isEquivalence
      ; reflexive     = λ f≗g → reflexive B f≗g
      ; trans         = λ f≤g g≤h → trans B f≤g g≤h
      }
    ; antisym         = λ f≤g g≤f → antisym B f≤g g≤f
    }

  module ≤° = IsPartialOrder ≤°-isPartialOrder

open Pointwise

-- Poset has a duality involution: the poset obtained by reversing the
-- partial order is again a poset.

module Opposite where

  -- NOTE: we flip the direction of the underlying equality _≈_ as
  -- well, so that |op (op A) ≡ A| definitionally.

  op : ∀ {a₁ a₂ a₃} → Poset a₁ a₂ a₃ → Poset a₁ a₂ a₃
  op A = record
    { Carrier           = Carrier A
    ; _≈_               = flip ₍ A ₎_≈_
    ; _≤_               = flip ₍ A ₎_≤_
    ; isPartialOrder    = record
      { isPreorder      = record
        { isEquivalence = record
          { refl        = Eq.refl A
          ; sym         = Eq.sym A
          ; trans       = flip (Eq.trans A)
          }
        ; reflexive     = reflexive A
        ; trans         = flip (trans A)
        }
      ; antisym         = antisym A
      }
    }

  module _ {a₁ a₂ a₃} {A : Poset a₁ a₂ a₃} where

    op-involutive : op (op A) ≡ A
    op-involutive = ≡.refl

    module _ {b₁ b₂ b₃} {B : Poset b₁ b₂ b₃} where

      op₁ : A ⇒ B → op A ⇒ op B
      op₁ f = record { fun = fun f ; monotone = monotone f }

      op₂ : {f g : A ⇒ B} → f ≤° g → op₁ g ≤° op₁ f
      op₂ f≤g = f≤g

  -- op induces an endofunctor on Posets

  op-functor : ∀ {a₁ a₂ a₃} → Endofunctor (Posets a₁ a₂ a₃)
  op-functor = record
    { F₀           = op
    ; F₁           = op₁
    ; identity     = λ {A}       → Eq.refl A
    ; homomorphism = λ {_ _ C}   → Eq.refl C
    ; F-resp-≈     = λ {_ B} x≈y → Eq.sym B x≈y
    }

  module op {a₁ a₂ a₃} = Functor (op-functor {a₁} {a₂} {a₃})

-- The category of posets has terminal objects.

module Terminals where

  unit : ∀ a₁ a₂ a₃ → Poset a₁ a₂ a₃
  unit a₁ a₂ a₃ = record
    { Carrier = Lift a₁ ⊤
    ; _≈_     = λ _ _ → Lift a₂ ⊤
    ; _≤_     = λ _ _ → Lift a₃ ⊤
    }

  module _ {a₁ a₂ a₃ b₁ b₂ b₃} {B : Poset b₁ b₂ b₃} where

    ! : B ⇒ unit a₁ a₂ a₃
    ! = _

    !-unique : (f : B ⇒ unit a₁ a₂ a₃) → ! ≗ f
    !-unique f = _

open Terminals

-- The category of posets has products.

module Products where

  infixr 2 _×_

  _×_ : ∀ {a₁ a₂ a₃ b₁ b₂ b₃} →
        Poset a₁ a₂ a₃ → Poset b₁ b₂ b₃ → Poset (a₁ ⊔ b₁) (a₂ ⊔ b₂) (a₃ ⊔ b₃)
  A × B = record
    { Carrier           = Carrier A |×| Carrier B
    ; _≈_               = ₍ A ₎_≈_ -< _|×|_ >- ₍ B ₎_≈_
    ; _≤_               = ₍ A ₎_≤_ -< _|×|_ >- ₍ B ₎_≤_
    ; isPartialOrder    = record
      { isPreorder      = record
        { isEquivalence = record
          { refl        = Eq.refl A , Eq.refl B
          ; sym         = Prod.map (Eq.sym A) (Eq.sym B)
          ; trans       = Prod.zip (Eq.trans A) (Eq.trans B)
          }
        ; reflexive     = Prod.map (reflexive A) (reflexive B)
        ; trans         = Prod.zip (trans A) (trans B)
        }
      ; antisym         = Prod.zip (antisym A) (antisym B)
      }
    }

  module _ {a₁ a₂ a₃ b₁ b₂ b₃} {A : Poset a₁ a₂ a₃} {B : Poset b₁ b₂ b₃} where

    π₁ : (A × B) ⇒ A
    π₁ = record { fun = Prod.proj₁ ; monotone = Prod.proj₁ }

    π₂ : (A × B) ⇒ B
    π₂ = record { fun = Prod.proj₂ ; monotone = Prod.proj₂ }

    module _ {c₁ c₂ c₃} {C : Poset c₁ c₂ c₃} where

      infix 11 ⟨_,_⟩

      ⟨_,_⟩ : C ⇒ A → C ⇒ B → C ⇒ (A × B)
      ⟨ f , g ⟩ = record
        { fun      = < fun f , fun g >
        ; monotone = < monotone f , monotone g >
        }

      π₁-comp  : {f : C ⇒ A} {g : C ⇒ B} → π₁ ∘ ⟨ f , g ⟩ ≗ f
      π₁-comp = Eq.refl A

      π₂-comp  : {f : C ⇒ A} {g : C ⇒ B} → π₂ ∘ ⟨ f , g ⟩ ≗ g
      π₂-comp = Eq.refl B

      ⟨,⟩-unique : {f : C ⇒ A} {g : C ⇒ B} {h : C ⇒ (A × B)} →
                   π₁ ∘ h ≗ f → π₂ ∘ h ≗ g → ⟨ f , g ⟩ ≗ h
      ⟨,⟩-unique hyp₁ hyp₂ {x} = Eq.sym A hyp₁ , Eq.sym B hyp₂

  infixr 2 _×₁_

  _×₁_ : ∀ {a₁ a₂ a₃ b₁ b₂ b₃ c₁ c₂ c₃ d₁ d₂ d₃}
         {A : Poset a₁ a₂ a₃} {B : Poset b₁ b₂ b₃}
         {C : Poset c₁ c₂ c₃} {D : Poset d₁ d₂ d₃} →
         A ⇒ C → B ⇒ D → (A × B) ⇒ (C × D)
  f ×₁ g = ⟨ f ∘ π₁ , g ∘ π₂ ⟩

open Products

-- The category of posets has exponential objects.
--
-- It's easier to define exponentials with respect to the *canonical*
-- product.  The more generic version can then be given by appealing
-- to uniqueness (up to iso) of products.

module Exponentials where

  -- Use arrow rather than exponential notation for readability.

  infixr 9 _⇨_

  _⇨_ : ∀ {a₁ a₂ a₃ b₁ b₂ b₃} → Poset a₁ a₂ a₃ → Poset b₁ b₂ b₃ →
        Poset (a₁ ⊔ a₃ ⊔ b₁ ⊔ b₃) (a₁ ⊔ b₂) (a₁ ⊔ b₃)
  A ⇨ B = record
    { Carrier           = A ⇒ B
    ; _≈_               = _≗_
    ; _≤_               = _≤°_
    ; isPartialOrder    = ≤°-isPartialOrder
    }

  module _ {a₁ a₂ a₃ b₁ b₂ b₃} {A : Poset a₁ a₂ a₃} {B : Poset b₁ b₂ b₃} where

    eval : (A ⇨ B × A) ⇒ B
    eval = record
      { fun      = λ{ (f , x) → fun f x }
      ; monotone = λ{ {f , _} (f≤g , x≤y) → trans B (monotone f x≤y) f≤g }
      }

    module _ {c₁ c₂ c₃} {C : Poset c₁ c₂ c₃} where

      curry : (C × A) ⇒ B → C ⇒ (A ⇨ B)
      curry f = record
        { fun        = λ x → record
          { fun      = Prod.curry (fun f) x
          ; monotone = Prod.curry (monotone f) (refl C)
          }
        ; monotone   = λ x≤y → monotone f (x≤y , refl A)
        }

      eval-comp : {f : (C × A) ⇒ B} → eval ∘ (curry f ×₁ id) ≗ f
      eval-comp = Eq.refl B

      curry-resp-≗ : {f g : (C × A) ⇒ B} → f ≗ g → curry f ≗ curry g
      curry-resp-≗ hyp = hyp

      curry-unique : {f : C ⇒ (A ⇨ B)} {g : (C × A) ⇒ B} →
                     eval ∘ (f ×₁ id) ≗ g → f ≗ curry g
      curry-unique hyp = hyp

open Exponentials

-- The category of posets is cartesian closed.

Posets-CanonicallyCCC : ∀ {a} → Canonical.CartesianClosed (Posets a a a)
Posets-CanonicallyCCC = record
  { !            = !
  ; π₁           = π₁
  ; π₂           = π₂
  ; ⟨_,_⟩        = ⟨_,_⟩
  ; !-unique     = !-unique
  ; π₁-comp      = λ {_ _ f _ g}   → π₁-comp {f = f} {g}
  ; π₂-comp      = λ {_ _ f _ g}   → π₂-comp {f = f} {g}
  ; ⟨,⟩-unique   = λ {_ _ f _ h g} → ⟨,⟩-unique {f = f} {g} {h}
  ; eval         = eval
  ; curry        = curry
  ; eval-comp    = λ {_ _ _ f}   → eval-comp {f = f}
  ; curry-resp-≈ = λ {_ _ _ f g} → curry-resp-≗ {f = f} {g}
  ; curry-unique = λ {_ _ _ f g} → curry-unique {f = f} {g}
  }

module CanonicallyCartesianClosed {a} =
  Canonical.CartesianClosed (Posets-CanonicallyCCC {a})

Posets-CCC : ∀ {a} → CCC.CartesianClosed (Posets a a a)
Posets-CCC = Canonical.Equivalence.fromCanonical _ Posets-CanonicallyCCC

module CartesianClosed {a} = CCC.CartesianClosed (Posets-CCC {a})


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

module Categories.Category.Construction.Graphs where

-- The "Underlying Graph" <-> "Free Category on a Graph" Adjunction.
-- Lots of surprises here, of various level of surprisingness
-- 1. The Free Category rises universe levels of arrows (and equivalences); the Underlying Graph does not
--   (due to the Transitive Closure "Star" construction)
-- 2. As a consequence of (1), the eventual Adjunction will be for all Levels being the same
-- 3. If you want a Functor Categories Graph, then the object/hom levels are straightforward,
--   but if you use "Categories" with NaturalIsomorphism, then the 2-point Groupoid is contractible
--   (thus equivalent to the 1-point Category), and yet those two Graphs shouldn't be considered to
--   be the same!  So _≡F_ must be used so that we have no wiggle room when mapping Graphs around.
--   Interestingly, this only shows up when trying to prove that the Underlying Functor respects
--   _≈_, which is not traditionally a requirement of a Functor! This requirement is usually left
--   implicit.  If we were to allow Functor to not respect _≈_, things would be fine. But of course
--   we couldn't do any reasonable equational reasoning anymore.  Of course, if _≈_ were the global
--   _≡_, this isn't an issue, but that is definitely not wanted here.
-- 4. A lot of weird little lemmas about _≡_ and subst/transport are needed, because we're in a mixed
--   reasoning situation with both ≡ and ≈ present.  These could probably be generalized and factored
--   into a separate module (or into the standard library).

open import Level
open import Function using (_$_; flip) renaming (id to idFun; _∘_ to _⊚_)
open import Relation.Binary hiding (_⇒_)
open import Relation.Binary.PropositionalEquality as ≡ using (_≡_)
import Relation.Binary.EqReasoning as EqR
open import Relation.Binary.Construct.Closure.ReflexiveTransitive
open import Relation.Binary.Construct.Closure.ReflexiveTransitive.Properties hiding (trans)
open import Data.Product using (proj₁; proj₂; _,_)

open import Categories.Category
open import Categories.Functor using (Functor)
open import Categories.Functor.Equivalence
open import Categories.Category.Instance.StrictCats
open import Categories.Utils.EqReasoning
open import Categories.NaturalTransformation hiding (id)
open import Categories.NaturalTransformation.NaturalIsomorphism hiding (refl; sym; trans; isEquivalence)
open import Categories.Adjoint
import Categories.Morphism.HeterogeneousIdentity as HId

-- a graph has vertices Obj and edges _⇒_, where edges form a setoid over _≈_.
record Graph o ℓ e : Set (suc (o ⊔ ℓ ⊔ e)) where
  infix 4 _≈_ _⇒_

  field
    Obj   : Set o
    _⇒_   : Rel Obj ℓ
    _≈_   : ∀ {A B} → Rel (A ⇒ B) e
    equiv : ∀ {A B} → IsEquivalence (_≈_ {A} {B})

  setoid : {A B : Obj} → Setoid _ _
  setoid {A} {B} = record
    { Carrier       = A ⇒ B
    ; _≈_           = _≈_
    ; isEquivalence = equiv
    }

  module Equiv {A} {B} = IsEquivalence (equiv {A} {B})
  module EdgeReasoning {A B : Obj} = EqR (setoid {A} {B})

private
  variable
    o o′ ℓ ℓ′ e e′ : Level

-- This doesn't belong here... but will do for now
module PathEquiv {o ℓ e : Level} {Obj : Set o} {_⇒_ : Obj → Obj → Set ℓ} (_≈_ : {A B : Obj} → Rel (A ⇒ B) e)
  (equiv : {A B : Obj} → IsEquivalence ( _≈_ {A} {B})) where

  private
    module E {A} {B} = IsEquivalence (equiv {A} {B})

  open E renaming (refl to ≈refl; sym to ≈sym; trans to ≈trans)

  infix 4 _≈*_
  data _≈*_ : {A B : Obj} (p q : Star _⇒_ A B) → Set (o ⊔ ℓ ⊔ e) where
      ε : {A : Obj} → _≈*_ {A} ε ε
      _◅_ : {A B C : Obj} {x y : A ⇒ B} {p q : Star _⇒_ B C} (x≈y : x ≈ y) (p≈q : p ≈* q) → x ◅ p ≈* y ◅ q

  refl : {A B : Obj} {p : Star _⇒_ A B} → p ≈* p
  refl {p = ε} = ε
  refl {p = x ◅ p} = ≈refl ◅ refl

  sym : {A B : Obj} {p q : Star _⇒_ A B} → p ≈* q → q ≈* p
  sym ε = ε
  sym (x≈y ◅ eq) = ≈sym x≈y ◅ sym eq

  ≡⇒≈* : {A B : Obj} {p q : Star _⇒_ A B} → p ≡ q → p ≈* q
  ≡⇒≈* ≡.refl = refl

  ≡⇒≈ : {A B : Obj} → {p q : A ⇒ B} → p ≡ q → p ≈ q
  ≡⇒≈ ≡.refl = ≈refl

  trans : {A B : Obj} {p q r : Star _⇒_ A B} → p ≈* q → q ≈* r → p ≈* r
  trans ε ε = ε
  trans (x≈y ◅ ss) (y≈z ◅ tt) = ≈trans x≈y y≈z ◅ trans ss tt

  isEquivalence : {A B : Obj} → IsEquivalence (λ (p q : Star _⇒_ A B) → p ≈* q)
  isEquivalence = record { refl = refl ; sym = sym ; trans = trans }

  setoid : Obj → Obj → Setoid (o ⊔ ℓ) (o ⊔ ℓ ⊔ e)
  setoid A B = record { _≈_ = _≈*_ ; isEquivalence = isEquivalence {A} {B} }

  -- convenient to define here
  --
  -- FIXME: this should go into the standard library at
  -- Relation.Binary.Construct.Closure.ReflexiveTransitive.Properties
  ◅◅-identityʳ : {A B : Obj} → (f : Star _⇒_ A B) → f ◅◅ ε ≈* f
  ◅◅-identityʳ ε = ε
  ◅◅-identityʳ (x ◅ f) = ≈refl ◅ ◅◅-identityʳ f

  module PathEqualityReasoning {A B} where
    open EqR (setoid A B) public

module _ (G : Graph o ℓ e) where
  open Graph G
  private module P = PathEquiv {o} {ℓ} {e} {Obj} {_⇒_} _≈_ equiv
  open P

  Free : Category o (o ⊔ ℓ) (o ⊔ ℓ ⊔ e)
  Free = record
    { Obj       = Obj
    ; _⇒_       = Star _⇒_
    ; _≈_       = _≈*_
    ; id        = ε
    ; _∘_       = _▻▻_
    ; assoc     = λ {_ _ _ _} {f g h} → sym $ ≡⇒≈* $ ◅◅-assoc f g h
    ; sym-assoc = λ {_ _ _ _} {f g h} → ≡⇒≈* $ ◅◅-assoc f g h
    ; identityˡ = λ {_ _ f} → ◅◅-identityʳ f
    ; identityʳ = refl
    ; identity² = refl
    ; equiv     = isEquivalence
    ; ∘-resp-≈  = resp
    }
    where resp : ∀ {A B C} {f h : Star _⇒_ B C} {g i : Star _⇒_ A B} →
                   f ≈* h → g ≈* i → (f ▻▻ g) ≈* (h ▻▻ i)
          resp eq ε = eq
          resp eq (eq₁ ◅ eq₂) = eq₁ ◅ (resp eq eq₂)

  open P public renaming (_≈*_ to [_]_≈*_)

record GraphMorphism (G : Graph o ℓ e) (G′ : Graph o′ ℓ′ e′) : Set (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) where
  private
    module G  = Graph G
    module G′ = Graph G′

  field
    M₀       : G.Obj → G′.Obj
    M₁       : ∀ {A B} → A G.⇒ B → M₀ A G′.⇒ M₀ B
    M-resp-≈ : ∀ {A B} {f g : A G.⇒ B} → f G.≈ g → M₁ f G′.≈ M₁ g

idGHom : {G : Graph o ℓ e} → GraphMorphism G G
idGHom = record { M₀ = idFun ; M₁ = idFun ; M-resp-≈ = idFun }

_∘GM_ : {G₁ G₂ G₃ : Graph o ℓ e} (m₁ : GraphMorphism G₂ G₃) (m₂ : GraphMorphism G₁ G₂) → GraphMorphism G₁ G₃
m₁ ∘GM m₂ = record
  { M₀ = M₀ m₁ ⊚ M₀ m₂
  ; M₁ = M₁ m₁ ⊚ M₁ m₂
  ; M-resp-≈ = M-resp-≈ m₁ ⊚ M-resp-≈ m₂
  }
  where open GraphMorphism

module Trsp (G : Graph o ℓ e) where
  open Graph G

  -- Two shorthands that will be useful for the definition of morphism
  -- equality: transport the domain or codomain of an edge along an
  -- equality.

  infixr 9 _◂_
  infixl 9 _▸_

  _◂_ : ∀ {A B C} → A ≡ B → B ⇒ C → A ⇒ C
  p ◂ f = ≡.subst (_⇒ _) (≡.sym p) f

  _▸_ : ∀ {A B C} → A ⇒ B → B ≡ C → A ⇒ C
  f ▸ p = ≡.subst (_ ⇒_) p f

  -- Some simple properties of transports

  ◂-▸-comm : ∀ {A B C D} (p : A ≡ B) (f : B ⇒ C) (q : C ≡ D) →
             p ◂ (f ▸ q) ≡ (p ◂ f) ▸ q
  ◂-▸-comm ≡.refl f ≡.refl = ≡.refl

  ◂-trans : ∀ {A B C D} (p : A ≡ B) (q : B ≡ C) (f : C ⇒ D) →
            p ◂ q ◂ f ≡ (≡.trans p q) ◂ f
  ◂-trans ≡.refl ≡.refl f = ≡.refl

  ▸-trans : ∀ {A B C D} (f : A ⇒ B) (p : B ≡ C) (q : C ≡ D) →
            f ▸ p ▸ q ≡ f ▸ ≡.trans p q
  ▸-trans f ≡.refl ≡.refl = ≡.refl

  ◂-resp-≈ : ∀ {A B C} (p : A ≡ B) {f g : B ⇒ C} → f ≈ g → p ◂ f ≈ p ◂ g
  ◂-resp-≈ ≡.refl f≈g = f≈g

  ▸-resp-≈ : ∀ {A B C} {f g : A ⇒ B} → f ≈ g → (p : B ≡ C) → f ▸ p ≈ g ▸ p
  ▸-resp-≈ f≈g ≡.refl = f≈g

module TrspGM {G : Graph o ℓ e} {H : Graph o′ ℓ′ e′}
              (m : GraphMorphism G H) where
  module G = Graph G
  module H = Graph H
  open GraphMorphism m
  open Trsp G
  open Trsp H using () renaming (_◂_ to _◃_; _▸_ to _▹_)

  M-resp-▸ : ∀ {A B C} (f : A G.⇒ B) (p : B ≡ C) →
             M₁ (f ▸ p) ≡ M₁ f ▹ ≡.cong M₀ p
  M-resp-▸ f ≡.refl = ≡.refl

  M-resp-◂ : ∀ {A B C} (p : A ≡ B) (f : B G.⇒ C) →
             M₁ (p ◂ f) ≡ ≡.cong M₀ p ◃ M₁ f
  M-resp-◂ ≡.refl f = ≡.refl

record GraphMorphism≈ {G : Graph o ℓ e} {G′ : Graph o′ ℓ′ e′}
                      (M M′ : GraphMorphism G G′) : Set (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) where
  private
    module G  = Graph G
    module G′ = Graph G′
    module M  = GraphMorphism M
    module M′ = GraphMorphism M′
  open Trsp G′

  -- Pick a presentation of equivalence for graph morphisms that works
  -- well with functor equality.

  field
    M₀≡ : ∀ {X} → M.M₀ X ≡ M′.M₀ X
    M₁≡ : ∀ {A B} {f : A G.⇒ B} → M.M₁ f ▸ M₀≡ G′.≈ M₀≡ ◂ M′.M₁ f

Graphs : ∀ o ℓ e → Category (suc (o ⊔ ℓ ⊔ e)) (o ⊔ ℓ ⊔ e) (o ⊔ ℓ ⊔ e)
Graphs o ℓ e = record
  { Obj       = Graph o ℓ e
  ; _⇒_       = GraphMorphism
  ; _≈_       = GraphMorphism≈
  ; id        = idGHom
  ; _∘_       = _∘GM_
  ; assoc     = λ {_ _ _ G} → record { M₀≡ = ≡.refl ; M₁≡ = Equiv.refl G }
  ; sym-assoc = λ {_ _ _ G} → record { M₀≡ = ≡.refl ; M₁≡ = Equiv.refl G }
  ; identityˡ = λ {_ G}     → record { M₀≡ = ≡.refl ; M₁≡ = Equiv.refl G }
  ; identityʳ = λ {_ G}     → record { M₀≡ = ≡.refl ; M₁≡ = Equiv.refl G }
  ; identity² = λ {G}       → record { M₀≡ = ≡.refl ; M₁≡ = Equiv.refl G }
  ; equiv     = λ {_ G} → record
    { refl  = record { M₀≡ = ≡.refl ; M₁≡ = Equiv.refl G }
    ; sym   = λ {i j} eq → record
      { M₀≡ = ≡.sym (M₀≡ eq)
      ; M₁≡ = λ {_ _ f} →
        let open EdgeReasoning G
            open Trsp G
        in begin
          M₁ j f ▸ ≡.sym (M₀≡ eq)
        ≡˘⟨ ≡.cong (_◂ (M₁ j f ▸ _)) (≡.trans-symˡ (M₀≡ eq)) ⟩
          ≡.trans (≡.sym $ M₀≡ eq) (M₀≡ eq) ◂ (M₁ j f ▸ ≡.sym (M₀≡ eq))
        ≡˘⟨ ◂-trans (≡.sym $ M₀≡ eq) (M₀≡ eq) _ ⟩
          ≡.sym (M₀≡ eq) ◂ M₀≡ eq ◂ (M₁ j f ▸ ≡.sym (M₀≡ eq))
        ≡⟨ ≡.cong (≡.sym (M₀≡ eq) ◂_)
                  (◂-▸-comm (M₀≡ eq) (M₁ j f) (≡.sym $ M₀≡ eq)) ⟩
          ≡.sym (M₀≡ eq) ◂ ((M₀≡ eq ◂ M₁ j f) ▸ ≡.sym (M₀≡ eq))
        ≈˘⟨ ◂-resp-≈ (≡.sym (M₀≡ eq)) (▸-resp-≈ (M₁≡ eq) (≡.sym (M₀≡ eq))) ⟩
          ≡.sym (M₀≡ eq) ◂ (M₁ i f ▸ M₀≡ eq ▸ ≡.sym (M₀≡ eq))
        ≡⟨ ≡.cong (≡.sym (M₀≡ eq) ◂_)
                  (▸-trans (M₁ i f) (M₀≡ eq) (≡.sym (M₀≡ eq))) ⟩
          ≡.sym (M₀≡ eq) ◂ (M₁ i f ▸ ≡.trans (M₀≡ eq) (≡.sym (M₀≡ eq)))
        ≡⟨ ≡.cong (λ p → ≡.sym (M₀≡ eq) ◂ (M₁ i f ▸ p)) (≡.trans-symʳ (M₀≡ eq)) ⟩
          ≡.sym (M₀≡ eq) ◂ M₁ i f
        ∎
      }
    ; trans = λ {i j k} eq eq′ → record
      { M₀≡ = ≡.trans (M₀≡ eq) (M₀≡ eq′)
      ; M₁≡ = λ {_ _ f} →
        let open EdgeReasoning G
            open Trsp G
        in begin
          M₁ i f ▸ ≡.trans (M₀≡ eq) (M₀≡ eq′)
        ≡˘⟨ ▸-trans (M₁ i f) (M₀≡ eq) (M₀≡ eq′) ⟩
          (M₁ i f ▸ M₀≡ eq) ▸ M₀≡ eq′
        ≈⟨ ▸-resp-≈ (M₁≡ eq) (M₀≡ eq′) ⟩
          (M₀≡ eq ◂ M₁ j f) ▸ M₀≡ eq′
        ≡˘⟨ ◂-▸-comm (M₀≡ eq) (M₁ j f) (M₀≡ eq′) ⟩
          M₀≡ eq ◂ (M₁ j f ▸ M₀≡ eq′)
        ≈⟨ ◂-resp-≈ (M₀≡ eq) (M₁≡ eq′) ⟩
          M₀≡ eq ◂ (M₀≡ eq′ ◂ M₁ k f)
        ≡⟨ ◂-trans (M₀≡ eq) (M₀≡ eq′) (M₁ k f) ⟩
          ≡.trans (M₀≡ eq) (M₀≡ eq′) ◂ M₁ k f
        ∎
      }
    }
  ; ∘-resp-≈  = λ {_ G H} {f g h i} eq eq′ → record
    { M₀≡ = ≡.trans (≡.cong (M₀ f) (M₀≡ eq′)) (M₀≡ eq)
    ; M₁≡ = λ {_ _ j} →
      let open EdgeReasoning H
          open Trsp H
          open Trsp G using () renaming (_▸_ to _▹_; _◂_ to _◃_)
          open TrspGM
      in begin
        M₁ (f ∘GM h) j ▸ ≡.trans (≡.cong (M₀ f) (M₀≡ eq′)) (M₀≡ eq)
      ≡˘⟨ ▸-trans (M₁ f (M₁ h j)) (≡.cong (M₀ f) (M₀≡ eq′)) (M₀≡ eq) ⟩
        M₁ f (M₁ h j) ▸ ≡.cong (M₀ f) (M₀≡ eq′) ▸ M₀≡ eq
      ≡˘⟨ ≡.cong (_▸ M₀≡ eq) (M-resp-▸ f (M₁ h j) (M₀≡ eq′)) ⟩
        M₁ f (M₁ h j ▹ M₀≡ eq′) ▸ M₀≡ eq
      ≈⟨ M₁≡ eq ⟩
        M₀≡ eq ◂ M₁ g (M₁ h j ▹ M₀≡ eq′)
      ≈⟨ ◂-resp-≈ (M₀≡ eq) (M-resp-≈ g (M₁≡ eq′)) ⟩
        M₀≡ eq ◂ M₁ g (M₀≡ eq′ ◃ M₁ i j)
      ≡⟨ ≡.cong (M₀≡ eq ◂_) (M-resp-◂ g (M₀≡ eq′) (M₁ i j)) ⟩
        M₀≡ eq ◂ ≡.cong (M₀ g) (M₀≡ eq′) ◂ M₁ g (M₁ i j)
      ≡⟨ ◂-trans (M₀≡ eq) (≡.cong (M₀ g) (M₀≡ eq′)) (M₁ g (M₁ i j)) ⟩
        ≡.trans (M₀≡ eq) (≡.cong (M₀ g) (M₀≡ eq′)) ◂ M₁ g (M₁ i j)
      ≡˘⟨ ≡.cong (_◂ M₁ g (M₁ i j)) (≡.naturality (λ _ → M₀≡ eq)) ⟩
        ≡.trans (≡.cong (M₀ f) (M₀≡ eq′)) (M₀≡ eq) ◂ M₁ g (M₁ i j)
      ∎
    }
  }
  where
    open Graph
    open GraphMorphism
    open GraphMorphism≈

open _≡F_

-- Put the rest of the Graph stuff here too:
Underlying₀ : Category o ℓ e → Graph o ℓ e
Underlying₀ C = record { Obj = C.Obj ; _⇒_ = C._⇒_ ; _≈_ = C._≈_ ; equiv = C.equiv }
  where module C = Category C

Underlying₁ : {C : Category o ℓ e} {D : Category o′ ℓ′ e′} → Functor C D → GraphMorphism (Underlying₀ C) (Underlying₀ D)
Underlying₁ F = record { M₀ = F.F₀ ; M₁ = F.F₁ ; M-resp-≈ = F.F-resp-≈ }
  where module F = Functor F

Underlying : Functor (Cats o ℓ e) (Graphs o ℓ e)
Underlying = record
  { F₀ = Underlying₀
  ; F₁ = Underlying₁
  ; identity = λ {A} → record { M₀≡ = ≡.refl ; M₁≡ = Category.Equiv.refl A }
  ; homomorphism = λ where {Z = Z} → record { M₀≡ = ≡.refl ; M₁≡ = Category.Equiv.refl Z }
  ; F-resp-≈ = λ {A} {B} {F} {G} F≈G → record
    { M₀≡ = λ {X} → eq₀ F≈G X
    ; M₁≡ = λ {x} {y} {f} →
      let open Category B
          open HId B
          open Trsp (Underlying₀ B)
          open Functor
          open Graph.EdgeReasoning (Underlying₀ B)
      in begin
        F₁ F f ▸ eq₀ F≈G y         ≈⟨ hid-subst-cod (F₁ F f) (eq₀ F≈G y) ⟩
        hid (eq₀ F≈G y) ∘ F₁ F f   ≈⟨ eq₁ F≈G f ⟩
        F₁ G f ∘ hid (eq₀ F≈G x)   ≈˘⟨ hid-subst-dom (eq₀ F≈G x) (F₁ G f) ⟩
        eq₀ F≈G x ◂ F₁ G f         ∎
    }
  }
  where
  open NaturalTransformation
  open NaturalIsomorphism

-- Transports on paths

module TrspStar (G : Graph o ℓ e) where
  open Graph G
  open Trsp (Underlying₀ (Free G)) public using () renaming
    ( _◂_ to _◂*_
    ; _▸_ to _▸*_
    )
  open Trsp G

  -- Lemmas relating transports to path operations.

  ◂*-▸*-ε : ∀ {A B : Obj} (p : A ≡ B) → ε ▸* p ≡ p ◂* ε
  ◂*-▸*-ε ≡.refl = ≡.refl

  ◂*-◅ : ∀ {A B C D : Obj} (p : A ≡ B) (f : B ⇒ C) (fs : Star _⇒_ C D) →
         p ◂* (f ◅ fs) ≡ (p ◂ f) ◅ fs
  ◂*-◅ ≡.refl f fs = ≡.refl

  ◅-▸* : ∀ {A B C D : Obj} (f : A ⇒ B) (fs : Star _⇒_ B C) (p : C ≡ D) →
         (f ◅ fs) ▸* p ≡ f ◅ (fs ▸* p)
  ◅-▸* f fs ≡.refl = ≡.refl

  ◅-◂*-▸ : ∀ {A B C D : Obj} (f : A ⇒ B) (p : B ≡ C) (fs : Star _⇒_ C D) →
           _≡_ {_} {Star _⇒_ A D} (f ◅ (p ◂* fs)) ((f ▸ p) ◅ fs)
  ◅-◂*-▸ f ≡.refl fs = ≡.refl

-- define these ahead of time
module _ {G₁ G₂ : Graph o ℓ e} (G⇒ : GraphMorphism G₁ G₂) where
  open Graph G₁ renaming (_⇒_ to _⇒₁_; Obj to Obj₁)
  open Graph G₂ renaming (_⇒_ to _⇒₂_; Obj to Obj₂; module Equiv to Equiv₂)
  open GraphMorphism G⇒

  mapGraph : {A B : Obj₁} → Star _⇒₁_ A B → Star _⇒₂_ (M₀ A) (M₀ B)
  mapGraph ε = ε
  mapGraph (x ◅ y) = M₁ x ◅ mapGraph y

  map-hom : {X Y Z : Graph.Obj G₁} (f : Star _⇒₁_ X Y) {g : Star _⇒₁_ Y Z} →
      [ G₂ ] mapGraph (f ◅◅ g) ≈* (mapGraph f ◅◅ mapGraph g)
  map-hom ε {g} = refl G₂
  map-hom (x ◅ f) {g} = Equiv₂.refl ◅ map-hom f

  map-resp : {A B : Obj₁} (f : Star _⇒₁_ A B) {g : Star _⇒₁_ A B} →
      [ G₁ ] f ≈* g → [ G₂ ] mapGraph f ≈* mapGraph g
  map-resp ε ε = ε
  map-resp (x ◅ f) (f≈* ◅ eq) = M-resp-≈ f≈* ◅ map-resp f eq

-- don't want a single global GraphMorphism
module _ {G : Graph o ℓ e} where
  open Graph G

  map-id : {A B : Obj} (f : Star _⇒_ A B) → [ G ] mapGraph (idGHom {G = G}) f ≈* f
  map-id ε        = ε
  map-id (fs ◅ f) = Equiv.refl ◅ map-id f

module _ {X Y Z : Graph o ℓ e} {G₁ : GraphMorphism X Y} {G₂ : GraphMorphism Y Z} where
  open Graph X

  map-∘ : {A B : Obj} (f : Star _⇒_ A B) → [ Z ] (mapGraph (G₂ ∘GM G₁) f) ≈* mapGraph G₂ (mapGraph G₁ f)
  map-∘ ε        = ε
  map-∘ (fs ◅ f) = Graph.Equiv.refl Z ◅ map-∘ f

module _ {G H : Graph o ℓ e} {f g : GraphMorphism G H}
         (f≈g : GraphMorphism≈ f g) where
  open Graph G
  open GraphMorphism
  open GraphMorphism≈ f≈g
  open TrspStar H
  open Trsp H

  map-M₁≡ : {A B : Obj} (hs : Star _⇒_ A B) →
            [ H ] mapGraph f hs ▸* M₀≡ ≈* M₀≡ ◂* mapGraph g hs
  map-M₁≡ ε        = ≡⇒≈* H (◂*-▸*-ε M₀≡)
  map-M₁≡ (hs ◅ h) = begin
    (M₁ f hs ◅ mapGraph f h) ▸* M₀≡   ≡⟨ ◅-▸* (M₁ f hs) _ M₀≡ ⟩
    M₁ f hs ◅ (mapGraph f h ▸* M₀≡)   ≈⟨ Graph.Equiv.refl H ◅ map-M₁≡ h ⟩
    M₁ f hs ◅ (M₀≡ ◂* mapGraph g h)   ≡⟨ ◅-◂*-▸ (M₁ f hs) M₀≡ _ ⟩
    (M₁ f hs ▸ M₀≡) ◅ mapGraph g h    ≈⟨ M₁≡ ◅ (refl H) ⟩
    (M₀≡ ◂ M₁ g hs) ◅ mapGraph g h    ≡˘⟨ ◂*-◅ M₀≡ (M₁ g hs) _ ⟩
    M₀≡ ◂* (M₁ g hs ◅ mapGraph g h)   ∎
    where open PathEqualityReasoning H

module _ (C : Category o ℓ e) where
  open Category C
  open HomReasoning

  -- A helper that should probably go into Categories.Morphism.Reasoning...

  toSquare : ∀ {A B} {f g : A ⇒ B} → f ≈ g → CommutativeSquare f id id g
  toSquare {_} {_} {f} {g} f≈g = begin
        id ∘ f   ≈⟨ identityˡ ⟩
        f        ≈⟨ f≈g ⟩
        g        ≈˘⟨ identityʳ ⟩
        g ∘ id   ∎

CatF : Functor (Graphs o ℓ e) (Cats o (o ⊔ ℓ) (o ⊔ ℓ ⊔ e))
CatF = record
  { F₀ = Free
  ; F₁ = λ {G₁} {G₂} G⇒ → record
    { F₀ = M₀ G⇒
    ; F₁ = mapGraph G⇒
    ; identity = refl G₂
    ; homomorphism = λ {_} {_} {_} {f} → map-hom G⇒ f
    ; F-resp-≈ = λ { {f = f} → map-resp G⇒ f}
    }
  ; identity = λ {G} → record
    { eq₀ = λ _ → ≡.refl
    ; eq₁ = λ f → toSquare (Free G) (map-id f)
    }
  ; homomorphism = λ {_} {_} {G} → record
    { eq₀ = λ _ → ≡.refl
    ; eq₁ = λ h → toSquare (Free G) (map-∘ h)
    }
  ; F-resp-≈ = λ {_} {G} {f} {g} f≈g → record
    { eq₀ = λ _ → M₀≡ f≈g
    ; eq₁ = λ h →
      let open Category (Free G)
          open HId      (Free G)
          open TrspStar G
          open HomReasoning
      in begin
        mapGraph f h ◅◅ (hid $ M₀≡ f≈g)
      ≈˘⟨ hid-subst-cod (mapGraph f h) (M₀≡ f≈g) ⟩
        mapGraph f h ▸* M₀≡ f≈g
      ≈⟨ map-M₁≡ f≈g h ⟩
        M₀≡ f≈g ◂* mapGraph g h
      ≈⟨ hid-subst-dom (M₀≡ f≈g) (mapGraph g h) ⟩
        (hid $ M₀≡ f≈g) ◅◅ mapGraph g h
      ∎
    }
  }
  where
  open GraphMorphism
  open GraphMorphism≈

-- Because of the Level changes in CatF, sizes must all be same:
CatF-is-Free : (o : Level) → Adjoint (CatF {o} {o} {o}) (Underlying)
CatF-is-Free o = record
  { unit = ntHelper record
    { η = GM
    ; commute = λ {X} {Y} f → record { M₀≡ = ≡.refl ; M₁≡ = Graph.Equiv.refl Y ◅ ε }
    }
  ; counit = ntHelper record
    { η = λ X → record
      { F₀ = idFun
      ; F₁ = unwind X
      ; identity = Category.Equiv.refl X
      ; homomorphism = λ { {f = f} {g} → unwind-◅◅ X {f = f} {g} }
      ; F-resp-≈ = unwind-resp-≈ X
      }
    ; commute = λ {_} {Y} F → record
      { eq₀ = λ _ → ≡.refl
      ; eq₁ = λ f → toSquare Y (comm F f)
      }
    }
  ; zig = λ {G} → record
    { eq₀ = λ _ → ≡.refl
    ; eq₁ = λ f → toSquare (Free G) (zig′ G f)
    }
  ; zag = λ {B} → record { M₀≡ = ≡.refl ; M₁≡ = Category.identityˡ B  }
  }
  where
  GM : (X : Graph o o o) → GraphMorphism X (Underlying₀ (Free X))
  GM _ = record { M₀ = idFun ; M₁ = return ; M-resp-≈ = λ f≈g → f≈g ◅ ε }
  module _ (X : Category o o o) where
    open Category X
    open HomReasoning
    unwind : {A B : Obj} → Star _⇒_ A B → A ⇒ B
    unwind = fold _⇒_ (flip _∘_) id
    unwind-◅◅ : {A B C : Obj} {f : Star _⇒_ A B} {g : Star _⇒_ B C} →
                unwind (f ◅◅ g) ≈ (unwind g) ∘ (unwind f)
    unwind-◅◅ {f = ε} {g} = Equiv.sym identityʳ
    unwind-◅◅ {f = x ◅ f} {g} = ∘-resp-≈ˡ (unwind-◅◅ {f = f} {g}) ○ assoc
    unwind-resp-≈ : {A B : Obj} {f g : Star _⇒_ A B} → [ Underlying₀ X ] f ≈* g → unwind f ≈ unwind g
    unwind-resp-≈ ε = Equiv.refl
    unwind-resp-≈ (x ◅ eq) = ∘-resp-≈ (unwind-resp-≈ eq) x

  zig′ : (X : Graph o o o) → {A B : Graph.Obj X} → (f : Star (Graph._⇒_ X) A B) →
    let Y = Free X in [ X ] (unwind Y) (mapGraph (GM X) f) ≈* f
  zig′ A ε        = ε
  zig′ A (fs ◅ f) = Graph.Equiv.refl A ◅ zig′ A f

  module _ {X Y : Category o o o} (F : Functor X Y) where
    open Category X renaming (Obj to Obj₁; _⇒_ to _⇒₁_)
    open Category Y renaming (_≈_ to _≈₂_; module Equiv to EY)
    open Category.HomReasoning Y
    open Functor F
    comm : {A B : Obj₁} (f : Star _⇒₁_ A B) → unwind Y (mapGraph (Underlying₁ F) f) ≈₂ F₁ (unwind X f)
    comm ε = EY.sym identity
    comm (x ◅ f) = EY.sym (homomorphism ○ Category.∘-resp-≈ˡ Y (EY.sym (comm f)))


module Pi0 where

open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product

open import Groupoid

infixr 10 _◎_
infixr 30 _⟷_

------------------------------------------------------------------------------
-- Level 0:
-- Types at this level are just plain sets with no interesting path structure.
-- The path structure is defined at levels 1 and beyond.

data U : Set where
  ZERO  : U
  ONE   : U
  PLUS  : U → U → U
  TIMES : U → U → U

⟦_⟧ : U → Set
⟦ ZERO ⟧        = ⊥
⟦ ONE ⟧         = ⊤
⟦ PLUS t₁ t₂ ⟧  = ⟦ t₁ ⟧ ⊎ ⟦ t₂ ⟧
⟦ TIMES t₁ t₂ ⟧ = ⟦ t₁ ⟧ × ⟦ t₂ ⟧

-- Programs
-- We use pointed types; programs map a pointed type to another
-- In other words, each program takes one particular value to another; if we
-- want to work on another value, we generally use another program

record U• : Set where
  constructor •[_,_]
  field
    ∣_∣ : U
    • : ⟦ ∣_∣ ⟧

open U•

Space : (t• : U•) → Set
Space •[ t , v ] = ⟦ t ⟧

point : (t• : U•) → Space t•
point •[ t , v ] = v

-- examples of plain types, values, and pointed types

ONE• : U•
ONE• = •[ ONE , tt ]

BOOL : U
BOOL = PLUS ONE ONE

BOOL² : U
BOOL² = TIMES BOOL BOOL

TRUE : ⟦ BOOL ⟧
TRUE = inj₁ tt

FALSE : ⟦ BOOL ⟧
FALSE = inj₂ tt

BOOL•F : U•
BOOL•F = •[ BOOL , FALSE ]

BOOL•T : U•
BOOL•T = •[ BOOL , TRUE ]

-- The actual programs are the commutative semiring isomorphisms between
-- pointed types.

data _⟷_ : U• → U• → Set where
  unite₊  : ∀ {t v} → •[ PLUS ZERO t , inj₂ v ] ⟷ •[ t , v ]
  uniti₊  : ∀ {t v} → •[ t , v ] ⟷ •[ PLUS ZERO t , inj₂ v ]
  swap1₊  : ∀ {t₁ t₂ v₁} → •[ PLUS t₁ t₂ , inj₁ v₁ ] ⟷ •[ PLUS t₂ t₁ , inj₂ v₁ ]
  swap2₊  : ∀ {t₁ t₂ v₂} → •[ PLUS t₁ t₂ , inj₂ v₂ ] ⟷ •[ PLUS t₂ t₁ , inj₁ v₂ ]
  assocl1₊ : ∀ {t₁ t₂ t₃ v₁} →
             •[ PLUS t₁ (PLUS t₂ t₃) , inj₁ v₁ ] ⟷
             •[ PLUS (PLUS t₁ t₂) t₃ , inj₁ (inj₁ v₁) ]
  assocl2₊ : ∀ {t₁ t₂ t₃ v₂} →
             •[ PLUS t₁ (PLUS t₂ t₃) , inj₂ (inj₁ v₂) ] ⟷
             •[ PLUS (PLUS t₁ t₂) t₃ , inj₁ (inj₂ v₂) ]
  assocl3₊ : ∀ {t₁ t₂ t₃ v₃} →
             •[ PLUS t₁ (PLUS t₂ t₃) , inj₂ (inj₂ v₃) ] ⟷
             •[ PLUS (PLUS t₁ t₂) t₃ , inj₂ v₃ ]
  assocr1₊ : ∀ {t₁ t₂ t₃ v₁} →
             •[ PLUS (PLUS t₁ t₂) t₃ , inj₁ (inj₁ v₁) ] ⟷
             •[ PLUS t₁ (PLUS t₂ t₃) , inj₁ v₁ ]
  assocr2₊ : ∀ {t₁ t₂ t₃ v₂} →
             •[ PLUS (PLUS t₁ t₂) t₃ , inj₁ (inj₂ v₂) ] ⟷
             •[ PLUS t₁ (PLUS t₂ t₃) , inj₂ (inj₁ v₂) ]
  assocr3₊ : ∀ {t₁ t₂ t₃ v₃} →
             •[ PLUS (PLUS t₁ t₂) t₃ , inj₂ v₃ ] ⟷
             •[ PLUS t₁ (PLUS t₂ t₃) , inj₂ (inj₂ v₃) ]
  unite⋆  : ∀ {t v} → •[ TIMES ONE t , (tt , v) ] ⟷ •[ t , v ]
  uniti⋆  : ∀ {t v} → •[ t , v ] ⟷ •[ TIMES ONE t , (tt , v) ]
  swap⋆   : ∀ {t₁ t₂ v₁ v₂} →
              •[ TIMES t₁ t₂ , (v₁ , v₂) ] ⟷ •[ TIMES t₂ t₁ , (v₂ , v₁) ]
  assocl⋆ : ∀ {t₁ t₂ t₃ v₁ v₂ v₃} →
            •[ TIMES t₁ (TIMES t₂ t₃) , (v₁ , (v₂ , v₃)) ] ⟷
            •[ TIMES (TIMES t₁ t₂) t₃ , ((v₁ , v₂) , v₃) ]
  assocr⋆ : ∀ {t₁ t₂ t₃ v₁ v₂ v₃} →
            •[ TIMES (TIMES t₁ t₂) t₃ , ((v₁ , v₂) , v₃) ] ⟷
            •[ TIMES t₁ (TIMES t₂ t₃) , (v₁ , (v₂ , v₃)) ]
  distz : ∀ {t v absurd} →
            •[ TIMES ZERO t , (absurd , v) ] ⟷ •[ ZERO , absurd ]
  factorz : ∀ {t v absurd} →
            •[ ZERO , absurd ] ⟷ •[ TIMES ZERO t , (absurd , v) ]
  dist1   : ∀ {t₁ t₂ t₃ v₁ v₃} →
            •[ TIMES (PLUS t₁ t₂) t₃ , (inj₁ v₁ , v₃) ] ⟷
            •[ PLUS (TIMES t₁ t₃) (TIMES t₂ t₃) , inj₁ (v₁ , v₃) ]
  dist2   : ∀ {t₁ t₂ t₃ v₂ v₃} →
            •[ TIMES (PLUS t₁ t₂) t₃ , (inj₂ v₂ , v₃) ] ⟷
            •[ PLUS (TIMES t₁ t₃) (TIMES t₂ t₃) , inj₂ (v₂ , v₃) ]
  factor1   : ∀ {t₁ t₂ t₃ v₁ v₃} →
            •[ PLUS (TIMES t₁ t₃) (TIMES t₂ t₃) , inj₁ (v₁ , v₃) ] ⟷
            •[ TIMES (PLUS t₁ t₂) t₃ , (inj₁ v₁ , v₃) ]
  factor2   : ∀ {t₁ t₂ t₃ v₂ v₃} →
            •[ PLUS (TIMES t₁ t₃) (TIMES t₂ t₃) , inj₂ (v₂ , v₃) ] ⟷
            •[ TIMES (PLUS t₁ t₂) t₃ , (inj₂ v₂ , v₃) ]
  id⟷    : ∀ {t v} → •[ t , v ] ⟷ •[ t , v ]
  sym⟷   : ∀ {t₁ t₂ v₁ v₂} → (•[ t₁ , v₁ ] ⟷ •[ t₂ , v₂ ]) →
           (•[ t₂ , v₂ ] ⟷ •[ t₁ , v₁ ])
  _◎_    : ∀ {t₁ t₂ t₃ v₁ v₂ v₃} → (•[ t₁ , v₁ ] ⟷ •[ t₂ , v₂ ]) →
           (•[ t₂ , v₂ ] ⟷ •[ t₃ , v₃ ]) →
           (•[ t₁ , v₁ ] ⟷ •[ t₃ , v₃ ])
  _⊕1_   : ∀ {t₁ t₂ t₃ t₄ v₁ v₂ v₃ v₄} →
           (•[ t₁ , v₁ ] ⟷ •[ t₃ , v₃ ]) → (•[ t₂ , v₂ ] ⟷ •[ t₄ , v₄ ]) →
           (•[ PLUS t₁ t₂ , inj₁ v₁ ] ⟷ •[ PLUS t₃ t₄ , inj₁ v₃ ])
  _⊕2_   : ∀ {t₁ t₂ t₃ t₄ v₁ v₂ v₃ v₄} →
           (•[ t₁ , v₁ ] ⟷ •[ t₃ , v₃ ]) → (•[ t₂ , v₂ ] ⟷ •[ t₄ , v₄ ]) →
           (•[ PLUS t₁ t₂ , inj₂ v₂ ] ⟷ •[ PLUS t₃ t₄ , inj₂ v₄ ])
  _⊗_     : ∀ {t₁ t₂ t₃ t₄ v₁ v₂ v₃ v₄} →
           (•[ t₁ , v₁ ] ⟷ •[ t₃ , v₃ ]) → (•[ t₂ , v₂ ] ⟷ •[ t₄ , v₄ ]) →
          (•[ TIMES t₁ t₂ , (v₁ , v₂) ] ⟷ •[ TIMES t₃ t₄ , (v₃ , v₄) ])

-- example programs

NOT•T : •[ BOOL , TRUE ] ⟷ •[ BOOL , FALSE ]
NOT•T = swap1₊

NOT•F : •[ BOOL , FALSE ] ⟷ •[ BOOL , TRUE ]
NOT•F = swap2₊

CNOT•Fx : {b : ⟦ BOOL ⟧} →
          •[ BOOL² , (FALSE , b) ] ⟷ •[ BOOL² , (FALSE , b) ]
CNOT•Fx = dist2 ◎ ((id⟷ ⊗ NOT•F) ⊕2 id⟷) ◎ factor2

CNOT•TF : •[ BOOL² , (TRUE , FALSE) ] ⟷ •[ BOOL² , (TRUE , TRUE) ]
CNOT•TF = dist1 ◎
          ((id⟷ ⊗ NOT•F) ⊕1 (id⟷ {TIMES ONE BOOL} {(tt , TRUE)})) ◎
          factor1

CNOT•TT : •[ BOOL² , (TRUE , TRUE) ] ⟷ •[ BOOL² , (TRUE , FALSE) ]
CNOT•TT = dist1 ◎
          ((id⟷ ⊗ NOT•T) ⊕1 (id⟷ {TIMES ONE BOOL} {(tt , TRUE)})) ◎
          factor1

-- The evaluation of a program is not done in order to figure out the output
-- value. Both the input and output values are encoded in the type of the
-- program; what the evaluation does is follow the path to constructively
-- reach the ouput value from the input value. Even though programs of the
-- same pointed types are, by definition, observationally equivalent, they
-- may follow different paths. At this point, we simply declare that all such
-- programs are "the same." At the next level, we will weaken this "path
-- irrelevant" equivalence and reason about which paths can be equated to
-- other paths via 2paths etc.

-- Even though individual types are sets, the universe of types is a
-- groupoid. The objects of this groupoid are the pointed types; the
-- morphisms are the programs; and the equivalence of programs is the
-- degenerate observational equivalence that equates every two programs that
-- are extensionally equivalent.

_obs≅_ : {t₁ t₂ : U•} → (c₁ c₂ : t₁ ⟷ t₂) → Set
c₁ obs≅ c₂ = ⊤

UG : 1Groupoid
UG = record
       { set = U•
       ; _↝_ = _⟷_
       ; _≈_ = _obs≅_
       ; id = id⟷
       ; _∘_ = λ y⟷z x⟷y → x⟷y ◎ y⟷z
       ; _⁻¹ = sym⟷
       ; lneutr = λ _ → tt
       ; rneutr = λ _ → tt
       ; assoc = λ _ _ _ → tt
       ; equiv = record { refl = tt
                        ; sym = λ _ → tt
                        ; trans = λ _ _ → tt
                        }
       ; linv = λ _ → tt
       ; rinv = λ _ → tt
       ; ∘-resp-≈ = λ _ _ → tt
       }

------------------------------------------------------------------------------
-- Simplifiy various compositions

simplifySym : {t₁ t₂ : U•} → (c₁ : t₁ ⟷ t₂) → (t₂ ⟷ t₁)
simplifySym unite₊ = uniti₊
simplifySym uniti₊ = unite₊
simplifySym swap1₊ = swap2₊
simplifySym swap2₊ = swap1₊
simplifySym assocl1₊ = assocr1₊
simplifySym assocl2₊ = assocr2₊
simplifySym assocl3₊ = assocr3₊
simplifySym assocr1₊ = assocl1₊
simplifySym assocr2₊ = assocl2₊
simplifySym assocr3₊ = assocl3₊
simplifySym unite⋆ = uniti⋆
simplifySym uniti⋆ = unite⋆
simplifySym swap⋆ = swap⋆
simplifySym assocl⋆ = assocr⋆
simplifySym assocr⋆ = assocl⋆
simplifySym distz = factorz
simplifySym factorz = distz
simplifySym dist1 = factor1
simplifySym dist2 = factor2
simplifySym factor1 = dist1
simplifySym factor2 = dist2
simplifySym id⟷ = id⟷
simplifySym (sym⟷ c) = c
simplifySym (c₁ ◎ c₂) = simplifySym c₂ ◎ simplifySym c₁
simplifySym (c₁ ⊕1 c₂) = simplifySym c₁ ⊕1 simplifySym c₂
simplifySym (c₁ ⊕2 c₂) = simplifySym c₁ ⊕2 simplifySym c₂
simplifySym (c₁ ⊗ c₂) = simplifySym c₁ ⊗ simplifySym c₂

simplifyl◎ : {t₁ t₂ t₃ : U•} → (c₁ : t₁ ⟷ t₂) → (c₂ : t₂ ⟷ t₃) → (t₁ ⟷ t₃)
simplifyl◎ id⟷ c = c
simplifyl◎ unite₊ uniti₊ = id⟷
simplifyl◎ uniti₊ unite₊ = id⟷
simplifyl◎ swap1₊ swap2₊ = id⟷
simplifyl◎ swap2₊ swap1₊ = id⟷
simplifyl◎ assocl1₊ assocr1₊ = id⟷
simplifyl◎ assocl2₊ assocr2₊ = id⟷
simplifyl◎ assocl3₊ assocr3₊ = id⟷
simplifyl◎ assocr1₊ assocl1₊ = id⟷
simplifyl◎ assocr2₊ assocl2₊ = id⟷
simplifyl◎ assocr3₊ assocl3₊ = id⟷
simplifyl◎ unite⋆ uniti⋆ = id⟷
simplifyl◎ uniti⋆ unite⋆ = id⟷
simplifyl◎ swap⋆ swap⋆ = id⟷
simplifyl◎ assocl⋆ assocr⋆ = id⟷
simplifyl◎ assocr⋆ assocl⋆ = id⟷
simplifyl◎ factorz distz = id⟷
simplifyl◎ dist1 factor1 = id⟷
simplifyl◎ dist2 factor2 = id⟷
simplifyl◎ factor1 dist1 = id⟷
simplifyl◎ factor2 dist2 = id⟷
simplifyl◎ (c₁ ◎ c₂) c₃ = c₁ ◎ (c₂ ◎ c₃)
simplifyl◎ (c₁ ⊕1 c₂) swap1₊ = swap1₊ ◎ (c₂ ⊕2 c₁)
simplifyl◎ (c₁ ⊕2 c₂) swap2₊ = swap2₊ ◎ (c₂ ⊕1 c₁)
simplifyl◎ (_⊗_ {ONE} c₁ c₂) unite⋆ = unite⋆ ◎ c₂
simplifyl◎ (c₁ ⊗ c₂) swap⋆ = swap⋆ ◎ (c₂ ⊗ c₁)
simplifyl◎ (c₁ ⊗ c₂) (c₃ ⊗ c₄) = (c₁ ◎ c₃) ⊗ (c₂ ◎ c₄)
simplifyl◎ c₁ c₂ = c₁ ◎ c₂

simplifyr◎ : {t₁ t₂ t₃ : U•} → (c₁ : t₁ ⟷ t₂) → (c₂ : t₂ ⟷ t₃) → (t₁ ⟷ t₃)
simplifyr◎ c id⟷ = c
simplifyr◎ unite₊ uniti₊ = id⟷
simplifyr◎ uniti₊ unite₊ = id⟷
simplifyr◎ swap1₊ swap2₊ = id⟷
simplifyr◎ swap2₊ swap1₊ = id⟷
simplifyr◎ assocl1₊ assocr1₊ = id⟷
simplifyr◎ assocl2₊ assocr2₊ = id⟷
simplifyr◎ assocl3₊ assocr3₊ = id⟷
simplifyr◎ assocr1₊ assocl1₊ = id⟷
simplifyr◎ assocr2₊ assocl2₊ = id⟷
simplifyr◎ assocr3₊ assocl3₊ = id⟷
simplifyr◎ unite⋆ uniti⋆ = id⟷
simplifyr◎ uniti⋆ unite⋆ = id⟷
simplifyr◎ swap⋆ swap⋆ = id⟷
simplifyr◎ assocl⋆ assocr⋆ = id⟷
simplifyr◎ assocr⋆ assocl⋆ = id⟷
simplifyr◎ factorz distz = id⟷
simplifyr◎ dist1 factor1 = id⟷
simplifyr◎ dist2 factor2 = id⟷
simplifyr◎ factor1 dist1 = id⟷
simplifyr◎ factor2 dist2 = id⟷
simplifyr◎ (c₁ ◎ c₂) c₃ = c₁ ◎ (c₂ ◎ c₃)
simplifyr◎ (c₁ ⊕1 c₂) swap1₊ = swap1₊ ◎ (c₂ ⊕2 c₁)
simplifyr◎ (c₁ ⊕2 c₂) swap2₊ = swap2₊ ◎ (c₂ ⊕1 c₁)
simplifyr◎ (_⊗_ {ONE} {ONE} c₁ c₂) unite⋆ = unite⋆ ◎ c₂
simplifyr◎ (c₁ ⊗ c₂) swap⋆ = swap⋆ ◎ (c₂ ⊗ c₁)
simplifyr◎ (c₁ ⊗ c₂) (c₃ ⊗ c₄) = (c₁ ◎ c₃) ⊗ (c₂ ◎ c₄)
simplifyr◎ c₁ c₂ = c₁ ◎ c₂


open import Agda.Builtin.Equality

module _ (A : Set) where

data Wrap (X : Set) : Set where
  wrap : X → Wrap X

data D (x : A) : ∀ y → Wrap (x ≡ y) → Set where
  c : ∀ y (x≡y : x ≡ y) → D x y (wrap x≡y)

test : ∀ x y (x≡y x≡y' : Wrap (x ≡ y)) → D x y x≡y → D x y x≡y' → Set
test y .y (wrap refl) .(wrap refl) (c .y .refl) (c .y refl) = A


------------------------------------------------------------------------
-- The Agda standard library
--
-- Divisibility
------------------------------------------------------------------------

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

module Data.Nat.Divisibility where

open import Algebra
open import Data.Nat as Nat
open import Data.Nat.DivMod
open import Data.Nat.Properties
open import Data.Nat.Solver
open import Data.Fin using (Fin; zero; suc; toℕ)
import Data.Fin.Properties as FP
open import Data.Product
open import Function
open import Function.Equivalence using (_⇔_; equivalence)
open import Relation.Nullary
import Relation.Nullary.Decidable as Dec
open import Relation.Binary
import Relation.Binary.Reasoning.PartialOrder as POR
open import Relation.Binary.PropositionalEquality as PropEq
  using (_≡_; _≢_; refl; sym; trans; cong; cong₂; subst)

open +-*-Solver

------------------------------------------------------------------------
-- m ∣ n is inhabited iff m divides n. Some sources, like Hardy and
-- Wright's "An Introduction to the Theory of Numbers", require m to
-- be non-zero. However, some things become a bit nicer if m is
-- allowed to be zero. For instance, _∣_ becomes a partial order, and
-- the gcd of 0 and 0 becomes defined.

infix 4 _∣_ _∤_

record _∣_ (m n : ℕ) : Set where
  constructor divides
  field quotient : ℕ
        equality : n ≡ quotient * m
open _∣_ using (quotient) public

_∤_ : Rel ℕ _
m ∤ n = ¬ (m ∣ n)

------------------------------------------------------------------------
-- _∣_ is a partial order

∣⇒≤ : ∀ {m n} → m ∣ suc n → m ≤ suc n
∣⇒≤         (divides zero    ())
∣⇒≤ {m} {n} (divides (suc q) eq) = begin
  m          ≤⟨ m≤m+n m (q * m) ⟩
  suc q * m  ≡⟨ sym eq ⟩
  suc n      ∎
  where open ≤-Reasoning

∣-reflexive : _≡_ ⇒ _∣_
∣-reflexive {n} refl = divides 1 (sym (*-identityˡ n))

∣-refl : Reflexive _∣_
∣-refl = ∣-reflexive refl

∣-trans : Transitive _∣_
∣-trans (divides p refl) (divides q refl) =
  divides (q * p) (sym (*-assoc q p _))

∣-antisym : Antisymmetric _≡_ _∣_
∣-antisym {m} {0} _ (divides q eq) = trans eq (*-comm q 0)
∣-antisym {0} {n} (divides p eq) _ = sym (trans eq (*-comm p 0))
∣-antisym {suc m} {suc n} (divides p eq₁) (divides q eq₂) =
  ≤-antisym (∣⇒≤ (divides p eq₁)) (∣⇒≤ (divides q eq₂))

∣-isPreorder : IsPreorder _≡_ _∣_
∣-isPreorder = record
  { isEquivalence = PropEq.isEquivalence
  ; reflexive     = ∣-reflexive
  ; trans         = ∣-trans
  }

∣-preorder : Preorder _ _ _
∣-preorder = record { isPreorder = ∣-isPreorder }

∣-isPartialOrder : IsPartialOrder _≡_ _∣_
∣-isPartialOrder = record
  { isPreorder = ∣-isPreorder
  ; antisym    = ∣-antisym
  }

poset : Poset _ _ _
poset = record
  { Carrier        = ℕ
  ; _≈_            = _≡_
  ; _≤_            = _∣_
  ; isPartialOrder = ∣-isPartialOrder
  }

module ∣-Reasoning = POR poset
  hiding   (_≈⟨_⟩_)
  renaming (_≤⟨_⟩_ to _∣⟨_⟩_)

------------------------------------------------------------------------
-- Simple properties of _∣_

infix 10 1∣_ _∣0

1∣_ : ∀ n → 1 ∣ n
1∣ n = divides n (sym (*-identityʳ n))

_∣0 : ∀ n → n ∣ 0
n ∣0 = divides 0 refl

n∣n : ∀ {n} → n ∣ n
n∣n = ∣-refl

n∣m*n : ∀ m {n} → n ∣ m * n
n∣m*n m = divides m refl

m∣m*n : ∀ {m} n → m ∣ m * n
m∣m*n n = divides n (*-comm _ n)

0∣⇒≡0 : ∀ {n} → 0 ∣ n → n ≡ 0
0∣⇒≡0 {n} 0∣n = ∣-antisym (n ∣0) 0∣n

∣1⇒≡1 : ∀ {n} → n ∣ 1 → n ≡ 1
∣1⇒≡1 {n} n∣1 = ∣-antisym n∣1 (1∣ n)

------------------------------------------------------------------------
-- Operators and divisibility

module _ where

  open PropEq.≡-Reasoning

  ∣m⇒∣m*n : ∀ {i m} n → i ∣ m → i ∣ m * n
  ∣m⇒∣m*n {i} {m} n (divides q eq) = divides (q * n) $ begin
    m * n       ≡⟨ cong (_* n) eq ⟩
    q * i * n   ≡⟨ *-assoc q i n ⟩
    q * (i * n) ≡⟨ cong (q *_) (*-comm i n) ⟩
    q * (n * i) ≡⟨ sym (*-assoc q n i) ⟩
    q * n * i ∎

  ∣n⇒∣m*n : ∀ {i} m {n} → i ∣ n → i ∣ m * n
  ∣n⇒∣m*n {i} m {n} (divides q eq) = divides (m * q) $ begin
    m * n       ≡⟨ cong (m *_) eq ⟩
    m * (q * i) ≡⟨ sym (*-assoc m q i) ⟩
    m * q * i ∎

  ∣m∣n⇒∣m+n : ∀ {i m n} → i ∣ m → i ∣ n → i ∣ m + n
  ∣m∣n⇒∣m+n (divides p refl) (divides q refl) =
    divides (p + q) (sym (*-distribʳ-+ _ p q))

  ∣m+n∣m⇒∣n : ∀ {i m n} → i ∣ m + n → i ∣ m → i ∣ n
  ∣m+n∣m⇒∣n {i} {m} {n} (divides p m+n≡p*i) (divides q m≡q*i) =
    divides (p ∸ q) $ begin
      n             ≡⟨ sym (m+n∸n≡m n m) ⟩
      n + m ∸ m     ≡⟨ cong (_∸ m) (+-comm n m) ⟩
      m + n ∸ m     ≡⟨ cong₂ _∸_ m+n≡p*i m≡q*i ⟩
      p * i ∸ q * i ≡⟨ sym (*-distribʳ-∸ i p q) ⟩
      (p ∸ q) * i   ∎

  ∣m∸n∣n⇒∣m : ∀ i {m n} → n ≤ m → i ∣ m ∸ n → i ∣ n → i ∣ m
  ∣m∸n∣n⇒∣m i {m} {n} n≤m (divides p m∸n≡p*i) (divides q n≡q*o) =
    divides (p + q) $ begin
      m             ≡⟨ sym (m+n∸m≡n n≤m) ⟩
      n + (m ∸ n)   ≡⟨ +-comm n (m ∸ n) ⟩
      m ∸ n + n     ≡⟨ cong₂ _+_ m∸n≡p*i n≡q*o ⟩
      p * i + q * i ≡⟨ sym (*-distribʳ-+ i p q)  ⟩
      (p + q) * i   ∎

  *-cong : ∀ {i j} k → i ∣ j → k * i ∣ k * j
  *-cong {i} {j} k (divides q j≡q*i) = divides q $ begin
    k * j        ≡⟨ cong (_*_ k) j≡q*i ⟩
    k * (q * i)  ≡⟨ sym (*-assoc k q i) ⟩
    (k * q) * i  ≡⟨ cong (_* i) (*-comm k q) ⟩
    (q * k) * i  ≡⟨ *-assoc q k i ⟩
    q * (k * i)  ∎

  /-cong : ∀ {i j} k → suc k * i ∣ suc k * j → i ∣ j
  /-cong {i} {j} k (divides q eq) =
    divides q (*-cancelʳ-≡ j (q * i) (begin
      j * (suc k)        ≡⟨ *-comm j (suc k) ⟩
      (suc k) * j        ≡⟨ eq ⟩
      q * ((suc k) * i)  ≡⟨ cong (q *_) (*-comm (suc k) i) ⟩
      q * (i * (suc k))  ≡⟨ sym (*-assoc q i (suc k)) ⟩
      (q * i) * (suc k)  ∎))

  m%n≡0⇒n∣m : ∀ m n → m % (suc n) ≡ 0 → suc n ∣ m
  m%n≡0⇒n∣m m n eq = divides (m div (suc n)) (begin
    m                                     ≡⟨ a≡a%n+[a/n]*n m n ⟩
    m % (suc n) + m div (suc n) * (suc n) ≡⟨ cong₂ _+_ eq refl ⟩
    m div (suc n) * (suc n)               ∎)

  n∣m⇒m%n≡0 : ∀ m n → suc n ∣ m → m % (suc n) ≡ 0
  n∣m⇒m%n≡0 m n (divides v eq) = begin
    m           % (suc n) ≡⟨ cong (_% (suc n)) eq ⟩
    (v * suc n) % (suc n) ≡⟨ kn%n≡0 v n ⟩
    0                     ∎

  m%n≡0⇔n∣m : ∀ m n → m % (suc n) ≡ 0 ⇔ suc n ∣ m
  m%n≡0⇔n∣m m n = equivalence (m%n≡0⇒n∣m m n) (n∣m⇒m%n≡0 m n)

-- Divisibility is decidable.

infix 4 _∣?_

_∣?_ : Decidable _∣_
zero  ∣? zero   = yes (0 ∣0)
zero  ∣? suc m  = no ((λ()) ∘′ 0∣⇒≡0)
suc n ∣? m      = Dec.map (m%n≡0⇔n∣m m n) (m % (suc n) ≟ 0)

------------------------------------------------------------------------
-- DEPRECATED - please use new names as continuing support for the old
-- names is not guaranteed.

∣-+ = ∣m∣n⇒∣m+n
{-# WARNING_ON_USAGE ∣-+
"Warning: ∣-+ was deprecated in v0.14.
Please use ∣m∣n⇒∣m+n instead."
#-}
∣-∸ = ∣m+n∣m⇒∣n
{-# WARNING_ON_USAGE ∣-∸
"Warning: ∣-∸ was deprecated in v0.14.
Please use ∣m+n∣m⇒∣n instead."
#-}
∣-* = n∣m*n
{-# WARNING_ON_USAGE ∣-*
"Warning: ∣-* was deprecated in v0.14.
Please use n∣m*n instead."
#-}

nonZeroDivisor-lemma : ∀ m q (r : Fin (1 + m)) → toℕ r ≢ 0 →
                       1 + m ∤ toℕ r + q * (1 + m)
nonZeroDivisor-lemma m zero r r≢zero (divides zero eq) = r≢zero $ begin
  toℕ r      ≡⟨ sym (*-identityˡ (toℕ r)) ⟩
  1 * toℕ r  ≡⟨ eq ⟩
  0          ∎
  where open PropEq.≡-Reasoning
nonZeroDivisor-lemma m zero r r≢zero (divides (suc q) eq) =
  i+1+j≰i m $ begin
    m + suc (q * suc m) ≡⟨ +-suc m (q * suc m) ⟩
    suc (m + q * suc m) ≡⟨ sym eq ⟩
    1 * toℕ r           ≡⟨ *-identityˡ (toℕ r) ⟩
    toℕ r               ≤⟨ FP.toℕ≤pred[n] r ⟩
    m                   ∎
  where open ≤-Reasoning
nonZeroDivisor-lemma m (suc q) r r≢zero d =
  nonZeroDivisor-lemma m q r r≢zero (∣m+n∣m⇒∣n d' ∣-refl)
  where
  lem = solve 3 (λ m r q → r :+ (m :+ q)  :=  m :+ (r :+ q))
                refl (suc m) (toℕ r) (q * suc m)
  d' = subst (1 + m ∣_) lem d
{-# WARNING_ON_USAGE nonZeroDivisor-lemma
"Warning: nonZeroDivisor-lemma was deprecated in v0.17."
#-}


{-# OPTIONS --safe --warning=error --without-K #-}

open import Setoids.Setoids
open import Groups.Definition
open import Groups.Abelian.Definition

module Groups.Abelian.DirectSum {a b c d : _} {A : Set a} {B : Set b} {S : Setoid {a} {c} A} {T : Setoid {b} {d} B} {_+1_ : A → A → A} {_+2_ : B → B → B} {G1' : Group S _+1_} {G2' : Group T _+2_} (G1 : AbelianGroup G1') (G2 : AbelianGroup G2') where

open import Groups.DirectSum.Definition G1' G2'
open import Setoids.Product S T

directSumAbGroup : AbelianGroup directSumGroup
AbelianGroup.commutative directSumAbGroup = productLift (AbelianGroup.commutative G1) (AbelianGroup.commutative G2)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Rational Literals
------------------------------------------------------------------------

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

module Data.Rational.Literals where

open import Agda.Builtin.FromNat
open import Agda.Builtin.FromNeg
open import Data.Unit
open import Data.Nat
open import Data.Nat.Coprimality
open import Data.Integer
open import Data.Rational hiding (-_)

fromℤ : ℤ → ℚ
fromℤ z = record
  { numerator     = z
  ; denominator-1 = zero
  ; isCoprime     = sym (1-coprimeTo ∣ z ∣)
  }

number : Number ℚ
number = record
  { Constraint = λ _ → ⊤
  ; fromNat    = λ n → fromℤ (+ n)
  }

negative : Negative ℚ
negative = record
  { Constraint = λ _ → ⊤
  ; fromNeg    = λ n → fromℤ (- (+ n))
  }


module Eval where

open import Coinduction
open import Data.Bool
open import Data.Fin     using (Fin)
import      Data.Fin     as F
open import Data.Product using (Σ; _,_)
import      Data.Product as P
open import Data.Stream  using (Stream; _∷_)
import      Data.Stream  as S
open import Data.Vec     using (Vec; []; _∷_)
import      Data.Vec     as V
open import Relation.Binary.PropositionalEquality

open import Types

⟦_⟧ᵗ : Type → Set
⟦ 𝔹     ⟧ᵗ = Bool
⟦ 𝔹⁺ n  ⟧ᵗ = Vec Bool n
⟦ ℂ τ   ⟧ᵗ = Stream ⟦ τ ⟧ᵗ
⟦ σ ⇒ τ ⟧ᵗ = ⟦ σ ⟧ᵗ → ⟦ τ ⟧ᵗ
⟦ σ × τ ⟧ᵗ = P._×_ ⟦ σ ⟧ᵗ ⟦ τ ⟧ᵗ

data Env : ∀ {n} → Ctx n → Set where
  [] : Env []
  _∷_ : ∀ {n} {Γ : Ctx n} {τ} → ⟦ τ ⟧ᵗ → Env Γ → Env (τ ∷ Γ)

lookupEnv : ∀ {n} {Γ : Ctx n} (i : Fin n) → Env Γ → ⟦ V.lookup i Γ ⟧ᵗ
lookupEnv F.zero (x ∷ env) = x
lookupEnv (F.suc i) (x ∷ env) = lookupEnv i env

private
  runReg : ∀ {σ τ} → (⟦ τ ⟧ᵗ → ⟦ τ ⟧ᵗ P.× ⟦ σ ⟧ᵗ) → ⟦ τ ⟧ᵗ → Stream ⟦ σ ⟧ᵗ
  runReg f s with f s
  runReg f s | s′ , x = x ∷ ♯ (runReg f s′)

_⟦_⟧ : ∀ {n} {Γ : Ctx n} {τ} → Env Γ → Term Γ τ → ⟦ τ ⟧ᵗ
env ⟦ bitI ⟧ = true
env ⟦ bitO ⟧ = false
env ⟦ [] ⟧ = []
env ⟦ x ∷ xs ⟧ = env ⟦ x ⟧ ∷ env ⟦ xs ⟧
env ⟦ x nand y ⟧ with env ⟦ x ⟧ | env ⟦ y ⟧
env ⟦ x nand y ⟧ |  true |  true = false
env ⟦ x nand y ⟧ |     _ |     _ =  true
env ⟦ reg xt ft ⟧ = runReg (env ⟦ ft ⟧) (env ⟦ xt ⟧)
env ⟦ pair xt yt ⟧ = (env ⟦ xt ⟧) , (env ⟦ yt ⟧)
env ⟦ latest t ⟧ = S.head (env ⟦ t ⟧)
env ⟦ head t ⟧ = V.head (env ⟦ t ⟧)
env ⟦ tail t ⟧ = V.tail (env ⟦ t ⟧)
env ⟦ var i refl ⟧ = lookupEnv i env
env ⟦ f ∙ x ⟧ = (env ⟦ f ⟧) (env ⟦ x ⟧)
env ⟦ lam t ⟧ = λ x → (x ∷ env) ⟦ t ⟧


module Function where

import      Lvl
open import Type

-- The domain type of a function.
domain : ∀{ℓ₁ ℓ₂} {A : Type{ℓ₁}}{B : Type{ℓ₂}} → (A → B) → Type{ℓ₁}
domain{A = A} _ = A

-- The codomain type of a function.
codomain : ∀{ℓ₁ ℓ₂} {A : Type{ℓ₁}}{B : Type{ℓ₂}} → (A → B) → Type{ℓ₂}
codomain{B = B} _ = B


open import Common.Prelude renaming (Nat to ℕ; _+_ to _+ℕ_)
open import Common.Product
open import Common.Equality

postulate
  _≤ℕ_ : (n m : ℕ) → Set
  maxℕ : (n m : ℕ) → ℕ

When : (b : Bool) (P : Set) → Set
When true P = P
When false P = ⊤

infix 30 _⊕_
infix 20 _+_
infix 10 _≤_
infix 10 _<_

infixr 4 _,_

mutual

  data Cxt : Set where
    ∘   : Cxt
    _,_ : (Δ : Cxt) (b : Size Δ) → Cxt

  data Var : (Δ : Cxt) → Set where
    vz : ∀{Δ b}             → Var (Δ , b)
    vs : ∀{Δ b} (i : Var Δ) → Var (Δ , b)

  data Base (Δ : Cxt) : Set where
    zero : Base Δ
    ∞    : Base Δ
    var  : Var Δ → Base Δ

  record Size (Δ : Cxt) : Set where
    inductive
    constructor _⊕_
    field h : Base Δ
          n : ℕ

↑ : ∀{Δ} (a : Size Δ) → Size Δ
↑ (h ⊕ n) = h ⊕ suc n

_+_ : ∀{Δ} (a : Size Δ) (m : ℕ) → Size Δ
(h ⊕ n) + m = h ⊕ (n +ℕ m)

_-_ : ∀{Δ} (a : Size Δ) (m : ℕ) → Size Δ
(h ⊕ n) - m = h ⊕ (n ∸ m)

ClosedSize = Size ∘

data _≤_ : (a b : ClosedSize) → Set where
  leZZ : ∀{n m} (p : n ≤ℕ m) → zero ⊕ n ≤ zero ⊕ m
  leZ∞ : ∀{n m}               → zero ⊕ n ≤ ∞ ⊕ m
  le∞∞ : ∀{n m} (p : n ≤ℕ m) → ∞ ⊕ n ≤ ∞ ⊕ m

_<_ : (a b : ClosedSize) → Set
a < b = ↑ a ≤ b

max : (a b : ClosedSize) → ClosedSize
max (zero ⊕ n) (zero ⊕ n') = zero ⊕ maxℕ n n'
max (zero ⊕ n) (∞ ⊕ n') = ∞ ⊕ n'
max (zero ⊕ n) (var () ⊕ n')
max (∞ ⊕ n) (zero ⊕ n') = ∞ ⊕ n
max (∞ ⊕ n) (∞ ⊕ n') =  ∞ ⊕ maxℕ n n'
max (∞ ⊕ n) (var () ⊕ n')
max (var () ⊕ n)

-- Closed size valuation
mutual

  data Val (chk : Bool) : (Δ : Cxt) → Set where
    ∘    : Val chk ∘
    _,_<_∣_ : ∀{Δ} (ξ : Val chk Δ) (a : ClosedSize) b (p : When chk (a < eval ξ b)) → Val chk (Δ , b)

  lookup : ∀{Δ chk} (ξ : Val chk Δ) (x : Var Δ) → ClosedSize
  lookup (ξ , a < b ∣ p) vz     = a
  lookup (ξ , a < b ∣ p) (vs x) = lookup ξ x

  evalBase : ∀{Δ chk} (ξ : Val chk Δ) (a : Base Δ) → ClosedSize
  evalBase ξ zero    = zero ⊕ 0
  evalBase ξ ∞       = ∞ ⊕ 0
  evalBase ξ (var x) = lookup ξ x

  eval : ∀{Δ chk} (ξ : Val chk Δ) (a : Size Δ) → ClosedSize
  eval ξ (h ⊕ n) = evalBase ξ h + n

UVal = Val false  -- unsound valuation
SVal = Val true   -- sound valuation

update : ∀{Δ} (ρ : UVal Δ) (x : Var Δ) (f : ClosedSize → ClosedSize) → UVal Δ
update (ρ , a < b ∣ _) vz     f = ρ , f a < b ∣ _
update (ρ , a < b ∣ _) (vs x) f = update ρ x f , a < b ∣ _

data _≤V_ {chk chk'} : ∀ {Δ} (ρ : Val chk Δ) (ξ : Val chk' Δ) → Set where
  ∘   : ∘ ≤V ∘
  _,_ : ∀{Δ} {ρ : Val chk Δ} {ξ : Val chk' Δ} {a a' b p q}
        (r : ρ ≤V ξ) (s : a ≤ a') → (ρ , a < b ∣ p) ≤V (ξ , a' < b ∣ q)

lemma-str :  ∀ {Δ} (x : Var Δ) {n} {a} {ρ : UVal Δ} {ξ : SVal Δ}
  → (p : ρ ≤V ξ)
  → (q : a < lookup ξ x + n)
  → update ρ x (max (↑ a - n)) ≤V ξ
lemma-str vz     (p , s) q = {!q!}
lemma-str (vs x) (p , s) q = {!q!}