module Pi.Examples where
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Data.Nat
open import Data.Vec as V hiding (map)
open import Pi.Syntax
open import Pi.Opsem
open import Pi.Eval

pattern 𝔹 = 𝟙 +ᵤ 𝟙
pattern 𝔽 = inj₁ tt
pattern 𝕋 = inj₂ tt

𝔹^ : ℕ → 𝕌
𝔹^ 0 = 𝟙
𝔹^ 1 = 𝔹
𝔹^ (suc (suc n)) = 𝔹 ×ᵤ 𝔹^ (suc n)

-----------------------------------------------------------------------------
-- Adaptors

[A+B]+C=[C+B]+A : ∀ {A B C} → (A +ᵤ B) +ᵤ C ↔ (C +ᵤ B) +ᵤ A
[A+B]+C=[C+B]+A = assocr₊ ⨾ (id↔ ⊕ swap₊) ⨾ swap₊

[A+B]+C=[A+C]+B : ∀ {A B C} → (A +ᵤ B) +ᵤ C ↔ (A +ᵤ C) +ᵤ B
[A+B]+C=[A+C]+B = assocr₊ ⨾ (id↔ ⊕ swap₊) ⨾ assocl₊

[A+B]+[C+D]=[A+C]+[B+D] : {A B C D : 𝕌} → (A +ᵤ B) +ᵤ (C +ᵤ D) ↔ (A +ᵤ C) +ᵤ (B +ᵤ D)
[A+B]+[C+D]=[A+C]+[B+D] =  assocl₊ ⨾ (assocr₊ ⊕ id↔) ⨾ ((id↔ ⊕ swap₊) ⊕ id↔) ⨾ (assocl₊ ⊕ id↔) ⨾ assocr₊

Ax[BxC]=Bx[AxC] : {A B C : 𝕌} → A ×ᵤ (B ×ᵤ C) ↔ B ×ᵤ (A ×ᵤ C)
Ax[BxC]=Bx[AxC] = assocl⋆ ⨾ (swap⋆ ⊗ id↔) ⨾  assocr⋆

[AxB]×C=[A×C]xB : ∀ {A B C} → (A ×ᵤ B) ×ᵤ C ↔ (A ×ᵤ C) ×ᵤ B
[AxB]×C=[A×C]xB = assocr⋆ ⨾ (id↔ ⊗ swap⋆) ⨾ assocl⋆

[A×B]×[C×D]=[A×C]×[B×D] : {A B C D : 𝕌} → (A ×ᵤ B) ×ᵤ (C ×ᵤ D) ↔ (A ×ᵤ C) ×ᵤ (B ×ᵤ D)
[A×B]×[C×D]=[A×C]×[B×D] = assocl⋆ ⨾ (assocr⋆ ⊗ id↔) ⨾ ((id↔ ⊗ swap⋆) ⊗ id↔) ⨾ (assocl⋆ ⊗ id↔) ⨾ assocr⋆

-- FST2LAST(b₁,b₂,…,bₙ) = (b₂,…,bₙ,b₁)
FST2LAST : ∀ {n} → 𝔹^ n ↔ 𝔹^ n
FST2LAST {0} = id↔
FST2LAST {1} = id↔
FST2LAST {2} = swap⋆
FST2LAST {suc (suc (suc n))} = Ax[BxC]=Bx[AxC] ⨾ (id↔ ⊗ FST2LAST)

FST2LAST⁻¹ : ∀ {n} → 𝔹^ n ↔ 𝔹^ n
FST2LAST⁻¹ = ! FST2LAST

-- NOT(b) = ¬b
NOT : 𝔹 ↔ 𝔹
NOT = swap₊

-- CNOT(b₁,b₂) = (b₁,b₁ xor b₂)
CNOT : 𝔹 ×ᵤ 𝔹 ↔ 𝔹 ×ᵤ 𝔹
CNOT = dist ⨾ (id↔ ⊕ (id↔ ⊗ swap₊)) ⨾ factor

-- CIF(c₁,c₂)(𝔽,a) = (𝔽,c₁ a)
-- CIF(c₁,c₂)(𝕋,a) = (𝕋,c₂ a)
CIF : {A : 𝕌} → (c₁ c₂ : A ↔ A) → 𝔹 ×ᵤ A ↔ 𝔹 ×ᵤ A
CIF c₁ c₂ = dist ⨾ ((id↔ ⊗ c₁) ⊕ (id↔ ⊗ c₂)) ⨾ factor

CIF₁ CIF₂ : {A : 𝕌} → (c : A ↔ A) → 𝔹 ×ᵤ A ↔ 𝔹 ×ᵤ A
CIF₁ c = CIF c id↔
CIF₂ c = CIF id↔ c

-- TOFFOLI(b₁,…,bₙ,b) = (b₁,…,bₙ,b xor (b₁ ∧ … ∧ bₙ))
TOFFOLI : {n : ℕ} → 𝔹^ n ↔ 𝔹^ n
TOFFOLI {0} = id↔
TOFFOLI {1} = swap₊
TOFFOLI {suc (suc n)} = CIF₂ TOFFOLI

-- TOFFOLI(b₁,…,bₙ,b) = (b₁,…,bₙ,b xor (¬b₁ ∧ … ∧ ¬bₙ))
TOFFOLI' : ∀ {n} → 𝔹^ n ↔ 𝔹^ n
TOFFOLI' {0} = id↔
TOFFOLI' {1} = swap₊
TOFFOLI' {suc (suc n)} = CIF₁ TOFFOLI'

-- RESET(b₁,…,bₙ) = (b₁ xor (b₁ ∨ … ∨ bₙ),…,bₙ)
RESET : ∀ {n} → 𝔹 ×ᵤ 𝔹^ n ↔ 𝔹 ×ᵤ 𝔹^ n
RESET {0} = id↔
RESET {1} = swap⋆ ⨾ CNOT ⨾ swap⋆
RESET {suc (suc n)} = Ax[BxC]=Bx[AxC] ⨾ CIF RESET (swap₊ ⊗ id↔) ⨾ Ax[BxC]=Bx[AxC]

-----------------------------------------------------------------------------
-- Reversible Copy
-- copy(𝔽,b₁,…,bₙ) = (b₁,b₁,…,bₙ)
COPY : ∀ {n} → 𝔹 ×ᵤ 𝔹^ n ↔ 𝔹 ×ᵤ 𝔹^ n
COPY {0} = id↔
COPY {1} = swap⋆ ⨾ CNOT ⨾ swap⋆
COPY {suc (suc n)} = assocl⋆ ⨾ (COPY {1} ⊗ id↔) ⨾ assocr⋆

-----------------------------------------------------------------------------
-- Arithmetic
-- INCR(b̅) = INCR(b̅ + 1)
INCR : ∀ {n} → 𝔹^ n ↔ 𝔹^ n
INCR {0} = id↔
INCR {1} = swap₊
INCR {suc (suc n)} = (id↔ ⊗ INCR) ⨾ FST2LAST ⨾ TOFFOLI' ⨾ FST2LAST⁻¹

iterate : (n : ℕ) → (𝔹^ n ↔ 𝔹^ n) → Vec ⟦ 𝔹^ n ⟧ (2 ^ n)
iterate n f = go initial
  where
    initial : ∀ {m} → ⟦ 𝔹^ m ⟧
    initial {zero} = tt
    initial {suc zero} = 𝔽
    initial {suc (suc m)} = 𝔽 , initial

    go : ∀ {m} → ⟦ 𝔹^ n ⟧ → Vec ⟦ 𝔹^ n ⟧ m
    go {0} v = []
    go {suc m} v = let v' = eval f v
                   in  v ∷ go v'

INCRₜₑₛₜ = iterate 4 INCR


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of rose trees
------------------------------------------------------------------------

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

module Data.Tree.Rose.Properties where

open import Level using (Level)
open import Size
open import Data.List.Base as List using (List)
open import Data.List.Extrema.Nat
import Data.List.Properties as Listₚ
open import Data.Nat.Base using (ℕ; zero; suc)
open import Data.Tree.Rose
open import Function.Base
open import Relation.Binary.PropositionalEquality
open ≡-Reasoning

private
  variable
    a b c : Level
    A : Set a
    B : Set b
    C : Set c
    i : Size

------------------------------------------------------------------------
-- map properties

map-compose : (f : A → B) (g : B → C) →
              map {i = i} (g ∘′ f) ≗ map {i = i} g ∘′ map {i = i} f
map-compose f g (node a ts) = cong (node (g (f a))) $ begin
  List.map (map (g ∘′ f)) ts             ≡⟨ Listₚ.map-cong (map-compose f g) ts ⟩
  List.map (map g ∘ map f) ts            ≡⟨ Listₚ.map-compose ts ⟩
  List.map (map g) (List.map (map f) ts) ∎

depth-map : (f : A → B) (t : Rose A i) → depth {i = i} (map {i = i} f t) ≡ depth {i = i} t
depth-map f (node a ts) = cong (suc ∘′ max 0) $ begin
  List.map depth (List.map (map f) ts) ≡˘⟨ Listₚ.map-compose ts ⟩
  List.map (depth ∘′ map f) ts         ≡⟨ Listₚ.map-cong (depth-map f) ts ⟩
  List.map depth ts ∎

------------------------------------------------------------------------
-- foldr properties

foldr-map : (f : A → B) (n : B → List C → C) (ts : Rose A i) →
            foldr {i = i} n (map {i = i} f ts) ≡ foldr {i = i} (n ∘′ f) ts
foldr-map f n (node a ts) = cong (n (f a)) $ begin
  List.map (foldr n) (List.map (map f) ts) ≡˘⟨ Listₚ.map-compose ts ⟩
  List.map (foldr n ∘′ map f) ts           ≡⟨ Listₚ.map-cong (foldr-map f n) ts ⟩
  List.map (foldr (n ∘′ f)) ts             ∎


{-# OPTIONS --universe-polymorphism #-}

module LevelWithBug where

open import Common.Level

postulate
  take : ∀ a → Set a → Set
  a : Level
  A : Set a
  Goal : Set → Set
  goal : ∀ X → Goal X

-- The meta got solved by Level (Max [Plus 0 (NeutralLevel a)]) which
-- didn't match the argument in the with expression which is simply a.
-- Now the level noise should go away when it's not useful.
foo : Goal (take _ A)
foo with take a A
... | z = goal z

-- Here's another more complicated one.

data List {a}(A : Set a) : Set a where
  [] : List A
  _∷_ : A → List A → List A

data _≡_ {a}{A : Set a}(x : A) : A → Set a where
  refl : x ≡ x

{-# BUILTIN EQUALITY _≡_ #-}
{-# BUILTIN REFL refl #-}

-- Sums commute with Any (for a fixed list).

data Any {a p} {A : Set a}
         (P : A → Set p) : List A → Set (a ⊔ p) where
  there : ∀ {x xs} (pxs : Any P xs) → Any P (x ∷ xs)

amap : ∀ {a p q} {A : Set a} {P : A → Set p} → {Q : A → Set q} →
      (∀ {x} → P x → Q x) → ∀ {xs} → Any P xs → Any Q xs
amap g (there pxs) = there (amap g pxs)

data _+_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  inl : (x : A) → A + B
  inr : (y : B) → A + B

smap : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
      (A → C) → (B → D) → (A + B → C + D)
smap f g (inl x) = inl (f x)
smap f g (inr y) = inr (g y)

postulate
  p q : Level
  P : A → Set p
  Q : A → Set q

to : ∀ xs → Any P xs + Any Q xs → Any (λ x → P x + Q x) xs
to xs (inl pxs) = amap inl pxs
to xs (inr pxs) = amap inr pxs

from : ∀ xs → Any (λ x → P x + Q x) xs → Any P xs + Any Q xs
from ._ (there p) = smap there there (from _ p)

-- Here the abstraction didn't work because a NeutralLevel was replaced
-- by an UnreducedLevel during abstraction.
fromto : ∀ xs (p : Any P xs + Any Q xs) → from xs (to xs p) ≡ p
fromto .(x ∷ xs) (inl (there {x}{xs} p)) rewrite fromto xs (inl p) = refl
fromto .(x ∷ xs) (inr (there {x}{xs} q)) rewrite fromto xs (inr q) = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Results concerning uniqueness of identity proofs, with axiom K
------------------------------------------------------------------------

{-# OPTIONS --with-K --safe #-}

module Axiom.UniquenessOfIdentityProofs.WithK where

open import Axiom.UniquenessOfIdentityProofs
open import Relation.Binary.PropositionalEquality.Core

-- Axiom K implies UIP.

uip : ∀ {a} {A : Set a} → UIP A
uip refl refl = refl


{-# OPTIONS --without-K #-}
module sets.nat.ordering.leq.level where

open import sum
open import equality
open import function.isomorphism
open import function.extensionality
open import hott.level
open import sets.nat.core
open import sets.nat.ordering.leq.core
open import container.core
open import container.w
open import sets.empty
open import sets.unit

open import hott.level.sets public
  using (nat-set)

module ≤-container where
  I : Set
  I = ℕ × ℕ

  A A₁ A₂ : I → Set
  A₁ (m , n) = m ≡ 0
  A₂ (m , n) = ( Σ I λ { (m' , n')
               → m ≡ suc m'
               × n ≡ suc n' } )
  A i = A₁ i ⊎ A₂ i

  A₂-struct-iso : ∀ m n
                → A₂ (m , n)
                ≅ (¬ (m ≡ 0) × ¬ (n ≡ 0))
  A₂-struct-iso m n = iso f g α β
    where
      f : ∀ {m n} → A₂ (m , n) → ¬ (m ≡ 0) × ¬ (n ≡ 0)
      f .{suc m}.{suc n}((m , n) , refl , refl) = (λ ()) , (λ ())

      g : ∀ {m n} → ¬ (m ≡ 0) × ¬ (n ≡ 0) → A₂ (m , n)
      g {m = zero} (u , v) = ⊥-elim (u refl)
      g {n = zero} (u , v) = ⊥-elim (v refl)
      g {suc m} {suc n} _ = ((m , n) , refl , refl)

      α : ∀ {m n}(a : A₂ (m , n)) → g (f a) ≡ a
      α .{suc m}.{suc n}((m , n) , refl , refl) = refl

      β : ∀ {m n}(u : ¬ (m ≡ 0) × ¬ (n ≡ 0)) → f (g u) ≡ u
      β {m = zero} (u , v) = ⊥-elim (u refl)
      β {n = zero} (u , v) = ⊥-elim (v refl)
      β {suc m} {suc n} _ = pair≡ (funext λ ()) (funext λ ())

  A₂-h1 : ∀ i → h 1 (A₂ i)
  A₂-h1 (m , n) = iso-level (sym≅ (A₂-struct-iso m n))
    (×-level (Π-level λ _ → ⊥-prop) (Π-level λ _ → ⊥-prop))

  A-disj : ∀ i → ¬ (A₁ i × A₂ i)
  A-disj (.zero , n) (refl , (m' , n') , () , pn)

  A-h1 : ∀ i → h 1 (A i)
  A-h1 i = ⊎-h1 (nat-set _ zero) (A₂-h1 i) (A-disj i)

  B : ∀ {i} → A i → Set
  B (inj₁ _) = ⊥
  B (inj₂ _) = ⊤

  r : ∀ {i}{a : A i} → B a → I
  r {a = inj₁ _} ()
  r {a = inj₂ (mn' , _)} _ = mn'

  c : Container _ _ _
  c = container I A B r

open ≤-container using (c; A-h1)

≤-struct-iso : ∀ {n m}
             → n ≤ m
             ≅ W c (n , m)
≤-struct-iso = iso f g α β
  where
    f : ∀ {n m} → n ≤ m → W c (n , m)
    f z≤n = sup (inj₁ refl) (λ ())
    f (s≤s p) = sup (inj₂ (_ , (refl , refl))) (λ _ → f p)

    g : ∀ {m n} → W c (m , n) → m ≤ n
    g (sup (inj₁ refl) _) = z≤n
    g (sup (inj₂ ((m' , n') , (refl , refl))) u)
      = s≤s (g (u tt))

    α : ∀ {n m}(p : n ≤ m) → g (f p) ≡ p
    α z≤n = refl
    α (s≤s p) = ap s≤s (α p)

    β : ∀ {n m}(p : W c (m , n)) → f (g p) ≡ p
    β (sup (inj₁ refl) u) = ap (sup (inj₁ refl)) (funext λ ())
    β (sup (inj₂ ((m' , n') , (refl , refl))) u)
      = ap (sup _) (funext λ { tt → β (u tt) })

≤-level : ∀ {m n} → h 1 (m ≤ n)
≤-level {m}{n} = iso-level (sym≅ ≤-struct-iso) (w-level A-h1 (m , n))


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

module TreeFold.Indexed where

open import Prelude
open import Data.Binary using (𝔹; 0ᵇ; 1ᵇ_; 2ᵇ_; ⟦_⇓⟧; ⟦_⇑⟧; inc)
open import Data.Binary.Isomorphism
open import Data.Nat

private
  variable
    n m : ℕ
    t : Level
    N : ℕ → Type t
    ns : 𝔹

double : ℕ → ℕ
double n = n * 2

2^_*_ : ℕ → ℕ → ℕ
2^ zero  * m = m
2^ suc n * m = double (2^ n * m)

infixr 5 _1∷_ _2∷_
data Array (T : ℕ → Type a) : 𝔹 → Type a where
  []  : Array T 0ᵇ
  _1∷_ : T 1 → Array (T ∘ double) ns → Array T (1ᵇ ns)
  _2∷_ : T 2 → Array (T ∘ double) ns → Array T (2ᵇ ns)

cons : (∀ n → N n → N n → N (double n)) → N 1 → Array N ns → Array N (inc ns)
cons branch x [] = x 1∷ []
cons branch x (y 1∷ ys) = branch 1 x y 2∷ ys
cons branch x (y 2∷ ys) = x 1∷ cons (branch ∘ double) y ys

array-foldr : (N : ℕ → Type t) → (∀ n m → N (2^ n * 1) → N (2^ n * m) → N (2^ n * suc m)) → N 0 → Array N ns → N ⟦ ns ⇓⟧
array-foldr {ns = 0ᵇ}    N c b []        = b
array-foldr {ns = 2ᵇ ns} N c b (x 2∷ xs) = c 1 ⟦ ns ⇓⟧       x (array-foldr (N ∘ double) (c ∘ suc) b xs)
array-foldr {ns = 1ᵇ ns} N c b (x 1∷ xs) = c 0 (⟦ ns ⇓⟧ * 2) x (array-foldr (N ∘ double) (c ∘ suc) b xs)

open import Data.Vec
import Data.Nat.Properties as ℕ

double≡*2 : ∀ n → n + n ≡ n * 2
double≡*2 zero    = refl
double≡*2 (suc n) = cong suc (ℕ.+-suc n n ; cong suc (double≡*2 n))

module NonNorm {t} (N : ℕ → Type t) (f : ∀ p n m → N (2^ p * n) → N (2^ p * m) → N (2^ p * (n + m))) (z : N 0) where
  spine : Vec (N 1) n → Array (N ) ⟦ n ⇑⟧
  spine [] = []
  spine (x ∷ xs) = cons (λ n x y → subst N (double≡*2 n) (f 0 n n x y)) x (spine xs)

  unspine : Array N ns → N ⟦ ns ⇓⟧
  unspine = array-foldr N (λ n → f n 1) z

  treeFold : Vec (N 1) n → N n
  treeFold xs = subst N (𝔹-rightInv _) (unspine (spine xs))

pow-suc : ∀ n m → (2^ n * 1) + (2^ n * m) ≡ (2^ n * suc m)
pow-suc zero m = refl
pow-suc (suc n) m = sym (ℕ.+-*-distrib (2^ n * 1) (2^ n * m) 2) ; cong (_* 2) (pow-suc n m)

module _ {t} (N : ℕ → Type t) (f : ∀ {n m} → N n → N m → N (n + m)) (z : N 0) where
  spine : Vec (N 1) n → Array (N ) ⟦ n ⇑⟧
  spine [] = []
  spine (x ∷ xs) = cons (λ n x y → subst N (double≡*2 n) (f x y)) x (spine xs)

  unspine : Array N ns → N ⟦ ns ⇓⟧
  unspine = array-foldr N (λ n m xs ys → subst N (pow-suc n m) (f xs ys)) z

  treeFold : Vec (N 1) n → N n
  treeFold xs = subst N (𝔹-rightInv _) (unspine (spine xs))


-- Agda supports full unicode everywhere. An Agda file should be written using
-- the UTF-8 encoding.

module Introduction.Unicode where

module ユーニコード where

  data _∧_ (P Q : Prop) : Prop where
    ∧-intro : P -> Q -> P ∧ Q

  ∧-elim₁ : {P Q : Prop} -> P ∧ Q -> P
  ∧-elim₁ (∧-intro p _) = p

  ∧-elim₂ : {P Q : Prop} -> P ∧ Q -> Q
  ∧-elim₂ (∧-intro _ q) = q

  data _∨_ (P Q : Prop) : Prop where
    ∨-intro₁ : P -> P ∨ Q
    ∨-intro₂ : Q -> P ∨ Q

  ∨-elim : {P Q R : Prop} -> (P -> R) -> (Q -> R) -> P ∨ Q -> R
  ∨-elim f g (∨-intro₁ p) = f p
  ∨-elim f g (∨-intro₂ q) = g q

  data ⊥ : Prop where

  data ⊤ : Prop where
    ⊤-intro : ⊤

  data ¬_ (P : Prop) : Prop where
    ¬-intro : (P -> ⊥) -> ¬ P

  data ∏ {A : Set}(P : A -> Prop) : Prop where
    ∏-intro : ((x : A) -> P x) -> ∏ P


open import Prelude

open import Data.Nat
open import Data.Nat.Properties
open import Induction.Nat

module Implicits.Resolution.GenericFinite.Examples.MaximumDepth
  where

open import Implicits.Resolution.GenericFinite.TerminationCondition

_<′?_ : (x y : ℕ) → Dec (x <′ y)
x <′? y with (suc x) ≤? y
x <′? y | yes p = yes (≤⇒≤′ p)
x <′? y | no ¬p = no (λ p → ¬p (≤′⇒≤ p))

MaxDepthCondition : TerminationCondition
MaxDepthCondition = record
  {  TCtx = ℕ
  ; _<_  = _<′_
  ; _<?_  = _<′?_
  ; step = λ n _ _ _ _ → n ∸ 1
  ; wf-< = <-well-founded
  }


module Category where

open import Function hiding (_∘_; id)
open import Level
open import Data.Product
open import Relation.Binary
open import Relation.Binary.Core using (_≡_)
open Setoid renaming (_≈_ to eqSetoid)

open import Basic

record Category (C₀ C₁ ℓ : Level) : Set (suc (C₀ ⊔ C₁ ⊔ ℓ)) where
  field
    Obj : Set C₀
    Homsetoid : Obj → Obj → Setoid C₁ ℓ

  Hom : Obj → Obj → Set C₁
  Hom A B = Carrier (Homsetoid A B)

  equal : {A B : Obj} → Hom A B → Hom A B → Set ℓ
  equal {A} {B} f g = eqSetoid (Homsetoid A B) f g

  field
    comp : {A B C : Obj} → Hom B C → Hom A B → Hom A C
    id : {A : Obj} → Hom A A

  field
    leftId : {A B : Obj} {f : Hom A B} → equal (comp id f) f
    rightId : {A B : Obj} {f : Hom A B} → equal (comp f id) f
    assoc : {A B C D : Obj} {f : Hom A B} {g : Hom B C} {h : Hom C D}
      → equal (comp (comp h g) f) (comp h (comp g f))
    ≈-composite : {A B C : Obj} {f g : Hom B C} {h i : Hom A B}
      → equal f g → equal h i → equal (comp f h) (comp g i)

  dom : {A B : Obj} → Hom A B → Obj
  dom {A} _ = A

  cod : {A B : Obj} → Hom A B → Obj
  cod {B} _ = B

  op : Category C₀ C₁ ℓ
  op = record
    { Obj = Obj
    ; Homsetoid = flip Homsetoid
    ; comp = flip comp
    ; id = id

    ; leftId = rightId
    ; rightId = leftId
    ; assoc = λ{A B C D} → IsEquivalence.sym (isEquivalence (Homsetoid D A)) assoc
    ; ≈-composite = flip ≈-composite
    }

open Category

infixr 9 _∘_
infixr 7 _[_∘_]
infixr 2 _≈_
infixr 2 _[_≈_]
infix 4 _[_≅_]

_[_∘_] : ∀{C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {a b c : Obj C} → Hom C b c → Hom C a b → Hom C a c
C [ f ∘ g ] = comp C f g

_∘_ : ∀{C₀ C₁ ℓ} {C : Category C₀ C₁ ℓ} {a b c : Obj C} → Hom C b c → Hom C a b → Hom C a c
_∘_ {C = C} = _[_∘_] C

_[_≈_] : ∀{C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {A B : Obj C} → Rel (Hom C A B) ℓ
C [ f ≈ g ] = equal C f g

_≈_ : ∀{C₀ C₁ ℓ} {C : Category C₀ C₁ ℓ} {A B : Obj C} → Rel (Hom C A B) ℓ
_≈_ {C = C} = equal C

equiv : ∀{C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {A B : Obj C} → IsEquivalence (eqSetoid (Homsetoid C A B))
equiv C {A} {B} = isEquivalence (Homsetoid C A B)

refl-hom : ∀{C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {A B : Obj C} {f : Hom C A B} → C [ f ≈ f ]
refl-hom C = IsEquivalence.refl (equiv C)

sym-hom : ∀{C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {A B : Obj C} {f g : Hom C A B} → C [ f ≈ g ] → C [ g ≈ f ]
sym-hom C = IsEquivalence.sym (equiv C)

trans-hom : ∀{C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {A B : Obj C} {f g h : Hom C A B} → C [ f ≈ g ] → C [ g ≈ h ] → C [ f ≈ h ]
trans-hom C = IsEquivalence.trans (equiv C)

record _[_≅_] {C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) (a b : Obj C) : Set (C₀ ⊔ C₁ ⊔ ℓ) where
  field
    map-→ : Hom C a b
    map-← : Hom C b a
    proof : (C [ C [ map-→ ∘ map-← ] ≈ id C ]) × (C [ C [ map-← ∘ map-→ ] ≈ id C ])

record isomorphism {C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {a b : Obj C} (f : Hom C a b) : Set (C₀ ⊔ C₁ ⊔ ℓ) where
  field
    inverse-map : Hom C b a
    proof : (C [ C [ f ∘ inverse-map ] ≈ id C ]) × (C [ C [ inverse-map ∘ f ] ≈ id C ])

iso-≅ : ∀ {C₀ C₁ ℓ} (C : Category C₀ C₁ ℓ) {a b : Obj C} {f : Hom C a b} → isomorphism C f → C [ a ≅ b ]
iso-≅ C {f = f} iso = record {
  map-→ = f ;
  map-← = isomorphism.inverse-map iso ;
  proof = isomorphism.proof iso }

Setoids : {c₀ ℓ : Level} → Category (suc (c₀ ⊔ ℓ)) (suc (c₀ ⊔ ℓ)) (c₀ ⊔ ℓ)
Setoids {c₀} {ℓ} = record {
  Obj = Setoid c₀ ℓ;
  Homsetoid = λ A B → record { Carrier = Map.Map A B; _≈_ = Map.equality; isEquivalence = Map-Equal-Equiv A B };
  comp = Map.compose;
  id = Map.identity;
  leftId = λ {_ B} _ → refl B;
  rightId = λ {_ B} _ → refl B;
  assoc = λ {_ _ _ D} x → refl D;
  ≈-composite = λ {_ _ C f g h} x x₁ x₂ → trans C (x (Map.Map.mapping h x₂)) (Map.Map.preserveEq g (x₁ x₂)) }
  where
    Map-Equal-Equiv : (A B : Setoid _ _) → IsEquivalence Map.equality
    Map-Equal-Equiv A B = record { refl = λ _ → refl B ; sym = λ x x₁ → sym B (x x₁) ; trans = λ x x₁ x₂ → trans B (x x₂) (x₁ x₂) }

module CategoryReasoning where
  infix 1 begin⟨_⟩_
  infixr 2 _≈⟨_⟩_ _≡⟨_⟩_
  infix 3 _∎

  data IsRelatedTo[_] {c₀ c₁ ℓ} (C : Category c₀ c₁ ℓ) (a b : Obj C) (x y : Hom C a b) : Set ℓ where
    relTo : C [ x ≈ y ] → IsRelatedTo[ C ] a b x y

  begin⟨_⟩_ : ∀ {c₀ c₁ ℓ} (C : Category c₀ c₁ ℓ) → {A B : Obj C} {f g : Hom C A B} → IsRelatedTo[ C ] A B f g → equal C f g
  begin⟨_⟩_ C {A} {B} (relTo x≈y) = x≈y

  _∎ : ∀ {c₀ c₁ ℓ} {C : Category c₀ c₁ ℓ} {A B : Obj C} (f : Hom C A B) → IsRelatedTo[ C ] A B f f
  _∎ {C = C} f = relTo (refl-hom C)

  _≈⟨_⟩_ : ∀ {c₀ c₁ ℓ} {C : Category c₀ c₁ ℓ} → {A B : Obj C} (f : Hom C A B) → {g h : Hom C A B} → equal C f g → IsRelatedTo[ C ] A B g h → IsRelatedTo[ C ] A B f h
  _≈⟨_⟩_ {C = C} f f≈g (relTo g≈h) = relTo (trans-hom C f≈g g≈h)

  _≡⟨_⟩_ : ∀ {c₀ c₁ ℓ} {C : Category c₀ c₁ ℓ} → {A B : Obj C} (f : Hom C A B) → {g h : Hom C A B} → f ≡ g → IsRelatedTo[ C ] A B g h → IsRelatedTo[ C ] A B f h
  _≡⟨_⟩_ {C = C} f _≡_.refl (relTo g≈h) = relTo g≈h

open CategoryReasoning public


------------------------------------------------------------------------------
-- Note on the equality type class using instance arguments
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

-- Adapted from: On the Bright Side of Type Classes: Instances
-- Arguments in Agda (ICFP'11).

module FOT.FOTC.TypeClasses.EqualityInstanceArguments where

open import FOTC.Base
open import FOTC.Data.Bool
open import FOTC.Data.Bool.Type
open import FOTC.Data.Nat.Type

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

∃-proj₁ : ∀ {A} → ∃ A → D
∃-proj₁ (x , _) = x

record Eq (P : D → Set) : Set₁ where
  field equal : ∀ {t₁ t₂} → P t₁ → P t₂ → Set

equal : {P : D → Set}{t₁ t₂ : D} → {{eqT : Eq P}} → P t₁ → P t₂ → Set
equal {{eqT}} = Eq.equal eqT

boolEq : ∀ {b₁ b₂} → Bool b₁ → Bool b₂ → Set
boolEq btrue  btrue  = Bool true
boolEq bfalse bfalse = Bool true
{-# CATCHALL #-}
boolEq _      _      = Bool false

nEq : ∀ {m n} → N m → N n → Set
nEq nzero      nzero      = Bool true
nEq (nsucc Nm) (nsucc Nn) = Bool true
{-# CATCHALL #-}
nEq _          _          = Bool false

instance
  eqInstanceBool : Eq Bool
  eqInstanceBool = record { equal = boolEq }

  eqInstanceN : Eq N
  eqInstanceN = record { equal = nEq }

test₁ : Set
test₁ = equal nzero (nsucc nzero)

test₂ : Set
test₂ = equal bfalse bfalse

eqN-sym : ∀ {m n} → (Nm : N m) → (Nn : N n) → equal Nm Nn → equal Nn Nm
eqN-sym nzero      nzero      h = h
eqN-sym nzero      (nsucc Nn) h = h
eqN-sym (nsucc Nm) nzero      h = h
eqN-sym (nsucc Nm) (nsucc Nn) h = h

postulate eqN-sym' : ∀ {m n} → (Nm : N m) → (Nn : N n) → equal Nm Nn → equal Nn Nm
-- {-# ATP prove eqN-sym' #-}


{-# OPTIONS --without-K --safe #-}
module Cats.Category.Constructions.Initial where

open import Data.Product using (proj₁ ; proj₂)
open import Level

open import Cats.Category.Base

import Cats.Category.Constructions.Iso as Iso
import Cats.Category.Constructions.Unique as Unique

module Build {lo la l≈} (Cat : Category lo la l≈) where

  open Category Cat
  open Iso.Build Cat
  open Unique.Build Cat

  IsInitial : Obj → Set (lo ⊔ la ⊔ l≈)
  IsInitial ⊥ = ∀ X → ∃! ⊥ X

  record HasInitial : Set (lo ⊔ la ⊔ l≈) where
    field
      ⊥ : Obj
      isInitial : IsInitial ⊥

    ¡ : ∀ X → ⊥ ⇒ X
    ¡ X = ∃!′.arr (isInitial X)

    ¡-unique : ∀ {X} (f : ⊥ ⇒ X) → ¡ X ≈ f
    ¡-unique {X} f = ∃!′.unique (isInitial X) _

    ⊥⇒-unique : ∀ {X} (f g : ⊥ ⇒ X) → f ≈ g
    ⊥⇒-unique f g = ≈.trans (≈.sym (¡-unique f)) (¡-unique g)

    ⊥-unique : ∀ {X} → IsInitial X → X ≅ ⊥
    ⊥-unique {X} init = record
      { forth = init ⊥ .∃!′.arr
      ; back = ¡ X
      ; back-forth = ≈.trans (≈.sym (init X .∃!′.unique _)) (init X .∃!′.unique _)
      ; forth-back = ⊥⇒-unique _ _
      }

  initial-unique : ∀ {X Y} → IsInitial X → IsInitial Y → X ≅ Y
  initial-unique X-init Y-init
    = HasInitial.⊥-unique (record { isInitial = Y-init }) X-init

open Build public using (HasInitial)

private
  open module Build′ {lo la l≈} {C : Category lo la l≈}
    = Build C public using (IsInitial ; initial-unique)


{-# OPTIONS --without-K --safe #-}
module Categories.Adjoint.Compose where

-- Composition of Adjoints

open import Level

open import Data.Product using (_,_; _×_)
open import Function using (_$_) renaming (_∘_ to _∙_)
open import Function.Equality using (Π; _⟶_)
import Function.Inverse as FI
open import Relation.Binary using (Rel; IsEquivalence; Setoid)

-- be explicit in imports to 'see' where the information comes from
open import Categories.Adjoint using (Adjoint; _⊣_)
open import Categories.Category.Core using (Category)
open import Categories.Category.Product using (Product; _⁂_)
open import Categories.Category.Instance.Setoids
open import Categories.Morphism
open import Categories.Functor using (Functor; _∘F_) renaming (id to idF)
open import Categories.Functor.Bifunctor using (Bifunctor)
open import Categories.Functor.Hom using (Hom[_][-,-])
open import Categories.Functor.Construction.LiftSetoids
open import Categories.NaturalTransformation using (NaturalTransformation; ntHelper; _∘ₕ_; _∘ᵥ_; _∘ˡ_; _∘ʳ_)
  renaming (id to idN)
open import Categories.NaturalTransformation.NaturalIsomorphism
  using (NaturalIsomorphism; unitorˡ; unitorʳ; associator; _≃_)
import Categories.Morphism.Reasoning as MR

private
  variable
    o o′ o″ ℓ ℓ′ ℓ″ e e′ e″ : Level
    C D E : Category o ℓ e

-- Adjoints compose; we can't be sloppy, so associators and unitors must be inserted.
-- Use single letters in pairs, so L & M on the left, and R & S on the right
_∘⊣_ : {L : Functor C D} {R : Functor D C} {M : Functor D E} {S : Functor E D} →
  L ⊣ R → M ⊣ S → (M ∘F L) ⊣ (R ∘F S)
_∘⊣_ {C = C} {D = D} {E = E} {L = L} {R} {M} {S} LR MS = record
  { unit =  ((F⇐G (associator _ S R) ∘ᵥ R ∘ˡ (F⇒G (associator L M S))) ∘ᵥ
            (R ∘ˡ (MSη′ ∘ʳ L)) ∘ᵥ (R ∘ˡ (F⇐G unitorˡ))) ∘ᵥ LRη′
  ; counit = MSε′ ∘ᵥ (((F⇒G (unitorʳ {F = M}) ∘ʳ S) ∘ᵥ ((M ∘ˡ LRε′) ∘ʳ S)) ∘ᵥ
             (F⇒G (associator R L M) ∘ʳ S) ) ∘ᵥ F⇐G (associator S R (M ∘F L) )
  ; zig = λ {A} → zig′ {A}
  ; zag = λ {B} → zag′ {B}
  }
  where
    open NaturalIsomorphism using (F⇒G; F⇐G)
    module LR = Adjoint LR using (zig; zag) renaming (unit to LRη′; counit to LRε′)
    module MS = Adjoint MS using (zig; zag) renaming (unit to MSη′; counit to MSε′)
    module LRη = NaturalTransformation (Adjoint.unit LR) using () renaming (η to ηLR)
    module MSη = NaturalTransformation (Adjoint.unit MS) using (commute) renaming (η to ηMS)
    module LRε = NaturalTransformation (Adjoint.counit LR) using (commute) renaming (η to εLR)
    module MSε = NaturalTransformation (Adjoint.counit MS) using () renaming (η to εMS)
    module C = Category C using (Obj; id; _∘_; _≈_; sym-assoc; ∘-resp-≈ˡ; ∘-resp-≈;
      ∘-resp-≈ʳ; identityˡ; identityʳ; module HomReasoning)
    module D = Category D using (id; _∘_; ∘-resp-≈ˡ; sym-assoc; ∘-resp-≈ʳ; identityˡ;
      module HomReasoning; assoc)
    module E = Category E using (Obj; id; _∘_; _≈_; identityˡ; ∘-resp-≈ʳ; module HomReasoning;
      assoc; sym-assoc; identityʳ)
    module L = Functor L using (homomorphism; F-resp-≈) renaming (F₀ to L₀; F₁ to L₁)
    module M = Functor M using (F-resp-≈; identity; homomorphism) renaming (F₀ to M₀; F₁ to M₁)
    module R = Functor R using (F-resp-≈; identity; homomorphism) renaming (F₀ to R₀; F₁ to R₁)
    module S = Functor S using (F-resp-≈; homomorphism) renaming (F₀ to S₀; F₁ to S₁)
    open LR; open MS; open LRη; open LRε; open MSε; open MSη; open L; open M; open R; open S

    zig′ : {A : C.Obj} → (εMS (M₀ (L₀ A)) E.∘
       ((E.id E.∘ M₁ (εLR (S₀ (M₀ (L₀ A))))) E.∘ E.id) E.∘ E.id)
      E.∘ M₁ (L₁ (((C.id C.∘ R₁ D.id) C.∘ R₁ (ηMS (L₀ A)) C.∘ R₁ D.id) C.∘ ηLR A))
      E.≈ E.id
    -- use "inverted" format here, where rules are out-dented
    zig′ {A} = begin
        (εMS (M₀ (L₀ A)) E.∘ ((E.id E.∘ M₁ (εLR (S₀ (M₀ (L₀ A))))) E.∘ E.id) E.∘ E.id)
        E.∘ M₁ (L₁ (((C.id C.∘ R₁ D.id) C.∘ R₁ (ηMS (L₀ A)) C.∘ R₁ D.id) C.∘ ηLR A))
      ≈⟨  ( refl⟩∘⟨ (E.identityʳ ○ E.identityʳ ○ E.identityˡ)) ⟩∘⟨refl ⟩  -- get rid of those pesky E.id
        (εMS (M₀ (L₀ A)) E.∘ M₁ (εLR (S₀ (M₀ (L₀ A)))))
        E.∘ M₁ (L₁ (((C.id C.∘ R₁ D.id) C.∘ R₁ (ηMS (L₀ A)) C.∘ R₁ D.id) C.∘ ηLR A))
      ≈⟨ E.assoc ○ E.∘-resp-≈ʳ (⟺ M.homomorphism) ⟩
        εMS (M₀ (L₀ A)) E.∘
        M₁ (εLR (S₀ (M₀ (L₀ A))) D.∘ L₁ (((C.id C.∘ R₁ D.id) C.∘ R₁ (ηMS (L₀ A)) C.∘ R₁ D.id) C.∘ ηLR A))
         -- below: get rid of lots of pesky id. Nasty bit of nested equational reasoning, but nothing deep
      ≈⟨  refl⟩∘⟨ M.F-resp-≈ (D.∘-resp-≈ʳ (L.F-resp-≈
             (C.∘-resp-≈ˡ (C.∘-resp-≈ C.identityˡ (C.∘-resp-≈ʳ R.identity) C.HomReasoning.○
                let _⊚_ = C.HomReasoning._○_ in C.∘-resp-≈ R.identity C.identityʳ ⊚ C.identityˡ)))) ⟩
        εMS (M₀ (L₀ A)) E.∘ M₁ (εLR (S₀ (M₀ (L₀ A))) D.∘ L₁ ((R₁ (ηMS (L₀ A))) C.∘ ηLR A))
      ≈⟨  refl⟩∘⟨ M.F-resp-≈ (D.∘-resp-≈ʳ L.homomorphism) ⟩
        εMS (M₀ (L₀ A)) E.∘ M₁ (εLR (S₀ (M₀ (L₀ A))) D.∘ L₁ (R₁ (ηMS (L₀ A))) D.∘ L₁ (ηLR A))
      ≈⟨ refl⟩∘⟨ M.F-resp-≈ D.sym-assoc ⟩
        εMS (M₀ (L₀ A)) E.∘ M₁ ((εLR (S₀ (M₀ (L₀ A))) D.∘ L₁ (R₁ (ηMS (L₀ A)))) D.∘ L₁ (ηLR A))
      ≈⟨  refl⟩∘⟨ M.F-resp-≈ (D.∘-resp-≈ˡ (LRε.commute _)) ⟩
        εMS (M₀ (L₀ A)) E.∘ M₁ ( (_ D.∘ εLR _) D.∘ L₁ (ηLR A))
      ≈⟨  refl⟩∘⟨ M.homomorphism ⟩
        εMS (M₀ (L₀ A)) E.∘ M₁ (_ D.∘ εLR _) E.∘ M₁ (L₁ (ηLR A))
      ≈⟨  refl⟩∘⟨ ( M.homomorphism ⟩∘⟨refl ) ⟩
        εMS (M₀ (L₀ A)) E.∘ (M₁ (ηMS (L₀ A)) E.∘ M₁ (εLR _)) E.∘ M₁ (L₁ (ηLR A))
      ≈⟨ E.∘-resp-≈ʳ E.assoc ○ E.sym-assoc ⟩
        (εMS (M₀ (L₀ A)) E.∘ M₁ (ηMS (L₀ A))) E.∘ (M₁ (εLR _) E.∘ M₁ (L₁ (ηLR A)))
      ≈⟨ MS.zig ⟩∘⟨refl ⟩
        E.id E.∘ (M₁ (εLR _) E.∘ M₁ (L₁ (ηLR A)))
      ≈⟨ E.identityˡ ⟩
        M₁ (εLR _) E.∘ M₁ (L₁ (ηLR A))
      ≈˘⟨ M.homomorphism ⟩
        M₁ (εLR _ D.∘ L₁ (ηLR A))
      ≈⟨ M.F-resp-≈ LR.zig ○ M.identity ⟩
        E.id ∎
      where open E.HomReasoning

    zag′ : {B : E.Obj} → R₁ (S₁ (εMS B E.∘ ((E.id E.∘ M₁ (εLR (S₀ B))) E.∘ E.id) E.∘ E.id))
      C.∘ ((C.id C.∘ R₁ D.id) C.∘ R₁ (ηMS (L₀ (R₀ (S₀ B)))) C.∘ R₁ D.id) C.∘
      ηLR (R₀ (S₀ B)) C.≈ C.id
    zag′ {B} =
      let _⊚_ = E.HomReasoning._○_ in
      begin
         R₁ (S₁ (εMS B E.∘ ((E.id E.∘ M₁ (εLR (S₀ B))) E.∘ E.id) E.∘ E.id))
          C.∘ ((C.id C.∘ R₁ D.id) C.∘ R₁ (ηMS (L₀ (R₀ (S₀ B)))) C.∘ R₁ D.id) C.∘
          ηLR (R₀ (S₀ B)) -- get rid of all those id
      ≈⟨ R.F-resp-≈ (S.F-resp-≈ (E.∘-resp-≈ʳ (E.identityʳ ⊚ (E.identityʳ ⊚ E.identityˡ)))) ⟩∘⟨
         C.∘-resp-≈ˡ (C.∘-resp-≈ C.identityˡ (C.∘-resp-≈ʳ R.identity) ○
          C.∘-resp-≈ R.identity C.identityʳ ○ C.identityˡ) ⟩
         R₁ (S₁ (εMS B E.∘ M₁ (εLR (S₀ B)))) C.∘ R₁ (ηMS (L₀ (R₀ (S₀ B)))) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ C.sym-assoc ⟩
         (R₁ (S₁ (εMS B E.∘ M₁ (εLR (S₀ B)))) C.∘ R₁ (ηMS (L₀ (R₀ (S₀ B))))) C.∘ ηLR (R₀ (S₀ B))
      ≈˘⟨  R.homomorphism ⟩∘⟨refl ⟩
         R₁ (S₁ (εMS B E.∘ M₁ (εLR (S₀ B))) D.∘ ηMS (L₀ (R₀ (S₀ B)))) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ R.F-resp-≈ (D.∘-resp-≈ˡ S.homomorphism) ⟩∘⟨refl ⟩
        R₁ ((S₁ (εMS B) D.∘ (S₁ (M₁ (εLR (S₀ B))))) D.∘ ηMS (L₀ (R₀ (S₀ B)))) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ R.F-resp-≈ D.assoc ⟩∘⟨refl ⟩
        R₁ (S₁ (εMS B) D.∘ S₁ (M₁ (εLR (S₀ B))) D.∘ ηMS (L₀ (R₀ (S₀ B)))) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ R.F-resp-≈ (D.∘-resp-≈ʳ (D.HomReasoning.⟺ (MSη.commute (εLR (S₀ B))))) ⟩∘⟨refl ⟩
         R₁ (S₁ (εMS B) D.∘ ηMS (S₀ B) D.∘ εLR (S₀ B)) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ R.F-resp-≈ D.sym-assoc ⟩∘⟨refl ⟩
         R₁ ((S₁ (εMS B) D.∘ ηMS (S₀ B)) D.∘ εLR (S₀ B)) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ R.F-resp-≈ (D.∘-resp-≈ˡ MS.zag) ⟩∘⟨refl ⟩
         R₁ (D.id D.∘ εLR (S₀ B)) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ C.∘-resp-≈ˡ (R.F-resp-≈ D.identityˡ) ⟩
         R₁ (εLR (S₀ B)) C.∘ ηLR (R₀ (S₀ B))
      ≈⟨ LR.zag ⟩
         C.id ∎
      where open C.HomReasoning


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

open import HoTT
open import experimental.TwoConstancyHIT

module experimental.TwoConstancy
  {i j} {A : Type i} {B : Type j} (B-is-gpd : is-gpd B)
  (f : A → B)
  (f-is-const₀ : ∀ a₁ a₂ → f a₁ == f a₂)
  (f-is-const₁ : ∀ a₁ a₂ a₃
    → f-is-const₀ a₁ a₂ ∙' f-is-const₀ a₂ a₃ == f-is-const₀ a₁ a₃) where

  private
    abstract
      lemma₁ : ∀ a (t₂ : TwoConstancy A) → point a == t₂
      lemma₁ a = TwoConstancy-elim {P = λ t₂ → point a == t₂}
        (λ _ → =-preserves-level 1 TwoConstancy-level)
        (λ b → link₀ a b)
        (λ b₁ b₂ → ↓-cst=idf-in' $ link₁ a b₁ b₂)
        (λ b₁ b₂ b₃ → set-↓-has-all-paths-↓ $ TwoConstancy-level _ _ )

      lemma₂ : ∀ (a₁ a₂ : A) →
        lemma₁ a₁ == lemma₁ a₂ [ (λ t₁ → ∀ t₂ → t₁ == t₂) ↓ link₀ a₁ a₂ ]
      lemma₂ a₁ a₂ = ↓-cst→app-in $
        TwoConstancy-elim
          {P = λ t₂ → lemma₁ a₁ t₂ == lemma₁ a₂ t₂ [ (λ t₁ → t₁ == t₂) ↓ link₀ a₁ a₂ ]}
          (λ _ → ↓-preserves-level 1 λ _ → =-preserves-level 1 TwoConstancy-level)
          (λ b → ↓-idf=cst-in $ ! $ link₁ a₁ a₂ b)
          (λ b₁ b₂ → prop-has-all-paths-↓ $ ↓-level λ _ → TwoConstancy-level _ _)
          (λ b₁ b₂ b₃ → prop-has-all-paths-↓ $ contr-is-prop
            $ ↓-level λ _ → ↓-level λ _ → TwoConstancy-level _ _)

      TwoConstancy-has-all-paths : has-all-paths (TwoConstancy A)
      TwoConstancy-has-all-paths =
        TwoConstancy-elim {P = λ t₁ → ∀ t₂ → t₁ == t₂}
          (λ _ → Π-level λ _ → =-preserves-level 1 TwoConstancy-level)
          lemma₁
          lemma₂
          (λ a₁ a₂ a₃ → prop-has-all-paths-↓
            $ ↓-level λ t₁ → Π-is-set λ t₂ → TwoConstancy-level t₁ t₂)

  TwoConstancy-is-prop : is-prop (TwoConstancy A)
  TwoConstancy-is-prop = all-paths-is-prop TwoConstancy-has-all-paths

  cst-extend : Trunc -1 A → B
  cst-extend = TwoConstancy-rec B-is-gpd f f-is-const₀ f-is-const₁
             ∘ Trunc-rec TwoConstancy-is-prop point

  -- The beta rule.
  -- This is definitionally true, so you don't need it.
  cst-extend-β : cst-extend ∘ [_] == f
  cst-extend-β = idp


------------------------------------------------------------------------
-- Some derivable properties
------------------------------------------------------------------------

open import Algebra

module Algebra.Props.AbelianGroup (g : AbelianGroup) where

open AbelianGroup g
import Relation.Binary.EqReasoning as EqR; open EqR setoid
open import Data.Function
open import Data.Product

private
  lemma : ∀ x y → x ∙ y ∙ x ⁻¹ ≈ y
  lemma x y = begin
    x ∙ y ∙ x ⁻¹    ≈⟨ comm _ _ ⟨ ∙-pres-≈ ⟩ refl ⟩
    y ∙ x ∙ x ⁻¹    ≈⟨ assoc _ _ _ ⟩
    y ∙ (x ∙ x ⁻¹)  ≈⟨ refl ⟨ ∙-pres-≈ ⟩ proj₂ inverse _ ⟩
    y ∙ ε           ≈⟨ proj₂ identity _ ⟩
    y               ∎

-‿∙-comm : ∀ x y → x ⁻¹ ∙ y ⁻¹ ≈ (x ∙ y) ⁻¹
-‿∙-comm x y = begin
  x ⁻¹ ∙ y ⁻¹                         ≈⟨ comm _ _ ⟩
  y ⁻¹ ∙ x ⁻¹                         ≈⟨ sym $ lem ⟨ ∙-pres-≈ ⟩ refl ⟩
  x ∙ (y ∙ (x ∙ y) ⁻¹ ∙ y ⁻¹) ∙ x ⁻¹  ≈⟨ lemma _ _ ⟩
  y ∙ (x ∙ y) ⁻¹ ∙ y ⁻¹               ≈⟨ lemma _ _ ⟩
  (x ∙ y) ⁻¹                          ∎
  where
  lem = begin
    x ∙ (y ∙ (x ∙ y) ⁻¹ ∙ y ⁻¹)  ≈⟨ sym $ assoc _ _ _ ⟩
    x ∙ (y ∙ (x ∙ y) ⁻¹) ∙ y ⁻¹  ≈⟨ sym $ assoc _ _ _ ⟨ ∙-pres-≈ ⟩ refl ⟩
    x ∙ y ∙ (x ∙ y) ⁻¹ ∙ y ⁻¹    ≈⟨ proj₂ inverse _ ⟨ ∙-pres-≈ ⟩ refl ⟩
    ε ∙ y ⁻¹                     ≈⟨ proj₁ identity _ ⟩
    y ⁻¹                         ∎


{-# OPTIONS --show-implicit #-}
module Exegesis where

module Superclasses where

  open import Agda.Primitive
  open import Agda.Builtin.Equality

  record Semigroup (A : Set) : Set where
    field
      _∙_ : A → A → A
      assoc : ∀ x y z → (x ∙ y) ∙ z ≡ x ∙ (y ∙ z)
      cong : ∀ w x y z → w ≡ y → x ≡ z → w ∙ x ≡ y ∙ z

  open Semigroup ⦃ … ⦄

  record Identity {A : Set} (_∙_ : A → A → A) : Set where
    field
      ε : A
      left-identity  : ∀ x → ε ∙ x ≡ x
      right-identity : ∀ x → x ∙ ε ≡ x

  open Identity ⦃ … ⦄

  record Monoid (A : Set) : Set where
    field
      ⦃ semigroup ⦄ : Semigroup A
      ⦃ identity ⦄  : Identity (_∙_ ⦃ semigroup ⦄)

  open Monoid ⦃ … ⦄

  foo : {A : Set} ⦃ m : Monoid A ⦄ → (x : A) → x ∙ ε ⦃ identity ⦄ ≡ x
  foo x = right-identity x

module StandardLibraryMethod where

  open import Agda.Primitive
  open import Agda.Builtin.Equality

  record Semigroup (A : Set) : Set where
    field
      _∙_ : A → A → A
      assoc : ∀ x y z → (x ∙ y) ∙ z ≡ x ∙ (y ∙ z)
      cong : ∀ w x y z → w ≡ y → x ≡ z → w ∙ x ≡ y ∙ z

  --open Semigroup ⦃ … ⦄

  record Identity (A : Set) (ε : A) (_∙_ : A → A → A) : Set where
    field
      left-identity  : ∀ x → ε ∙ x ≡ x
      right-identity : ∀ x → x ∙ ε ≡ x

  --open Identity ⦃ … ⦄

  record Monoid (A : Set) : Set where
    field
      s : Semigroup A

    open Semigroup s public

    field
      ε : A
      i : Identity A ε _∙_

    open Identity i public

  open Monoid ⦃ … ⦄

  foo : {A : Set} ⦃ m : Monoid A ⦄ → (x : A) → x ∙ ε ≡ x
  foo x = right-identity x -- right-identity x

module Cascade where

  open import Agda.Primitive
  open import Agda.Builtin.Equality

  record Semigroup (A : Set) : Set where
    field
      _∙_ : A → A → A
      assoc : ∀ x y z → (x ∙ y) ∙ z ≡ x ∙ (y ∙ z)
      cong : ∀ w x y z → w ≡ y → x ≡ z → w ∙ x ≡ y ∙ z

  open Semigroup ⦃ … ⦄

  record RawMonoid (A : Set) ⦃ _ : Semigroup A ⦄ : Set where
    no-eta-equality
    field
      ε : A

  open RawMonoid ⦃ … ⦄

  record Identity (A : Set) (ε : A) (_∙_ : A → A → A) : Set where
    field
      left-identity  : ∀ x → ε ∙ x ≡ x
      right-identity : ∀ x → x ∙ ε ≡ x

  open Identity ⦃ … ⦄

  foo : {A : Set} ⦃ _ : Semigroup A ⦄ ⦃ _ : RawMonoid A ⦄ ⦃ i : Identity A ε _∙_ ⦄ → (x : A) → x ∙ ε ≡ x
  foo ⦃ s ⦄ ⦃ rm ⦄ ⦃ i ⦄ x = right-identity ⦃ i ⦄ x

module Buncha where

  open import Agda.Primitive
  open import Agda.Builtin.Equality

  record Semigroup (A : Set) : Set where
    field
      _∙_ : A → A → A
      assoc : ∀ x y z → (x ∙ y) ∙ z ≡ x ∙ (y ∙ z)
      cong : ∀ w x y z → w ≡ y → x ≡ z → w ∙ x ≡ y ∙ z

  open Semigroup ⦃ … ⦄

  record RawMonoid (A : Set) : Set where
    field
      ε : A

  open RawMonoid ⦃ … ⦄

  record Identity {A : Set} (_∙_ : A → A → A) ⦃ _ : RawMonoid A ⦄ : Set where
    field
      left-identity  : ∀ x → ε ∙ x ≡ x
      right-identity : ∀ x → x ∙ ε ≡ x

  open Identity ⦃ … ⦄

  record Monoid (A : Set) : Set where
    field
      ⦃ semigroup ⦄ : Semigroup A
      ⦃ rawmonoid ⦄ : RawMonoid A
      ⦃ identity ⦄ : Identity {A} _∙_

  open Monoid ⦃ … ⦄

  foo : {A : Set} ⦃ _ : Semigroup A ⦄ ⦃ _ : RawMonoid A ⦄ ⦃ _ : Identity _∙_ ⦄ → (x : A) → x ∙ ε ≡ x
  foo x = right-identity x

  bar : {A : Set} ⦃ _ : Monoid A ⦄ → (x : A) → x ∙ ε ≡ x
  bar x = right-identity x

module SomethingThatWorks where

  open import Agda.Primitive
  open import Agda.Builtin.Equality

  record Equivalence (A : Set) : Set₁ where
    infix 4 _≈_
    field
      _≈_ : A → A → Set
      reflexive : ∀ x → x ≈ x
      symmetric : ∀ {x y} → x ≈ y → y ≈ x
      transitive : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z

  open Equivalence ⦃ … ⦄

  record Operator (A : Set) : Set where
    field
      _∙_ : A → A → A

  record Semigroup (A : Set) : Set₁ where
    field
      ⦃ operator ⦄ : Operator A
      ⦃ equivalence ⦄ : Equivalence A

    open Operator operator public
    field
      assoc : ∀ x y z → (x ∙ y) ∙ z ≡ x ∙ (y ∙ z)
      cong : ∀ w x y z → w ≡ y → x ≡ z → w ∙ x ≡ y ∙ z

  open Semigroup ⦃ … ⦄

  record IdentityElement (A : Set) : Set where
    field
      ε : A

  module ε where

    open IdentityElement ⦃ … ⦄ public

  record Identity {A : Set} (_∙_ : A → A → A) ⦃ _ : IdentityElement A ⦄ : Set where
    open ε
    field
      left-identity  : ∀ x → ε ∙ x ≡ x
      right-identity : ∀ x → x ∙ ε ≡ x

  open Identity ⦃ … ⦄

  record Monoid (A : Set) : Set₁ where
    field
      ⦃ semigroup ⦄ : Semigroup A
      ⦃ identityElement ⦄ : IdentityElement A
      ⦃ identity ⦄ : Identity {A} _∙_

  open Monoid ⦃ … ⦄

  bar : {A : Set} ⦃ _ : Monoid A ⦄ {B : Set} ⦃ _ : Monoid B ⦄ (open ε) → (x : A) → x ∙ ε ≡ x
  bar x = right-identity x

module SeparatingIsFromOught where

  open import Agda.Primitive
  open import Agda.Builtin.Equality

  record Equivalence (A : Set) : Set₁ where
    infix 4 _≈_
    field
      _≈_ : A → A → Set
      reflexive : ∀ x → x ≈ x
      symmetric : ∀ {x y} → x ≈ y → y ≈ x
      transitive : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z

  open Equivalence ⦃ … ⦄

  record Operator (A : Set) : Set where
    field
      _∙_ : A → A → A

  open Operator ⦃ … ⦄

  record IsSemigroup (A : Set) ⦃ _ : Operator A ⦄ ⦃ _ : Equivalence A ⦄ : Set where
    field
      assoc : ∀ (x : A) y z → (x ∙ y) ∙ z ≈ x ∙ (y ∙ z)
      cong : ∀ (w : A) x y z → w ≈ y → x ≈ z → w ∙ x ≈ y ∙ z

  open IsSemigroup ⦃ … ⦄

  record Semigroup (A : Set) : Set₁ where
    field
      ⦃ operator ⦄ : Operator A
      ⦃ equivalence ⦄ : Equivalence A
      ⦃ isSemigroup ⦄ : IsSemigroup A

  open Semigroup ⦃ … ⦄
{-
  record IdentityElement (A : Set) : Set where
    field
      ε : A

  open IdentityElement ⦃ … ⦄
-}
  record IsIdentity (A : Set) ⦃ _ : Operator A ⦄ {-⦃ _ : IdentityElement A ⦄-} ⦃ _ : Equivalence A ⦄ : Set where

--  record IsIdentity {A : Set} (_∙_ : A → A → A) (ε : A) ⦃ _ : Equivalence A ⦄ : Set where
    field
      ε : A
      left-identity  : ∀ (x : A) → ε ∙ x ≈ x
      right-identity : ∀ (x : A) → x ∙ ε ≈ x

  open IsIdentity ⦃ … ⦄

  record Monoid (A : Set) : Set₁ where
    field
      ⦃ semigroup ⦄ : Semigroup A
      --⦃ identityElement ⦄ : IdentityElement A
      --ε : A
      --⦃ identity ⦄ : IsIdentity (_∙_ {A}) ε
--      ⦃ operator ⦄ : Operator A
      ⦃ identity ⦄ : IsIdentity A

  open Monoid ⦃ … ⦄

  bar : {A : Set} ⦃ m : Monoid A ⦄ {B : Set} ⦃ _ : Semigroup B ⦄ → (x : A) → x ∙ ε ≈ x
  bar x = right-identity x

  bar2 : {A : Set} ⦃ _ : Monoid A ⦄ → (x y : A) → (x ∙ ε) ∙ y ≈ x ∙ (ε ∙ y)
  bar2 x y = assoc x ε y

module FineControl where

  record IsEquivalence {A : Set} (_≈_ : A → A → Set) : Set₁ where
    field
      reflexive : ∀ x → x ≈ x
      symmetric : ∀ {x y} → x ≈ y → y ≈ x
      transitive : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z

  open IsEquivalence ⦃ … ⦄

  record Equivalence (A : Set) : Set₁ where
    infix 4 _≈_
    field
      _≈_ : A → A → Set
      ⦃ isEquivalence ⦄ : IsEquivalence _≈_

  open Equivalence ⦃ … ⦄

  record IsSemigroup {A : Set} (_∙_ : A → A → A) ⦃ _ : Equivalence A ⦄ : Set where
    field
      assoc : ∀ (x : A) y z → (x ∙ y) ∙ z ≈ x ∙ (y ∙ z)
      cong : ∀ (w : A) x y z → w ≈ y → x ≈ z → w ∙ x ≈ y ∙ z

  open IsSemigroup ⦃ … ⦄

  record Semigroup (A : Set) : Set₁ where
    field
      ⦃ equivalence ⦄ : Equivalence A
      _∙_ : A → A → A
      ⦃ isSemigroup ⦄ : IsSemigroup _∙_

  open Semigroup ⦃ … ⦄

  record Identity (A : Set) : Set where
    field
      ε : A

  open Identity ⦃ … ⦄

--  record IsLeftIdentity {A B : Set} (_∙_ : A → B → B) ⦃ _ : Identity A ⦄ (ε : A) ⦃ _ : Equivalence B ⦄ : Set where
  record IsLeftIdentity {A B : Set} (_∙_ : A → B → B) ⦃ _ : Identity A ⦄ ⦃ _ : Equivalence B ⦄ : Set where
    field
      left-identity  : ∀ x → ε ∙ x ≈ x

  open IsLeftIdentity ⦃ … ⦄

--  record IsRightIdentity {A B : Set} (_∙_ : B → A → B) (ε : A) ⦃ _ : Equivalence B ⦄ : Set where
  record IsRightIdentity {A B : Set} (_∙_ : B → A → B) ⦃ _ : Identity A ⦄ ⦃ _ : Equivalence B ⦄ : Set where
    field
      right-identity : ∀ x → x ∙ ε ≈ x

  open IsRightIdentity ⦃ … ⦄

  record Monoid (A : Set) : Set₁ where
    field
      ⦃ semigroup ⦄ : Semigroup A
      --ε : A
      ⦃ identity ⦄ : Identity A
      ⦃ lidentity ⦄ : IsLeftIdentity {A = A} _∙_
      ⦃ ridentity ⦄ : IsRightIdentity {A = A} _∙_

  open Monoid ⦃ … ⦄

  bar : {A : Set} ⦃ m : Monoid A ⦄ → (x : A) → x ∙ ε ≈ x
  bar {A} x = right-identity {_∙_ = _∙_} x -- right-identity {_∙_ = _∙_} x

  bar' : {A : Set} ⦃ m : Monoid A ⦄ → (x : A) → x ∙ ε ≈ x
  bar' x = right-identity x

  bar2 : {A : Set} ⦃ _ : Monoid A ⦄ → (x y z : A) → (x ∙ y) ∙ z ≈ x ∙ (y ∙ z)
  bar2 x y z = assoc x y z

module FineControl2 where

  record IsEquivalence {A : Set} (_≈_ : A → A → Set) : Set₁ where
    field
      reflexive : ∀ x → x ≈ x
      symmetric : ∀ {x y} → x ≈ y → y ≈ x
      transitive : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z

  open IsEquivalence ⦃ … ⦄

  record Equivalence (A : Set) : Set₁ where
    infix 4 _≈_
    field
      _≈_ : A → A → Set
      ⦃ isEquivalence ⦄ : IsEquivalence _≈_

  open Equivalence ⦃ … ⦄

  record IsSemigroup {A : Set} (_∙_ : A → A → A) ⦃ _ : Equivalence A ⦄ : Set where
    field
      assoc : ∀ (x : A) y z → (x ∙ y) ∙ z ≈ x ∙ (y ∙ z)
      cong : ∀ (w : A) x y z → w ≈ y → x ≈ z → w ∙ x ≈ y ∙ z

  open IsSemigroup ⦃ … ⦄

  record Semigroup (A : Set) : Set₁ where
    field
      overlap ⦃ equivalence ⦄ : Equivalence A
      _∙_ : A → A → A
      ⦃ isSemigroup ⦄ : IsSemigroup _∙_

  open Semigroup ⦃ … ⦄

  record Identity (A : Set) : Set where
    field
      ε : A

  open Identity ⦃ … ⦄

--  record IsLeftIdentity {A B : Set} (_∙_ : A → B → B) (ε : A) ⦃ _ : Equivalence B ⦄ : Set where
--  record IsLeftIdentity {A B : Set} (_∙_ : A → B → B) : Set₁ where
  record IsLeftIdentity {A B : Set} (_∙_ : A → B → B) ⦃ _ : Identity A ⦄ ⦃ _ : Equivalence B ⦄ : Set₁ where
    field
      left-identity  : ∀ x → ε ∙ x ≈ x

  open IsLeftIdentity ⦃ … ⦄

--  record IsRightIdentity {A B : Set} (_∙_ : B → A → B) (ε : A) ⦃ _ : Equivalence B ⦄ : Set where
--  record IsRightIdentity {A B : Set} (_∙_ : B → A → B) : Set₁ where
  record IsRightIdentity {A B : Set} (_∙_ : B → A → B) ⦃ _ : Identity A ⦄ ⦃ _ : Equivalence B ⦄ : Set₁ where
    field
      right-identity : ∀ x → x ∙ ε ≈ x

--  open IsRightIdentity ⦃ … ⦄

  record Monoid (A : Set) : Set₁ where
    field
      ⦃ semigroup ⦄ : Semigroup A
      --ε : A
      ⦃ identity ⦄ : Identity A
      ⦃ lidentity ⦄ : IsLeftIdentity {A = A} _∙_
      ⦃ ridentity ⦄ : IsRightIdentity {A = A} _∙_

    open IsRightIdentity ridentity public

  open Monoid ⦃ … ⦄

  bar : {A : Set} ⦃ m : Monoid A ⦄ → (x : A) → x ∙ ε ≈ x
  bar {A} x = right-identity x -- right-identity {_∙_ = _∙_} x

  bar' : {A : Set} ⦃ m : Monoid A ⦄ → (x : A) → x ∙ ε ≈ x
  bar' x = right-identity x

  bar2 : {A : Set} ⦃ _ : Monoid A ⦄ → (x y z : A) → (x ∙ y) ∙ z ≈ x ∙ (y ∙ z)
  bar2 x y z = assoc x y z


{- Type classes and instances for categories. -}
module CategoryTheory.Categories where

open import Relation.Binary.PropositionalEquality
            using (_≡_ ; refl ; sym ; trans ; subst ; cong) public
open import Relation.Binary using (IsEquivalence)
open import Agda.Primitive using (Level ; _⊔_ ; lzero ; lsuc) public
open import Relation.Binary.PropositionalEquality

-- Function extensionality
postulate
    ext : ∀{a b} -> Extensionality a b

-- Type class for categories.
-- Based on https://github.com/UlfNorell/category-theory-experiments
record Category (n : Level) : Set (lsuc (lsuc n)) where
    infixr 10 _~>_
    infixl 40 _≈_
    infixr 60 _∘_
    field
        -- || Data
        -- Objects
        obj  : Set (lsuc n)
        -- Arrows
        _~>_ : obj -> obj -> Set n

        -- || Operations
        -- Identity arrow
        id   : {A : obj} -> A ~> A
        -- Composition of arrows
        _∘_  : {A B C : obj} -> (B ~> C) -> (A ~> B) -> (A ~> C)
        -- Equality of arrows (as we don't have function extensionality)
        _≈_  : {A B : obj} -> (A ~> B) -> (A ~> B) -> Set n

        -- || Laws
        -- Left identity
        id-left  : {A B : obj} {f : A ~> B} -> id ∘ f ≈ f
        -- Right identity
        id-right : {A B : obj} {f : A ~> B} -> f ∘ id ≈ f
        -- Associativity of composition
        ∘-assoc  : {A B C D : obj} {f : C ~> D} {g : B ~> C} {h : A ~> B}
                -> (f ∘ g) ∘ h ≈ f ∘ (g ∘ h)
        -- Arrow equality is an equivalence relation
        ≈-equiv  : ∀{A B : obj} -> IsEquivalence (_≈_ {A} {B})
        -- Congruence of equality and composition
        ≈-cong   : ∀{A B C : obj} {f f′ : A ~> B} {g g′ : B ~> C}
                -> f ≈ f′ -> g ≈ g′ -> g ∘ f ≈ g′ ∘ f′

    -- Equational reasoning for ≈ (based on the standard library definitions)
    infix  3 _∎
    infixr 2 _≈⟨⟩_ _≈⟨_⟩_
    infix  1 begin_
    infixl 20 _[sym]
    infixr 15 _≈>_

    begin_ : ∀{A B : obj} {x y : A ~> B} → x ≈ y → x ≈ y
    begin_ x≈y = x≈y

    _≈⟨⟩_ : ∀{A B : obj} (x {y} : A ~> B) → x ≈ y → x ≈ y
    _ ≈⟨⟩ x≈y = x≈y

    _≈⟨_⟩_ : ∀{A B : obj} (x {y z} : A ~> B) → x ≈ y → y ≈ z → x ≈ z
    _ ≈⟨ x≈y ⟩ y≈z = IsEquivalence.trans ≈-equiv x≈y y≈z

    _∎ : ∀{A B : obj} (x : A ~> B) → x ≈ x
    _∎ _ = IsEquivalence.refl ≈-equiv

    r≈ : ∀{A B : obj} {x : A ~> B} → x ≈ x
    r≈ = IsEquivalence.refl ≈-equiv

    _[sym] : ∀{A B : obj} {x y : A ~> B} → x ≈ y → y ≈ x
    p [sym] = IsEquivalence.sym ≈-equiv p

    _≈>_ : ∀{A B : obj} {x y z : A ~> B} → x ≈ y → y ≈ z → x ≈ z
    p1 ≈> p2 = IsEquivalence.trans ≈-equiv p1 p2

    id-comm : {A B : obj} {f : A ~> B} -> f ∘ id ≈ id ∘ f
    id-comm = id-right ≈> id-left [sym]

    -- Derived congruence properties
    ≈-cong-left : ∀{A B C : obj} {f : A ~> B} {g g′ : B ~> C}
            -> g ≈ g′ -> g ∘ f ≈ g′ ∘ f
    ≈-cong-left e = ≈-cong r≈ e
    ≈-cong-right : ∀{A B C : obj} {g : B ~> C} {f f′ : A ~> B}
            -> f ≈ f′ -> g ∘ f ≈ g ∘ f′
    ≈-cong-right e = ≈-cong e r≈

    -- Isomorphism of two objects
    record _<~>_  (A B : obj) : Set (lsuc n) where
        field
            -- | Data
            -- Arrow in one direction
            iso~> : A ~> B
            -- Arrow in other direction
            iso<~ : B ~> A

            -- | Laws
            iso-id₁ : iso<~ ∘ iso~> ≈ id {A}
            iso-id₂ : iso~> ∘ iso<~ ≈ id {B}


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

module Data.Binary.Proofs.Bijection where

open import Relation.Binary.PropositionalEquality
open import Data.Binary.Operations.Unary
open import Data.Binary.Proofs.Unary
open import Data.Binary.Definitions
open import Data.Binary.Operations.Semantics
open import Data.Nat as ℕ using (ℕ; suc; zero)
open import Relation.Binary.PropositionalEquality.FasterReasoning
import Data.Nat.Properties as ℕ
open import Function

homo : ∀ n → ⟦ ⟦ n ⇑⟧ ⇓⟧ ≡ n
homo zero = refl
homo (suc n) = inc-homo ⟦ n ⇑⟧ ⟨ trans ⟩ cong suc (homo n)

inj : ∀ {x y} → ⟦ x ⇓⟧ ≡ ⟦ y ⇓⟧ → x ≡ y
inj {xs} {ys} eq = go (subst (IncView xs) eq (inc-view xs)) (inc-view ys)
  where
  go : ∀ {n xs ys} → IncView xs n → IncView ys n → xs ≡ ys
  go Izero Izero = refl
  go (Isuc refl xs) (Isuc refl ys) = cong inc (go xs ys)

open import Function.Bijection

𝔹↔ℕ : 𝔹 ⤖ ℕ
𝔹↔ℕ = bijection ⟦_⇓⟧ ⟦_⇑⟧ inj homo


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.DStructures.Structures.PeifferGraph where

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

open import Cubical.Functions.FunExtEquiv

open import Cubical.Homotopy.Base

open import Cubical.Data.Sigma

open import Cubical.Relation.Binary

open import Cubical.Algebra.Group
open import Cubical.Structures.LeftAction

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

open import Cubical.DStructures.Structures.Type
open import Cubical.DStructures.Structures.Group
open import Cubical.DStructures.Structures.ReflGraph

open GroupLemmas
open MorphismLemmas

private
  variable
    ℓ ℓ' : Level

module _ (𝒢 : ReflGraph ℓ ℓ') where
  open ReflGraphNotation 𝒢

  ----------------------------------------------------
  -- The peiffer condition for reflexive graphs,
  -- i.e. the missing property to turn a precrossed
  -- module into a crossed module
  ----------------------------------------------------
  isPeifferGraph : Type ℓ'
  isPeifferGraph = (a b : ⟨ G₁ ⟩) → ((is b +₁ (a -₁ it a)) +₁ (-is b +₁ b)) +₁ it a ≡ b +₁ a

  -- G₁ is a set, so isPeifferGraph is a proposition
  isPropIsPeifferGraph : isProp isPeifferGraph
  isPropIsPeifferGraph = isPropΠ2 (λ a b → set₁ (((is b +₁ (a -₁ it a)) +₁ (-is b +₁ b)) +₁ it a) (b +₁ a))

  ---------------------------------------------------
  -- lemmas about the peiffer graphs
  ---------------------------------------------------
  module _ (isPeifferG : isPeifferGraph) where
   abstract
     -- the peiffer condition can be simplified to this
     isPeifferGraph2 : (a b : ⟨ G₁ ⟩) → (a -₁ it a) +₁ (-is b +₁ b) ≡ (-is b +₁ (b +₁ a)) -₁ it a
     isPeifferGraph2 a b = (a -₁ ita) +₁ (-isb +₁ b)
                              ≡⟨ sym (lCancel-lId G₁ isb ((a -₁ ita) +₁ (-isb +₁ b)))
                                 ∙ sym (rCancel-rId G₁ ((-isb +₁ isb) +₁ ((a -₁ ita) +₁ (-isb +₁ b))) ita) ⟩
                           ((-isb +₁ isb) +₁ ((a -₁ ita) +₁ (-isb +₁ b))) +₁ (ita -₁ ita)
                              ≡⟨ assoc₁ ((-isb +₁ isb) +₁ ((a -₁ ita) +₁ (-isb +₁ b))) ita -ita ⟩
                           (((-isb +₁ isb) +₁ ((a -₁ ita) +₁ (-isb +₁ b))) +₁ ita) +₁ -ita
                              ≡⟨ cong (λ z → (z +₁ ita) -₁ ita)
                                      (sym (assoc₁ -isb isb _)) ⟩
                           ((-isb +₁ (isb +₁ ((a -₁ ita) +₁ (-isb +₁ b)))) +₁ ita) +₁ -ita
                              ≡⟨ cong (λ z → ((-isb +₁ z) +₁ ita) +₁ -ita)
                                      (assoc₁ _ _ _) ⟩
                           ((-isb +₁ ((isb +₁ (a -₁ ita)) +₁ (-isb +₁ b))) +₁ ita) +₁ -ita
                              ≡⟨ cong (_+₁ -ita)
                                      (sym (assoc₁ -isb _ ita)) ⟩
                           (-isb +₁ (((isb +₁ (a -₁ ita)) +₁ (-isb +₁ b)) +₁ ita)) -₁ ita
                             ≡⟨ cong (λ z → (-isb +₁ z) -₁ ita)
                                     (isPeifferG a b) ⟩
                           (-isb +₁ (b +₁ a)) -₁ ita ∎
                           where
                             -a = -₁ a
                             -ita = -it a
                             ita = it a
                             isb = is b
                             -isb = -is b
                             -b = -₁ b

     -- inverting both sides of the identity isPeifferGraph2 and simplifying
     -- gives this
     isPeifferGraph3 : (a b : ⟨ G₁ ⟩) → (-₁ b) +₁ (is b +₁ (it a -₁ a)) ≡ it a +₁ ((-₁ a) +₁ ((-₁ b) +₁ is b))
     isPeifferGraph3 a b = -b +₁ (isb +₁ (ita -₁ a))
                              ≡⟨ cong (λ z → -b +₁ (z +₁ (ita -₁ a)))
                                      (sym (invInvo G₁ isb))
                                 ∙ cong (λ z → -b +₁ ((-₁ -isb) +₁ (z -₁ a)))
                                        (sym (invInvo G₁ ita)) ⟩
                           -b +₁ ((-₁ -isb) +₁ ((-₁ -ita) -₁ a))
                              ≡⟨ sym (invDistr₄ G₁ a -ita -isb b) ⟩
                           -₁ (((a +₁ -ita) +₁ -isb) +₁ b)
                              ≡⟨ cong -₁_ (sym (assoc₁ _ -isb b)) ⟩
                           -₁ ((a -₁ ita) +₁ (-isb +₁ b))
                             ≡⟨ cong -₁_ (isPeifferGraph2 a b) ⟩
                           -₁ ((-isb +₁ (b +₁ a)) -₁ ita)
                             ≡⟨ cong (λ z → -₁ (z -₁ ita)) (assoc₁ -isb b a) ⟩
                           -₁ (((-isb +₁ b) +₁ a) -₁ ita)
                             ≡⟨ invDistr₄ G₁ -isb b a -ita ⟩
                           (-₁ -ita) +₁ (-a +₁ (-b +₁ (-₁ -isb)))
                             ≡⟨ cong (_+₁ (-a +₁ (-b +₁ (-₁ -isb))))
                                     (invInvo G₁ ita)
                                ∙ cong (λ z → ita +₁ (-a +₁ (-b +₁ z)))
                                       (invInvo G₁ isb) ⟩
                           ita +₁ (-a +₁ (-b +₁ isb)) ∎
                           where
                             -a = -₁ a
                             -ita = -₁ (it a)
                             ita = it a
                             isb = is b
                             -isb = -₁ isb
                             -b = -₁ b

     -- plugging -a and -b into isPeifferGraph4 gives this
     isPeifferGraph4 : (a b : ⟨ G₁ ⟩) → b +₁ ((-₁ (is b)) +₁ ((-₁ (it a)) +₁ a)) ≡ (-₁ (it a)) +₁ (a +₁ (b -₁ (is b)))
     isPeifferGraph4 a b = b +₁ (-isb +₁ (-ita +₁ a))
                             ≡⟨ cong (_+₁ (-isb +₁ (-ita +₁ a)))
                                     (sym (invInvo G₁ b)) ⟩
                           (-₁ -b) +₁ (-isb +₁ (-ita +₁ a))
                             ≡⟨ cong (λ z → (-₁ -b) +₁ (-isb +₁ (-ita +₁ z)))
                                     (sym (invInvo G₁ a)) ⟩
                           (-₁ -b) +₁ (-isb +₁ (-ita -₁ -a))
                             ≡⟨ cong (λ z → (-₁ -b) +₁ (-isb +₁ (z -₁ -a))) (sym (mapInv ι∘τ a)) ⟩
                           (-₁ -b) +₁ (-isb +₁ ((it -a) -₁ -a))
                             ≡⟨ cong (λ z → (-₁ -b) +₁ (z +₁ ((it -a) -₁ -a))) (sym (mapInv ι∘σ b)) ⟩
                           (-₁ -b) +₁ (is -b +₁ ((it -a) -₁ -a))
                             ≡⟨ isPeifferGraph3 -a -b ⟩
                           it -a +₁ ((-₁ -a) +₁ ((-₁ -b) +₁ is -b))
                             ≡⟨ cong (_+₁ ((-₁ -a) +₁ ((-₁ -b) +₁ is -b)))
                                     (mapInv ι∘τ a) ⟩
                           -ita +₁ ((-₁ -a) +₁ ((-₁ -b) +₁ is -b))
                             ≡⟨ cong (λ z → -ita +₁ (z +₁ ((-₁ -b) +₁ is -b)))
                                     (invInvo G₁ a) ⟩
                           -ita +₁ (a +₁ ((-₁ -b) +₁ is -b))
                             ≡⟨ cong (λ z → -ita +₁ (a +₁ (z +₁ is -b)))
                                     (invInvo G₁ b) ⟩
                           -ita +₁ (a +₁ (b +₁ is -b))
                             ≡⟨ cong (λ z → -ita +₁ (a +₁ (b +₁ z)))
                                     (mapInv ι∘σ b) ⟩
                           -ita +₁ (a +₁ (b -₁ isb)) ∎
                           where
                             -a = -₁ a
                             -ita = -it a
                             isb = is b
                             -isb = -is b
                             -b = -₁ b

-----------------------------------------------
-- URG structure on the type of peiffer graphs
--
--   isPeifferGraph
--        |
--    ReflGraph
-----------------------------------------------

module _ (ℓ ℓ' : Level) where
  private
    ℓℓ' = ℓ-max ℓ ℓ'

  𝒮ᴰ-ReflGraph\Peiffer : URGStrᴰ (𝒮-ReflGraph ℓ ℓℓ')
                                 (λ 𝒢 → isPeifferGraph 𝒢)
                                 ℓ-zero
  𝒮ᴰ-ReflGraph\Peiffer = Subtype→Sub-𝒮ᴰ (λ 𝒢 → isPeifferGraph 𝒢 , isPropIsPeifferGraph 𝒢)
                                        (𝒮-ReflGraph ℓ ℓℓ')

  PeifferGraph : Type (ℓ-suc ℓℓ')
  PeifferGraph = Σ[ 𝒢 ∈ ReflGraph ℓ ℓℓ' ] isPeifferGraph 𝒢

  𝒮-PeifferGraph : URGStr PeifferGraph ℓℓ'
  𝒮-PeifferGraph = ∫⟨ 𝒮-ReflGraph ℓ ℓℓ' ⟩ 𝒮ᴰ-ReflGraph\Peiffer


import Structure.Logic.Classical.NaturalDeduction

-- TODO: MAybe rename to SetBoundedQuantification
module Structure.Logic.Classical.SetTheory.SetBoundedQuantification {ℓₗ} {Formula} {ℓₘₗ} {Proof} {ℓₒ} {Domain} ⦃ classicLogic : _ ⦄ (_∈_ : Domain → Domain → Formula) where
open Structure.Logic.Classical.NaturalDeduction.ClassicalLogic {ℓₗ} {Formula} {ℓₘₗ} {Proof} {ℓₒ} {Domain} (classicLogic)

open import Functional hiding (Domain)
open import Lang.Instance
import      Lvl
open import Type.Dependent
open import Structure.Logic.Classical.PredicateBoundedQuantification {ℓₗ} {Formula} {ℓₘₗ} {Proof} {ℓₒ} {Domain} ⦃ classicLogic ⦄

-- Bounded universal quantifier
∀ₛ : Domain → (Domain → Formula) → Formula
∀ₛ(S)(P) = ∀ₚ(_∈ S)(P)

[∀ₛ]-intro : ∀{S}{P} → (∀{x} → Proof(x ∈ S) → Proof(P(x))) → Proof(∀ₛ(S)(P))
[∀ₛ]-intro = [∀ₚ]-intro

[∀ₛ]-elim : ∀{S}{P} → Proof(∀ₛ(S)(P)) → ∀{x} → Proof(x ∈ S) → Proof(P(x))
[∀ₛ]-elim = [∀ₚ]-elim

-- Bounded existential quantifier
∃ₛ : Domain → (Domain → Formula) → Formula
∃ₛ(S)(P) = ∃ₚ(_∈ S)(P)

[∃ₛ]-intro : ∀{S}{P}{x} → Proof(x ∈ S) → Proof(P(x)) → Proof(∃ₛ(S)(P))
[∃ₛ]-intro = [∃ₚ]-intro

[∃ₛ]-elim : ∀{S}{P}{Q} → (∀{x} → Proof(x ∈ S) → Proof(P(x)) → Proof(Q)) → Proof(∃ₛ(S)(P)) → Proof(Q)
[∃ₛ]-elim = [∃ₚ]-elim

[∃ₛ]-witness : ∀{S : Domain}{P : Domain → Formula} → ⦃ _ : Proof(∃ₛ S P) ⦄ → Domain
[∃ₛ]-witness = [∃ₚ]-witness

[∃ₛ]-domain : ∀{S : Domain}{P : Domain → Formula} → ⦃ p : Proof(∃ₛ S P) ⦄ → Proof([∃ₛ]-witness{S}{P} ⦃ p ⦄ ∈ S)
[∃ₛ]-domain = [∃ₚ]-bound

[∃ₛ]-proof : ∀{S : Domain}{P : Domain → Formula} → ⦃ p : Proof(∃ₛ S P) ⦄ → Proof(P([∃ₛ]-witness{S}{P} ⦃ p ⦄))
[∃ₛ]-proof = [∃ₚ]-proof

Uniqueₛ : Domain → (Domain → Formula) → Formula
Uniqueₛ(S)(P) = Uniqueₚ(_∈ S)(P)

-- Bounded unique existential quantifier
∃ₛ! : Domain → (Domain → Formula) → Formula
∃ₛ!(S)(P) = ∃ₚ!(_∈ S)(P)

[∃ₛ!]-witness : ∀{S : Domain}{P : Domain → Formula} → ⦃ _ : Proof(∃ₛ! S P) ⦄ → Domain
[∃ₛ!]-witness = [∃ₚ!]-witness

[∃ₛ!]-domain : ∀{S : Domain}{P : Domain → Formula} → ⦃ p : Proof(∃ₛ! S P) ⦄ → Proof([∃ₛ!]-witness{S}{P} ⦃ p ⦄ ∈ S)
[∃ₛ!]-domain = [∃ₚ!]-bound

[∃ₛ!]-proof : ∀{S : Domain}{P : Domain → Formula} → ⦃ p : Proof(∃ₛ! S P) ⦄ → Proof(P([∃ₛ!]-witness{S}{P} ⦃ p ⦄))
[∃ₛ!]-proof = [∃ₚ!]-proof

[∃ₛ!]-unique : ∀{S : Domain}{P : Domain → Formula} → ⦃ p : Proof(∃ₛ! S P) ⦄ → Proof(∀ₗ(x ↦ P(x) ⟶ (x ≡ [∃ₛ!]-witness{S}{P} ⦃ p ⦄)))
[∃ₛ!]-unique = [∃ₚ!]-unique


module Basic.AST where

import Data.Bool as Bool using (not)
open import Data.Bool hiding (not; if_then_else_)
open import Data.Fin using (Fin; suc; zero; #_)
open import Data.Nat
open import Data.Vec
open import Relation.Nullary
open import Relation.Nullary.Decidable
open import Utils.Decidable

{-
This module covers the first chapter of the book.

We omitted a couple of proofs that are present in the chapter.

In particular, determinism and totality of the evaluation of expressions
is a trivial consequence of Agda's totality, so there's no need to bother
with it here. Also, the notion of "composionality" as mentioned in book corresponds
simply to structural recursion in Agda.

Also, we skipped the proofs about the evaluation of expression with substitutions
(exercise 1.14) and contexts with substitutions (exercise 1.13), since we make
no use of these in other proofs.
-}

-- Our type universe (it's not a large one).
data Ty : Set where
  bool nat : Ty

{-
Interpretation of types into Agda sets.
Note that we use natural numbers instead of integers, since naturals are
much simpler to handle in formal contexts (also, there's not enough Agda library
support for integers).
-}

⟦_⟧ᵗ : Ty → Set
⟦ nat  ⟧ᵗ = ℕ
⟦ bool ⟧ᵗ = Bool

{-
This is a point where we make a departure from the book. The book defines states as:

State = String → ℕ

This is supposed to be a total funuction, and the book doesn't concern itself with scope
errors or shadowing, but we certainly have to do so in Agda.

So we opt for de Bruijn indices as variables and a finite vector as State. These are as
simple to handle as it can get, and we also get alpha equality of programs for free.

On the flip side, we have much less readable programs.
-}

State : ℕ → Set
State = Vec ℕ

{-
In the book there's a mutual definition of boolean and numeric expressions.
We instead have a single universe-indexed type family. It's more convenient, and
it's also equivalent to the mutual definition (it's a standard Agda/Haskell trick
to encode multiple types data as a single indexed type).
-}
data Exp (n : ℕ) : Ty → Set where
  lit         : ℕ → Exp n nat
  add mul sub : Exp n nat → Exp n nat → Exp n nat
  var         : Fin n → Exp n nat
  tt ff       : Exp n bool
  eq lte lt   : Exp n nat → Exp n nat → Exp n bool
  and         : Exp n bool → Exp n bool → Exp n bool
  not         : Exp n bool → Exp n bool

{-
Statements are parameterized by the size of the State they operate on. Since the
vanilla "While" language has no declarations or other features that might change
the size of the state, we are fine with this. It also allows us to use finite numbers
as de Bruijn indices.
-}

infixr 5 _:=_
infixr 4 _,_
data St (n : ℕ) : Set where
  _:=_          : Fin n → Exp n nat → St n
  skip          : St n
  _,_           : St n → St n → St n
  if_then_else_ : Exp n bool → St n → St n → St n
  while_do_     : Exp n bool → St n → St n

{-
The semantics of expressions follows the book exactly.
-}

⟦_⟧ᵉ : ∀ {n t} → Exp n t → State n → ⟦ t ⟧ᵗ
⟦ lit x   ⟧ᵉ s = x
⟦ add a b ⟧ᵉ s = ⟦ a ⟧ᵉ s + ⟦ b ⟧ᵉ s
⟦ mul a b ⟧ᵉ s = ⟦ a ⟧ᵉ s * ⟦ b ⟧ᵉ s
⟦ sub a b ⟧ᵉ s = ⟦ a ⟧ᵉ s ∸ ⟦ b ⟧ᵉ s
⟦ var x   ⟧ᵉ s = lookup x s
⟦ tt      ⟧ᵉ s = true
⟦ ff      ⟧ᵉ s = false
⟦ eq  a b ⟧ᵉ s = ⌊ ⟦ a ⟧ᵉ s ≡⁇ ⟦ b ⟧ᵉ s ⌋
⟦ lte a b ⟧ᵉ s = ⌊ ⟦ a ⟧ᵉ s ≤⁇ ⟦ b ⟧ᵉ s ⌋
⟦ lt  a b ⟧ᵉ s = ⌊ ⟦ a ⟧ᵉ s <⁇ ⟦ b ⟧ᵉ s ⌋
⟦ and a b ⟧ᵉ s =   ⟦ a ⟧ᵉ s ∧  ⟦ b ⟧ᵉ s
⟦ not e   ⟧ᵉ s = Bool.not (⟦ e ⟧ᵉ s)


module Cats.Category.Sets where

open import Data.Product using (Σ ; _×_ ; proj₁ ; proj₂)
open import Level
open import Relation.Binary using (Rel ; IsEquivalence ; _Preserves₂_⟶_⟶_)
open import Relation.Binary.PropositionalEquality as ≡ using (_≡_)

open import Cats.Category.Base
open import Cats.Util.Function
open import Cats.Util.Logic.Constructive

module _ {l} {A B : Set l} where

  infixr 4 _≈_

  _≈_ : (f g : A → B) → Set l
  f ≈ g = ∀ x → f x ≡ g x

  equiv : IsEquivalence _≈_
  equiv = record
      { refl = λ x → ≡.refl
      ; sym = λ eq x → ≡.sym (eq x)
      ; trans = λ eq₁ eq₂ x → ≡.trans (eq₁ x) (eq₂ x)
      }

instance Sets : ∀ l → Category (suc l) l l
Sets l = record
    { Obj = Set l
    ; _⇒_ = λ A B → A → B
    ; _≈_ = _≈_
    ; id = id
    ; _∘_ = _∘_
    ; equiv = equiv
    ; ∘-resp = λ {_} {_} {_} {f} eq₁ eq₂ x
        → ≡.trans (≡.cong f (eq₂ _)) (eq₁ _)
    ; id-r = λ _ → ≡.refl
    ; id-l = λ _ → ≡.refl
    ; assoc = λ _ → ≡.refl
    }


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

open import lib.Base
open import lib.Function
open import lib.NType
open import lib.PathGroupoid
open import lib.Relation
open import lib.types.Coproduct
open import lib.types.Empty

module lib.types.Nat where

infixl 80 _+_
_+_ : ℕ → ℕ → ℕ
0 + n = n
(S m) + n = S (m + n)

{-# BUILTIN NATPLUS _+_ #-}

abstract
  +-unit-r : (m : ℕ) → m + 0 == m
  +-unit-r 0     = idp
  +-unit-r (S m) = ap S (+-unit-r m)

  +-βr : (m n : ℕ) → m + (S n) == S (m + n)
  +-βr 0     n = idp
  +-βr (S m) n = ap S (+-βr m n)

  +-comm : (m n : ℕ) → m + n == n + m
  +-comm m 0     = +-unit-r m
  +-comm m (S n) = +-βr m n ∙ ap S (+-comm m n)

  +-assoc : (k m n : ℕ) → (k + m) + n == k + (m + n)
  +-assoc 0     m n = idp
  +-assoc (S k) m n = ap S (+-assoc k m n)

-- the [+] is like [ℕ-S^]
ℕ-S^' : ℕ → ℕ → ℕ
ℕ-S^' O n = n
ℕ-S^' (S m) n = ℕ-S^' m (S n)

ℕ-pred : ℕ → ℕ
ℕ-pred 0 = 0
ℕ-pred (S n) = n

abstract
  ℕ-S-is-inj : is-inj (S :> (ℕ → ℕ))
  ℕ-S-is-inj n m p = ap ℕ-pred p

  ℕ-S-≠ : ∀ {n m : ℕ} (p : n ≠ m) → S n ≠ S m :> ℕ
  ℕ-S-≠ {n} {m} p = p ∘ ℕ-S-is-inj n m

  private
    ℕ-S≠O-type : ℕ → Type₀
    ℕ-S≠O-type O = Empty
    ℕ-S≠O-type (S n) = Unit

  ℕ-S≠O : (n : ℕ) → S n ≠ O
  ℕ-S≠O n p = transport ℕ-S≠O-type p unit

  ℕ-O≠S : (n : ℕ) → (O ≠ S n)
  ℕ-O≠S n p = ℕ-S≠O n (! p)

  ℕ-has-dec-eq : has-dec-eq ℕ
  ℕ-has-dec-eq O O = inl idp
  ℕ-has-dec-eq O (S n) = inr (ℕ-O≠S n)
  ℕ-has-dec-eq (S n) O = inr (ℕ-S≠O n)
  ℕ-has-dec-eq (S n) (S m) with ℕ-has-dec-eq n m
  ℕ-has-dec-eq (S n) (S m) | inl p = inl (ap S p)
  ℕ-has-dec-eq (S n) (S m) | inr ¬p = inr (ℕ-S-≠ ¬p)

ℕ-is-set : is-set ℕ
ℕ-is-set = dec-eq-is-set ℕ-has-dec-eq

instance
  ℕ-level : {n : ℕ₋₂} → has-level (S (S n)) ℕ
  ℕ-level {⟨-2⟩} = ℕ-is-set
  ℕ-level {n = S n} = raise-level (S (S n)) ℕ-level

{- Inequalities -}
infix 40 _<_ _≤_

data _<_ : ℕ → ℕ → Type₀ where
  ltS : {m : ℕ} → m < (S m)
  ltSR : {m n : ℕ} → m < n → m < (S n)

_≤_ : ℕ → ℕ → Type₀
m ≤ n = Coprod (m == n) (m < n)

-- experimental: [lteE] [lteS] [lteSR]
lteE : {m : ℕ} → m ≤ m
lteE = inl idp

lteS : {m : ℕ} → m ≤ S m
lteS = inr ltS

lteSR : {m n : ℕ} → m ≤ n → m ≤ (S n)
lteSR (inl idp) = lteS
lteSR (inr lt) = inr (ltSR lt)

-- properties of [<]

O<S : (m : ℕ) → O < S m
O<S O = ltS
O<S (S m) = ltSR (O<S m)

O≤ : (m : ℕ) → O ≤ m
O≤ O = inl idp
O≤ (S m) = inr (O<S m)

≮O : ∀ n → ¬ (n < O)
≮O _ ()

S≰O : ∀ n → ¬ (S n ≤ O)
S≰O _ (inl ())
S≰O _ (inr ())

<-trans : {m n k : ℕ} → m < n → n < k → m < k
<-trans lt₁ ltS = ltSR lt₁
<-trans lt₁ (ltSR lt₂) = ltSR (<-trans lt₁ lt₂)

≤-refl : {m : ℕ} → m ≤ m
≤-refl = lteE

≤-trans : {m n k : ℕ} → m ≤ n → n ≤ k → m ≤ k
≤-trans (inl idp) lte₂ = lte₂
≤-trans lte₁ (inl idp) = lte₁
≤-trans (inr lt₁) (inr lt₂) = inr (<-trans lt₁ lt₂)

private
  test₀ : {m n : ℕ} (m≤n : m ≤ n) → lteSR m≤n == ≤-trans m≤n lteS
  test₀ (inl idp) = idp
  test₀ (inr lt) = idp

<-ap-S : {m n : ℕ} → m < n → S m < S n
<-ap-S ltS = ltS
<-ap-S (ltSR lt) = ltSR (<-ap-S lt)

≤-ap-S : {m n : ℕ} → m ≤ n → S m ≤ S n
≤-ap-S (inl p) = inl (ap S p)
≤-ap-S (inr lt) = inr (<-ap-S lt)

<-cancel-S : {m n : ℕ} → S m < S n → m < n
<-cancel-S ltS = ltS
<-cancel-S (ltSR lt) = <-trans ltS lt

≤-cancel-S : {m n : ℕ} → S m ≤ S n → m ≤ n
≤-cancel-S (inl p) = inl (ap ℕ-pred p)
≤-cancel-S (inr lt) = inr (<-cancel-S lt)

<-dec : Decidable _<_
<-dec _ O = inr (≮O _)
<-dec O (S m) = inl (O<S m)
<-dec (S n) (S m) with <-dec n m
<-dec (S n) (S m) | inl p = inl (<-ap-S p)
<-dec (S n) (S m) | inr ¬p = inr (¬p ∘ <-cancel-S)

≤-dec : Decidable _≤_
≤-dec O m = inl (O≤ m) -- important for the current cohomology development
≤-dec (S n) O = inr (S≰O n)
≤-dec (S n) (S m) with ≤-dec n m
≤-dec (S n) (S m) | inl p = inl (≤-ap-S p)
≤-dec (S n) (S m) | inr ¬p = inr (¬p ∘ ≤-cancel-S)

abstract
  <-to-≠ : {m n : ℕ} → m < n → m ≠ n
  <-to-≠ {m = O} ltS = ℕ-O≠S _
  <-to-≠ {m = O} (ltSR lt) = ℕ-O≠S _
  <-to-≠ {m = S m} {n = O} ()
  <-to-≠ {m = S m} {n = S n} lt = ℕ-S-≠ (<-to-≠ (<-cancel-S lt))

  <-to-≱ : {m n : ℕ} → m < n → ¬ (n ≤ m)
  <-to-≱ m<n (inl idp) = <-to-≠ m<n idp
  <-to-≱ m<n (inr n<m) = <-to-≠ (<-trans m<n n<m) idp

  <-has-all-paths : {m n : ℕ} → has-all-paths (m < n)
  <-has-all-paths = <-has-all-paths' idp where
    <-has-all-paths' : {m n₁ n₂ : ℕ} (eqn : n₁ == n₂) (lt₁ : m < n₁) (lt₂ : m < n₂)
      → PathOver (λ n → m < n) eqn lt₁ lt₂
    <-has-all-paths' eqn ltS ltS = transport (λ eqn₁ → PathOver (_<_ _) eqn₁ ltS ltS)
      (prop-has-all-paths idp eqn) idp
    <-has-all-paths' idp ltS (ltSR lt₂) = ⊥-rec (<-to-≠ lt₂ idp)
    <-has-all-paths' idp (ltSR lt₁) ltS = ⊥-rec (<-to-≠ lt₁ idp)
    <-has-all-paths' idp (ltSR lt₁) (ltSR lt₂) = ap ltSR (<-has-all-paths' idp lt₁ lt₂)

  instance
    <-is-prop : {m n : ℕ} → is-prop (m < n)
    <-is-prop = all-paths-is-prop <-has-all-paths

  ≤-has-all-paths : {m n : ℕ} → has-all-paths (m ≤ n)
  ≤-has-all-paths = λ{
    (inl eq₁) (inl eq₂) → ap inl (prop-has-all-paths eq₁ eq₂);
    (inl eq)  (inr lt)  → ⊥-rec (<-to-≠ lt eq);
    (inr lt)  (inl eq)  → ⊥-rec (<-to-≠ lt eq);
    (inr lt₁) (inr lt₂) → ap inr (<-has-all-paths lt₁ lt₂)}

  instance
    ≤-is-prop : {m n : ℕ} → is-prop (m ≤ n)
    ≤-is-prop = all-paths-is-prop ≤-has-all-paths

<-+-l : {m n : ℕ} (k : ℕ) → m < n → (k + m) < (k + n)
<-+-l O lt = lt
<-+-l (S k) lt = <-ap-S (<-+-l k lt)

≤-+-l : {m n : ℕ} (k : ℕ) → m ≤ n → (k + m) ≤ (k + n)
≤-+-l k (inl p) = inl (ap (λ t → k + t) p)
≤-+-l k (inr lt) = inr (<-+-l k lt)

<-+-r : {m n : ℕ} (k : ℕ) → m < n → (m + k) < (n + k)
<-+-r k ltS = ltS
<-+-r k (ltSR lt) = ltSR (<-+-r k lt)

≤-+-r : {m n : ℕ} (k : ℕ) → m ≤ n → (m + k) ≤ (n + k)
≤-+-r k (inl p) = inl (ap (λ t → t + k) p)
≤-+-r k (inr lt) = inr (<-+-r k lt)

<-witness : {m n : ℕ} → (m < n) → Σ ℕ (λ k → S k + m == n)
<-witness ltS = (O , idp)
<-witness (ltSR lt) = let w' = <-witness lt in (S (fst w') , ap S (snd w'))

≤-witness : {m n : ℕ} → (m ≤ n) → Σ ℕ (λ k → k + m == n)
≤-witness (inl p) = (O , p)
≤-witness (inr lt) = let w' = <-witness lt in (S (fst w') , snd w')

-- Double

infix 120 _*2
_*2 : ℕ → ℕ
O *2 = O
(S n) *2 = S (S (n *2))

*2-increasing : (m : ℕ) → (m ≤ m *2)
*2-increasing O = inl idp
*2-increasing (S m) = ≤-trans (≤-ap-S (*2-increasing m)) (inr ltS)

*2-monotone-< : {m n : ℕ} → m < n → m *2 < n *2
*2-monotone-< ltS = ltSR ltS
*2-monotone-< (ltSR lt) = ltSR (ltSR (*2-monotone-< lt))

*2-monotone-≤ : {m n : ℕ} → m ≤ n → m *2 ≤ n *2
*2-monotone-≤ (inl p) = inl (ap _*2 p)
*2-monotone-≤ (inr lt) = inr (*2-monotone-< lt)

-- Trichotomy

ℕ-trichotomy : (m n : ℕ) → (m == n) ⊔ ((m < n) ⊔ (n < m))
ℕ-trichotomy O O = inl idp
ℕ-trichotomy O (S n) = inr (inl (O<S n))
ℕ-trichotomy (S m) O = inr (inr (O<S m))
ℕ-trichotomy (S m) (S n) with ℕ-trichotomy m n
ℕ-trichotomy (S m) (S n) | inl m=n = inl (ap S m=n)
ℕ-trichotomy (S m) (S n) | inr (inl m<n) = inr (inl (<-ap-S m<n))
ℕ-trichotomy (S m) (S n) | inr (inr m>n) = inr (inr (<-ap-S m>n))


-- Andreas, 2022-06-14, issue #5953 reported by Szumi Xi
-- Cubical Agda switches dot-pattern termination off (#4606).
-- However, this breaks also some benign cases.
--
-- In this example with inductive-inductive types,
-- dotted variables should still be recognized
-- as variable patterns for termination certification.

{-# OPTIONS --cubical #-}  -- worked with --without-K but not --cubical

-- Debugging:
-- {-# OPTIONS -v term.check.clause:25 #-}
-- {-# OPTIONS -v term:20 #-}

data Con : Set
data Ty : Con → Set

data Con where
  nil : Con
  ext : (Γ : Con) → Ty Γ → Con

data Ty where
  univ : (Γ : Con) → Ty Γ
  pi : (Γ : Con) (A : Ty Γ) → Ty (ext Γ A) → Ty Γ

module _
  (A : Set)
  (B : A → Set)
  (n : A)
  (e : (a : A) → B a → A)
  (u : (a : A) → B a)
  (p : (a : A) (b : B a) → B (e a b) → B a)
  where
  recCon : Con → A
  recTy : (Γ : Con) → Ty Γ → B (recCon Γ)

  recCon nil = n
  recCon (ext Γ A) = e (recCon Γ) (recTy Γ A)

  recTy Γ (univ Γ) = u (recCon Γ)
  recTy Γ (pi Γ A B) = p (recCon Γ) (recTy Γ A) (recTy (ext Γ A) B)

-- Should pass the termination checker.


module Chunk where

open import Codata.Stream using (chunksOf; iterate; take)
open import Data.Nat using (ℕ; suc)
open import Data.Vec using (Vec; []; _∷_)

-- mylist 3 = [[1,2,3],[4,5,6],[7,8,9]]
myVec : (n : ℕ) → Vec (Vec ℕ n) n
myVec n = take n (chunksOf n (iterate suc 1))


{-# OPTIONS --cubical --safe #-}
module Cubical.Data.Unit.Properties where

open import Cubical.Core.Everything

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

open import Cubical.Data.Nat
open import Cubical.Data.Unit.Base
open import Cubical.Data.Prod.Base

isContrUnit : isContr Unit
isContrUnit = tt , λ {tt → refl}

isPropUnit : isProp Unit
isPropUnit _ _ i = tt -- definitionally equal to: isContr→isProp isContrUnit

isOfHLevelUnit : (n : ℕ) → isOfHLevel n Unit
isOfHLevelUnit n = isContr→isOfHLevel n isContrUnit

diagonal-unit : Unit ≡ Unit × Unit
diagonal-unit = isoToPath (iso (λ x → tt , tt) (λ x → tt) (λ {(tt , tt) i → tt , tt}) λ {tt i → tt})
  where open import Cubical.Foundations.Isomorphism


module Data.Finitude.FinType where
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Data.Nat as ℕ
open import Data.Fin as Fin using (Fin; #_)
open import Data.Finitude
open import Function.Equality using (_⟨$⟩_)
open import Function.Injection as Inj using (Injective)
open import Function.Inverse as Inv using (Inverse)
open import Data.Vec
open import Data.Vec.Membership.Propositional
open import Data.Vec.Membership.Propositional.Properties
open import Data.Vec.Membership.Propositional.Distinct as Distinct using (Distinct)

record FinType {a} (A : Set a) : Set a where
  field
    size : ℕ
    finitude : Finitude (P.setoid A) size
  open import Data.Vec.Properties

  index  : A → Fin size
  index  = Inverse.to finitude ⟨$⟩_

  index-injective : ∀ {x y} → index x ≡ index y → x ≡ y
  index-injective = Inverse.injective finitude

  enum  : Fin size → A
  enum = Inverse.from finitude ⟨$⟩_

  enum-injective : ∀ {i j} → enum i ≡ enum j → i ≡ j
  enum-injective = Inverse.injective (Inv.sym finitude)

  elems : Vec A size
  elems = tabulate enum

  elems-distinct : Distinct elems
  elems-distinct = Distinct.tabulate (Inverse.injection (Inv.sym finitude))

  _∈elems : ∀ x → x ∈ elems
  x ∈elems rewrite P.sym (Inverse.left-inverse-of finitude x) = ∈-tabulate⁺ enum (index x)

open FinType ⦃ ... ⦄ using (index ; _∈elems)

size : ∀ {a} (A : Set a) ⦃ fin : FinType A ⦄ → ℕ
size A {{ fin }} = FinType.size fin

elems : ∀ {a} (A : Set a) ⦃ fin : FinType A ⦄ → Vec A (size A)
elems A {{ fin }} = FinType.elems fin

instance
  open import Data.Bool
  open import Data.Unit
  open import Data.Empty
  empty   : FinType ⊥
  empty = record {
            size = 0
          ; finitude = record {
              to = P.→-to-⟶ λ()
            ; from = P.→-to-⟶ λ()
            ; inverse-of = record {
              left-inverse-of = λ ()
            ; right-inverse-of = λ ()
            }
            }
          }

  unit   : FinType ⊤
  unit   = record {
          size = 1
        ; finitude = record {
          to = P.→-to-⟶ λ _ → Fin.zero
        ; from = P.→-to-⟶ λ _ → tt
        ; inverse-of = record {
           left-inverse-of = λ _ → P.refl
         ; right-inverse-of = λ { Fin.zero → P.refl ; (Fin.suc ()) }
        }
        }
        }

  bool    : FinType Bool
  bool    = record {
          size = 2
        ; finitude = record {
            to = P.→-to-⟶ λ { false → # 0 ; true → # 1 }
          ; from = P.→-to-⟶ λ { Fin.zero → false ; (Fin.suc Fin.zero) → true ; (Fin.suc (Fin.suc ())) }
          ; inverse-of = record {
            left-inverse-of = λ { false → P.refl ; true → P.refl }
          ; right-inverse-of = λ { Fin.zero → P.refl ; (Fin.suc Fin.zero) → P.refl ; (Fin.suc (Fin.suc ())) }
          }
          }
          }
private
  bools : Vec Bool _
  bools = elems Bool

  example : bools ≡ false ∷ true ∷ []
  example = P.refl


module FunctorComposition where

open import Functor as F

compose : {F₁ F₂ : Setoid → Setoid} →
          Functor F₁ → Functor F₂ → Functor (λ A → F₁ (F₂ A))
compose {F₁} {F₂} FF₁ FF₂ = record
  { map      = map FF₁ ∘ map FF₂
  ; identity = λ {A} →
      trans (F₁ (F₂ A) ⇨ F₁ (F₂ A))
            {i = map FF₁ ⟨$⟩ (map FF₂ ⟨$⟩ id)}
            {j = map FF₁ ⟨$⟩ id}
            {k = id}
            (cong (map FF₁) (identity FF₂))
            (identity FF₁)
  ; composition = λ {A B C} f g →
      trans (F₁ (F₂ A) ⇨ F₁ (F₂ C))
            {i = map FF₁ ⟨$⟩ (map FF₂ ⟨$⟩ (f ∘ g))}
            {j = map FF₁ ⟨$⟩ ((map FF₂ ⟨$⟩ f) ∘ (map FF₂ ⟨$⟩ g))}
            {k = (map FF₁ ⟨$⟩ (map FF₂ ⟨$⟩ f)) ∘
                 (map FF₁ ⟨$⟩ (map FF₂ ⟨$⟩ g))}
            (cong (map FF₁) (composition FF₂ f g))
            (composition FF₁ (map FF₂ ⟨$⟩ f) (map FF₂ ⟨$⟩ g))
  }
  where
  open Setoid
  open F.Functor


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of sums (disjoint unions)
------------------------------------------------------------------------

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

module Data.Sum.Properties where

open import Level
open import Data.Sum.Base
open import Function
open import Relation.Binary using (Decidable)
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Decidable using (map′)

private
  variable
    a b c d e f : Level
    A : Set a
    B : Set b
    C : Set c
    D : Set d
    E : Set e
    F : Set f

inj₁-injective : ∀ {x y} → (A ⊎ B ∋ inj₁ x) ≡ inj₁ y → x ≡ y
inj₁-injective refl = refl

inj₂-injective : ∀ {x y} → (A ⊎ B ∋ inj₂ x) ≡ inj₂ y → x ≡ y
inj₂-injective refl = refl

module _ (dec₁ : Decidable {A = A} {B = A} _≡_)
         (dec₂ : Decidable {A = B} {B = B} _≡_) where

  ≡-dec : Decidable {A = A ⊎ B} _≡_
  ≡-dec (inj₁ x) (inj₁ y) = map′ (cong inj₁) inj₁-injective (dec₁ x y)
  ≡-dec (inj₁ x) (inj₂ y) = no λ()
  ≡-dec (inj₂ x) (inj₁ y) = no λ()
  ≡-dec (inj₂ x) (inj₂ y) = map′ (cong inj₂) inj₂-injective (dec₂ x y)

swap-involutive : swap {A = A} {B = B} ∘ swap ≗ id
swap-involutive = [ (λ _ → refl) , (λ _ → refl) ]

[,]-∘-distr : {f : A → B}
              {g : C → A} {h : D → A} →
              f ∘ [ g , h ] ≗ [ f ∘ g , f ∘ h ]
[,]-∘-distr (inj₁ _) = refl
[,]-∘-distr (inj₂ _) = refl

[,]-map-commute : {f : A → B}  {g : C → D}
                  {f′ : B → E} {g′ : D → E} →
                  [ f′ , g′ ] ∘ (map f g) ≗ [ f′ ∘ f , g′ ∘ g ]
[,]-map-commute (inj₁ _) = refl
[,]-map-commute (inj₂ _) = refl

map-commute : {f : A → B}  {g : C → D}
              {f′ : B → E} {g′ : D → F} →
              ((map f′ g′) ∘ (map f g)) ≗ map (f′ ∘ f) (g′ ∘ g)
map-commute (inj₁ _) = refl
map-commute (inj₂ _) = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Semi-heterogeneous vector equality
------------------------------------------------------------------------

module Data.Vec.Equality where

open import Data.Vec
open import Data.Nat using (suc)
open import Function
open import Level using (_⊔_)
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P using (_≡_)

module Equality {s₁ s₂} (S : Setoid s₁ s₂) where

  private
    open module SS = Setoid S
      using () renaming (_≈_ to _≊_; Carrier to A)

  infix 4 _≈_

  data _≈_ : ∀ {n¹} → Vec A n¹ →
             ∀ {n²} → Vec A n² → Set (s₁ ⊔ s₂) where
    []-cong  : [] ≈ []
    _∷-cong_ : ∀ {x¹ n¹} {xs¹ : Vec A n¹}
                 {x² n²} {xs² : Vec A n²}
                 (x¹≈x² : x¹ ≊ x²) (xs¹≈xs² : xs¹ ≈ xs²) →
                 x¹ ∷ xs¹ ≈ x² ∷ xs²

  length-equal : ∀ {n¹} {xs¹ : Vec A n¹}
                   {n²} {xs² : Vec A n²} →
                 xs¹ ≈ xs² → n¹ ≡ n²
  length-equal []-cong        = P.refl
  length-equal (_ ∷-cong eq₂) = P.cong suc $ length-equal eq₂

  refl : ∀ {n} (xs : Vec A n) → xs ≈ xs
  refl []       = []-cong
  refl (x ∷ xs) = SS.refl ∷-cong refl xs

  sym : ∀ {n m} {xs : Vec A n} {ys : Vec A m} →
        xs ≈ ys → ys ≈ xs
  sym []-cong                = []-cong
  sym (x¹≡x² ∷-cong xs¹≈xs²) = SS.sym x¹≡x² ∷-cong sym xs¹≈xs²

  trans : ∀ {n m l} {xs : Vec A n} {ys : Vec A m} {zs : Vec A l} →
          xs ≈ ys → ys ≈ zs → xs ≈ zs
  trans []-cong            []-cong            = []-cong
  trans (x≈y ∷-cong xs≈ys) (y≈z ∷-cong ys≈zs) =
    SS.trans x≈y y≈z ∷-cong trans xs≈ys ys≈zs

  _++-cong_ : ∀ {n₁¹ n₂¹} {xs₁¹ : Vec A n₁¹} {xs₂¹ : Vec A n₂¹}
                {n₁² n₂²} {xs₁² : Vec A n₁²} {xs₂² : Vec A n₂²} →
              xs₁¹ ≈ xs₁² → xs₂¹ ≈ xs₂² →
              xs₁¹ ++ xs₂¹ ≈ xs₁² ++ xs₂²
  []-cong          ++-cong eq₃ = eq₃
  (eq₁ ∷-cong eq₂) ++-cong eq₃ = eq₁ ∷-cong (eq₂ ++-cong eq₃)

module DecidableEquality {d₁ d₂} (D : DecSetoid d₁ d₂) where

  private module DS = DecSetoid D
  open DS using () renaming (_≟_ to _≟′_ ; Carrier to A)
  open Equality DS.setoid
  open import Relation.Nullary

  _≟_ : ∀ {n m} (xs : Vec A n) (ys : Vec A m) → Dec (xs ≈ ys)
  _≟_ []       []       = yes []-cong
  _≟_ []       (y ∷ ys) = no (λ())
  _≟_ (x ∷ xs) []       = no (λ())
  _≟_ (x ∷ xs) (y ∷ ys) with xs ≟ ys | x ≟′ y
  ... | yes xs≈ys | yes x≊y = yes (x≊y ∷-cong xs≈ys)
  ... | no ¬xs≈ys | _       = no helper
    where
    helper : ¬ (x ∷ xs ≈ y ∷ ys)
    helper (_ ∷-cong xs≈ys) = ¬xs≈ys xs≈ys
  ... | _         | no ¬x≊y = no helper
    where
    helper : ¬ (x ∷ xs ≈ y ∷ ys)
    helper (x≊y ∷-cong _) = ¬x≊y x≊y

module PropositionalEquality {a} {A : Set a} where

  open Equality (P.setoid A) public

  to-≡ : ∀ {n} {xs ys : Vec A n} → xs ≈ ys → xs ≡ ys
  to-≡ []-cong                 = P.refl
  to-≡ (P.refl ∷-cong xs¹≈xs²) = P.cong (_∷_ _) $ to-≡ xs¹≈xs²

  from-≡ : ∀ {n} {xs ys : Vec A n} → xs ≡ ys → xs ≈ ys
  from-≡ P.refl = refl _


{-# OPTIONS --allow-unsolved-metas #-}
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- CS410 2017/18 Exercise 1  VECTORS AND FRIENDS (worth 25%)
------------------------------------------------------------------------------
------------------------------------------------------------------------------

-- NOTE (19/9/17) This file is currently incomplete: more will arrive on
-- GitHub.

-- MARK SCHEME (transcribed from paper): the (m) numbers add up to slightly
-- more than 25, so should be taken as the maximum number of marks losable on
-- the exercise. In fact, I did mark it negatively, but mostly because it was
-- done so well (with Agda's help) that it was easier to find the errors.

------------------------------------------------------------------------------
-- Dependencies
------------------------------------------------------------------------------

open import CS410-Prelude

------------------------------------------------------------------------------
-- Vectors
------------------------------------------------------------------------------

data Vec (X : Set) : Nat -> Set where  -- like lists, but length-indexed
  []   :                              Vec X zero
  _,-_ : {n : Nat} -> X -> Vec X n -> Vec X (suc n)
infixr 4 _,-_   -- the "cons" operator associates to the right

-- I like to use the asymmetric ,- to remind myself that the element is to
-- the left and the rest of the list is to the right.

-- Vectors are useful when there are important length-related safety
-- properties.

------------------------------------------------------------------------------
-- Heads and Tails
------------------------------------------------------------------------------

-- We can rule out nasty head and tail errors by insisting on nonemptiness!

--??--1.1-(2)-----------------------------------------------------------------

vHead : {X : Set}{n : Nat} -> Vec X (suc n) -> X
vHead (x ,- xs) = x

vTail : {X : Set}{n : Nat} -> Vec X (suc n) -> Vec X n
vTail (x ,- xs) = xs

vHeadTailFact :  {X : Set}{n : Nat}(xs : Vec X (suc n)) ->
                 (vHead xs ,- vTail xs) == xs
vHeadTailFact (x ,- xs) = refl (x ,- xs)

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Concatenation and its Inverse
------------------------------------------------------------------------------

--??--1.2-(2)-----------------------------------------------------------------

_+V_ : {X : Set}{m n : Nat} -> Vec X m -> Vec X n -> Vec X (m +N n)
[] +V ys = ys
(x ,- xs) +V ys = x ,- xs +V ys
infixr 4 _+V_

vChop : {X : Set}(m : Nat){n : Nat} -> Vec X (m +N n) -> Vec X m * Vec X n
vChop zero xs = [] , xs
vChop (suc m) (x ,- xs) with vChop m xs
vChop (suc m) (x ,- xs) | fst₁ , snd₁ = (x ,- fst₁) , snd₁

vChopAppendFact : {X : Set}{m n : Nat}(xs : Vec X m)(ys : Vec X n) ->
                  vChop m (xs +V ys) == (xs , ys)
vChopAppendFact [] ys = refl ([] , ys)
vChopAppendFact (x ,- xs) ys rewrite vChopAppendFact xs ys = refl ((x ,- xs) , ys)

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Map, take I
------------------------------------------------------------------------------

-- Implement the higher-order function that takes an operation on
-- elements and does it to each element of a vector. Use recursion
-- on the vector.
-- Note that the type tells you the size remains the same.

-- Show that if the elementwise function "does nothing", neither does
-- its vMap. "map of identity is identity"

-- Show that two vMaps in a row can be collapsed to just one, or
-- "composition of maps is map of compositions"

--??--1.3-(2)-----------------------------------------------------------------

vMap : {X Y : Set} -> (X -> Y) -> {n : Nat} -> Vec X n -> Vec Y n
vMap f [] = []
vMap f (x ,- xs) = f x ,- vMap f xs

vMapIdFact : {X : Set}{f : X -> X}(feq : (x : X) -> f x == x) ->
             {n : Nat}(xs : Vec X n) -> vMap f xs == xs
vMapIdFact feq [] = refl []
vMapIdFact feq (x ,- xs) rewrite feq x | vMapIdFact feq xs = refl (x ,- xs)

vMapCpFact : (X Y Z : Set)(f : Y -> Z)(g : X -> Y)(h : X -> Z)
               (heq : (x : X) -> f (g x) == h x) ->
             {n : Nat}(xs : Vec X n) ->
               vMap f (vMap g xs) == vMap h xs
vMapCpFact _ _ _ _ _ _ heq [] = refl []
vMapCpFact X Y Z f g h heq (x ,- xs) rewrite heq x | vMapCpFact X Y Z f g h heq xs = refl (h x ,- vMap h xs)

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- vMap and +V
------------------------------------------------------------------------------

-- Show that if you've got two vectors of Xs and a function from X to Y,
-- and you want to concatenate and map, it doesn't matter which you do
-- first.

--??--1.4-(1)-----------------------------------------------------------------

vMap+VFact : {X Y : Set}(f : X -> Y) ->
             {m n : Nat}(xs : Vec X m)(xs' : Vec X n) ->
             vMap f (xs +V xs') == (vMap f xs +V vMap f xs')
vMap+VFact f [] xs' = refl (vMap f xs')
vMap+VFact f (x ,- xs) xs' rewrite vMap+VFact f xs xs' = refl _

--??--------------------------------------------------------------------------

-- Think about what you could prove, relating vMap with vHead, vTail, vChop...
-- Now google "Philip Wadler" "Theorems for Free"

------------------------------------------------------------------------------
-- Applicative Structure (giving mapping and zipping cheaply)
------------------------------------------------------------------------------

--??--1.5-(2)-----------------------------------------------------------------

-- HINT: you will need to override the default invisibility of n to do this.
vPure : {X : Set} -> X -> {n : Nat} -> Vec X n
vPure x {zero} = []
vPure x {suc n} = x ,- vPure x

_$V_ : {X Y : Set}{n : Nat} -> Vec (X -> Y) n -> Vec X n -> Vec Y n
[] $V [] = []
x ,- fs $V x₁ ,- xs = x x₁ ,- (fs $V xs)
infixl 3 _$V_  -- "Application associates to the left,
               --  rather as we all did in the sixties." (Roger Hindley)

-- Pattern matching and recursion are forbidden for the next two tasks.

-- implement vMap again, but as a one-liner
vec : {X Y : Set} -> (X -> Y) -> {n : Nat} -> Vec X n -> Vec Y n
vec f xs = vPure f $V xs

-- implement the operation which pairs up corresponding elements
vZip : {X Y : Set}{n : Nat} -> Vec X n -> Vec Y n -> Vec (X * Y) n
vZip [] [] = []
vZip (x ,- xs) (x₁ ,- ys) = (x , x₁) ,- vZip xs ys

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Applicative Laws
------------------------------------------------------------------------------

-- According to "Applicative programming with effects" by
--   Conor McBride and Ross Paterson
-- some laws should hold for applicative functors.
-- Check that this is the case.

--??--1.6-(2)-----------------------------------------------------------------

vIdentity : {X : Set}{f : X -> X}(feq : (x : X) -> f x == x) ->
            {n : Nat}(xs : Vec X n) -> (vPure f $V xs) == xs
vIdentity feq [] = refl []
vIdentity feq (x ,- xs) rewrite feq x | vIdentity feq xs = refl (x ,- xs)

vHomomorphism : {X Y : Set}(f : X -> Y)(x : X) ->
                {n : Nat} -> (vPure f $V vPure x) == vPure (f x) {n}
vHomomorphism f x {zero} = refl []
vHomomorphism f x {suc n} rewrite vHomomorphism f x {n} = refl (f x ,- vPure (f x))

vInterchange : {X Y : Set}{n : Nat}(fs : Vec (X -> Y) n)(x : X) ->
               (fs $V vPure x) == (vPure (_$ x) $V fs)
vInterchange [] x = refl []
vInterchange (x₁ ,- fs) x rewrite vInterchange fs x = refl _

vComposition : {X Y Z : Set}{n : Nat}
               (fs : Vec (Y -> Z) n)(gs : Vec (X -> Y) n)(xs : Vec X n) ->
               (vPure _<<_ $V fs $V gs $V xs) == (fs $V (gs $V xs))
vComposition [] [] [] = refl []
vComposition (x₁ ,- fs) (x₂ ,- gs) (x ,- xs) rewrite vComposition fs gs xs = refl _

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Order-Preserving Embeddings (also known in the business as "thinnings")
------------------------------------------------------------------------------

-- What have these to do with Pascal's Triangle?

data _<=_ : Nat -> Nat -> Set where
  oz :                          zero  <= zero
  os : {n m : Nat} -> n <= m -> suc n <= suc m
  o' : {n m : Nat} -> n <= m ->     n <= suc m

-- Find all the values in each of the following <= types.
-- This is a good opportunity to learn to use C-c C-a with the -l option
--   (a.k.a. "google the type" without "I feel lucky")
-- The -s n option also helps.

--??--1.7-(1)-----------------------------------------------------------------

all0<=4 : Vec (0 <= 4) 3
all0<=4 = o' (o' (o' (o' oz))) ,- o' (o' (o' (o' oz))) ,- o' (o' (o' (o' oz))) ,- []

all1<=4 : Vec (1 <= 4) 2
all1<=4 = os (o' (o' (o' oz))) ,- os (o' (o' (o' oz))) ,- []

all2<=4 : Vec (2 <= 4) _
all2<=4 = os (o' (o' (os oz))) ,- os (os (o' (o' oz))) ,- []

all3<=4 : Vec (3 <= 4) zero
all3<=4 = []

all4<=4 : Vec (4 <= 4) zero
all4<=4 = []

-- Prove the following. A massive case analysis "rant" is fine.

no5<=4 : 5 <= 4 -> Zero
no5<=4 (os (os (os (os ()))))
no5<=4 (os (os (os (o' ()))))
no5<=4 (os (os (o' (os ()))))
no5<=4 (os (os (o' (o' ()))))
no5<=4 (os (o' (os (os ()))))
no5<=4 (os (o' (os (o' ()))))
no5<=4 (os (o' (o' (os ()))))
no5<=4 (os (o' (o' (o' ()))))
no5<=4 (o' (os (os (os ()))))
no5<=4 (o' (os (os (o' ()))))
no5<=4 (o' (os (o' (os ()))))
no5<=4 (o' (os (o' (o' ()))))
no5<=4 (o' (o' (os (os ()))))
no5<=4 (o' (o' (os (o' ()))))
no5<=4 (o' (o' (o' (os ()))))
no5<=4 (o' (o' (o' (o' ()))))

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Order-Preserving Embeddings Select From Vectors
------------------------------------------------------------------------------

-- Use n <= m to encode the choice of n elements from an m-Vector.
-- The os constructor tells you to take the next element of the vector;
-- the o' constructor tells you to omit the next element of the vector.

--??--1.8-(2)-----------------------------------------------------------------

_<?=_ : {X : Set}{n m : Nat} -> n <= m -> Vec X m
                     -> Vec X n
oz <?= [] = []
os th <?= (x ,- xs) = x ,- (th <?= xs)
o' th <?= (x ,- xs) = th <?= xs

-- it shouldn't matter whether you map then select or select then map

vMap<?=Fact : {X Y : Set}(f : X -> Y)
              {n m : Nat}(th : n <= m)(xs : Vec X m) ->
              vMap f (th <?= xs) == (th <?= vMap f xs)
vMap<?=Fact f oz [] = refl []
vMap<?=Fact f (os th) (x ,- xs) rewrite vMap<?=Fact f th xs = refl _
vMap<?=Fact f (o' th) (x ,- xs) = vMap<?=Fact f th xs

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Our Favourite Thinnings
------------------------------------------------------------------------------

-- Construct the identity thinning and the empty thinning.

--??--1.9-(1)-----------------------------------------------------------------

oi : {n : Nat} -> n <= n
oi {zero} = oz
oi {suc n} = os oi

oe : {n : Nat} -> 0 <= n
oe {zero} = oz
oe {suc n} = o' oe

--??--------------------------------------------------------------------------

-- Show that all empty thinnings are equal to yours.

--??--1.10-(1)----------------------------------------------------------------

oeUnique : {n : Nat}(th : 0 <= n) -> th == oe
oeUnique oz = refl oz
oeUnique (o' i) rewrite oeUnique i = refl (o' oe)

--??--------------------------------------------------------------------------

-- Show that there are no thinnings of form big <= small  (TRICKY)
-- Then show that all the identity thinnings are equal to yours.
-- Note that you can try the second even if you haven't finished the first.
-- HINT: you WILL need to expose the invisible numbers.
-- HINT: check CS410-Prelude for a reminder of >=

--??--1.11-(3)----------------------------------------------------------------

thinMore : {n m : Nat} -> suc n <= m -> n <= m
thinMore (os n<=m) = o' n<=m
thinMore (o' n<=m) = o' (thinMore n<=m)

oTooBig : {n m : Nat} -> n >= m -> suc n <= m -> Zero
oTooBig {zero} {.(suc _)} n>=m (os th) = n>=m
oTooBig {zero} {.(suc _)} n>=m (o' th) = n>=m
oTooBig {suc n} {.(suc _)} n>=m (os th) = oTooBig n>=m th
oTooBig {suc n} {.(suc _)} n>=m (o' th) = oTooBig n>=m (thinMore th)

oiUnique : {n : Nat}(th : n <= n) -> th == oi
oiUnique oz = refl oz
oiUnique (os th) rewrite oiUnique th = refl (os oi)
oiUnique {m} (o' th) with oTooBig (refl->= m) th
oiUnique {.(suc _)} (o' th) | ()

--??--------------------------------------------------------------------------

-- Show that the identity thinning selects the whole vector

--??--1.12-(1)----------------------------------------------------------------

id-<?= : {X : Set}{n : Nat}(xs : Vec X n) -> (oi <?= xs) == xs
id-<?= [] = refl []
id-<?= (x ,- xs) rewrite id-<?= xs = refl (x ,- xs)

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Composition of Thinnings
------------------------------------------------------------------------------

-- Define the composition of thinnings and show that selecting by a
-- composite thinning is like selecting then selecting again.
-- A small bonus applies to minimizing the length of the proof.
-- To collect the bonus, you will need to think carefully about
-- how to make the composition as *lazy* as possible.

--??--1.13-(3)----------------------------------------------------------------

_o>>_ : {p n m : Nat} -> p <= n -> n <= m -> p <= m
th o>> oz = th
os th o>> os th' = os (th o>> th')
o' th o>> os th' = o' (th o>> th')
th o>> o' th' = o' (th o>> th')

cp-<?= : {p n m : Nat}(th : p <= n)(th' : n <= m) ->
         {X : Set}(xs : Vec X m) ->
         ((th o>> th') <?= xs) == (th <?= (th' <?= xs))
cp-<?= th oz [] = refl (th <?= [])
cp-<?= oz (o' th') (x ,- xs) = cp-<?= oz th' xs
cp-<?= (os th) (os th') (x ,- xs) rewrite cp-<?= th th' xs = refl (x ,- (th <?= (th' <?= xs)))
cp-<?= (os th) (o' th') (x ,- xs) = cp-<?= (os th) th' xs
cp-<?= (o' th) (os th') (x ,- xs) = cp-<?= th th' xs
cp-<?= (o' th) (o' th') (x ,- xs) = cp-<?= (o' th) th' xs

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Thinning Dominoes
------------------------------------------------------------------------------

--??--1.14-(3)----------------------------------------------------------------

idThen-o>> : {n m : Nat}(th : n <= m) -> (oi o>> th) == th
idThen-o>> oz = refl oz
idThen-o>> (os th) rewrite idThen-o>> th = refl (os th)
idThen-o>> (o' th) rewrite idThen-o>> th = refl (o' th)

idAfter-o>> : {n m : Nat}(th : n <= m) -> (th o>> oi) == th
idAfter-o>> oz = refl oz
idAfter-o>> (os th) rewrite idAfter-o>> th = refl (os th)
idAfter-o>> (o' th) rewrite idAfter-o>> th = refl (o' th)

assoc-o>> : {q p n m : Nat}(th0 : q <= p)(th1 : p <= n)(th2 : n <= m) ->
            ((th0 o>> th1) o>> th2) == (th0 o>> (th1 o>> th2))
assoc-o>> th0 th1 oz = refl (th0 o>> th1)
assoc-o>> th0 (o' th1) (os th2) rewrite assoc-o>> th0 th1 th2 = refl (o' (th0 o>> (th1 o>> th2)))
assoc-o>> (os th0) (os th1) (os th2) rewrite assoc-o>> th0 th1 th2 = refl (os (th0 o>> (th1 o>> th2)))
assoc-o>> (o' th0) (os th1) (os th2) rewrite assoc-o>> th0 th1 th2 = refl (o' (th0 o>> (th1 o>> th2)))
assoc-o>> th0 th1 (o' th2) rewrite assoc-o>> th0 th1 th2 = refl (o' (th0 o>> (th1 o>> th2)))

--??--------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Vectors as Arrays
------------------------------------------------------------------------------

-- We can use 1 <= n as the type of bounded indices into a vector and do
-- a kind of "array projection". First we select a 1-element vector from
-- the n-element vector, then we take its head to get the element out.

vProject : {n : Nat}{X : Set} -> Vec X n -> 1 <= n -> X
vProject xs i = vHead (i <?= xs)

-- Your (TRICKY) mission is to reverse the process, tabulating a function
-- from indices as a vector. Then show that these operations are inverses.

--??--1.15-(3)----------------------------------------------------------------

selectHead : (n : Nat) -> 1 <= (suc n)
selectHead n = os oe

-- HINT: composition of functions
vTabulate : {n : Nat}{X : Set} -> (1 <= n -> X) -> Vec X n
vTabulate {zero} f = []
vTabulate {suc n} f = (f (selectHead n)) ,- (vTabulate (\p -> f (o' p)))

-- This should be easy if vTabulate is correct.
vTabulateProjections : {n : Nat}{X : Set}(xs : Vec X n) ->
                       vTabulate (vProject xs) == xs
vTabulateProjections [] = refl []
vTabulateProjections (x ,- xs) rewrite vTabulateProjections xs = refl (x ,- xs)

-- HINT: oeUnique
vProjectFromTable : {n : Nat}{X : Set}(f : 1 <= n -> X)(i : 1 <= n) ->
                    vProject (vTabulate f) i == f i
vProjectFromTable {.(suc _)} {X} f (os i) with oeUnique i
vProjectFromTable {.(suc _)} {X} f (os .oe) | refl .oe = refl (f (os oe))
vProjectFromTable {.(suc _)} {X} f (o' i) = vProjectFromTable (λ z → f (o' z)) i

--??--------------------------------------------------------------------------