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

open import HoTT
open import homotopy.HSpace
open import homotopy.EM1HSpace
open import homotopy.EilenbergMacLane1 using (EM₁-level₁)
open import lib.types.TwoSemiCategory
open import lib.two-semi-categories.FundamentalCategory

module homotopy.EM1HSpaceAssoc where

module EM₁HSpaceAssoc {i} (G : AbGroup i) where

  private
    module G = AbGroup G
  open EM₁HSpace G public
  private
    module H-⊙EM₁ = HSpaceStructure H-⊙EM₁

  mult-loop-assoc : (g : G.El) (y z : EM₁ G.grp)
    → mult-loop g (mult y z) == ap (λ x' → mult x' z) (mult-loop g y)
  mult-loop-assoc g y z =
    EM₁-prop-elim {P = λ y → mult-loop g (mult y z) == ap (λ x' → mult x' z) (mult-loop g y)}
                  {{λ y → has-level-apply (has-level-apply (EM₁-level₁ G.grp) _ _) _ _}}
                  base' y
    where
    base' : mult-loop g (mult embase z) == ap (λ x' → mult x' z) (mult-loop g embase)
    base' = ! (mult-emloop-β g z)

  H-⊙EM₁-assoc : (x y z : EM₁ G.grp) → mult (mult x y) z == mult x (mult y z)
  H-⊙EM₁-assoc x y z =
    EM₁-set-elim {P = λ x → mult (mult x y) z == mult x (mult y z)}
                 {{λ x → has-level-apply (EM₁-level₁ G.grp) _ _}}
                 idp
                 comp'
                 x
    where
      abstract
        comp' : (g : G.El) → idp == idp [ (λ x → mult (mult x y) z == mult x (mult y z)) ↓ emloop g ]
        comp' g =
          ↓-='-in $
          idp ∙' ap (λ x → mult x (mult y z)) (emloop g)
            =⟨ ∙'-unit-l _ ⟩
          ap (λ x → mult x (mult y z)) (emloop g)
            =⟨ mult-emloop-β g (mult y z) ⟩
          mult-loop g (mult y z)
            =⟨ mult-loop-assoc g y z ⟩
          ap (λ v → mult v z) (mult-loop g y)
            =⟨ ap (ap (λ v → mult v z)) (! (mult-emloop-β g y)) ⟩
          ap (λ v → mult v z) (ap (λ x → mult x y) (emloop g))
            =⟨ ∘-ap (λ v → mult v z) (λ x → mult x y) (emloop g) ⟩
          ap (λ x → mult (mult x y) z) (emloop g)
            =⟨ ! (∙-unit-r _) ⟩
          ap (λ x → mult (mult x y) z) (emloop g) ∙ idp =∎

  H-EM₁-assoc-coh-unit-l : coh-unit-l H-⊙EM₁ H-⊙EM₁-assoc
  H-EM₁-assoc-coh-unit-l x y = =ₛ-in idp

  H-EM₁-assoc-coh-unit-r : coh-unit-r H-⊙EM₁ H-⊙EM₁-assoc
  H-EM₁-assoc-coh-unit-r =
    EM₁-prop-elim {P = λ x → ∀ y → P x y} {{λ x → Π-level (P-level x)}}
      (EM₁-prop-elim {P = P embase} {{P-level embase}} (=ₛ-in idp))
    where
    P : EM₁ G.grp → EM₁ G.grp → Type i
    P = coh-unit-r-eq H-⊙EM₁ H-⊙EM₁-assoc
    P-level : ∀ x y → is-prop (P x y)
    P-level x y = =ₛ-level (EM₁-level₁ G.grp)

  H-EM₁-assoc-coh-unit-l-r-pentagon : coh-unit-l-r-pentagon H-⊙EM₁ H-⊙EM₁-assoc
  H-EM₁-assoc-coh-unit-l-r-pentagon =
    coh-unit-l-to-unit-l-r-pentagon H-⊙EM₁ H-⊙EM₁-assoc H-EM₁-assoc-coh-unit-l

  import homotopy.Pi2HSuspCompose H-⊙EM₁ H-⊙EM₁-assoc H-EM₁-assoc-coh-unit-l-r-pentagon as Pi2EMSuspCompose
  open Pi2EMSuspCompose hiding (comp-functor) public

  abstract
    H-EM₁-assoc-pentagon : coh-assoc-pentagon H-⊙EM₁ H-⊙EM₁-assoc
    H-EM₁-assoc-pentagon w x y z =
      EM₁-prop-elim {P = λ w' → P w' x y z} {{λ w' → P-level w' x y z}}
        (EM₁-prop-elim {P = λ x' → P embase x' y z} {{λ x' → P-level embase x' y z}} (=ₛ-in idp) x)
        w
      where
      P : (w x y z : EM₁ G.grp) → Type i
      P = coh-assoc-pentagon-eq H-⊙EM₁ H-⊙EM₁-assoc
      P-level : ∀ w x y z → is-prop (P w x y z)
      P-level w x y z = =ₛ-level (EM₁-level₁ G.grp)

  EM₁-2-semi-category : TwoSemiCategory lzero i
  EM₁-2-semi-category = HSpace-2-semi-category H-⊙EM₁ H-⊙EM₁-assoc H-EM₁-assoc-pentagon

  comp-functor :
    TwoSemiFunctor
      EM₁-2-semi-category
      (=ₜ-fundamental-cat (Susp (EM₁ G.grp)))
  comp-functor =
    record
    { F₀ = λ _ → [ north ]
    ; F₁ = λ x → [ η x ]
    ; pres-comp = comp
    ; pres-comp-coh = comp-coh
    }
    -- this is *exactly* the same as
    --   `Pi2EMSuspCompose.comp-functor H-EM₁-assoc-pentagon`
    -- inlined but Agda chokes on this shorter definition


module AbsurdLam where

data Nat : Set where
  zero : Nat
  suc  : Nat -> Nat

data Fin : Nat -> Set where
  fzero : forall {n} -> Fin (suc n)
  fsuc  : forall {n} -> Fin n -> Fin (suc n)

data False : Set where

elimFalse : (A : Set) -> False -> A
elimFalse A = \()

data _==_ {A : Set}(x : A) : A -> Set where
  refl : x == x

magic : forall {n} -> suc n == zero -> False
magic = \()

hidden : Nat -> {x : Fin zero} -> False
hidden = \n {}


open import Relation.Binary
open import Relation.Binary.PropositionalEquality

module Category.Monad.Monotone.Identity {i}(pre : Preorder i i i) where

open Preorder pre renaming (Carrier to I; _∼_ to _≤_; refl to ≤-refl)

open import Relation.Unary.PredicateTransformer using (Pt)
open import Category.Monad.Monotone pre
open RawMPMonad

Identity : Pt I i
Identity = λ P i → P i

instance
  id-monad : RawMPMonad Identity
  return id-monad px = px
  _≥=_ id-monad c f = f ≤-refl c


------------------------------------------------------------------------
-- If the "Outrageous but Meaningful Coincidences" approach is used to
-- formalise a language, then you can end up with an extensional type
-- theory (with equality reflection)
------------------------------------------------------------------------

module README.DependentlyTyped.Extensional-type-theory where

import Axiom.Extensionality.Propositional as E
open import Data.Empty
open import Data.Product renaming (curry to c)
open import Data.Unit
open import Data.Universe.Indexed
import deBruijn.Context
open import Function hiding (_∋_) renaming (const to k)
import Level
import Relation.Binary.PropositionalEquality as P

------------------------------------------------------------------------
-- A non-indexed universe

mutual

  data U : Set where
    empty : U
    π     : (a : U) (b : El a → U) → U

  El : U → Set
  El empty   = ⊥
  El (π a b) = (x : El a) → El (b x)

Uni : IndexedUniverse _ _ _
Uni = record { I = ⊤; U = λ _ → U; El = El }

------------------------------------------------------------------------
-- Contexts and variables

-- We get these for free.

open deBruijn.Context Uni public
  renaming (_·_ to _⊙_; ·-cong to ⊙-cong)

-- Some abbreviations.

⟨empty⟩ : ∀ {Γ} → Type Γ
⟨empty⟩ = -, λ _ → empty

⟨π⟩ : ∀ {Γ} (σ : Type Γ) → Type (Γ ▻ σ) → Type Γ
⟨π⟩ σ τ = -, k π ˢ indexed-type σ ˢ c (indexed-type τ)

⟨π̂⟩ : ∀ {Γ} (σ : Type Γ) →
      ((γ : Env Γ) → El (indexed-type σ γ) → U) → Type Γ
⟨π̂⟩ σ τ = -, k π ˢ indexed-type σ ˢ τ

------------------------------------------------------------------------
-- Well-typed terms

mutual

  infixl 9 _·_
  infix  3 _⊢_

  -- Terms.

  data _⊢_ (Γ : Ctxt) : Type Γ → Set where
    var : ∀ {σ} (x : Γ ∋ σ) → Γ ⊢ σ
    ƛ   : ∀ {σ τ} (t : Γ ▻ σ ⊢ τ) → Γ ⊢ ⟨π⟩ σ τ
    _·_ : ∀ {σ τ} (t₁ : Γ ⊢ ⟨π̂⟩ σ τ) (t₂ : Γ ⊢ σ) → Γ ⊢ (-, τ ˢ ⟦ t₂ ⟧)

  -- Semantics of terms.

  ⟦_⟧ : ∀ {Γ σ} → Γ ⊢ σ → Value Γ σ
  ⟦ var x   ⟧ γ = lookup x γ
  ⟦ ƛ t     ⟧ γ = λ v → ⟦ t ⟧ (γ , v)
  ⟦ t₁ · t₂ ⟧ γ = (⟦ t₁ ⟧ γ) (⟦ t₂ ⟧ γ)

------------------------------------------------------------------------
-- We can define looping terms (assuming extensionality)

module Looping (ext : E.Extensionality Level.zero Level.zero) where

  -- The casts are examples of the use of equality reflection: the
  -- casts are meta-language constructions, not object-language
  -- constructions.

  cast₁ : ∀ {Γ} →
          Γ ▻ ⟨empty⟩ ⊢ ⟨π⟩ ⟨empty⟩ ⟨empty⟩ → Γ ▻ ⟨empty⟩ ⊢ ⟨empty⟩
  cast₁ t = P.subst (_⊢_ _) (P.cong (_,_ tt) (ext (⊥-elim ∘ proj₂))) t

  cast₂ : ∀ {Γ} →
          Γ ▻ ⟨empty⟩ ⊢ ⟨empty⟩ → Γ ▻ ⟨empty⟩ ⊢ ⟨π⟩ ⟨empty⟩ ⟨empty⟩
  cast₂ t = P.subst (_⊢_ _) (P.cong (_,_ tt) (ext (⊥-elim ∘ proj₂))) t

  ω : ε ▻ ⟨empty⟩ ⊢ ⟨empty⟩
  ω = cast₁ (ƛ (cast₂ (var zero) · var zero))

  -- A simple example of a term which does not have a normal form.

  Ω : ε ▻ ⟨empty⟩ ⊢ ⟨empty⟩
  Ω = cast₂ ω · ω

  const-Ω : ε ⊢ ⟨π⟩ ⟨empty⟩ ⟨empty⟩
  const-Ω = ƛ Ω

-- Some observations:
--
-- * The language with spines (in README.DependentlyTyped.Term) does
--   not support terms like the ones above; in the casts one would
--   have to prove that distinct spines are equal.
--
-- * The spines ensure that the normaliser in
--   README.DependentlyTyped.NBE terminates. The existence of terms
--   like Ω implies that it is impossible to implement a normaliser
--   for the spine-less language defined in this module.


{-
This module contains:
- constant displayed structures of URG structures
- products of URG structures
-}
{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.DStructures.Structures.Constant where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv

open import Cubical.Data.Sigma

open import Cubical.DStructures.Base
open import Cubical.DStructures.Meta.Properties

open import Cubical.Relation.Binary

private
  variable
    ℓA ℓ≅A ℓB ℓ≅B ℓC ℓ≅C ℓ≅A×B : Level

-- The constant displayed structure of a URG structure 𝒮-B over 𝒮-A
𝒮ᴰ-const : {A : Type ℓA} (𝒮-A : URGStr A ℓ≅A)
           {B : Type ℓB} (𝒮-B : URGStr B ℓ≅B)
           → URGStrᴰ 𝒮-A (λ _ → B) ℓ≅B
𝒮ᴰ-const {A = A} 𝒮-A {B} 𝒮-B
  = urgstrᴰ (λ b _ b' → b ≅ b') ρ uni
    where
      open URGStr 𝒮-B

-- the total structure of the constant structure gives
-- nondependent product of URG structures
_×𝒮_ : {A : Type ℓA} (StrA : URGStr A ℓ≅A)
         {B : Type ℓB} (StrB : URGStr B ℓ≅B)
         → URGStr (A × B) (ℓ-max ℓ≅A ℓ≅B)
_×𝒮_ StrA {B} StrB = ∫⟨ StrA ⟩ (𝒮ᴰ-const StrA StrB)

-- any displayed structure defined over a
-- structure on a product can also be defined
-- over the swapped product
×𝒮-swap :  {A : Type ℓA} {B : Type ℓB} {C : A × B → Type ℓC}
         {ℓ≅A×B ℓ≅ᴰ : Level}
         {StrA×B : URGStr (A × B) ℓ≅A×B}
         (StrCᴰ : URGStrᴰ StrA×B C ℓ≅ᴰ)
         → URGStrᴰ (𝒮-transport Σ-swap-≃ StrA×B)
                   (λ (b , a) → C (a , b))
                   ℓ≅ᴰ
×𝒮-swap {C = C} {ℓ≅ᴰ = ℓ≅ᴰ} {StrA×B = StrA×B} StrCᴰ =
  make-𝒮ᴰ (λ c p c' → c ≅ᴰ⟨ p ⟩ c')
              ρᴰ
              λ (b , a) c → isUnivalent→contrRelSingl (λ c c' → c ≅ᴰ⟨ URGStr.ρ StrA×B (a , b) ⟩ c')
                                                        ρᴰ
                                                        uniᴰ
                                                        c
              where
                open URGStrᴰ StrCᴰ


{-# OPTIONS --cubical --safe #-}

module Strict.Properties where

open import Path
open import Level
open import Strict
open import Agda.Builtin.Strict
open import Function

$!-≡ : {A : Type a} {B : A → Type b} → (f : ∀ x → B x) → ∀ x → (f $! x) ≡ f x
$!-≡ f x = builtin-eq-to-path (primForceLemma x f)

$!≡$ : {A : Type a} {B : A → Type b} → _$!_ {A = A} {B = B} ≡ _$_ {A = A} {B = B}
$!≡$ i f x = $!-≡ f x i


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

open import HoTT
open import cw.CW
open import cw.FinCW
open import cw.FinBoundary
open import cohomology.Theory

module cw.cohomology.cochainequiv.DualizedHigherBoundary (OT : OrdinaryTheory lzero)
  {n} (⊙fin-skel : ⊙FinSkeleton (S (S n))) where

open OrdinaryTheory OT

private
  fin-skel = ⊙FinSkeleton.skel ⊙fin-skel
  I = AttachedFinSkeleton.numCells fin-skel

  fin-skel₋₁ = AttachedFinSkeleton.skel fin-skel
  I₋₁ = AttachedFinSkeleton.numCells fin-skel₋₁

abstract
  rephrase-dualized-higher-boundary-in-degree : ∀ g <I
    → GroupIso.g (FreeAbGroup-extend-iso (C2-abgroup 0))
        (GroupIso.f (FreeAbGroup-extend-iso (C2-abgroup 0)) g ∘ᴳ fboundary-last fin-skel) <I
    == Group.sum (C2 0) (λ <I₋₁ → Group.exp (C2 0) (g <I₋₁) (fdegree-last fin-skel <I <I₋₁))
  rephrase-dualized-higher-boundary-in-degree g <I =
    FormalSum-extend (C2-abgroup 0) g (GroupHom.f (fboundary-last fin-skel) fs[ inl <I :: nil ])
      =⟨ ap (FormalSum-extend (C2-abgroup 0) g) $
          app= (is-equiv.g-f (FreeAbGroup-extend-is-equiv (FreeAbGroup (Fin I₋₁))) (fboundary'-last fin-skel)) <I ⟩
    FormalSum-extend (C2-abgroup 0) g
      (Group.sum (FreeAbGroup.grp (Fin I₋₁))
        (λ <I₋₁ → Group.exp (FreeAbGroup.grp (Fin I₋₁)) fs[ inl <I₋₁ :: nil ] (fdegree-last fin-skel <I <I₋₁)))
      =⟨ GroupHom.pres-sum (FreeAbGroup-extend (C2-abgroup 0) g)
          (λ <I₋₁ → Group.exp (FreeAbGroup.grp (Fin I₋₁)) fs[ inl <I₋₁ :: nil ] (fdegree-last fin-skel <I <I₋₁)) ⟩
    Group.sum (C2 0)
      (λ <I₋₁ →
        (FormalSum-extend (C2-abgroup 0) g
          (Group.exp (FreeAbGroup.grp (Fin I₋₁)) fs[ inl <I₋₁ :: nil ] (fdegree-last fin-skel <I <I₋₁))))
      =⟨ ap (Group.sum (C2 0))
          (λ= λ <I₋₁ →
            GroupHom.pres-exp (FreeAbGroup-extend (C2-abgroup 0) g)
              fs[ inl <I₋₁ :: nil ]
              (fdegree-last fin-skel <I <I₋₁)) ⟩
    Group.sum (C2 0)
      (λ <I₋₁ →
        (Group.exp (C2 0)
          (FormalSum-extend (C2-abgroup 0) g fs[ inl <I₋₁ :: nil ])
          (fdegree-last fin-skel <I <I₋₁)))
      =⟨ ap (Group.sum (C2 0))
          (λ= λ <I₋₁ → ap (λ g → Group.exp (C2 0) g (fdegree-last fin-skel <I <I₋₁)) $
            app= (is-equiv.g-f (FreeAbGroup-extend-is-equiv (C2-abgroup 0)) g) <I₋₁) ⟩
    Group.sum (C2 0) (λ <I₋₁ → (Group.exp (C2 0) (g <I₋₁) (fdegree-last fin-skel <I <I₋₁)))
      =∎


module BasicIS4.Metatheory.Hilbert-BasicKripkeOno where

open import BasicIS4.Syntax.Hilbert public
open import BasicIS4.Semantics.BasicKripkeOno public

-- Soundness with respect to all models, or evaluation.

eval : ∀ {A Γ} → Γ ⊢ A → Γ ⊨ A
eval (var i)           γ = lookup i γ
eval (app {A} {B} t u) γ = _⟪$⟫_ {A} {B} (eval t γ) (eval u γ)
eval ci                γ = K I
eval (ck {A} {B})      γ = K (⟪K⟫ {A} {B})
eval (cs {A} {B} {C})  γ = K (⟪S⟫′ {A} {B} {C})
eval (box t)           γ = λ ζ → eval t ∙
eval (cdist {A} {B})   γ = K (_⟪D⟫′_ {A} {B})
eval (cup {A})         γ = K (⟪↑⟫ {A})
eval (cdown {A})       γ = K (⟪↓⟫ {A})
eval (cpair {A} {B})   γ = K (_⟪,⟫′_ {A} {B})
eval cfst              γ = K π₁
eval csnd              γ = K π₂
eval unit              γ = ∙

-- TODO: Correctness of evaluation with respect to conversion.


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.HITs.Rationals.HITQ.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Relation.Nullary

open import Cubical.HITs.Ints.QuoInt

open import Cubical.Data.Nat as ℕ hiding (_·_)
open import Cubical.Data.NatPlusOne
open import Cubical.Data.Sigma

-- ℚ as a higher inductive type

data ℚ : Type₀ where
  con : (u : ℤ) (a : ℤ) → ¬ (a ≡ pos 0) → ℚ
  path : ∀ u a v b {p q} → (u · b) ≡ (v · a) → con u a p ≡ con v b q
  trunc : isSet ℚ

[_] : ℤ × ℕ₊₁ → ℚ
[ a , 1+ b ] = con a (pos (suc b)) (ℕ.snotz ∘ cong abs)

-- Natural number and negative integer literals for ℚ

open import Cubical.Data.Nat.Literals public

instance
  fromNatℚ : HasFromNat ℚ
  fromNatℚ = record { Constraint = λ _ → Unit ; fromNat = λ n → [ pos n , 1 ] }

instance
  fromNegℚ : HasFromNeg ℚ
  fromNegℚ = record { Constraint = λ _ → Unit ; fromNeg = λ n → [ neg n , 1 ] }


open import Categories
open import Functors
open import RMonads

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

open import Library
open import RAdjunctions
open import RMonads.REM M
open import RMonads.REM.Functors J M

open Fun
open RAdj
open RAlg
open RAlgMorph
open RMonad M
open Cat D

REMAdj : RAdj J EM
REMAdj = record {
  L = REML;
  R = REMR;
  left   = λ f → comp (amor f) η;
  right  = λ{_ B} f → record{
    amor = astr B f;
    ahom = sym (alaw2 B)};
  lawa    = λ {_ Y} f →
    RAlgMorphEq (
      proof
      astr Y (comp (amor f) η)
      ≅⟨ sym (ahom f) ⟩
      comp (amor f) (bind η)
      ≅⟨ cong (comp (amor f)) law1 ⟩
      comp (amor f) iden
      ≅⟨ idr ⟩
      amor f ∎);
  lawb    = λ {_ B} f → sym (alaw1 B);
  natleft = λ f g h →
    proof
    comp (amor g) (comp (comp (amor h) η) (HMap J f))
    ≅⟨ cong (comp (amor g)) ass ⟩
    comp (amor g) (comp (amor h) (comp η (HMap J f)))
    ≅⟨ cong (comp (amor g) ∘ comp (amor h)) (sym law2) ⟩
    comp (amor g) (comp (amor h) (comp (bind (comp η (HMap J f))) η))
    ≅⟨ cong (comp (amor g)) (sym ass) ⟩
    comp (amor g) (comp (comp (amor h) (bind (comp η (HMap J f)))) η)
    ≅⟨ sym ass ⟩
    comp (comp (amor g) (comp (amor h) (bind (comp η (HMap J f))))) η
    ∎;
  natright = λ{W}{X}{Y}{Z} f g h →
    RAlgMorphEq (
      proof
      astr Z (comp (amor g) (comp h (HMap J f)))
      ≅⟨ sym (ahom g) ⟩
      comp (amor g) (astr Y (comp h (HMap J f)))
      ≅⟨ cong (λ h₁ → comp (amor g) (astr Y (comp h₁ (HMap J f)))) (alaw1 Y) ⟩
      comp (amor g) (astr Y (comp (comp (astr Y h) η) (HMap J f)))
      ≅⟨ cong (comp (amor g) ∘ astr Y) ass ⟩
      comp (amor g) (astr Y (comp (astr Y h) (comp η (HMap J f))))
      ≅⟨ cong (comp (amor g)) (alaw2 Y) ⟩
      comp (amor g) (comp (astr Y h) (bind (comp η (HMap J f))))
      ∎)}


open import Mockingbird.Forest using (Forest)

-- The Forest Without a Name
module Mockingbird.Problems.Chapter16 {b ℓ} (forest : Forest {b} {ℓ}) where

open import Data.Product using (_×_; _,_; ∃-syntax)
open import Data.Sum using (_⊎_; inj₁; inj₂)
open import Function using (_$_; _⇔_; Equivalence)
open import Relation.Binary using (_Respects_)
open import Relation.Nullary using (¬_)
open import Relation.Unary using (Pred)

open Forest forest
import Mockingbird.Problems.Chapter14 forest as Chapter₁₄
open import Mockingbird.Problems.Chapter15 forest using (⇔-¬)

module _ {s} (Sings : Pred Bird s)
         (respects : Sings Respects _≈_)
         (LEM : ∀ x → Sings x ⊎ ¬ Sings x)
         (e : Bird)
         (law₁ : ∀ {x y} → Sings (e ∙ x ∙ y) → Sings y)
         (law₂ : ∀ {x y} → Sings (e ∙ x ∙ y) ⇔ (¬ Sings x))
         (law₃ : ∀ {x y} → Sings (e ∙ x ∙ y) ⇔ (¬ Sings x × Sings y))
         (law₄ : ∀ x → ∃[ y ] Sings y ⇔ Sings (e ∙ y ∙ x)) where

  IsSilent : Pred Bird s
  IsSilent x = ¬ Sings x

  private
    respects′ : IsSilent Respects _≈_
    respects′ x≈y ¬x-sings y-sings = ¬x-sings $ respects (sym x≈y) y-sings

    LEM′ : ∀ x → IsSilent x ⊎ ¬ IsSilent x
    LEM′ x with LEM x
    ... | inj₁ x-sings = inj₂ λ ¬x-sings → ¬x-sings x-sings
    ... | inj₂ ¬x-sings = inj₁ ¬x-sings

    law₁′ : ∀ {x y} → IsSilent y → IsSilent (e ∙ x ∙ y)
    law₁′ ¬y-sings exy-sings = ¬y-sings $ law₁ exy-sings

    law₂′ : ∀ {x y} → ¬ IsSilent x → IsSilent (e ∙ x ∙ y)
    law₂′ = Equivalence.g $ ⇔-¬ law₂

    law₃′ : ∀ {x y} → IsSilent x → IsSilent (e ∙ x ∙ y) → IsSilent y
    law₃′ {x} {y} ¬x-sings ¬exy-sings y-sings = Equivalence.f (⇔-¬ law₃) ¬exy-sings (¬x-sings , y-sings)

    law₄′ : ∀ x → ∃[ y ] IsSilent y ⇔ IsSilent (e ∙ y ∙ x)
    law₄′ x =
      let (y , y-sings⇔eyx-sings) = law₄ x
      in (y , ⇔-¬ y-sings⇔eyx-sings)

  problem : ∀ x → ¬ Sings x
  problem = Chapter₁₄.problem₁ IsSilent respects′ LEM′ e law₁′ law₂′ law₃′ law₄′


------------------------------------------------------------------------
-- The Agda standard library
--
-- List membership and some related definitions
------------------------------------------------------------------------

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

open import Relation.Binary

module Data.List.Membership.Setoid {c ℓ} (S : Setoid c ℓ) where

open import Function using (_∘_; id; flip)
open import Data.Fin using (Fin; zero; suc)
open import Data.List.Base as List using (List; []; _∷_; length; lookup)
open import Data.List.Relation.Unary.Any
  using (Any; index; map; here; there)
open import Data.Product as Prod using (∃; _×_; _,_)
open import Relation.Unary using (Pred)
open import Relation.Nullary using (¬_)

open Setoid S renaming (Carrier to A)

open Data.List.Relation.Unary.Any using (_∷=_; _─_) public

------------------------------------------------------------------------
-- Definitions

infix 4 _∈_ _∉_

_∈_ : A → List A → Set _
x ∈ xs = Any (x ≈_) xs

_∉_ : A → List A → Set _
x ∉ xs = ¬ x ∈ xs

------------------------------------------------------------------------
-- Operations

mapWith∈ : ∀ {b} {B : Set b}
           (xs : List A) → (∀ {x} → x ∈ xs → B) → List B
mapWith∈ []       f = []
mapWith∈ (x ∷ xs) f = f (here refl) ∷ mapWith∈ xs (f ∘ there)

------------------------------------------------------------------------
-- Finding and losing witnesses

module _ {p} {P : Pred A p} where

  find : ∀ {xs} → Any P xs → ∃ λ x → x ∈ xs × P x
  find (here px)   = (_ , here refl , px)
  find (there pxs) = Prod.map id (Prod.map there id) (find pxs)

  lose : P Respects _≈_ →  ∀ {x xs} → x ∈ xs → P x → Any P xs
  lose resp x∈xs px = map (flip resp px) x∈xs

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

-- Version 0.16

map-with-∈ = mapWith∈
{-# WARNING_ON_USAGE map-with-∈
"Warning: map-with-∈ was deprecated in v0.16.
Please use mapWith∈ instead."
#-}


------------------------------------------------------------------------
-- Simple combinators working solely on and with functions
------------------------------------------------------------------------

module Data.Function where

infixr 9 _∘_ _∘′_ _∘₀_ _∘₁_
infixl 1 _on_ _on₁_
infixl 1 _⟨_⟩_ _⟨_⟩₁_
infixr 0 _-[_]₁-_ _-[_]-_ _$_
infix  0 _∶_ _∶₁_

------------------------------------------------------------------------
-- Types

-- Unary functions on a given set.

Fun₁ : Set → Set
Fun₁ a = a → a

-- Binary functions on a given set.

Fun₂ : Set → Set
Fun₂ a = a → a → a

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

_∘_ : {A : Set} {B : A → Set} {C : {x : A} → B x → Set} →
      (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
      ((x : A) → C (g x))
f ∘ g = λ x → f (g x)

_∘′_ : {A B C : Set} → (B → C) → (A → B) → (A → C)
f ∘′ g = _∘_ f g

_∘₀_ : {A : Set} {B : A → Set} {C : {x : A} → B x → Set₁} →
       (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
       ((x : A) → C (g x))
f ∘₀ g = λ x → f (g x)

_∘₁_ : {A : Set₁} {B : A → Set₁} {C : {x : A} → B x → Set₁} →
       (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
       ((x : A) → C (g x))
f ∘₁ g = λ x → f (g x)

id : {a : Set} → a → a
id x = x

id₁ : {a : Set₁} → a → a
id₁ x = x

const : {a b : Set} → a → b → a
const x = λ _ → x

const₁ : {a : Set₁} {b : Set} → a → b → a
const₁ x = λ _ → x

flip : {A B : Set} {C : A → B → Set} →
       ((x : A) (y : B) → C x y) → ((y : B) (x : A) → C x y)
flip f = λ x y → f y x

flip₁ : {A B : Set} {C : A → B → Set₁} →
        ((x : A) (y : B) → C x y) → ((y : B) (x : A) → C x y)
flip₁ f = λ x y → f y x

-- Note that _$_ is right associative, like in Haskell. If you want a
-- left associative infix application operator, use
-- Category.Functor._<$>_, available from
-- Category.Monad.Identity.IdentityMonad.

_$_ : {a : Set} {b : a → Set} → ((x : a) → b x) → ((x : a) → b x)
f $ x = f x

_⟨_⟩_ : {a b c : Set} → a → (a → b → c) → b → c
x ⟨ f ⟩ y = f x y

_⟨_⟩₁_ : {a b : Set} → a → (a → b → Set) → b → Set
x ⟨ f ⟩₁ y = f x y

_on_ : {a b c : Set} → (b → b → c) → (a → b) → (a → a → c)
_*_ on f = λ x y → f x * f y

_on₁_ : {a b : Set} {c : Set₁} → (b → b → c) → (a → b) → (a → a → c)
_*_ on₁ f = λ x y → f x * f y

_-[_]-_ : {a b c d e : Set} →
          (a → b → c) → (c → d → e) → (a → b → d) → (a → b → e)
f -[ _*_ ]- g = λ x y → f x y * g x y

_-[_]₁-_ : {a b : Set} →
           (a → b → Set) → (Set → Set → Set) → (a → b → Set) →
           (a → b → Set)
f -[ _*_ ]₁- g = λ x y → f x y * g x y

-- In Agda you cannot annotate every subexpression with a type
-- signature. This function can be used instead.
--
-- You should think of the colon as being mirrored around its vertical
-- axis; the type comes first.

_∶_ : (A : Set) → A → A
_ ∶ x = x

_∶₁_ : (A : Set₁) → A → A
_ ∶₁ x = x


-- Properties of insertion sort

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

open import Relation.Binary as B

module Algorithms.List.Sort.Insertion.Properties
  {c ℓ₁ ℓ₂} (DTO : DecTotalOrder c ℓ₁ ℓ₂)
  where

-- agda-stdlib
open import Level

open import Data.Bool hiding (_≤_; _≤?_; _<_)
open import Data.Empty using (⊥; ⊥-elim)
open import Data.List
import      Data.List.Properties as Listₚ
import      Data.Nat as ℕ
import      Data.Nat.Properties as ℕₚ
open import Data.Product hiding (swap)
open import Data.Sum using (_⊎_; inj₁; inj₂)

import      Data.List.Relation.Binary.Equality.Setoid as ListSetoidEquality
import      Data.List.Relation.Binary.Permutation.Setoid as PermutationSetoid
import      Data.List.Relation.Binary.Permutation.Setoid.Properties
  as PermutationSetoidProperties
open import Data.List.Relation.Unary.All as All
import      Data.List.Relation.Unary.All.Properties as Allₚ
open import Data.List.Relation.Unary.Linked as Linked
open import      Data.List.Relation.Unary.Linked.Properties as Linkedₚ
open import Data.List.Relation.Unary.AllPairs as AllPairs
import      Data.List.Relation.Unary.AllPairs.Properties as AllPairsₚ

open import Function.Base using (_∘_; _$_; flip)

import      Relation.Binary.Properties.DecTotalOrder as DecTotalOrderProperties
open import Relation.Binary.PropositionalEquality using (_≡_)
import      Relation.Binary.PropositionalEquality as ≡ hiding ([_])
import      Relation.Binary.Reasoning.Setoid as SetoidReasoning
open import Relation.Nullary
open import Relation.Unary as U

-- agda-misc
open import Algorithms.List.Sort.Common DTO
open import Algorithms.List.Sort.Insertion
open import Experiment.ListRelationProperties
  using (foldr-preservesʳ; Linked-∷⁻ʳ; Linked-resp-≋)

open DecTotalOrder DTO renaming (Carrier to A)
open DecTotalOrderProperties DTO
open PermutationSetoid Eq.setoid renaming (refl to PSrefl; trans to PStrans)
open PermutationSetoidProperties Eq.setoid
open ListSetoidEquality Eq.setoid
open InsertionSortOperation _≤?_

private
  _≰_ : Rel A _
  x ≰ y = ¬ (x ≤ y)

  -- stdlib
  <⇒≤ : ∀ {x y} → x < y → x ≤ y
  <⇒≤ = proj₁

  ≰⇒≥ : ∀ {x y} → x ≰ y → y ≤ x
  ≰⇒≥ = <⇒≤ ∘ ≰⇒>

  ≰∧≱⇒⊥ : ∀ {x y} → x ≰ y → y ≰ x → ⊥
  ≰∧≱⇒⊥ x≰y y≰x = x≰y (≰⇒≥ y≰x)

  ≰⇒≉ : ∀ {x y} → x ≰ y → ¬ (x ≈ y)
  ≰⇒≉ x≰y x≈y = x≰y (≤-respʳ-≈ x≈y refl)

  ≱⇒≉ : ∀ {x y} → y ≰ x → ¬ (x ≈ y)
  ≱⇒≉ x≱y x≈y = ≰⇒≉ x≱y (Eq.sym x≈y)

  -- stdlib
  _≈?_ : ∀ x y → Dec (x ≈ y)
  x ≈? y with x ≤? y | y ≤? x
  ... | yes x≤y | yes y≤x = yes (antisym x≤y y≤x)
  ... | yes x≤y | no  y≰x = no (≱⇒≉ y≰x)
  ... | no  x≰y | yes y≤x = no (≰⇒≉ x≰y)
  ... | no  x≰y | no  y≰x = ⊥-elim $ ≰∧≱⇒⊥ x≰y y≰x

  ≤⇒<∨≈ : ∀ {x y} → x ≤ y → x < y ⊎ x ≈ y
  ≤⇒<∨≈ {x} {y} x≤y with x ≈? y
  ... | yes x≈y = inj₂ x≈y
  ... | no  x≉y = inj₁ (≤∧≉⇒< x≤y x≉y)

insert-permutation : ∀ x xs → insert x xs ↭ x ∷ xs
insert-permutation x []       = ↭-refl
insert-permutation x (y ∷ ys) with x ≤? y
... | true  because _ = ↭-refl
... | false because _ = ↭-trans (prep Eq.refl (insert-permutation x ys))
                      (swap Eq.refl Eq.refl ↭-refl)

insert-pres-Sorted : ∀ x {xs} → Sorted xs → Sorted (insert x xs)
insert-pres-Sorted x {[]}     _ = [-]
insert-pres-Sorted x {y ∷ ys} xs-Sorted with x ≤? y
... | yes x≤y = x≤y ∷ xs-Sorted
... | no  x≰y = Linkedₚ.AllPairs⇒Linked (hd ∷ tl)
  where
  y≤x : y ≤ x
  y≤x = ≰⇒≥ x≰y
  lem : All (y ≤_) ys
  lem = AllPairs.head (toAllPairs xs-Sorted)
  hd : All (y ≤_) (insert x ys)
  hd = All-resp-↭ ≤-respʳ-≈ (↭-sym (insert-permutation x ys)) (y≤x ∷ lem)
  insert[x,ys]-Sorted : Sorted (insert x ys)
  insert[x,ys]-Sorted = insert-pres-Sorted x {ys} (Linked-∷⁻ʳ xs-Sorted)
  tl : AllPairs _≤_ (insert x ys)
  tl = toAllPairs insert[x,ys]-Sorted

sort-Sorted : ∀ xs → Sorted (sort xs)
sort-Sorted xs = foldr-preservesʳ insert-pres-Sorted [] xs

sort-permutation : ∀ xs → sort xs ↭ xs
sort-permutation []       = ↭-refl
sort-permutation (x ∷ xs) = begin
  sort (x ∷ xs)      ≡⟨⟩
  insert x (sort xs) ≈⟨ insert-permutation x (sort xs) ⟩
  x ∷ sort xs        ≈⟨ prep Eq.refl (sort-permutation xs) ⟩
  x ∷ xs             ∎
  where open SetoidReasoning ↭-setoid

insert-cong-≋ : ∀ {x y xs ys} → x ≈ y → xs ≋ ys → insert x xs ≋ insert y ys
insert-cong-≋ {x} {y} {[]}      {[]}      x≈y xs≋ys = x≈y ∷ []
insert-cong-≋ {x} {y} {x₁ ∷ xs} {y₁ ∷ ys} x≈y (x₁≈y₁ ∷ xs≋ys) with x ≤? x₁ | y ≤? y₁
... | yes p | yes p₁ = x≈y ∷ x₁≈y₁ ∷ xs≋ys
... | yes p | no ¬p  = ⊥-elim (¬p (≤-respʳ-≈ x₁≈y₁ (≤-respˡ-≈ x≈y p)))
... | no ¬p | yes p  = ⊥-elim (¬p (≤-respʳ-≈ (Eq.sym x₁≈y₁) (≤-respˡ-≈ (Eq.sym x≈y) p)))
... | no ¬p | no ¬p₁ = x₁≈y₁ ∷ insert-cong-≋ x≈y xs≋ys

insert-stop : ∀ {x y} ys → x ≤ y → insert x (y ∷ ys) ≋ x ∷ y ∷ ys
insert-stop {x} {y} ys x≤y with x ≤? y
... | yes _   = ≋-refl
... | no  x≰y = ⊥-elim (x≰y x≤y)

insert-into : ∀ {x y} ys → x ≰ y → insert x (y ∷ ys) ≋ y ∷ insert x ys
insert-into {x} {y} ys x≰y with x ≤? y
... | yes x≤y = ⊥-elim (x≰y x≤y)
... | no  _   = ≋-refl

Sorted-insert : ∀ {x xs} → Sorted (x ∷ xs) → insert x xs ≋ x ∷ xs
Sorted-insert {x} {[]}     _         = ≋-refl
Sorted-insert {x} {y ∷ ys} (x≤y ∷ _) = insert-stop ys x≤y

sort-Sorted-id : ∀ xs → Sorted xs → sort xs ≋ xs
sort-Sorted-id []       xs-Sorted   = ≋-refl
sort-Sorted-id (x ∷ xs) x∷xs-Sorted = begin
  sort (x ∷ xs)
    ≡⟨⟩
  insert x (sort xs)
    ≈⟨ insert-cong-≋ Eq.refl
                     (sort-Sorted-id xs (Linked-∷⁻ʳ x∷xs-Sorted)) ⟩
  insert x xs
    ≈⟨ Sorted-insert x∷xs-Sorted ⟩
  x ∷ xs
    ∎
  where open SetoidReasoning ≋-setoid

sort-idem : ∀ xs → sort (sort xs) ≋ sort xs
sort-idem xs = sort-Sorted-id (sort xs) (sort-Sorted xs)

module _ where
  open SetoidReasoning ≋-setoid

  insert-swap : ∀ x y xs → insert x (insert y xs) ≋ insert y (insert x xs)
  insert-swap x y [] with x ≤? y | y ≤? x
  ... | yes x≤y | yes y≤x = x≈y ∷ Eq.sym x≈y ∷ []
    where x≈y = antisym x≤y y≤x
  ... | yes _   | no  _   = ≋-refl
  ... | no  _   | yes _   = ≋-refl
  ... | no  x≰y | no  y≰x = ⊥-elim $ ≰∧≱⇒⊥ x≰y y≰x
  insert-swap x y (z ∷ zs) with y ≤? z | x ≤? z
  insert-swap x y (z ∷ zs) | yes _   | yes _ with x ≤? y | y ≤? x
  insert-swap x y (z ∷ zs) | yes y≤z | yes x≤z | yes x≤y | yes y≤x =
    x≈y ∷ Eq.sym x≈y ∷ ≋-refl
    where x≈y = antisym x≤y y≤x
  insert-swap x y (z ∷ zs) | yes y≤z | yes _   | yes _  | no _ = begin
    x ∷ y ∷ z ∷ zs        ≈˘⟨ Eq.refl ∷ insert-stop zs y≤z ⟩
    x ∷ insert y (z ∷ zs) ∎
  insert-swap x y (z ∷ zs) | yes _   | yes x≤z | no _   | yes _ = begin
    y ∷ insert x (z ∷ zs) ≈⟨ Eq.refl ∷ insert-stop zs x≤z ⟩
    y ∷ x ∷ z ∷ zs        ∎
  insert-swap x y (z ∷ zs) | yes y≤z | yes x≤z | no x≰y | no y≰x =
    ⊥-elim $ ≰∧≱⇒⊥ x≰y y≰x
  insert-swap x y (z ∷ zs) | yes _   | no  _ with x ≤? y
  insert-swap x y (z ∷ zs) | yes y≤z | no  x≰z | yes x≤y =
    ⊥-elim $ x≰z (trans x≤y y≤z)
  insert-swap x y (z ∷ zs) | yes y≤z | no  x≰z | no  x≰y = begin
    y ∷ insert x (z ∷ zs)      ≈⟨ Eq.refl ∷ insert-into zs x≰z ⟩
    y ∷ z ∷ insert x zs        ≈˘⟨ insert-stop (insert x zs) y≤z ⟩
    insert y (z ∷ insert x zs) ∎
  insert-swap x y (z ∷ zs) | no _   | yes _ with y ≤? x
  insert-swap x y (z ∷ zs) | no y≰z | yes x≤z | yes y≤x =
    ⊥-elim (y≰z (trans y≤x x≤z))
  insert-swap x y (z ∷ zs) | no y≰z | yes x≤z | no  _   = begin
    insert x (z ∷ insert y zs) ≈⟨ insert-stop (insert y zs) x≤z ⟩
    x ∷ z ∷ insert y zs        ≈˘⟨ Eq.refl ∷ insert-into zs y≰z ⟩
    x ∷ insert y (z ∷ zs)      ∎
  insert-swap x y (z ∷ zs) | no y≰z | no  x≰z = begin
    insert x (z ∷ insert y zs) ≈⟨ insert-into (insert y zs) x≰z ⟩
    z ∷ insert x (insert y zs) ≈⟨ Eq.refl ∷ insert-swap x y zs ⟩
    z ∷ insert y (insert x zs) ≈˘⟨ insert-into (insert x zs) y≰z ⟩
    insert y (z ∷ insert x zs) ∎

sort-cong-↭-≋ : ∀ {xs ys} → xs ↭ ys → sort xs ≋ sort ys
sort-cong-↭-≋ {xs}         {.xs}          PSrefl               = ≋-refl
sort-cong-↭-≋ {_ ∷ _}      {_ ∷ _}        (prep eq xs↭ys)      =
  insert-cong-≋ eq (sort-cong-↭-≋ xs↭ys)
sort-cong-↭-≋ {x ∷ y ∷ xs} {y′ ∷ x′ ∷ ys} (swap eq₁ eq₂ xs↭ys) = begin
  insert x  (insert y  (sort xs)) ≈⟨ insert-cong-≋ eq₁
                                    (insert-cong-≋ eq₂ (sort-cong-↭-≋ xs↭ys)) ⟩
  insert x′ (insert y′ (sort ys)) ≈⟨ insert-swap x′ y′ (sort ys) ⟩
  insert y′ (insert x′ (sort ys)) ∎
  where open SetoidReasoning ≋-setoid
sort-cong-↭-≋ {xs} {ys} (PStrans {ys = zs} xs↭zs zs↭ys) = begin
  sort xs ≈⟨ sort-cong-↭-≋ xs↭zs ⟩
  sort zs ≈⟨ sort-cong-↭-≋ zs↭ys ⟩
  sort ys ∎
  where open SetoidReasoning ≋-setoid

Sorted-unique : ∀ {xs ys} → xs ↭ ys → Sorted xs → Sorted ys → xs ≋ ys
Sorted-unique {xs} {ys} xs↭ys ixs iys = begin
  xs      ≈⟨ ≋-sym $ sort-Sorted-id xs ixs ⟩
  sort xs ≈⟨ sort-cong-↭-≋ xs↭ys ⟩
  sort ys ≈⟨ sort-Sorted-id ys iys ⟩
  ys      ∎
  where open SetoidReasoning ≋-setoid


module RewriteAndUniversePolymorphism where

open import Common.Equality

data ℕ : Set where
  zero : ℕ
  suc  : ℕ → ℕ

test : (a b : ℕ) → a ≡ b → b ≡ a
test a b eq rewrite eq = refl


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

module Categories.Bicategory.Bigroupoid where

open import Level
open import Function using (_$_)
open import Data.Product using (Σ; _,_)

open import Categories.Category
open import Categories.Category.Equivalence using (WeakInverse)
open import Categories.Category.Product
open import Categories.Category.Groupoid using (IsGroupoid)
open import Categories.Bicategory
open import Categories.Functor renaming (id to idF)
open import Categories.Functor.Properties
open import Categories.Functor.Bifunctor.Properties
open import Categories.Functor.Construction.Constant
open import Categories.NaturalTransformation using (NaturalTransformation; ntHelper)
open import Categories.NaturalTransformation.NaturalIsomorphism using (_≃_; NaturalIsomorphism)

import Categories.Morphism as Mor
import Categories.Morphism.Properties as MP
import Categories.Morphism.Reasoning as MR

-- https://link.springer.com/article/10.1023/A:1011270417127
record IsBigroupoid {o ℓ e t} (C : Bicategory o ℓ e t) : Set (o ⊔ ℓ ⊔ e ⊔ t) where
  open Bicategory C public

  field
    hom-isGroupoid : ∀ A B → IsGroupoid (hom A B)
    hom[_,_]⁻¹     : ∀ A B → Functor (hom A B) (hom B A)
    cancel         : ∀ A B → ⊚ ∘F (hom[ A , B ]⁻¹ ※ idF) ≃ const id₁
    cancel′        : ∀ A B → ⊚ ∘F (idF ※ hom[ A , B ]⁻¹) ≃ const id₁

  module hom⁻¹ {A B}   = Functor (hom[ A , B ]⁻¹)
  module cancel {A B}  = NaturalIsomorphism (cancel A B)
  module cancel′ {A B} = NaturalIsomorphism (cancel′ A B)

  infix 13 _⁻¹ _⁻¹′

  _⁻¹ : ∀ {A B} → A ⇒₁ B → B ⇒₁ A
  _⁻¹ = hom⁻¹.F₀

  _⁻¹′ : ∀ {A B} {f g : A ⇒₁ B} → f ⇒₂ g → f ⁻¹ ⇒₂ g ⁻¹
  _⁻¹′ = hom⁻¹.F₁

  open hom.Commutation

  field
    pentagon₁ : ∀ {A B} {f : A ⇒₁ B} →
                  [ (f ∘ₕ f ⁻¹) ∘ₕ f ⇒ f ]⟨
                    associator.from      ⇒⟨ f ∘ₕ f ⁻¹ ∘ₕ f ⟩
                    f ▷ cancel.⇒.η f     ⇒⟨ f ∘ₕ id₁ ⟩
                    unitorʳ.from
                  ≈ cancel′.⇒.η f ◁ f    ⇒⟨ id₁ ∘ₕ f ⟩
                    unitorˡ.from
                  ⟩
    pentagon₂ : ∀ {A B} {f : A ⇒₁ B} →
                  [ (f ⁻¹ ∘ₕ f) ∘ₕ f ⁻¹ ⇒ f ⁻¹ ]⟨
                    associator.from            ⇒⟨ f ⁻¹ ∘ₕ f ∘ₕ f ⁻¹ ⟩
                    f ⁻¹ ▷ cancel′.⇒.η f       ⇒⟨ f ⁻¹ ∘ₕ id₁ ⟩
                    unitorʳ.from
                  ≈ cancel.⇒.η f ◁ f ⁻¹        ⇒⟨ id₁ ∘ₕ f ⁻¹ ⟩
                    unitorˡ.from
                  ⟩

  private
    variable
      A B : Obj
      f g : A ⇒₁ B
      α β : f ⇒₂ g

    open hom.HomReasoning
    module MR′ {A B} where
      open MR (hom A B) public
      open Mor (hom A B) public
      open MP (hom A B) public
    open MR′
    module ℱ = Functor

  cancel-comm : ∀ {α : f ⇒₂ g} → cancel.⇒.η g ∘ᵥ (α ⁻¹′ ⊚₁ α) ≈ cancel.⇒.η f
  cancel-comm {α = α} = cancel.⇒.commute α ○ identity₂ˡ

  cancel⁻¹-comm : ∀ {α : f ⇒₂ g} → (α ⁻¹′ ⊚₁ α) ∘ᵥ cancel.⇐.η f ≈ cancel.⇐.η g
  cancel⁻¹-comm {α = α} = ⟺ (cancel.⇐.commute α) ○ identity₂ʳ

  cancel′-comm : ∀ {α : f ⇒₂ g} → cancel′.⇒.η g ∘ᵥ (α ⊚₁ α ⁻¹′) ≈ cancel′.⇒.η f
  cancel′-comm {α = α} = cancel′.⇒.commute α ○ identity₂ˡ

  cancel′⁻¹-comm : ∀ {α : f ⇒₂ g} → (α ⊚₁ α ⁻¹′) ∘ᵥ cancel′.⇐.η f ≈ cancel′.⇐.η g
  cancel′⁻¹-comm {α = α} = ⟺ (cancel′.⇐.commute α) ○ identity₂ʳ

  hom⁻¹⁻¹≃id : ∀ {A B} → hom[ B , A ]⁻¹ ∘F hom[ A , B ]⁻¹ ≃ idF
  hom⁻¹⁻¹≃id {A} {B} = record
    { F⇒G = ntHelper record
      { η       = λ f → (((unitorˡ.from ∘ᵥ cancel.⇒.η (f ⁻¹) ◁ f) ∘ᵥ associator.to) ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
      ; commute = λ {f g} α → begin
        ((((unitorˡ.from ∘ᵥ cancel.⇒.η (g ⁻¹) ◁ g) ∘ᵥ associator.to) ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇐.η g) ∘ᵥ unitorʳ.to) ∘ᵥ α ⁻¹′ ⁻¹′
          ≈˘⟨ pushʳ ◁-∘ᵥ-λ⁻¹ ⟩
        (((unitorˡ.from ∘ᵥ cancel.⇒.η (g ⁻¹) ◁ g) ∘ᵥ associator.to) ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇐.η g) ∘ᵥ ((α ⁻¹′ ⁻¹′ ◁ id₁) ∘ᵥ unitorʳ.to)
          ≈⟨ center ◁-▷-exchg ⟩
        ((unitorˡ.from ∘ᵥ cancel.⇒.η (g ⁻¹) ◁ g) ∘ᵥ associator.to) ∘ᵥ (α ⁻¹′ ⁻¹′ ◁ (g ⁻¹ ∘ₕ g) ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η g) ∘ᵥ unitorʳ.to
          ≈⟨ center (⟺ assoc₂ ○ hom.∘-resp-≈ assoc⁻¹-◁-∘ₕ (ℱ.F-resp-≈ ((f ⁻¹ ⁻¹) ⊚-) (⟺ cancel⁻¹-comm))) ⟩
        (unitorˡ.from ∘ᵥ cancel.⇒.η (g ⁻¹) ◁ g) ∘ᵥ ((α ⁻¹′ ⁻¹′ ◁ g ⁻¹ ◁ g ∘ᵥ associator.to) ∘ᵥ f ⁻¹ ⁻¹ ▷ ((α ⁻¹′ ⊚₁ α) ∘ᵥ cancel.⇐.η f)) ∘ᵥ unitorʳ.to
          ≈⟨ refl ⟩∘⟨ (hom.∘-resp-≈ʳ (ℱ.homomorphism ((f ⁻¹ ⁻¹) ⊚-)) ○ center (⊚-assoc.⇐.commute _) ○ center⁻¹ ([ ⊚ ]-merge (⟺ [ ⊚ ]-decompose₁) identity₂ˡ) refl) ⟩∘⟨ refl ⟩
        (unitorˡ.from ∘ᵥ cancel.⇒.η (g ⁻¹) ◁ g) ∘ᵥ (((α ⁻¹′ ⁻¹′ ⊚₁ α ⁻¹′) ⊚₁ α) ∘ᵥ associator.to ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
          ≈˘⟨ assoc₂ ⟩
        ((unitorˡ.from ∘ᵥ cancel.⇒.η (g ⁻¹) ◁ g) ∘ᵥ (((α ⁻¹′ ⁻¹′ ⊚₁ α ⁻¹′) ⊚₁ α) ∘ᵥ associator.to ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f)) ∘ᵥ unitorʳ.to
          ≈⟨ center ([ ⊚ ]-merge cancel-comm identity₂ˡ) ⟩∘⟨refl ⟩
        (unitorˡ.from ∘ᵥ cancel.⇒.η (f ⁻¹) ⊚₁ α ∘ᵥ associator.to ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
          ≈˘⟨ (assoc₂ ○ assoc₂) ⟩∘⟨refl ⟩
        (((unitorˡ.from ∘ᵥ cancel.⇒.η (f ⁻¹) ⊚₁ α) ∘ᵥ associator.to) ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
          ≈⟨ (hom.∘-resp-≈ʳ [ ⊚ ]-decompose₂) ⟩∘⟨refl ⟩∘⟨refl ⟩∘⟨refl ⟩
        (((unitorˡ.from ∘ᵥ id₁ ▷ α ∘ᵥ cancel.⇒.η (f ⁻¹) ◁ f) ∘ᵥ associator.to) ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
          ≈⟨ pullˡ ρ-∘ᵥ-▷ ⟩∘⟨refl ⟩∘⟨refl ⟩∘⟨refl ⟩
        ((((α ∘ᵥ unitorˡ.from) ∘ᵥ cancel.⇒.η (f ⁻¹) ◁ f) ∘ᵥ associator.to) ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
          ≈⟨ (assoc₂ ○ assoc₂ ○ assoc₂ ○ assoc₂) ⟩
        α ∘ᵥ unitorˡ.from ∘ᵥ cancel.⇒.η (f ⁻¹) ◁ f ∘ᵥ associator.to ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f ∘ᵥ unitorʳ.to
          ≈˘⟨ refl⟩∘⟨ (assoc₂ ○ assoc₂ ○ assoc₂) ⟩
        α ∘ᵥ (((unitorˡ.from ∘ᵥ cancel.⇒.η (f ⁻¹) ◁ f) ∘ᵥ associator.to) ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇐.η f) ∘ᵥ unitorʳ.to
          ∎
      }
    ; F⇐G = ntHelper record
      { η       = λ f → unitorʳ.from ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇒.η f ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (f ⁻¹) ◁ f ∘ᵥ unitorˡ.to
      ; commute = λ {f g} α → begin
        (unitorʳ.from ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇒.η g ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (g ⁻¹) ◁ g ∘ᵥ unitorˡ.to) ∘ᵥ α
          ≈⟨ assoc₂ ○ hom.∘-resp-≈ʳ (assoc₂ ○ hom.∘-resp-≈ʳ (assoc₂ ○ hom.∘-resp-≈ʳ assoc₂)) ⟩
        unitorʳ.from ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇒.η g ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (g ⁻¹) ◁ g ∘ᵥ unitorˡ.to ∘ᵥ α
          ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ ⟺ ▷-∘ᵥ-ρ⁻¹ ⟩
        unitorʳ.from ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇒.η g ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (g ⁻¹) ◁ g ∘ᵥ id₁ ▷ α ∘ᵥ unitorˡ.to
          ≈⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ pullˡ (⟺ [ ⊚ ]-decompose₁ ○ ⊚.F-resp-≈ (⟺ cancel⁻¹-comm , refl)) ⟩
        unitorʳ.from ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇒.η g ∘ᵥ associator.from ∘ᵥ (α ⁻¹′ ⁻¹′ ⊚₁ α ⁻¹′ ∘ᵥ cancel.⇐.η (f ⁻¹)) ⊚₁ α ∘ᵥ unitorˡ.to
          ≈˘⟨ refl⟩∘⟨ refl⟩∘⟨ refl⟩∘⟨ hom.∘-resp-≈ˡ ([ ⊚ ]-merge refl identity₂ʳ) ⟩
        unitorʳ.from ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇒.η g ∘ᵥ associator.from ∘ᵥ ((α ⁻¹′ ⁻¹′ ⊚₁ α ⁻¹′) ⊚₁ α ∘ᵥ cancel.⇐.η (f ⁻¹) ◁ f) ∘ᵥ unitorˡ.to
          ≈⟨ refl⟩∘⟨ refl⟩∘⟨ center⁻¹ (⊚-assoc.⇒.commute _) refl ⟩
        unitorʳ.from ∘ᵥ g ⁻¹ ⁻¹ ▷ cancel.⇒.η g ∘ᵥ (α ⁻¹′ ⁻¹′ ⊚₁ α ⁻¹′ ⊚₁ α ∘ᵥ associator.from) ∘ᵥ cancel.⇐.η (f ⁻¹) ◁ f ∘ᵥ unitorˡ.to
          ≈⟨ refl⟩∘⟨ (hom.∘-resp-≈ʳ assoc₂ ○ pullˡ ([ ⊚ ]-merge identity₂ˡ cancel-comm)) ⟩
        unitorʳ.from ∘ᵥ (α ⁻¹′ ⁻¹′) ⊚₁ (cancel.⇒.η f) ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (f ⁻¹) ◁ f ∘ᵥ unitorˡ.to
          ≈⟨ refl⟩∘⟨ (hom.∘-resp-≈ˡ [ ⊚ ]-decompose₁ ○ assoc₂) ⟩
        unitorʳ.from ∘ᵥ (α ⁻¹′ ⁻¹′) ◁ id₁ ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇒.η f ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (f ⁻¹) ◁ f ∘ᵥ unitorˡ.to
          ≈⟨ (pullˡ λ-∘ᵥ-◁) ○ assoc₂ ⟩
        α ⁻¹′ ⁻¹′ ∘ᵥ unitorʳ.from ∘ᵥ f ⁻¹ ⁻¹ ▷ cancel.⇒.η f ∘ᵥ associator.from ∘ᵥ cancel.⇐.η (f ⁻¹) ◁ f ∘ᵥ unitorˡ.to
          ∎
      }
    ; iso = λ f → Iso-∘ (Iso-swap (unitʳ.iso _)) $
                  Iso-∘ ([ (f ⁻¹ ⁻¹) ⊚- ]-resp-Iso (Iso-swap (cancel.iso f))) $
                  Iso-∘ (Iso-swap associator.iso) $
                  Iso-∘ ([ -⊚ f ]-resp-Iso (cancel.iso _))
                        (unitˡ.iso _)
    }

  hom⁻¹-weakInverse : ∀ {A B} → WeakInverse hom[ A , B ]⁻¹ hom[ B , A ]⁻¹
  hom⁻¹-weakInverse = record { F∘G≈id = hom⁻¹⁻¹≃id ; G∘F≈id = hom⁻¹⁻¹≃id }

  module hom⁻¹-weakInverse {A B} = WeakInverse (hom⁻¹-weakInverse {A} {B})

  open hom⁻¹-weakInverse using () renaming (F⊣G to hom⁻¹-⊣Equivalence) public

-- A bigroupoid is a bicategory that has a bigroupoid structure

record Bigroupoid (o ℓ e t : Level) : Set (suc (o ⊔ ℓ ⊔ e ⊔ t)) where
  field
    bicategory   : Bicategory o ℓ e t
    isBigroupoid : IsBigroupoid bicategory

  open IsBigroupoid isBigroupoid public


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

open import Base
open import Algebra.Groups

{-
    The definition of G-sets.  Thanks to Daniel Grayson.
-}

module Algebra.GroupSets {i} (grp : group i) where

  private
    module G = group grp

  -- The right group action with respect to the group [grp].
  -- [Y] should be some set, but that condition is not needed
  -- in the definition.
  record action (Y : Set i) : Set i where
    constructor act[_,_,_]
    field
      _∙_ : Y → G.carrier → Y
      right-unit : ∀ y → y ∙ G.e ≡ y
      assoc : ∀ y p₁ p₂ → (y ∙ p₁) ∙ p₂ ≡ y ∙ (p₁ G.∙ p₂)

  {-
  action-eq : ∀ {Y} ⦃ _ : is-set Y ⦄ {act₁ act₂ : action Y}
    → action._∙_ act₁ ≡ action._∙_ act₂ → act₁ ≡ act₂
  action-eq ∙≡ =
  -}

  -- The definition of a G-set.  A set [carrier] equipped with
  -- a right group action with respect to [grp].
  record gset : Set (suc i) where
    constructor gset[_,_,_]
    field
      carrier : Set i
      act : action carrier
      set : is-set carrier
    open action act public

  -- A convenient tool to compare two G-sets.  Many data are
  -- just props and this function do the coversion for them
  -- for you.  You only need to show the non-trivial parts.
  gset-eq : ∀ {gs₁ gs₂ : gset} (carrier≡ : gset.carrier gs₁ ≡ gset.carrier gs₂)
    → (∙≡ : transport (λ Y → Y → G.carrier → Y) carrier≡ (action._∙_ (gset.act gs₁))
          ≡ action._∙_ (gset.act gs₂))
    → gs₁ ≡ gs₂
  gset-eq
    {gset[  Y , act[  ∙ , unit₁ , assoc₁ ] , set₁ ]}
    {gset[ ._ , act[ ._ , unit₂ , assoc₂ ] , set₂ ]}
    refl refl =
      gset[ Y , act[ ∙ , unit₁ , assoc₁ ] , set₁ ]
        ≡⟨ ap (λ unit → gset[ Y , act[ ∙ , unit , assoc₁ ] , set₁ ])
              $ prop-has-all-paths (Π-is-prop λ _ → set₁ _ _) _ _ ⟩
      gset[ Y , act[ ∙ , unit₂ , assoc₁ ] , set₁ ]
        ≡⟨ ap (λ assoc → gset[ Y , act[ ∙ , unit₂ , assoc ] , set₁ ])
              $ prop-has-all-paths (Π-is-prop λ _ → Π-is-prop λ _ → Π-is-prop λ _ → set₁ _ _) _ _ ⟩
      gset[ Y , act[ ∙ , unit₂ , assoc₂ ] , set₁ ]
        ≡⟨ ap (λ set → gset[ Y , act[ ∙ , unit₂ , assoc₂ ] , set ])
              $ prop-has-all-paths is-set-is-prop _ _ ⟩∎
      gset[ Y , act[ ∙ , unit₂ , assoc₂ ] , set₂ ]
        ∎


{-# OPTIONS --without-K #-}
module function.core where

open import level

-- copied from Agda's standard library

infixr 9 _∘'_
infixr 0 _$_

_∘'_ : ∀ {a b c}
      {A : Set a} {B : A → Set b} {C : {x : A} → B x → Set c} →
      (∀ {x} (y : B x) → C y) → (g : (x : A) → B x) →
      ((x : A) → C (g x))
f ∘' g = λ x → f (g x)

const : ∀ {a b} {A : Set a} {B : Set b} → A → B → A
const x = λ _ → x

_$_ : ∀ {a b} {A : Set a} {B : A → Set b} →
      ((x : A) → B x) → ((x : A) → B x)
f $ x = f x

flip : ∀ {a b c} {A : Set a} {B : Set b} {C : A → B → Set c} →
       ((x : A) (y : B) → C x y) → ((y : B) (x : A) → C x y)
flip f = λ y x → f x y

record Composition u₁ u₂ u₃ u₁₂ u₂₃ u₁₃
        : Set (lsuc (u₁ ⊔ u₂ ⊔ u₃ ⊔ u₁₂ ⊔ u₂₃ ⊔ u₁₃)) where
  infixl 9 _∘_
  field
    U₁ : Set u₁
    U₂ : Set u₂
    U₃ : Set u₃

    hom₁₂ : U₁ → U₂ → Set u₁₂
    hom₂₃ : U₂ → U₃ → Set u₂₃
    hom₁₃ : U₁ → U₃ → Set u₁₃

    _∘_ : {X₁ : U₁}{X₂ : U₂}{X₃ : U₃} → hom₂₃ X₂ X₃ → hom₁₂ X₁ X₂ → hom₁₃ X₁ X₃

record Identity u e : Set (lsuc (u ⊔ e)) where
  field
    U : Set u
    endo : U → Set e
    id : {X : U} → endo X

  id- : (X : U) → endo X
  id- X = id {X}

instance
  func-comp : ∀ {i j k} → Composition _ _ _ _ _ _
  func-comp {i}{j}{k} = record
    { U₁ = Set i
    ; U₂ = Set j
    ; U₃ = Set k
    ; hom₁₂ = λ X Y → X → Y
    ; hom₂₃ = λ X Y → X → Y
    ; hom₁₃ = λ X Y → X → Y
    ; _∘_ = λ f g x → f (g x) }

instance
  func-id : ∀ {i} → Identity _ _
  func-id {i} = record
    { U = Set i
    ; endo = λ X → X → X
    ; id = λ x → x }

module ComposeInterface {u₁ u₂ u₃ u₁₂ u₂₃ u₁₃}
                        ⦃ comp : Composition u₁ u₂ u₃ u₁₂ u₂₃ u₁₃ ⦄ where
  open Composition comp public using (_∘_)
open ComposeInterface public

module IdentityInterface {i e}
                         ⦃ identity : Identity i e ⦄ where
  open Identity identity public using (id; id-)
open IdentityInterface public


data M : Set where
  m : (I : _) → (I → M) → M

-- inferred
-- m : (I : Set) → (I → M) → M


-- Basic intuitionistic propositional calculus, without ∨ or ⊥.
-- Hilbert-style formalisation of closed syntax.
-- Nested terms.

module BasicIPC.Syntax.ClosedHilbert where

open import BasicIPC.Syntax.Common public

-- Derivations.

infix 3 ⊢_
data ⊢_ : Ty → Set where
  app   : ∀ {A B}   → ⊢ A ▻ B → ⊢ A → ⊢ B
  ci    : ∀ {A}     → ⊢ A ▻ A
  ck    : ∀ {A B}   → ⊢ A ▻ B ▻ A
  cs    : ∀ {A B C} → ⊢ (A ▻ B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
  cpair : ∀ {A B}   → ⊢ A ▻ B ▻ A ∧ B
  cfst  : ∀ {A B}   → ⊢ A ∧ B ▻ A
  csnd  : ∀ {A B}   → ⊢ A ∧ B ▻ B
  unit  : ⊢ ⊤

infix 3 ⊢⋆_
⊢⋆_ : Cx Ty → Set
⊢⋆ ∅     = 𝟙
⊢⋆ Ξ , A = ⊢⋆ Ξ × ⊢ A

-- Cut and multicut.

cut : ∀ {A B} → ⊢ A → ⊢ A ▻ B → ⊢ B
cut t u = app u t

multicut : ∀ {Ξ A} → ⊢⋆ Ξ → ⊢ Ξ ▻⋯▻ A → ⊢ A
multicut {∅}     ∙        u = u
multicut {Ξ , B} (ts , t) u = app (multicut ts u) t

-- Contraction.

ccont : ∀ {A B} → ⊢ (A ▻ A ▻ B) ▻ A ▻ B
ccont = app (app cs cs) (app ck ci)

cont : ∀ {A B} → ⊢ A ▻ A ▻ B → ⊢ A ▻ B
cont t = app ccont t

-- Exchange, or Schönfinkel’s C combinator.

cexch : ∀ {A B C} → ⊢ (A ▻ B ▻ C) ▻ B ▻ A ▻ C
cexch = app (app cs (app (app cs (app ck cs))
                         (app (app cs (app ck ck)) cs)))
            (app ck ck)

exch : ∀ {A B C} → ⊢ A ▻ B ▻ C → ⊢ B ▻ A ▻ C
exch t = app cexch t

-- Composition, or Schönfinkel’s B combinator.

ccomp : ∀ {A B C} → ⊢ (B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
ccomp = app (app cs (app ck cs)) ck

comp : ∀ {A B C} → ⊢ B ▻ C → ⊢ A ▻ B → ⊢ A ▻ C
comp t u = app (app ccomp t) u

-- Useful theorems in functional form.

pair : ∀ {A B} → ⊢ A → ⊢ B → ⊢ A ∧ B
pair t u = app (app cpair t) u

fst : ∀ {A B} → ⊢ A ∧ B → ⊢ A
fst t = app cfst t

snd : ∀ {A B} → ⊢ A ∧ B → ⊢ B
snd t = app csnd t

-- Convertibility.

data _⋙_ : ∀ {A} → ⊢ A → ⊢ A → Set where
  refl⋙     : ∀ {A} → {t : ⊢ A}
                     → t ⋙ t

  trans⋙    : ∀ {A} → {t t′ t″ : ⊢ A}
                     → t ⋙ t′ → t′ ⋙ t″
                     → t ⋙ t″

  sym⋙      : ∀ {A} → {t t′ : ⊢ A}
                     → t ⋙ t′
                     → t′ ⋙ t

  congapp⋙  : ∀ {A B} → {t t′ : ⊢ A ▻ B} → {u u′ : ⊢ A}
                       → t ⋙ t′ → u ⋙ u′
                       → app t u ⋙ app t′ u′

  congi⋙    : ∀ {A} → {t t′ : ⊢ A}
                     → t ⋙ t′
                     → app ci t ⋙ app ci t′

  congk⋙    : ∀ {A B} → {t t′ : ⊢ A} → {u u′ : ⊢ B}
                       → t ⋙ t′ → u ⋙ u′
                       → app (app ck t) u ⋙ app (app ck t′) u′

  congs⋙    : ∀ {A B C} → {t t′ : ⊢ A ▻ B ▻ C} → {u u′ : ⊢ A ▻ B} → {v v′ : ⊢ A}
                         → t ⋙ t′ → u ⋙ u′ → v ⋙ v′
                         → app (app (app cs t) u) v ⋙ app (app (app cs t′) u′) v′

  congpair⋙ : ∀ {A B} → {t t′ : ⊢ A} → {u u′ : ⊢ B}
                       → t ⋙ t′ → u ⋙ u′
                       → app (app cpair t) u ⋙ app (app cpair t′) u′

  congfst⋙  : ∀ {A B} → {t t′ : ⊢ A ∧ B}
                       → t ⋙ t′
                       → app cfst t ⋙ app cfst t′

  congsnd⋙  : ∀ {A B} → {t t′ : ⊢ A ∧ B}
                       → t ⋙ t′
                       → app csnd t ⋙ app csnd t′

  -- TODO: Verify this.
  beta▻ₖ⋙   : ∀ {A B} → {t : ⊢ A} → {u : ⊢ B}
                       → app (app ck t) u ⋙ t

  -- TODO: Verify this.
  beta▻ₛ⋙   : ∀ {A B C} → {t : ⊢ A ▻ B ▻ C} → {u : ⊢ A ▻ B} → {v : ⊢ A}
                         → app (app (app cs t) u) v ⋙ app (app t v) (app u v)

  -- TODO: What about eta for ▻?

  beta∧₁⋙   : ∀ {A B} → {t : ⊢ A} → {u : ⊢ B}
                       → app cfst (app (app cpair t) u) ⋙ t

  beta∧₂⋙   : ∀ {A B} → {t : ⊢ A} → {u : ⊢ B}
                       → app csnd (app (app cpair t) u) ⋙ u

  eta∧⋙     : ∀ {A B} → {t : ⊢ A ∧ B}
                       → t ⋙ app (app cpair (app cfst t)) (app csnd t)

  eta⊤⋙    : ∀ {t : ⊢ ⊤} → t ⋙ unit


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

open import Base using (Type; Type₀; _==_; idp)

module PathInductionSimplified where

{-<pathinduction>-}
record Coh {i} (A : Type i) : Type i where
  field & : A
open Coh public

instance
  J : ∀ {i j} {A : Type i} {a : A} {B : (a' : A) → a == a' → Type j}
    → Coh (B a idp)
    → Coh ({a' : A} (p : a == a') → B a' p)
  & (J d) idp = & d

  idp-Coh : ∀ {i} {A : Type i} {a : A} → Coh (a == a)
  & idp-Coh = idp

path-induction : ∀ {i} {A : Type i} {{a : A}} → A
path-induction {{a}} = a

composition : ∀ {i} {A : Type i} {a : A}
     → Coh ({b : A} (p : a == b) {c : A} (q : b == c) → a == c)
composition = path-induction

postulate
  A : Type₀
  a b c : A
  p : a == b
  q : b == c

pq : a == c
pq = & composition p q
{-</>-}


{-# OPTIONS --cubical #-}

module HyperPositive where

open import Prelude

infixr 4 _↬_
{-# NO_POSITIVITY_CHECK #-}
record _↬_ (A : Type a) (B : Type b) : Type (a ℓ⊔ b) where
  inductive; constructor hyp
  field invoke : ((A ↬ B) → A) → B
open _↬_

open import Data.List using (List; _∷_; []; foldr)

module _ {a b} {A : Type a} {B : Type b} where
  XT = List (A × B) ↬ (A → List (A × B)) → List (A × B)
  YT = List (A × B) ↬ (A → List (A × B))

  yfld : List B → YT
  yfld = foldr f n
    where
    f : B → YT → YT
    f y yk .invoke xk x = (x , y) ∷ xk yk

    n : YT
    n .invoke _ _ = []

  xfld : List A → XT
  xfld = foldr f n
    where
    f : A → XT → XT
    f x xk yk = yk .invoke xk x

    n : XT
    n _ = []

  zip : List A → List B → List (A × B)
  zip xs ys = xfld xs (yfld ys)

open import Data.List using (_⋯_)
open import Data.List.Syntax

_ : zip (1 ⋯ 5) (11 ⋯ 15) ≡ [ 5 ][ (1 , 11) , (2 , 12) , (3 , 13) , (4 , 14) , (5 , 15) ]
_ = refl


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

open import Categories.Category
open import Categories.Category.Monoidal

module Experiment.Categories.Solver.Category.Monoidal
  {o ℓ e} {𝒞 : Category o ℓ e} (M : Monoidal 𝒞) where

open import Level
open import Relation.Binary using (Rel; REL)
open import Data.List

open import Categories.Morphism 𝒞

open Category 𝒞
open Monoidal M

infixr 10 _:⊗₀_ _:⊗₁_

data Sig : Set o where
  ∥_∥   : Obj → Sig
  :unit : Sig
  _:⊗₀_ : Sig → Sig → Sig

private
  variable
    A B C : Obj
    W X Y Z : Sig

data Expr : Rel Sig (o ⊔ ℓ) where
  :id  : Expr X X
  _:∘_ : Expr Y Z → Expr X Y → Expr X Z
  _:⊗₁_ : Expr X Y → Expr Z W → Expr (X :⊗₀ Z) (Y :⊗₀ W)
  :λ⇒ : Expr (:unit :⊗₀ X) X
  :λ⇐ : Expr X (:unit :⊗₀ X)
  :ρ⇒ : Expr (X :⊗₀ :unit) X
  :ρ⇐ : Expr X (X :⊗₀ :unit)
  :α⇒ : Expr ((X :⊗₀ Y) :⊗₀ Z) (X :⊗₀ (Y :⊗₀ Z))
  :α⇐ : Expr (X :⊗₀ (Y :⊗₀ Z)) ((X :⊗₀ Y) :⊗₀ Z)
  -- ∥_∥  : A ⇒ B → Expr ∥ A ∥ ∥ B ∥

NSig : Set o
NSig = List Obj

{-
data NExpr : REL Sig NSig (o ⊔ ℓ) where
  :id : NExpr ∥ A ∥ (A ∷ [])
  :λ⇒ : NExpr (:unit :⊗₀ X) (normaliseSig X)
-}

⟦_⟧Sig : Sig → Obj
⟦ ∥ A ∥   ⟧Sig = A
⟦ :unit   ⟧Sig = unit
⟦ X :⊗₀ Y ⟧Sig = ⟦ X ⟧Sig ⊗₀ ⟦ Y ⟧Sig

⟦_⟧NSig : NSig → Obj
⟦ []    ⟧NSig = unit
⟦ A ∷ X ⟧NSig = A ⊗₀ ⟦ X ⟧NSig

normaliseSig : Sig → NSig
normaliseSig ∥ A ∥     = A ∷ []
normaliseSig :unit     = []
normaliseSig (X :⊗₀ Y) = normaliseSig X ++ normaliseSig Y

private
  λ⇒ = unitorˡ.from
  λ⇐ = unitorˡ.to
  ρ⇒ = unitorʳ.from
  ρ⇐ = unitorʳ.to
  α⇒ = associator.from
  α⇐ = associator.to

⟦_⟧ : Expr X Y → ⟦ X ⟧Sig ⇒ ⟦ Y ⟧Sig
⟦ :id       ⟧ = id
⟦ e₁ :∘ e₂  ⟧ = ⟦ e₁ ⟧ ∘ ⟦ e₂ ⟧
⟦ e₁ :⊗₁ e₂ ⟧ = ⟦ e₁ ⟧ ⊗₁ ⟦ e₂ ⟧
⟦ :λ⇒       ⟧ = λ⇒
⟦ :λ⇐       ⟧ = λ⇐
⟦ :ρ⇒       ⟧ = ρ⇒
⟦ :ρ⇐       ⟧ = ρ⇐
⟦ :α⇒       ⟧ = α⇒
⟦ :α⇐       ⟧ = α⇐

⟦_⟧⁻¹ : Expr X Y → ⟦ Y ⟧Sig ⇒ ⟦ X ⟧Sig
⟦ :id       ⟧⁻¹ = id
⟦ e₁ :∘ e₂  ⟧⁻¹ = ⟦ e₂ ⟧⁻¹ ∘ ⟦ e₁ ⟧⁻¹
⟦ e₁ :⊗₁ e₂ ⟧⁻¹ = ⟦ e₁ ⟧⁻¹ ⊗₁ ⟦ e₂ ⟧⁻¹
⟦ :λ⇒       ⟧⁻¹ = λ⇐
⟦ :λ⇐       ⟧⁻¹ = λ⇒
⟦ :ρ⇒       ⟧⁻¹ = ρ⇐
⟦ :ρ⇐       ⟧⁻¹ = ρ⇒
⟦ :α⇒       ⟧⁻¹ = α⇐
⟦ :α⇐       ⟧⁻¹ = α⇒

⟦_⟧≅ : Expr X Y → ⟦ X ⟧Sig ≅ ⟦ Y ⟧Sig
⟦ :id       ⟧≅ = ≅.refl
⟦ e₁ :∘ e₂  ⟧≅ = ≅.trans ⟦ e₂ ⟧≅ ⟦ e₁ ⟧≅
⟦ e₁ :⊗₁ e₂ ⟧≅ = {!   !}
⟦ :λ⇒       ⟧≅ = {!   !}
⟦ :λ⇐       ⟧≅ = {!   !}
⟦ :ρ⇒       ⟧≅ = {!   !}
⟦ :ρ⇐       ⟧≅ = {!   !}
⟦ :α⇒       ⟧≅ = {!   !}
⟦ :α⇐       ⟧≅ = {!   !}

-- theorem : (e₁ e₂ : Expr S T) → ⟦ e₁ ⟧ ≈ ⟦ e₂ ⟧
-- lemma : Expr S T → ⟦ S ⟧Sig ≅ ⟦ T ⟧Sig
-- lemma : ⟦ normaliseSig S ⟧NSig ≅ ⟦ S ⟧Sig
-- resp : ⟦ S ⟧NSig ≅ ⟦ T ⟧NSig
-- lemma : Expr S T → normaliseSig S ≡ normaliseSig T


open import Relation.Binary
open import Level
module GGT.Setoid
  {a ℓ}
  (S : Setoid a ℓ)
  (l : Level)
  where

open import Level
open Setoid S
open import Data.Product
open import Relation.Unary
open import Relation.Binary.Construct.On renaming (isEquivalence to isEq)

open import Function

subSetoid : (Pred Carrier l) → Setoid (a ⊔ l) ℓ
subSetoid P =
  record { Carrier = Σ Carrier P ;
           _≈_ = (_≈_ on proj₁) ;
           isEquivalence = isEq proj₁ isEquivalence }


------------------------------------------------------------------------
-- The Agda standard library
--
-- Union of two binary relations
------------------------------------------------------------------------

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

module Relation.Binary.Construct.Union where

open import Data.Product
open import Data.Sum.Base as Sum
open import Function using (_∘_)
open import Level using (Level; _⊔_)
open import Relation.Binary
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Sum using (_⊎-dec_)

private
  variable
    a b ℓ ℓ₁ ℓ₂ ℓ₃ : Level
    A : Set a
    B : Set b

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

_∪_ : ∀ {A : Set a} {B : Set b} →
      REL A B ℓ₁ → REL A B ℓ₂ → REL A B (ℓ₁ ⊔ ℓ₂)
L ∪ R = λ i j → L i j ⊎ R i j

------------------------------------------------------------------------
-- Properties

module _ (L : Rel A ℓ) (R : Rel A ℓ) where

  reflexive : Reflexive L ⊎ Reflexive R → Reflexive (L ∪ R)
  reflexive (inj₁ L-refl) = inj₁ L-refl
  reflexive (inj₂ R-refl) = inj₂ R-refl

  total : Total L ⊎ Total R → Total (L ∪ R)
  total (inj₁ L-total) x y = [ inj₁ ∘ inj₁ , inj₂ ∘ inj₁ ] (L-total x y)
  total (inj₂ R-total) x y = [ inj₁ ∘ inj₂ , inj₂ ∘ inj₂ ] (R-total x y)

  min : ∀ {⊤} → Minimum L ⊤ ⊎ Minimum R ⊤ → Minimum (L ∪ R) ⊤
  min = [ inj₁ ∘_ , inj₂ ∘_ ]

  max : ∀ {⊥} → Maximum L ⊥ ⊎ Maximum R ⊥ → Maximum (L ∪ R) ⊥
  max = [ inj₁ ∘_ , inj₂ ∘_ ]

module _ {L : Rel A ℓ} {R : Rel A ℓ} where

  symmetric : Symmetric L → Symmetric R → Symmetric (L ∪ R)
  symmetric L-sym R-sym = [ inj₁ ∘ L-sym , inj₂ ∘ R-sym ]

  respects : ∀ {p} {P : A → Set p} →
             P Respects L → P Respects R → P Respects (L ∪ R)
  respects resp-L resp-R = [ resp-L , resp-R ]

module _ {≈ : Rel A ℓ₁} (L : REL A B ℓ₂) (R : REL A B ℓ₃) where

  respˡ : L Respectsˡ ≈ → R Respectsˡ ≈ → (L ∪ R) Respectsˡ ≈
  respˡ Lˡ Rˡ x≈y = Sum.map (Lˡ x≈y) (Rˡ x≈y)

module _ {≈ : Rel B ℓ₁} (L : REL A B ℓ₂) (R : REL A B ℓ₃) where

  respʳ : L Respectsʳ ≈ → R Respectsʳ ≈ → (L ∪ R) Respectsʳ ≈
  respʳ Lʳ Rʳ x≈y = Sum.map (Lʳ x≈y) (Rʳ x≈y)

module _ {≈ : Rel A ℓ₁} {L : Rel A ℓ₂} {R : Rel A ℓ₃} where

  resp₂ : L Respects₂ ≈ → R Respects₂ ≈ → (L ∪ R) Respects₂ ≈
  resp₂ (Lʳ , Lˡ) (Rʳ , Rˡ) = respʳ L R Lʳ Rʳ , respˡ L R Lˡ Rˡ

module _ (≈ : REL A B ℓ₁) (L : REL A B ℓ₂) (R : REL A B ℓ₃) where

  implies : (≈ ⇒ L) ⊎ (≈ ⇒ R) → ≈ ⇒ (L ∪ R)
  implies = [ inj₁ ∘_ , inj₂ ∘_ ]

  irreflexive : Irreflexive ≈ L → Irreflexive ≈ R → Irreflexive ≈ (L ∪ R)
  irreflexive L-irrefl R-irrefl x≈y = [ L-irrefl x≈y , R-irrefl x≈y ]

module _ {L : REL A B ℓ₁} {R : REL A B ℓ₂} where

  decidable : Decidable L → Decidable R → Decidable (L ∪ R)
  decidable L? R? x y = L? x y ⊎-dec R? x y


{- Basic combinators, basic permutations, and their relation -}
module CombPerm where

import Data.Fin as F
--
open import Data.Unit
open import Data.Nat using (ℕ ; zero ; suc ; _+_ ; _>_ )
open import Data.Sum using (inj₁ ; inj₂ ; _⊎_)
open import Data.Vec
open import Function using ( id ) renaming (_∘_ to _○_)
open import Relation.Binary  -- to make certain goals look nicer
open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl ; sym ; cong ; trans ; subst ; module ≡-Reasoning )
open ≡-Reasoning

-- start re-splitting things up, as this is getting out of hand
open import FT -- Finite Types
open import VecHelpers
open import NatSimple
open import Eval
open import Permutations

-- This should really go elsewhere!
finToVal : {n : ℕ} → F.Fin n → ⟦ fromℕ n ⟧
finToVal F.zero = inj₁ tt
finToVal (F.suc n) = inj₂ (finToVal n)

valToFin : {n : ℕ} → ⟦ fromℕ n ⟧ → F.Fin n
valToFin {zero} ()
valToFin {suc n} (inj₁ tt) = F.zero
valToFin {suc n} (inj₂ v) = F.suc (valToFin v)

finToValToFin : {n : ℕ} → (v : ⟦ fromℕ n ⟧) → finToVal (valToFin v) ≡ v
finToValToFin {zero} ()
finToValToFin {suc n} (inj₁ tt)  = refl -- (inj₁ tt)
finToValToFin {suc n} (inj₂ v) = cong inj₂ (finToValToFin v)

valToFinToVal : {n : ℕ} → (i : F.Fin n) → valToFin (finToVal i) ≡ i
valToFinToVal F.zero = refl -- F.zero
valToFinToVal (F.suc i) = cong F.suc (valToFinToVal i)

-- construct a combinator which represents the swapping of the i-th and
-- (i+1)-th 'bit' of a finite type.
-- Best to think of this as an 'elementary permutation', in the same way
-- we have 'elementary matrices' (which turn out to be permutations when they
-- are unitary).
swapi : {n : ℕ} → F.Fin n → (fromℕ (suc n)) ⇛ (fromℕ (suc n))
swapi {zero}  ()
swapi {suc n} F.zero    = assocl₊⇛ ◎ (swap₊⇛ ⊕ id⇛) ◎ assocr₊⇛
swapi {suc n} (F.suc i) = id⇛ ⊕ swapi {n} i

swapiPerm : {n : ℕ} → F.Fin n → Permutation (suc n)
swapiPerm {zero} ()
swapiPerm {suc n} F.zero = F.suc F.zero ∷ idP
swapiPerm {suc n} (F.suc i) = F.zero ∷ swapiPerm {n} i

swapiAct : {n : ℕ} {A : Set} → (i : F.Fin n) → (v : Vec A (suc n)) →
    permute (swapiPerm i) v ≡ insert (remove (F.inject₁ i) v) (F.suc i) (v !! (F.inject₁ i))
swapiAct {zero} () v
swapiAct {suc n} F.zero (x ∷ y ∷ v) = cong (λ z → y ∷ x ∷ z) (idP-id v)
swapiAct {suc n} (F.suc i) (x ∷ v) = cong (_∷_ x) (swapiAct i v)

-- swapUpTo i permutes the combinator left by one up to i
-- if possible values are X a b c Y d e, swapUpTo 3's possible outputs
-- are a b c X Y d e
swapUpTo : {n : ℕ} → F.Fin n → (fromℕ (suc n)) ⇛ (fromℕ (suc n))
swapUpTo F.zero    = id⇛
swapUpTo (F.suc i) = (id⇛ ⊕ swapUpTo i) ◎ swapi F.zero

-- 1 i times, 0 for the rest
1iP : {n : ℕ} → F.Fin n → Permutation (suc n)
1iP F.zero = idP
1iP (F.suc i) = (F.suc F.zero) ∷ 1iP i

-- The permutation we need:
-- [i, 0, 0, 0, 0, ...]
swapUpToPerm : {n : ℕ} → F.Fin n → Permutation (suc n)
swapUpToPerm F.zero    = idP
swapUpToPerm (F.suc j) = (F.inject₁ (F.suc j)) ∷ idP

-- swapDownFrom i permutes the combinator right by one up to i (the reverse
-- of swapUpTo)
swapDownFrom : {n : ℕ} → F.Fin n → (fromℕ (suc n)) ⇛ (fromℕ (suc n))
swapDownFrom F.zero    = id⇛
swapDownFrom (F.suc i) = swapi F.zero ◎ (id⇛ ⊕ swapDownFrom i)

-- The permutation we need:
-- [1, 1, 1, ..., 1, 0, 0, 0, ...]
-- |--i-1 times---|
swapDownFromPerm : {n : ℕ} → F.Fin n → Permutation (suc n)
swapDownFromPerm = 1iP
-- swapDownFromPerm F.zero = idP
-- swapDownFromPerm (F.suc i) = (F.suc F.zero) ∷ swapDownFromPerm i

-- swapm i returns a combinator that swaps 0 and i
swapm : {n : ℕ} → F.Fin n → (fromℕ n) ⇛ (fromℕ n)
swapm F.zero = id⇛
swapm (F.suc i) = swapDownFrom i ◎ swapi i ◎ swapUpTo i

swapOne : {n : ℕ} → (i : F.Fin n) → Permutation n
swapOne F.zero = idP
swapOne {suc zero} (F.suc ())
swapOne {suc (suc n)} (F.suc i) = (F.suc F.zero) ∷ swapOne i

-- which should correspond to this permutation!!
swapmPerm : {n : ℕ} → F.Fin n → Permutation n
swapmPerm F.zero = idP
swapmPerm {suc n} (F.suc i) = F.suc i ∷ swapOne i

permToComb : {n : ℕ} → Permutation n → (fromℕ n ⇛ fromℕ n)
permToComb [] = id⇛
permToComb (F.zero ∷ p) = id⇛ ⊕ permToComb p
permToComb (F.suc n ∷ p) = swapm (F.suc n) ◎ (id⇛ ⊕ permToComb p)

swap01 : (n : ℕ) → Permutation n
swap01 zero = []
swap01 (suc zero) = F.zero ∷ []
swap01 (suc (suc n)) = F.suc (F.zero) ∷ idP

vmap-insert : {n : ℕ} {A B : Set} {x : A} → (v : Vec A n) → (f : A → B) → (i : F.Fin (suc n)) → vmap f (insert v i x) ≡ insert (vmap f v) i (f x)
vmap-insert {zero} [] f F.zero = refl
vmap-insert {zero} [] f (F.suc ())
vmap-insert {suc n} v f F.zero = refl
vmap-insert {suc n} {x = x} (y ∷ v) f (F.suc i) = cong (_∷_ (f y)) (vmap-insert v f i)

vmap-permute : {n : ℕ} {A B : Set} → (p : Permutation n) → (v : Vec A n) → (f : A → B) → vmap f (permute p v) ≡ permute p (vmap f v)
vmap-permute {zero} [] [] f = refl
vmap-permute {suc n} (i ∷ p) (x ∷ v) f = trans (vmap-insert (permute p v) f i) (cong (λ y → insert y i (f x)) (vmap-permute p v f))

evalPerm : {n : ℕ} → Permutation n → F.Fin n → F.Fin n
evalPerm {zero} _ ()
evalPerm {suc n} p k = lookup k (permute p (allFin (suc n)))

testP5 : Vec (F.Fin five) five
testP5 = permute (swapmPerm (F.inject (F.fromℕ three))) (allFin five)

push-f-through : {n : ℕ} {A B : Set} → (f : A → B) → (j : F.Fin n) → (p : Permutation n) → (g : F.Fin n → A) → f (lookup j (permute p (tabulate g))) ≡ lookup j (permute p (tabulate (f ○ g)))
push-f-through {zero} f () p g
push-f-through {suc n} f j p g = begin
   f (lookup j (permute p (tabulate g)))            ≡⟨ sym (map!! f (permute p (tabulate g)) j) ⟩
   lookup j (vmap f (permute p (tabulate g)))       ≡⟨ cong (lookup j) (vmap-permute p (tabulate g) f) ⟩
   lookup j (permute p (vmap f (tabulate g)))       ≡⟨ cong (λ x → lookup j (permute p x)) (mapTab f g) ⟩
   lookup j (permute p (tabulate (f ○ g))) ∎

-- Just to make a lot of the proofs look nicer
vId : {n : ℕ} → Vec (F.Fin n) n
vId = tabulate id

vS : {n : ℕ} → Vec (F.Fin (suc n)) n
vS = tabulate F.suc

vSS : {n : ℕ} → Vec (F.Fin (suc (suc n))) n
vSS = tabulate (F.suc ○ F.suc)

swapiCorrect : {n : ℕ} → (i : F.Fin n) → (j : F.Fin (1 + n)) → evalComb (swapi i) (finToVal j) ≡ finToVal (evalPerm (swapiPerm i) j)
swapiCorrect {zero} () _
swapiCorrect {suc n} F.zero F.zero = refl
swapiCorrect {suc zero} F.zero (F.suc F.zero) = refl
swapiCorrect {suc zero} F.zero (F.suc (F.suc ()))
swapiCorrect {suc zero} (F.suc ()) j
swapiCorrect {suc (suc n)} (F.suc i) F.zero = refl
swapiCorrect {suc (suc n)} F.zero (F.suc F.zero) = refl
swapiCorrect {suc (suc n)} F.zero (F.suc (F.suc j)) = cong finToVal
  let F2 = F.suc ○ F.suc in
  begin
    F2 j
                                 ≡⟨ cong F2 (sym (lookupTab {f = id} j)) ⟩
    F2 (lookup j (tabulate id))
                                 ≡⟨ cong (λ x → F2 (lookup j x)) (sym (idP-id (tabulate id))) ⟩
    F2 (lookup j (permute idP (tabulate id)))
                                 ≡⟨ push-f-through F2 j idP id ⟩
    lookup j (permute idP (tabulate F2)) ∎
swapiCorrect {suc (suc n)} (F.suc i) (F.suc j) =
  begin
   evalComb (swapi (F.suc i)) (finToVal (F.suc j))
                                   ≡⟨ refl ⟩
   inj₂ (evalComb (swapi i) (finToVal j))
                                   ≡⟨ cong inj₂ (swapiCorrect {suc n} i j) ⟩
   inj₂ (finToVal (evalPerm (swapiPerm i) j))
                                   ≡⟨ refl ⟩
   inj₂ (finToVal (lookup j (permute (swapiPerm i) (tabulate id))))
                                   ≡⟨ refl ⟩
   finToVal (F.suc (lookup j (permute (swapiPerm i) (tabulate id))))
                                   ≡⟨ cong finToVal (push-f-through F.suc j (swapiPerm i) id) ⟩
   finToVal (lookup j (permute (swapiPerm i) (tabulate F.suc))) ∎

-- JC: Need seriously better names for the next 3 lemmas!!
lookup-insert : {n : ℕ} {A : Set} → (v : Vec A (suc n)) → {i : F.Fin (suc n)} → {a : A} →
    lookup F.zero (insert v (F.suc i) a) ≡ lookup F.zero v
lookup-insert (x ∷ v) = refl

lookup-insert′ : {n : ℕ} {A : Set} (i : F.Fin (suc n)) {a : A} → (v : Vec A n) →
    lookup i (insert v i a) ≡ a
lookup-insert′ F.zero [] = refl
lookup-insert′ (F.suc ()) []
lookup-insert′ F.zero (x ∷ v) = refl
lookup-insert′ (F.suc i) (x ∷ v) = lookup-insert′ i v

-- this is what is needed in swapDownCorrect, but would be true for any j < i instead of F.suc F.zero < F.suc i
lookup-insert′′ : {n : ℕ} {A : Set} {a : A} → (i : F.Fin n) → (v : Vec A (suc n)) →
    lookup (F.suc i) v ≡ lookup (F.suc (F.suc i)) (insert v (F.suc F.zero) a)
lookup-insert′′ {zero} () v
lookup-insert′′ {suc n} i (x ∷ v) = refl

-- this should be unified with the previous
lookup-insert3 : {n : ℕ} {A : Set} {a : A} → (i : F.Fin n) → (v : Vec A n) →
    lookup i v ≡ lookup (F.suc i) (insert v (F.inject₁ i) a)
lookup-insert3 {zero} () v
lookup-insert3 {suc n} F.zero (x ∷ v) = refl
lookup-insert3 {suc n} (F.suc i) (x ∷ v) = lookup-insert3 i v

lookup+1-insert-remove : {n : ℕ} {A : Set} {a : A} → (i : F.Fin n) → (v : Vec A (suc n)) →
  lookup (F.inject₁ i) (insert (remove (F.inject₁ i) v) (F.suc i) a) ≡ lookup (F.suc i) v
lookup+1-insert-remove {zero} () _
lookup+1-insert-remove {suc n} F.zero (x ∷ x₁ ∷ v) = refl
lookup+1-insert-remove {suc n} (F.suc i) (x ∷ v) = lookup+1-insert-remove i v

-- insert (remove (F.inject₁ (F.suc i)) (tabulate id)) (F.suc (F.suc i)) (F.inject₁ (F.suc i)) !! (F.inject₁ (F.suc i))

lookup-idP-id : {n : ℕ} → (i : F.Fin n) → lookup i (permute idP vId) ≡ i
lookup-idP-id i = trans (cong (lookup i) (idP-id vId)) (lookupTab i)

swapUpToAct : {n : ℕ} {A : Set} → (i : F.Fin n) → (v : Vec A (suc n)) →
    permute (swapUpToPerm i) v ≡ insert (remove F.zero v) (F.inject₁ i) (v !! F.zero)
swapUpToAct F.zero v = trans (idP-id v) (remove0 v)
swapUpToAct (F.suc i) (x ∷ v) = cong (λ z → insert z (F.suc (F.inject₁ i)) x) (idP-id v)

swapDownFromVec : {n : ℕ} {A : Set} → (i : F.Fin (suc n)) → Vec A (suc n) →
                  Vec A (suc n)
swapDownFromVec i v = (v !! i) ∷ remove i v

swapDownFromAct : {n : ℕ} → {A : Set} → (i : F.Fin n) → (v : Vec A (suc n)) →
                  permute (swapDownFromPerm i) v ≡
                    swapDownFromVec (F.inject₁ i) v
swapDownFromAct {zero} () _
swapDownFromAct {suc n} F.zero (x ∷ v) =
  begin
    permute (swapDownFromPerm F.zero) (x ∷ v)
      ≡⟨ refl ⟩
    permute idP (x ∷ v)
      ≡⟨ idP-id (x ∷ v) ⟩
    swapDownFromVec F.zero (x ∷ v) ∎
swapDownFromAct {suc zero} (F.suc ()) _
swapDownFromAct {suc (suc n)} (F.suc i) (x ∷ y ∷ v) =
  begin
    permute (swapDownFromPerm (F.suc i)) (x ∷ y ∷ v)
      ≡⟨ refl ⟩
    permute (F.suc F.zero ∷ 1iP i) (x ∷ y ∷ v)
      ≡⟨ refl ⟩
    insert (permute (swapDownFromPerm i) (y ∷ v)) (F.suc F.zero) x
      ≡⟨ cong (λ q → insert q (F.suc F.zero) x) (swapDownFromAct i (y ∷ v)) ⟩
    insert (swapDownFromVec (F.inject₁ i) (y ∷ v)) (F.suc F.zero) x
      ≡⟨ refl ⟩
    ((y ∷ v) !! (F.inject₁ i)) ∷ x ∷ remove (F.inject₁ i) (y ∷ v)
      ≡⟨ refl ⟩
    swapDownFromVec (F.inject₁ (F.suc i)) (x ∷ y ∷ v) ∎

swapmVec : {n : ℕ} {A : Set} → F.Fin n → Vec A n → Vec A n
swapmVec {zero} () _
swapmVec {suc n} F.zero v = v
swapmVec {suc zero} (F.suc ()) _
swapmVec {suc (suc n)} (F.suc i) (x ∷ v) =
  (v !! i) ∷ (insert (remove i v) i x)

swapOneVec : {n : ℕ} {A : Set} → F.Fin n → Vec A n → Vec A n
swapOneVec {zero} () _
swapOneVec {suc n} F.zero v = v
swapOneVec {suc n} (F.suc i) v = (v !! (F.suc i)) ∷ (remove (F.suc i) v)

swapOneAct : {n : ℕ} {A : Set} → (i : F.Fin n) → (v : Vec A n) →
           permute (swapOne i) v ≡ swapOneVec i v
swapOneAct {zero} () _
swapOneAct {suc n} F.zero (x ∷ v) = cong (_∷_ x) (idP-id v)
swapOneAct {suc zero} (F.suc ()) v
swapOneAct {suc (suc n)} (F.suc F.zero) (x ∷ y ∷ v) = cong (λ z → y ∷ x ∷ z) (idP-id v)
swapOneAct {suc (suc n)} (F.suc (F.suc i)) (x ∷ v) =
  begin
    permute (swapOne (F.suc (F.suc i))) (x ∷ v)
        ≡⟨ refl ⟩
    insert (permute (swapOne (F.suc i)) v) (F.suc F.zero) x
        ≡⟨ cong (λ y → insert y (F.suc F.zero) x) (swapOneAct (F.suc i) v) ⟩
    insert (swapOneVec (F.suc i) v) (F.suc F.zero) x
        ≡⟨ refl ⟩ -- lots of β reduction
    ((x ∷ v) !! (F.suc (F.suc i))) ∷ (remove (F.suc (F.suc i)) (x ∷ v))
        ≡⟨ refl ⟩
    swapOneVec (F.suc (F.suc i)) (x ∷ v)
  ∎

swapmAct : {n : ℕ} {A : Set} → (i : F.Fin n) → (v : Vec A n) →
           permute (swapmPerm i) v ≡ swapmVec i v
swapmAct {zero} () _
swapmAct {suc n} F.zero (x ∷ v) =
  begin
    permute (swapmPerm F.zero) (x ∷ v)
      ≡⟨ idP-id (x ∷ v) ⟩
    swapmVec F.zero (x ∷ v) ∎
swapmAct {suc zero} (F.suc ()) _
swapmAct {suc (suc n)} (F.suc F.zero) (x ∷ y ∷ v) = cong (λ z → y ∷ x ∷ z) (idP-id v)
swapmAct {suc (suc n)} (F.suc (F.suc i)) (x ∷ y ∷ v) =
  begin
    permute (swapmPerm (F.suc (F.suc i))) (x ∷ y ∷ v)
      ≡⟨ refl ⟩
    permute (F.suc (F.suc i) ∷ swapOne (F.suc i)) (x ∷ y ∷ v)
      ≡⟨ refl ⟩
    insert (permute (swapOne (F.suc i)) (y ∷ v)) (F.suc (F.suc i)) x
      ≡⟨ cong (λ z → insert z (F.suc (F.suc i)) x) (swapOneAct (F.suc i) (y ∷ v)) ⟩
    insert (swapOneVec (F.suc i) (y ∷ v)) (F.suc (F.suc i)) x
      ≡⟨ refl ⟩  -- by lots of beta on both sides
    ((y ∷ v) !! (F.suc i)) ∷ (insert (remove (F.suc i) (y ∷ v)) (F.suc i) x)
      ≡⟨ refl ⟩
    swapmVec (F.suc (F.suc i)) (x ∷ y ∷ v) ∎

swapi≡swap01 : {n : ℕ} → (j : F.Fin (suc (suc n))) →  evalComb (assocl₊⇛ ◎ (swap₊⇛ ⊕ id⇛) ◎ assocr₊⇛) (finToVal j) ≡ finToVal (evalPerm (swap01 (suc (suc n))) j)
swapi≡swap01 F.zero = refl
swapi≡swap01 (F.suc F.zero) = refl
swapi≡swap01 (F.suc (F.suc j)) = sym (trans
    (cong (λ x → finToVal (x !! j)) (idP-id _))
    (cong finToVal (lookupTab j)))

-- this is the raw swap01 vector
swap01vec : {n : ℕ} → Vec (F.Fin (2 + n)) (2 + n)
swap01vec = F.suc F.zero ∷ F.zero ∷ vSS

-- this is the one we 'naturally' get via permutation
swap01vec′ : (n : ℕ) → Vec (F.Fin (2 + n)) (2 + n)
swap01vec′ n = permute (swap01 (suc (suc n))) vId

-- but they are the same!
swap01Correct : (n : ℕ) → swap01vec′ n ≡ swap01vec {n}
swap01Correct zero = refl
swap01Correct (suc n) = cong (λ x → F.suc F.zero ∷ F.zero ∷ x) (idP-id _)

newlemma6 : {m n : ℕ} → (i : F.Fin n) → (v : Vec (F.Fin m) n) →
            (vmap F.suc (insert (vmap F.suc v) (F.inject₁ i) F.zero)) ∘̬′ swap01vec
          ≡ insert (vmap F.suc (vmap F.suc v)) (F.inject₁ i) F.zero
newlemma6 F.zero (x ∷ v) =
  let suc2v = vmap F.suc (vmap F.suc v) in
  begin
  vmap F.suc (insert (vmap F.suc (x ∷ v)) F.zero F.zero) ∘̬′ swap01vec
    ≡⟨ refl ⟩
  (F.suc F.zero ∷ F.suc (F.suc x) ∷ suc2v) ∘̬′ swap01vec
    ≡⟨ refl ⟩
  F.zero ∷ ((F.suc (F.suc x) ∷ suc2v) ∘̬′ swap01vec)
    ≡⟨ refl ⟩
  F.zero ∷ (vSS !! x) ∷ (suc2v ∘̬′ swap01vec)
    ≡⟨ cong (λ x → F.zero ∷ x ∷ suc2v ∘̬′ swap01vec) (lookupTab x) ⟩
  F.zero ∷ F.suc (F.suc x) ∷ (suc2v ∘̬′ swap01vec)
    ≡⟨ cong (λ q → F.zero ∷ F.suc (F.suc x) ∷ q) (map2+id v) ⟩
  F.zero ∷ F.suc (F.suc x) ∷ suc2v
    ≡⟨ refl ⟩
  insert (vmap F.suc (vmap F.suc (x ∷ v))) F.zero F.zero ∎
newlemma6 (F.suc i) (x ∷ v) =
  let v′ = insert (vmap F.suc v) (F.inject₁ i) F.zero in
  begin
  vmap F.suc (insert (vmap F.suc (x ∷ v)) (F.inject₁ (F.suc i)) F.zero) ∘̬′ swap01vec
    ≡⟨ refl ⟩
  (tabulate (F.suc ○ F.suc) !! x) ∷ ((vmap F.suc v′) ∘̬′ swap01vec)
    ≡⟨ cong (λ q → q ∷ ((vmap F.suc v′) ∘̬′ swap01vec)) (lookupTab x) ⟩
  F.suc (F.suc x) ∷ ((vmap F.suc v′) ∘̬′ swap01vec)
    ≡⟨ cong (_∷_ (F.suc (F.suc x))) (newlemma6 i v) ⟩
  F.suc (F.suc x) ∷ insert (vmap F.suc (vmap F.suc v)) (F.inject₁ i) F.zero
    ≡⟨ refl ⟩
  insert (vmap F.suc (vmap F.suc (x ∷ v))) (F.inject₁ (F.suc i)) F.zero ∎

---
--- testing

tests01 : Vec (F.Fin five) five
tests01 = permute (swap01 five) (allFin five)

tests02 : (fromℕ five ⇛ fromℕ five)
tests02 = permToComb (swap01 five)

tests03 : Vec ⟦ PLUS ONE (PLUS ONE (PLUS ONE (PLUS ONE (PLUS ONE ZERO)))) ⟧ five
tests03 = tabulate (λ i → evalComb tests02 (finToVal i))

l6test1 : _
l6test1 = (vmap F.suc (insert (tabulate {4} F.suc) (F.suc (F.suc F.zero)) F.zero)) ∘̬ (F.suc F.zero ∷ F.zero ∷ tabulate (F.suc ○ F.suc))

l6test1b : _
l6test1b = permute (swapUpToPerm {5} (F.suc (F.suc (F.suc F.zero)))) (tabulate id)

test1a : Vec _ 5 -- Vec (F.Fin 5) 5
--test1a = (insert (tabulate {4} F.suc) (F.suc F.zero) F.zero)
test1a = (vmap F.suc (insert (tabulate {4} F.suc) (F.suc F.zero) F.zero))
       ∘̬ (F.suc F.zero ∷ F.zero ∷ tabulate (λ x → F.suc (F.suc x)))

test1b : Vec _ 5
test1b = insert (tabulate (F.suc ○ F.suc)) (F.suc F.zero) F.zero

aPerm : Permutation six
aPerm = (F.suc (F.suc (F.suc F.zero))) ∷ (F.suc F.zero) ∷ (F.suc F.zero) ∷ F.zero ∷ F.zero ∷ F.zero ∷ []

test5 : Vec (F.Fin six) six
test5 = permute aPerm (tabulate id)

combToVec : {n : ℕ} → (fromℕ n) ⇛ (fromℕ n) → Vec (F.Fin n) n
combToVec c = tabulate (valToFin ○ (evalComb c) ○ finToVal)

test6a : Vec (F.Fin six) six
test6a = combToVec (swapUpTo (F.suc F.zero))

test6b : Vec (F.Fin six) six
test6b = permute (swapUpToPerm (F.suc F.zero)) (tabulate id)

test7a : Vec (F.Fin six) six
test7a = combToVec (swapUpTo (F.zero))

test7b : Vec (F.Fin six) six
test7b = permute (swapUpToPerm (F.zero)) (tabulate id)

test8a : Vec (F.Fin six) six
test8a = combToVec (swapUpTo (F.suc (F.suc (F.suc (F.zero)))))

test8b : Vec (F.Fin six) six
test8b = permute (swapUpToPerm (F.suc (F.suc (F.suc F.zero)))) (tabulate id)

test9 : Vec (F.Fin six) six
test9 = permute (swapOne (F.suc (F.suc (F.suc F.zero)))) (tabulate id)

test10 : Vec (F.Fin six) six
test10 = permute (swapmPerm (F.suc (F.suc (F.suc F.zero)))) (tabulate id)


import      Lvl
open import Structure.Operator.Vector
open import Structure.Setoid
open import Type

module Structure.Operator.Vector.FiniteDimensional.Proofs
  {ℓᵥ ℓₛ ℓᵥₑ ℓₛₑ}
  {V : Type{ℓᵥ}} ⦃ equiv-V : Equiv{ℓᵥₑ}(V) ⦄
  {S : Type{ℓₛ}} ⦃ equiv-S : Equiv{ℓₛₑ}(S) ⦄
  {_+ᵥ_ : V → V → V}
  {_⋅ₛᵥ_ : S → V → V}
  {_+ₛ_ _⋅ₛ_ : S → S → S}
  ⦃ vectorSpace : VectorSpace(_+ᵥ_)(_⋅ₛᵥ_)(_+ₛ_)(_⋅ₛ_) ⦄
  where

open VectorSpace(vectorSpace)

open import Data.Tuple as Tuple using (_,_)
open import Functional using (id ; _∘_ ; _∘₂_ ; _$_ ; swap ; _on₂_)
open import Function.Equals
open import Logic
open import Logic.Classical
open import Logic.Propositional
open import Logic.Predicate
open import Numeral.CoordinateVector as Vec using () renaming (Vector to Vec)
open import Numeral.CoordinateVector.Proofs
open import Numeral.Finite
open import Numeral.Finite.Proofs
open import Numeral.Natural
open import Numeral.Natural.Relation.Order
open import Numeral.Natural.Relation.Order.Proofs
import      Relator.Equals as Eq
open import Relator.Equals.Proofs.Equivalence
open import Structure.Function.Domain
open import Structure.Function.Domain.Proofs
open import Structure.Operator.Proofs
open import Structure.Operator
open import Structure.Operator.Vector.FiniteDimensional        ⦃ vectorSpace = vectorSpace ⦄
open import Structure.Operator.Vector.LinearCombination        ⦃ vectorSpace = vectorSpace ⦄
open import Structure.Operator.Vector.LinearCombination.Proofs
open import Structure.Relator.Properties
open import Syntax.Function
open import Syntax.Number
open import Syntax.Transitivity

private variable ℓ ℓ₁ ℓ₂ ℓₗ : Lvl.Level
private variable n n₁ n₂ : ℕ

private variable vf vf₁ vf₂ : Vec(n)(V)
private variable sf sf₁ sf₂ : Vec(n)(S)
private variable i j : 𝕟(n)

-- A basis could essentially be defined as being linearly independent and spanning the vector space.
linearIndependence-spanning-basis-equivalence : (LinearlyIndependent(vf) ∧ Spanning(vf)) ↔ Basis(vf)
linearIndependence-spanning-basis-equivalence = injective-surjective-bijective-equivalence _

-- Linearly independent sequence of vectors are vectors such that a linear combination of them never maps to zero.
-- Note that this is the usual definition of linear independence.
linearIndependence-equivalence : LinearlyIndependent(vf) ↔ (∀{sf} → (linearCombination(vf)(sf) ≡ 𝟎ᵥ) → (sf ⊜ Vec.fill(𝟎ₛ)))
linearIndependence-equivalence =
  Two.injective-kernel
    {_▫₁_ = Vec.map₂(_+ₛ_)}
    ⦃ func = BinaryOperator.right linearCombination-binaryOperator ⦄
    ⦃ cancₗ₂ = One.cancellationₗ-by-associativity-inverse ⦄
    {inv₁ = Vec.map(−ₛ_)}

-- postulate linearCombination-when-zero : (linearCombination(vf)(sf) ≡ 𝟎ᵥ) → (∀{i} → (vf(i) ≡ 𝟎ᵥ) ∨ (sf(i) ≡ 𝟎ₛ))

-- A sequence of vectors with a zero vector in it are not linearly independent, and a linearly independent sequence of vectors do not contain zero vectors.
linearIndependence-no-zero-vectors : LinearlyIndependent(vf) → ∀{i} → (vf(i) ≡ 𝟎ᵥ) → ⊥
linearIndependence-no-zero-vectors {𝐒(n)}{vf} li {i} vfzero = distinct-identitiesₛ $
  𝟎ₛ                         🝖[ _≡_ ]-[]
  Vec.fill 𝟎ₛ i              🝖[ _≡_ ]-[ _⊜_.proof ([↔]-to-[→] linearIndependence-equivalence li p) ]-sym
  Vec.indexProject i 𝟏ₛ 𝟎ₛ i 🝖[ _≡_ ]-[ [↔]-to-[→] (indexProject-true{i = i}{false = 𝟎ₛ}) ([∨]-introₗ(reflexivity(Eq._≡_))) ]
  𝟏ₛ                         🝖-end
  where
    p =
      linearCombination vf (Vec.indexProject i 𝟏ₛ 𝟎ₛ) 🝖[ _≡_ ]-[ linearCombination-of-indexProject{vf = vf} ]
      vf(i)                                           🝖[ _≡_ ]-[ vfzero ]
      𝟎ᵥ                                              🝖-end

--∀{i} → (vf(i) ≡ 𝟎ᵥ) → Spanning{𝐒(n)}(vf) → Spanning{n}(Vec.without i vf)

-- There are no duplicates in a sequence of linearly independent vectors.
linearIndependence-to-distinct : LinearlyIndependent(vf) → Vec.Distinct(vf)
Injective.proof (linearIndependence-to-distinct {vf = vf} (intro inj)) {x} {y} vfxy = [¬¬]-elim ⦃ [≡][𝕟]-classical ⦄ $ nxy ↦
  let
    p : linearCombination vf (Vec.indexProject x 𝟏ₛ 𝟎ₛ) ≡ linearCombination vf (Vec.indexProject y 𝟏ₛ 𝟎ₛ)
    p =
      linearCombination vf (Vec.indexProject x 𝟏ₛ 𝟎ₛ) 🝖[ _≡_ ]-[ linearCombination-of-indexProject{vf = vf} ]
      vf(x)                                           🝖[ _≡_ ]-[ vfxy ]
      vf(y)                                           🝖[ _≡_ ]-[ linearCombination-of-indexProject{vf = vf} ]-sym
      linearCombination vf (Vec.indexProject y 𝟏ₛ 𝟎ₛ) 🝖-end

    q : 𝟎ₛ ≡ 𝟏ₛ
    q =
      𝟎ₛ                                      🝖[ _≡_ ]-[ [↔]-to-[→] (indexProject-false{true = 𝟏ₛ}) ([∨]-introₗ nxy) ]-sym
      Vec.proj(Vec.indexProject(x) 𝟏ₛ 𝟎ₛ) (y) 🝖[ _≡_ ]-[ _⊜_.proof(inj p) {y} ]
      Vec.proj(Vec.indexProject(y) 𝟏ₛ 𝟎ₛ) (y) 🝖[ _≡_ ]-[ [↔]-to-[→] (indexProject-true{false = 𝟎ₛ}) ([∨]-introₗ(reflexivity(Eq._≡_) {x = y})) ]
      𝟏ₛ                                      🝖-end
  in distinct-identitiesₛ q

-- A subsequence of a linearly independent sequence of vectors are linearly independent.
postulate independent-subsequence-is-independent : ∀{N : 𝕟(n₁) → 𝕟(n₂)} ⦃ inj : Injective ⦃ [≡]-equiv ⦄ ⦃ [≡]-equiv ⦄ (N) ⦄ → LinearlyIndependent{n₂}(vf) → LinearlyIndependent{n₁}(vf ∘ N)

postulate linear-independent-sequence-set-equivalence : ∀{N : 𝕟(n₁) → 𝕟(n₂)} ⦃ bij : Bijective ⦃ [≡]-equiv ⦄ ⦃ [≡]-equiv ⦄ (N)⦄ → LinearlyIndependent{n₂}(vf) ↔ LinearlyIndependent{n₁}(vf ∘ N)

postulate spanning-supersequence-is-spanning : ∀{N : 𝕟(n₁) → 𝕟(n₂)} ⦃ surj : Surjective ⦃ [≡]-equiv ⦄ (N) ⦄ → Spanning{n₂}(vf) → Spanning{n₁}(vf ∘ N)

postulate spanning-sequence-set-equivalence : ∀{N : 𝕟(n₁) → 𝕟(n₂)} ⦃ bij : Bijective ⦃ [≡]-equiv ⦄ ⦃ [≡]-equiv ⦄ (N) ⦄ → Spanning{n₂}(vf) ↔ Spanning{n₁}(vf ∘ N)

-- The number of linearly independent vectors is always less than the cardinality of a set of spanning vectors.
-- TODO: It is important to prove this if possible
postulate independent-less-than-spanning : ∀{n₁}{vf₁} → LinearlyIndependent{n₁}(vf₁) → ∀{n₂}{vf₂} → Spanning{n₂}(vf₂) → (n₁ ≤ n₂)
{- TODO: Here is an idea of a proof, but it probably requires more development in the theory of cardinalities. Or maybe just some stuff on linearCombination
  LinearlyIndependent{n₁}(vf₁)
  Injective(linearCombination{n = n₁}(vf₁)) (essentially: Vec(n₁)(S) ≤ V)

  Spanning{n₂}(vf₂)
  Surjective(linearCombination{n = n₂}(vf₂)) (essentially: Vec(n₂)(S) ≥ V)
  Injective(linearCombination{n = n₂}(vf₂) ∘ inv) (Is this really true then? Essentially: V ≤ Vec(n₂)(S))

  Injective(linearCombination{n = n₂}(vf₂) ∘ inv ∘ linearCombination{n = n₁}(vf₁)) (essentially: Vec(n₁)(S) ≤ Vec(n₂)(S))
  n₁ ≤ n₂ (Note: This is not true in general. Only if the morphism is the "natural one" (the 𝟎 ↦ 𝟎 and n-tuples only maps to n-tuples and so on)), but is it really obtained by the proofs above?
-}

-- Two bases in a vector space are always of the same length.
basis-equal-length : Basis{n₁}(vf₁) → Basis{n₂}(vf₂) → (n₁ Eq.≡ n₂)
basis-equal-length b₁ b₂
  with (li₁ , sp₁) ← [↔]-to-[←] linearIndependence-spanning-basis-equivalence b₁
  |    (li₂ , sp₂) ← [↔]-to-[←] linearIndependence-spanning-basis-equivalence b₂
  = antisymmetry(_≤_)(Eq._≡_) (independent-less-than-spanning li₁ sp₂) (independent-less-than-spanning li₂ sp₁)

-- A finite basis can always be constructed by a subsequence of a finite spanning sequence of vectors.
-- TODO: One way of proving this is by assuming that the relation LinearlyIndependent is decidable (it is because of the isomorphism from matrices (all vector spaces of the same dimension are isomorphic) and then matrix operations can tell whether a set of finite vectors are decidable), and then remove vectors one by one from the spanning sequence until it is linearly independent (which it will be at the end. In extreme cases, a sequence of zero vectors are linearly independent). This algorithm will always terminate because all vectors are finite, but how will this be shown?
postulate basis-subsequence-from-spanning : Spanning{n₂}(vf) → ∃(n₁ ↦ ∃{Obj = 𝕟(n₁) → 𝕟(n₂)}(N ↦ Injective ⦃ [≡]-equiv ⦄ ⦃ [≡]-equiv ⦄ (N) ∧ Basis{n₁}(vf ∘ N)))

module _ ⦃ fin-dim@([∃]-intro(spanSize) ⦃ [∃]-intro span ⦃ span-spanning ⦄ ⦄) : FiniteDimensional ⦄ where
  -- A basis always exists for finite dimensional vector spaces.
  basis-existence : ∃(n ↦ ∃(vf ↦ Basis{n}(vf)))
  basis-existence
    with [∃]-intro(n) ⦃ [∃]-intro N ⦃ [∧]-intro inj basis ⦄ ⦄ ← basis-subsequence-from-spanning span-spanning
    = [∃]-intro(n) ⦃ [∃]-intro (span ∘ N) ⦃ basis ⦄ ⦄

  -- The dimension of the vector space is the length of a basis, which are the same for every vector space.
  dim : ℕ
  dim = [∃]-witness basis-existence

  postulate basis-supersequence-from-linear-independence : LinearlyIndependent{n₂}(vf) → ∃(n₁ ↦ ∃{Obj = 𝕟(n₁) → 𝕟(n₂)}(N ↦ Surjective ⦃ [≡]-equiv ⦄ (N) ∧ Basis{n₁}(vf ∘ N)))
  -- TODO: One idea is to start with vf. Then try to add the basis vectors one by one from basis-existence while maintaining the linear independence

  postulate independence-spanning-equivalence-for-dimension : LinearlyIndependent{dim}(vf) ↔ Spanning{dim}(vf)

-- TODO: For this to be formulated, reorganize some stuff
-- finite-subspace-set-equality : ∀{Vₛ₁ Vₛ₂} → Subspace(Vₛ₁) → Subspace(Vₛ₂) → (dim(Vₛ₁) ≡ dim(Vₛ₂)) → (Vₛ₁ ≡ Vₛ₂) -- as in set equality


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

open import HoTT

module stash.modalities.FiberedPushout {i j k} {X : Type i} {Y : Type j} where

  {-
     X ---glue-left--- P ---glue-right--- Y
  -}

  module _ (P : X → Y → Type k) where
    fibered-pushout-span : Span
    fibered-pushout-span = record
      { A = Σ X λ x → Σ Y λ y → P x y
      ; B = Coprod X Y
      ; C = Coprod (Σ X λ x → Σ Y λ y → P x y) (Σ X λ x → Σ Y λ y → P x y)
      ; f = λ{ (inl p) → p ; (inr p) → p}
      ; g = λ{ (inl p) → inl (fst p) ; (inr p) → inr (fst (snd p))}
      }

    FiberedPushout : Type (lmax (lmax i j) k)
    FiberedPushout = Pushout fibered-pushout-span

  module _ {P : X → Y → Type k} where
    in-left : X → FiberedPushout P
    in-left = right ∘ inl

    in-right : Y → FiberedPushout P
    in-right = right ∘ inr

    in-mid : ∀ {x y} → P x y → FiberedPushout P
    in-mid p = left (_ , _ , p)

    glue-left : ∀ {x y} (p : P x y) → in-mid p == in-left x
    glue-left p = glue (inl (_ , _ , p))

    glue-right : ∀ {x y} (p : P x y) → in-mid p == in-right y
    glue-right p = glue (inr (_ , _ , p))

    module FiberedPushoutElim {l}
      (C : FiberedPushout P → Type l)
      (b1 : (x : X) → C (in-left x))
      (b2 : (x : X) (y : Y) (p : P x y) → C (in-mid p))
      (b3 : (y : Y) → C (in-right y))
      (glue-left' : (x : X) (y : Y) (p : P x y) → b2 x y p == b1 x [ C ↓ glue-left p ])
      (glue-right' : (x : X) (y : Y) (p : P x y) → b2 x y p == b3 y [ C ↓ glue-right p ])
      where

      private
        module PElim = PushoutElim {d = fibered-pushout-span P} {P = C}
          (λ{(x , y , p) → b2 x y p})
          (λ{(inl x) → b1 x ; (inr y) → b3 y})
          (λ{(inl (_ , _ , p)) → glue-left'  _ _ p
           ; (inr (_ , _ , p)) → glue-right' _ _ p})

      f = PElim.f

      glue-left-β : ∀ {x y} (p : P x y) → apd f (glue-left p) == glue-left' x y p
      glue-left-β p = PElim.glue-β (inl (_ , _ , p))

      glue-right-β : ∀ {x y} (p : P x y) → apd f (glue-right p) == glue-right' x y p
      glue-right-β p = PElim.glue-β (inr (_ , _ , p))


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

open import HoTT
open import homotopy.PtdAdjoint

module homotopy.SuspAdjointLoopLadder where

  import homotopy.SuspAdjointLoop as A

  step : ∀ {i} {X Y Z : Ptd i} (f : Y ⊙→ Z)
    → CommSquareEquiv
        ((f ⊙∘_) :> ((⊙Susp X ⊙→ Y) → (⊙Susp X ⊙→ Z)))
        (⊙Ω-fmap f ⊙∘_)
        (–> (A.eq X Y))
        (–> (A.eq X Z))
  step f = comm-sqr (! ∘ A.nat-cod _ f) ,
    snd (A.eq _ _) , snd (A.eq _ _)

  rail : ∀ {i} {X Y : Ptd i} n
    → (⊙Susp^ n X ⊙→ Y) → (X ⊙→ ⊙Ω^' n Y)
  rail O = idf _
  rail (S n) = rail n ∘ –> (A.eq _ _)

  ladder : ∀ {i} {X Y Z : Ptd i} n (f : Y ⊙→ Z)
    → CommSquareEquiv
        ((f ⊙∘_) :> ((⊙Susp^ n X ⊙→ Y) → (⊙Susp^ n X ⊙→ Z)))
        (⊙Ω^'-fmap n f ⊙∘_)
        (rail n)
        (rail n)
  ladder O _ = (comm-sqr λ _ → idp) , idf-is-equiv _ , idf-is-equiv _
  ladder (S n) f = CommSquareEquiv-∘v (ladder n (⊙Ω-fmap f)) (step f)


module Data.Environment {a} {A : Set a} where

open import Algebra
open import Data.Nat
open import Data.Empty                                    using (⊥-elim)
open import Data.Pos                                      using (ℕ⁺; suc; replicate⁺)
open import Data.List as L                                using (List; []; _∷_; [_]; _++_; map; concatMap; replicate)
open import Data.List.Any                                 using (here; there)
open import Data.List.Any.BagAndSetEquality as B          using ()
open import Data.List.Any.Membership.Propositional public using (_∈_; _∼[_]_; bag)
open import Data.Product                                  using (Σ-syntax; ∃₂; _×_; proj₁; proj₂; _,_)
open import Data.Sum                                      using (_⊎_; inj₁; inj₂)
open import Function                                      using (_$_; flip)
open import Function.Equality                             using (_⟨$⟩_)
open import Function.Inverse                              using (id; sym; _∘_)
open        Function.Inverse.Inverse                      using (to; from)
open import Relation.Binary.PropositionalEquality as P    using (_≡_)

private module ++ = Monoid (L.monoid A)

bubble : (xs {ys} : List A) {x y z : A} →
         z ∈ xs ++ y ∷ x ∷ ys → z ∈ xs ++ x ∷ y ∷ ys
bubble []       (here px)         = there (here px)
bubble []       (there (here px)) = here px
bubble []       (there (there i)) = there (there i)
bubble (x ∷ xs) (here px)         = here px
bubble (x ∷ xs) (there i)         = there (bubble xs i)

-- There is a bijection between indices into the environment
-- ΓBAΔ and the environment ΓABΔ. This is called a 'bubble',
-- because it swaps two adjacent elements, as in bubble
-- sort.
bbl : (xs {ys} : List A) {x y : A} →
      xs ++ y ∷ x ∷ ys ∼[ bag ] xs ++ x ∷ y ∷ ys
bbl xs {ys} {x} {y} = record
  { to         = P.→-to-⟶ (bubble xs)
  ; from       = P.→-to-⟶ (bubble xs)
  ; inverse-of = record
    { left-inverse-of  = inv xs
    ; right-inverse-of = inv xs } }
  where
    inv : (xs {ys} : List A) {x y z : A} →
          (i : z ∈ xs ++ y ∷ x ∷ ys) →
          bubble xs (bubble xs i) ≡ i
    inv []       (here px)         = P.refl
    inv []       (there (here px)) = P.refl
    inv []       (there (there i)) = P.refl
    inv (x ∷ xs) (here px)         = P.refl
    inv (x ∷ xs) (there i)         = P.cong there (inv xs i)

-- There is a bijection between indices into the environment
-- ΓΔAΘ and the environment ΓAΔΘ.
fwd : (xs ys {zs} : List A) {w : A} →
      xs ++ ys ++ w ∷ zs ∼[ bag ] xs ++ w ∷ ys ++ zs
fwd (x ∷ xs) ys = B.∷-cong P.refl (fwd xs ys)
fwd []       ys = fwd' ys
  where
    fwd' : (xs {ys} : List A) {w : A} →
           xs ++ w ∷ ys ∼[ bag ] w ∷ xs ++ ys
    fwd' []       = id
    fwd' (x ∷ xs) = bbl [] ∘ B.∷-cong P.refl (fwd' xs)

bwd : (xs ys {zs} : List A) {w : A} →
      xs ++ w ∷ ys ++ zs ∼[ bag ] xs ++ ys ++ w ∷ zs
bwd xs ys = sym (fwd xs ys)

-- List all possible ways to split a list xs into two lists xs₁ and xs₂.
allSplit : (xs : List A) → List (Σ[ xs₁ ∈ List A ] Σ[ xs₂ ∈ List A ] xs ≡ xs₁ ++ xs₂)
allSplit []       = L.[ [] , [] , P.refl ]
allSplit (x ∷ xs) = now ∷ later
  where
    now   = ([] , x ∷ xs , P.refl)
    later = flip map (allSplit xs) $
      λ{(xs₁ , xs₂ , p) → (x ∷ xs₁ , xs₂ , P.cong (x ∷_) p)}

-- Test if allSplit does as advertised.
{-
private
  module _ where

    postulate x y z w : A

    test-allSplit :
      allSplit (x ∷ y ∷ z ∷ w ∷ []) ≡
      ( ( []                    , (x ∷ y ∷ z ∷ w ∷ []) , P.refl )
      ∷ ( (x ∷ [])              ,     (y ∷ z ∷ w ∷ []) , P.refl )
      ∷ ( (x ∷ y ∷ [])          ,         (z ∷ w ∷ []) , P.refl )
      ∷ ( (x ∷ y ∷ z ∷ [])      ,             (w ∷ []) , P.refl )
      ∷ ( (x ∷ y ∷ z ∷ w ∷ [] ) ,                  []  , P.refl )
      ∷ [] )
    test-allSplit = P.refl
-}

-- It is possible to list all environments Δ for which there
-- exists a bijection between indices into the environment Γ
-- and the environment Δ.
all : (xs : List A) → List (Σ[ ys ∈ List A ] xs ∼[ bag ] ys)
all [] = ([] , id) ∷ []
all (x ∷ xs) = concatMap insAll (all xs)
  where
    insAll : Σ[ ys ∈ List A ] xs ∼[ bag ] ys
           → List (Σ[ ys ∈ List A ] x ∷ xs ∼[ bag ] ys)
    insAll (ys , b) = map insOne (allSplit ys)
      where
        insOne : Σ[ ys₁ ∈ List A ] Σ[ ys₂ ∈ List A ] ys ≡ ys₁ ++ ys₂
               → Σ[ zs ∈ List A ] x ∷ xs ∼[ bag ] zs
        insOne (ys₁ , ys₂ , p) = ys₁ ++ x ∷ ys₂ ,
          bwd [] ys₁ {ys₂} {x} ∘ B.∷-cong P.refl (P.subst (ys ∼[ bag ]_) p id ∘ b)

-- Any element which is contained in a list created using replicate
-- is equal to the replicated element.
∈-replicate : {x y : A} (n : ℕ) → x ∈ y ∷ replicate n y → x ≡ y
∈-replicate zero    (here px) = px
∈-replicate zero    (there ())
∈-replicate (suc n) (here px) = px
∈-replicate (suc n) (there m) = ∈-replicate n m

-- Any bag which is equivalent to the bag created using replicate,
-- is equal to the bag created using replicate.
all-replicate : {xs : List A} {y : A} (n : ℕ) →

  xs ∼[ bag ] replicate n y → xs ≡ replicate n y

all-replicate {[]}     {y} zero    b = P.refl
all-replicate {[]}     {y} (suc n) b with from b ⟨$⟩ here P.refl; ... | ()
all-replicate {x ∷ xs} {y} zero    b with to   b ⟨$⟩ here P.refl; ... | ()
all-replicate {x ∷ xs} {y} (suc n) b = P.cong₂ _∷_ x=y (all-replicate n b')
  where
    x=y : x ≡ y
    x=y = ∈-replicate n (to b ⟨$⟩ here P.refl)
    b'  : xs ∼[ bag ] replicate n y
    b'  = B.drop-cons (P.subst (λ x → x ∷ xs ∼[ bag ] y ∷ replicate n y) x=y b)

-- The same lemma holds for replicate⁺.
all-replicate⁺ : {xs : List A} {y : A} (n : ℕ⁺) →

  xs ∼[ bag ] replicate⁺ n y → xs ≡ replicate⁺ n y

all-replicate⁺ {[]}     {y} (suc n) b with from b ⟨$⟩ here P.refl; ... | ()
all-replicate⁺ {x ∷ xs} {y} (suc n) b = P.cong₂ _∷_ x=y (all-replicate n b')
  where
    x=y : x ≡ y
    x=y = ∈-replicate n (to b ⟨$⟩ here P.refl)
    b'  : xs ∼[ bag ] replicate n y
    b'  = B.drop-cons (P.subst (λ x → x ∷ xs ∼[ bag ] y ∷ replicate n y) x=y b)

-- There is a bijection between indices into the environment
-- ΓΣΔΠ and the environment ΓΔΣΠ.
swp₄ : (xs ys zs {ws} : List A) →
      xs ++ zs ++ ys ++ ws ∼[ bag ] xs ++ ys ++ zs ++ ws
swp₄ xs []       zs = id
swp₄ xs (y ∷ ys) zs =
  ( P.subst₂ _∼[ bag ]_ (++.assoc xs [ y ] _) (++.assoc xs [ y ] _)
  $ swp₄ (xs ++ [ y ]) ys zs
  ) ∘ fwd xs zs

-- Alias for swp₄.
swp = swp₄

-- There is a bijection between indices into the environment
-- ΓΣΔ and the environment ΓΔΣ. This is mostly a convenience
-- function because of the annoyance of using ++.identity
-- in the logic proofs.
swp₃ : (xs ys {zs} : List A) →
       xs ++ zs ++ ys ∼[ bag ] xs ++ ys ++ zs
swp₃ xs ys {zs} =
  ( P.subst₂ (λ ys' zs' → xs ++ zs ++ ys' ∼[ bag ] xs ++ ys ++ zs')
             (proj₂ ++.identity ys)
             (proj₂ ++.identity zs)
  $ swp₄ xs ys zs
  )

-- There is a bijection between indices into the environment
-- ΓΣΔ and the environment ΓΔΣ. This is mostly a convenience
-- function because of the annoyance of using ++.identity
-- in the logic proofs.
swp₂ : (xs {ys} : List A) →
       ys ++ xs ∼[ bag ] xs ++ ys
swp₂ = swp₃ []

_-_ : (xs : List A) {x : A} (i : x ∈ xs) → List A
(x ∷ xs) - (here  _) = xs
(x ∷ xs) - (there i) = x ∷ xs - i

del-[] : {x y : A} (i : y ∈ x ∷ []) → (x ∷ []) - i ≡ []
del-[] (here px) = P.refl
del-[] (there ())

-- Given a proof of membership x ∈ xs, we can be sure that
-- there are two lists xs₁ and xs₂ s.t.
--
--   (1) xs = xs₁ ++ x ∷ xs₂; and
--   (2) xs - i = xs₁ ++ xs₂.
--
-- That is to say, that we can break the list xs into a prefix,
-- the element x, and a suffix, and that if we delete that occurrence
-- of x, we are left with the prefix and the suffix.
∈→++ : {xs : List A} {x : A} (i : x ∈ xs) →
      ∃₂ λ xs₁ xs₂ → xs ≡ xs₁ ++ x ∷ xs₂ × xs - i ≡ xs₁ ++ xs₂
∈→++ {x ∷ xs} (here P.refl) = ([] , xs , P.refl , P.refl)
∈→++ {x ∷ xs} (there i) with ∈→++ {xs} i
... | (xs₁ , xs₂ , p₁ , p₂) = (x ∷ xs₁ , xs₂ , P.cong (x ∷_) p₁ , P.cong (x ∷_) p₂)

-- Given two lists which are bag-equal, we can prove that if we delete
-- the same element from both lists, the resulting lists are also bag-equal.
del-to : {xs ys : List A} {x : A} →
         (eq : xs ∼[ bag ] ys) (i : x ∈ xs) →
         xs - i ∼[ bag ] ys - (to eq ⟨$⟩ i)
del-to {xs} {ys} {x} eq i
  with ∈→++ {xs} i
... | (xs₁ , xs₂ , p₁ , p₂) rewrite p₁ | p₂
  with ∈→++ {ys} (to eq ⟨$⟩ i)
... | (ys₁ , ys₂ , q₁ , q₂) rewrite q₁ | q₂
    = B.drop-cons (fwd [] ys₁ ∘ eq ∘ sym (fwd [] xs₁))

del-from : {xs ys : List A} {x : A} →
           (eq : ys ∼[ bag ] xs) (i : x ∈ xs) →
           ys - (from eq ⟨$⟩ i) ∼[ bag ] xs - i
del-from {xs} {ys} {x} eq i
  with ∈→++ {xs} i
... | (xs₁ , xs₂ , p₁ , p₂) rewrite p₁ | p₂
  with ∈→++ {ys} (from eq ⟨$⟩ i)
... | (ys₁ , ys₂ , q₁ , q₂) rewrite q₁ | q₂
    = B.drop-cons (fwd [] xs₁ ∘ eq ∘ sym (fwd [] ys₁))

-- Split a environment based on a proof of membership (used as index).
split : ∀ (xs {ys} : List A) {x : A} →
        (i : x ∈ xs ++ ys) →
        Σ[ j ∈ x ∈ xs ] ((xs ++ ys) - i ≡ xs - j ++ ys) ⊎
        Σ[ j ∈ x ∈ ys ] ((xs ++ ys) - i ≡ xs ++ ys - j)
split []       i         = inj₂ (i , P.refl)
split (_ ∷ xs) (here px) = inj₁ (here px , P.refl)
split (_ ∷ xs) (there i) with split xs i
... | inj₁ (j , p) = inj₁ (there j , P.cong (_ ∷_) p)
... | inj₂ (j , p) = inj₂ (j , P.cong (_ ∷_) p)

-- -}
-- -}
-- -}
-- -}
-- -}


{-# OPTIONS --without-K #-}
module hott.loop.properties where

open import sum
open import equality
open import function.core
open import function.extensionality
open import function.isomorphism.core
open import function.overloading
open import pointed
open import sets.nat.core
open import hott.loop.core

mapΩ₁-const : ∀ {i j}{𝓧 : PSet i}{𝓨 : PSet j}
            → mapΩ₁ (constP 𝓧 𝓨) ≡ constP _ _
mapΩ₁-const = apply≅ pmap-eq (ap-const _ , refl)
  where
    ap-const : ∀ {i j}{X : Set i}{Y : Set j}(y : Y)
             → {x x' : X}(p : x ≡ x') → ap (λ _ → y) p ≡ refl
    ap-const y refl = refl

mapΩP-const : ∀ {i j} n → {𝓧 : PSet i}{𝓨 : PSet j}
            → mapΩP n (constP 𝓧 𝓨) ≡ constP _ _
mapΩP-const zero = refl
mapΩP-const (suc n) = ap (mapΩP n) mapΩ₁-const · mapΩP-const n

mapΩ-const : ∀ {i j} n → {X : Set i}{Y : Set j}(y : Y)
           → (x : X) (p : Ω n x)
           → mapΩ n (λ _ → y) p ≡ refl' n y
mapΩ-const n y x p = funext-inv (ap proj₁ (mapΩP-const n)) p

mapΩ₁-hom : ∀ {i j k}{𝓧 : PSet i}{𝓨 : PSet j}{𝓩 : PSet k}
          → (f : PMap 𝓧 𝓨)(g : PMap 𝓨 𝓩)
          → mapΩ₁ g ∘ mapΩ₁ f ≡ mapΩ₁ (g ∘ f)
mapΩ₁-hom (f , refl) (g , refl) = apply≅ pmap-eq (ap-hom f g , refl)

mapΩP-hom : ∀ {i j k} n → {𝓧 : PSet i}{𝓨 : PSet j}{𝓩 : PSet k}
          → (f : PMap 𝓧 𝓨)(g : PMap 𝓨 𝓩)
          → mapΩP n g ∘ mapΩP n f ≡ mapΩP n (g ∘ f)
mapΩP-hom zero f g = refl
mapΩP-hom (suc n) f g = mapΩP-hom n (mapΩ₁ f) (mapΩ₁ g)
                      · ap (mapΩP n) (mapΩ₁-hom f g)

mapΩ-hom : ∀ {i j k} n {X : Set i}{Y : Set j}{Z : Set k}
         → (f : X → Y)(g : Y → Z){x : X}(p : Ω n x)
         → mapΩ n g (mapΩ n f p) ≡ mapΩ n (g ∘ f) p
mapΩ-hom n f g = proj₁ (invert≅ pmap-eq (mapΩP-hom n (f , refl) (g , refl)))

mapΩ-refl : ∀ {i j} n {X : Set i}{Y : Set j}
          → (f : X → Y){x : X}
          → mapΩ n f (refl' n x) ≡ refl' n (f x)
mapΩ-refl n f = proj₂ (mapΩP n (f , refl))


open import Agda.Builtin.Nat
open import Agda.Builtin.Equality
open import Agda.Builtin.Sigma
open import Agda.Builtin.List
open import Agda.Builtin.Reflection renaming (bindTC to _>>=_)

data Fin : Nat → Set where
  zero : {n : Nat} → Fin (suc n)
  suc  : {n : Nat} → Fin n → Fin (suc n)

test : (x : Nat) (y : Fin x) {z : Nat} → x ≡ suc z → Set
test .(suc z) y {z} refl = Nat

-- Telescope: (z : Nat) (y : Fin (suc z))
--  agda rep: ("z" , Nat) ("y" , Fin (suc @0))
-- Patterns:  test .(suc @1) @0 @1 refl = ?

macro
  testDef : Term → TC _
  testDef goal = do
    d  ← getDefinition (quote test)
    `d ← quoteTC d
    unify `d goal

foo : Definition
foo = testDef

fooTest : foo ≡ function
                  (clause
                   (("z" , arg (arg-info hidden relevant) (def (quote Nat) [])) ∷
                    ("y" ,
                     arg (arg-info visible relevant)
                     (def (quote Fin)
                      (arg (arg-info visible relevant)
                       (con (quote Nat.suc)
                        (arg (arg-info visible relevant) (var 0 []) ∷ []))
                       ∷ [])))
                    ∷ [])
                   (arg (arg-info visible relevant)
                    (dot
                     (con (quote Nat.suc)
                      (arg (arg-info visible relevant) (var 1 []) ∷ [])))
                    ∷
                    arg (arg-info visible relevant) (var 0) ∷
                    arg (arg-info hidden relevant) (var 1) ∷
                    arg (arg-info visible relevant) (con (quote refl) []) ∷ [])
                   (def (quote Nat) [])
                   ∷ [])
fooTest = refl

defBar : (name : Name) → TC _
defBar x = do
  function cls ← returnTC foo
    where _ → typeError []
  defineFun x cls

bar : (x : Nat) (y : Fin x) {z : Nat} → x ≡ suc z → Set
unquoteDef bar = defBar bar