------------------------------------------------------------------------
-- The Agda standard library
--
-- Indexed binary relations
------------------------------------------------------------------------

-- This file contains some core definitions which are reexported by
-- Relation.Binary.Indexed.

module Relation.Binary.Indexed.Core where

open import Function
open import Level
import Relation.Binary.Core as B
import Relation.Binary.Core as P

------------------------------------------------------------------------
-- Indexed binary relations

-- Heterogeneous.

REL : ∀ {i₁ i₂ a₁ a₂} {I₁ : Set i₁} {I₂ : Set i₂} →
      (I₁ → Set a₁) → (I₂ → Set a₂) → (ℓ : Level) → Set _
REL A₁ A₂ ℓ = ∀ {i₁ i₂} → A₁ i₁ → A₂ i₂ → Set ℓ

-- Homogeneous.

Rel : ∀ {i a} {I : Set i} → (I → Set a) → (ℓ : Level) → Set _
Rel A ℓ = REL A A ℓ

------------------------------------------------------------------------
-- Simple properties of indexed binary relations

-- Reflexivity.

Reflexive : ∀ {i a ℓ} {I : Set i} (A : I → Set a) → Rel A ℓ → Set _
Reflexive _ _∼_ = ∀ {i} → B.Reflexive (_∼_ {i})

-- Symmetry.

Symmetric : ∀ {i a ℓ} {I : Set i} (A : I → Set a) → Rel A ℓ → Set _
Symmetric _ _∼_ = ∀ {i j} → B.Sym (_∼_ {i} {j}) _∼_

-- Transitivity.

Transitive : ∀ {i a ℓ} {I : Set i} (A : I → Set a) → Rel A ℓ → Set _
Transitive _ _∼_ = ∀ {i j k} → B.Trans _∼_ (_∼_ {j}) (_∼_ {i} {k})

------------------------------------------------------------------------
-- Setoids

record IsEquivalence {i a ℓ} {I : Set i} (A : I → Set a)
                     (_≈_ : Rel A ℓ) : Set (i ⊔ a ⊔ ℓ) where
  field
    refl  : Reflexive A _≈_
    sym   : Symmetric A _≈_
    trans : Transitive A _≈_

  reflexive : ∀ {i} → P._≡_ ⟨ B._⇒_ ⟩ _≈_ {i}
  reflexive P.refl = refl

record Setoid {i} (I : Set i) c ℓ : Set (suc (i ⊔ c ⊔ ℓ)) where
  infix 4 _≈_
  field
    Carrier       : I → Set c
    _≈_           : Rel Carrier ℓ
    isEquivalence : IsEquivalence Carrier _≈_

  open IsEquivalence isEquivalence public


module Data.Vec where

open import Prelude
open import Data.Nat
open import Data.Fin hiding (_==_; _<_)
open import Logic.Structure.Applicative
open import Logic.Identity
open import Logic.Base

infixl 90 _#_
infixr 50 _::_
infixl 45 _!_ _[!]_

data Vec (A : Set) : Nat -> Set where
  []   : Vec A zero
  _::_ : {n : Nat} -> A -> Vec A n -> Vec A (suc n)

-- Indexing
_!_ : {n : Nat}{A : Set} -> Vec A n -> Fin n -> A
x :: xs ! fzero  = x
x :: xs ! fsuc i = xs ! i

-- Insertion
insert : {n : Nat}{A : Set} -> Fin (suc n) -> A -> Vec A n -> Vec A (suc n)
insert fzero	y  xs	    = y :: xs
insert (fsuc i) y (x :: xs) = x :: insert i y xs

-- Index view
data IndexView {A : Set} : {n : Nat}(i : Fin n) -> Vec A n -> Set where
   ixV : {n : Nat}{i : Fin (suc n)}(x : A)(xs : Vec A n) ->
	 IndexView i (insert i x xs)

_[!]_ : {A : Set}{n : Nat}(xs : Vec A n)(i : Fin n) -> IndexView i xs
x :: xs	[!] fzero  = ixV x xs
x :: xs [!] fsuc i = aux xs i (xs [!] i)
  where
    aux : {n : Nat}(xs : Vec _ n)(i : Fin n) ->
	  IndexView i xs -> IndexView (fsuc i) (x :: xs)
    aux .(insert i y xs) i (ixV y xs) = ixV y (x :: xs)

-- Build a vector from an indexing function (inverse of _!_)
build : {n : Nat}{A : Set} -> (Fin n -> A) -> Vec A n
build {zero } f = []
build {suc _} f = f fzero :: build (f ∘ fsuc)

-- Constant vectors
vec : {n : Nat}{A : Set} -> A -> Vec A n
vec {zero } _ = []
vec {suc m} x = x :: vec x

-- Vector application
_#_ : {n : Nat}{A B : Set} -> Vec (A -> B) n -> Vec A n -> Vec B n
[]	  # []	      = []
(f :: fs) # (x :: xs) = f x :: fs # xs

-- Vectors of length n form an applicative structure
ApplicativeVec : {n : Nat} -> Applicative (\A -> Vec A n)
ApplicativeVec {n} = applicative (vec {n}) (_#_ {n})

-- Map
map : {n : Nat}{A B : Set} -> (A -> B) -> Vec A n -> Vec B n
map f xs = vec f # xs

-- Zip
zip : {n : Nat}{A B C : Set} -> (A -> B -> C) -> Vec A n -> Vec B n -> Vec C n
zip f xs ys = vec f # xs # ys

module Elem where

  infix 40 _∈_ _∉_

  data _∈_ {A : Set}(x : A) : {n : Nat}(xs : Vec A n) -> Set where
    hd : {n : Nat}	 {xs : Vec A n}		  -> x ∈ x :: xs
    tl : {n : Nat}{y : A}{xs : Vec A n} -> x ∈ xs -> x ∈ y :: xs

  data _∉_ {A : Set}(x : A) : {n : Nat}(xs : Vec A n) -> Set where
    nl	: x ∉ []
    cns : {n : Nat}{y : A}{xs : Vec A n} -> x ≢ y -> x ∉ xs -> x ∉ y :: xs

  ∉=¬∈ : {A : Set}{x : A}{n : Nat}{xs : Vec A n} -> x ∉ xs -> ¬ (x ∈ xs)
  ∉=¬∈ nl	       ()
  ∉=¬∈ {A} (cns x≠x _) hd    = elim-False (x≠x refl)
  ∉=¬∈ {A} (cns _ ne) (tl e) = ∉=¬∈ ne e

  ∈=¬∉ : {A : Set}{x : A}{n : Nat}{xs : Vec A n} -> x ∈ xs -> ¬ (x ∉ xs)
  ∈=¬∉ e ne = ∉=¬∈ ne e

  find : {A : Set}{n : Nat} -> ((x y : A) -> x ≡ y \/ x ≢ y) ->
	 (x : A)(xs : Vec A n) -> x ∈ xs \/ x ∉ xs
  find _  _ []	      = \/-IR nl
  find eq y (x :: xs) = aux x y (eq y x) (find eq y xs) where
    aux : forall x y -> y ≡ x \/ y ≢ x -> y ∈ xs \/ y ∉ xs -> y ∈ x :: xs \/ y ∉ x :: xs
    aux x .x (\/-IL refl)  _	       = \/-IL hd
    aux x y  (\/-IR y≠x)  (\/-IR y∉xs) = \/-IR (cns y≠x y∉xs)
    aux x y  (\/-IR _)	  (\/-IL y∈xs) = \/-IL (tl y∈xs)

  delete : {A : Set}{n : Nat}(x : A)(xs : Vec A (suc n)) -> x ∈ xs -> Vec A n
  delete	   .x (x :: xs)  hd    = xs
  delete {A}{zero } _  ._	(tl ())
  delete {A}{suc _} y (x :: xs) (tl p) = x :: delete y xs p


{-# OPTIONS --without-K #-}
module Functoriality {a} {A : Set a} where

open import PathOperations
open import Types

ap⁻¹ : ∀ {b} {B : Set b} {x y : A} (f : A → B) (p : x ≡ y) →
  ap f (p ⁻¹) ≡ (ap f p) ⁻¹
ap⁻¹ f = J
  (λ _ _ p → ap f (p ⁻¹) ≡ ap f p ⁻¹)
  (λ _ → refl) _ _

ap· : ∀ {b} {B : Set b} {x y z : A} (f : A → B)
  (p : x ≡ y) (q : y ≡ z) →
  ap f (p · q) ≡ ap f p · ap f q
ap· {z = z} f = J
  (λ _ y p → (q : y ≡ z) → ap f (p · q) ≡ ap f p · ap f q)
  (λ _ _ → refl) _ _

ap-id : {x y : A} (p : x ≡ y) → ap id p ≡ p
ap-id = J (λ _ _ p → ap id p ≡ p) (λ _ → refl) _ _

ap-∘ : ∀ {b c} {B : Set b} {C : Set c} {x y : A}
  (f : B → C) (g : A → B) (p : x ≡ y) →
  ap (f ∘ g) p ≡ ap f (ap g p)
ap-∘ f g = J
  (λ _ _ p → ap (f ∘ g) p ≡ ap f (ap g p))
  (λ _ → refl) _ _


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

module Ch1 where

-- open import lib.Base
open import Base

-- warmup
module Eq-1-11-2 {i j} {A : Type i} {B : Type j} where

  -- import lib.types.Coproduct
  -- open import lib.types.Empty
  -- open import lib.types.Sigma

  -- If not A and not B, then not (A or B)
  deMorgan1 : (A → ⊥) × (B → ⊥) → (Coprod A B → ⊥)
  -- deMorgan1 (notA , notB) ab = {!!} -- {!!} becomes =>
  -- deMorgan1 (notA , notB) (inl x) = {!!} -- by cases, C-c C-c on param ab
  -- deMorgan1 (notA , notB) (inr x) = {!!}
  deMorgan1 (notA , notB) (inl a) = notA a -- the 'a part' of (A + B) → ⊥
  deMorgan1 (notA , notB) (inr b) = notB b

module Ex1-1 where

  {-
  Exercise 1.1. Given functions f : A -> B and g : B -> C, define their composite g o f : A ->  C.
  Show that we have h o (g o f ) == (h o g) o f
  -}

  _o_ : ∀ {i} {A B C : Type i} → (B → C) → (A → B) → A → C
  (g o f) x = g (f x)

  o-assoc : ∀ {i} {A B C D : Type i} →
    (h : C → D) → (g : B → C) → (f : A → B) →
    h o (g o f) == (h o g) o f
  o-assoc _ _ _ = idp -- "it's de proof"

module Ex1-2 where

  -- open import lib.types.Sigma

  -- Derive the recursion principle for products recA×B using only the projections, and
  -- verify that the definitional equalities are valid. Do the same for Σ-types.

  recAxB : ∀ {i} {A B C : Type i} →
    (A → B → C) → A × B → C
  recAxB f ab = f (fst ab) (snd ab)

  recAxB= : ∀ {i} {A B C : Type i}
    {f : A → B → C}
    {a : A} {b : B} →
    recAxB f (a , b) == f a b
  recAxB= = idp

  recA+B : ∀ {i j} {A : Type i} {B : Type j} {C : Type (lmax i j)} →
    (A → C) → (B → C) → (Coprod A B → C)
  recA+B f g (inl x) = f x
  recA+B f g (inr x) = g x

  recA+B= : {A B C : Type0}
    {f : A → C} {g : B → C}
    {a : A} {b : B} →
    (recA+B f g (inl a) == f a) × (recA+B f g (inr b) == g b)
  recA+B= = idp , idp

  recΣ : ∀ {i j} {A : Type i} {B : A → Type j} {C : Type (lmax i j)} →
    ((a : A) → B a → C) →
    Σ A B → C
  recΣ f ab = f (fst ab) (snd ab)

  recΣ= : ∀ {i j} {A : Type i} {B : A → Type j} {C : Type (lmax i j)}
    {f : (a : A) → B a → C}
    {a : A} {ba : B a} →
    recΣ f (a , ba) == f a ba
  recΣ= = idp

module Ex1-3 {i j} {A : Type i} {B : Type j} where

  -- open import lib.types.Sigma

  {- Derive the induction principle for products indA×B, using only the projections
  and the propositional uniqueness principle uppt. Verify that the definitional
  equalities are valid. Gen- eralize uppt to Σ-types, and do the same for Σ-types.
  -}

  -- book definition of uppt
  uppt-× : (ab : (A × B)) → (fst ab , snd ab) == ab
  uppt-× = λ _ → idp

  -- definition of induction principle the projections and the transported uniqueness principle
  -- (this was a confusing question to work out in Agda, since the transport is redundant / inferred by the typechecker)
  ind-× : (C :  (A × B) → Type (lmax i j)) →
    ((a : A ) → (b : B) → C (a , b)) → Π (A × B) C
  ind-× C g ab = transport C (uppt-× ab) (g (fst ab) (snd ab))

  -- (propositional) verification of defining equations
  ind-×= : (C :  (A × B) → Type (lmax i j)) →
    (g : ((a : A ) → (b : B) → C (a , b))) →
    ∀ {a b} → ind-× C g (a , b) == g a b
  ind-×= C g {a} {b} = -- idp
    ind-× C g (a , b) =⟨ idp ⟩
    transport C (uppt-× (a , b)) (g a b) =⟨ idp ⟩
    transport C idp (g a b) =⟨ idp ⟩
    g a b ∎

  -- book definition of induction principle using pattern matching
  pattern-match-ind-× : (C :  (A × B) → Type (lmax i j)) →
    Π A (λ x → Π B (λ y → C (x , y))) → Π (A × B) C
  pattern-match-ind-× _ g (a , b) = g a b

  -- alternative definition of uniqueness principle (from induction principle)
  ind-uppt-× : (x : (A × B)) → (fst x , snd x) == x
  ind-uppt-× = ind-× (λ z → z == z) (λ x x₁ → idp)

  -- validate definition for uppt
  uppt-×= : {x : (A × B)} → (uppt-× x) == (ind-uppt-× x)
  uppt-×= = idp

  -- uppt for Σ types
  upptΣ : {B : (a : A) → Type j} → (ab : Σ A B) → ab == (fst ab , snd ab)
  upptΣ _ = idp

module Ex1-4 where

  -- open import lib.types.Sigma

  {-
  Assuming as given only the iterator for natural numbers
  iter : ∏ C → (C → C) → N → C
       C:U
  with the defining equations
  iter(C, c0, cs, succ(n)) :≡ cs(iter(C, c0, cs, n)),
  derive a function having the type of the recursor recN. Show that the defining
  equations of the recursor hold propositionally for this function, using the
  induction principle for N.
  -}

  iter : {C : Type0} →
    C → (C → C) → ℕ → C
  iter c0 cs 0 = c0
  iter c0 cs (S n) = cs (iter c0 cs n)

  -- used in the definition of recNiter below to keep a count of the applications of a function
  sucApply : {C : Type0} → (ℕ → C → C) → ℕ × C → ℕ × C
  sucApply {C} cs = cs' where
    cs' : ℕ × C → ℕ × C
    cs' (n , c) = S n , cs n c

  {- Induction principle (dependent eliminator) for ℕ from the book:

  indN : ∏(C:N→U)C(0) → ∏(n:N)C(n) → C(succ(n)) → ∏(n:N)C(n)
  indN(C, c0, cs, 0) :≡ c0,
  indN(C, c0, cs, succ(n)) :≡ cs(n, indN(C, c0, cs, n)).
  -}
  indN : ∀ {i} {C : ℕ → Type i} →
    C 0 → ((n : ℕ) → C n → C (S n)) → (n : ℕ) → C n
  indN c0 cs 0 = c0
  indN c0 cs (S n) = cs n (indN c0 cs n)

  -- digression / warmup - inductive proof that evens-are-even

  -- nth even
  even : ℕ → ℕ
  even n = iter 0 (λ n → S (S n)) n

  S= : {m n : ℕ} → m == n → (S m == S n)
  S= idp = idp

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

  evens-are-even : (n : ℕ) → Σ ℕ (λ m → even n == m *2)
  evens-are-even = indN (0 , idp) inductiveCase where
    inductiveCase : (n : ℕ) → Σ ℕ (λ m → even n == m *2) → Σ ℕ (λ m → even (S n) == m *2)
    inductiveCase n (m , p) = S m , p'' where
      p' = S= p
      p'' = S= p'

  {- derive a function having the type of the recursor recN
  recN : ∏(C:U)C→(N→C→C)→N→C
  recN(C, c0, cs, 0) :≡ c0,
  recN(C, c0, cs, Sc(n)) :≡ cs(n, recN(C, c0, cs, n)).
  -}
  recNiter : {C : Type0} →
    C → (ℕ → C → C) → ℕ → C
  recNiter c0 cs n = snd (iter (0 , c0) cs' n) where
           cs' = sucApply cs

  {- Show that the defining
  equations of the recursor hold propositionally for this function, using the
  induction principle for N
  -}
  recNiter= : {C : Type0} {c0 : C} {cs : ℕ → C → C } →
    (n : ℕ) →
    (recNiter c0 cs (S n)) == (cs n (recNiter c0 cs n))
  recNiter= {C} {c0} {cs} n = cs= n'= -- or, as spelled out below (using equational reasoning from HoTT-Agda)
    -- recNiter c0 cs (S n) =⟨ idp ⟩
    -- snd (iter' (S n)) =⟨ idp ⟩
    -- snd (cs' (iter' n)) =⟨ idp ⟩
    -- snd (cs' (n' , snd (iter' n))) =⟨ idp ⟩
    -- cs n' (snd (iter' n)) =⟨ cs= n'= ⟩
    -- cs n (snd (iter' n)) =⟨ idp ⟩
    -- cs n (recNiter c0 cs n) ∎
    where
      cs' = sucApply cs

      iter' : ℕ → ℕ × C
      iter' = iter (0 , c0) cs'

      n' = fst (iter' n)

      -- proof that the first (counter) projection of the iter version of recN at n is just n
      fstIter'= : (n : ℕ) → (fst (iter (0 , c0) cs' n) == n)
      fstIter'= = indN idp inductiveCase where
        inductiveCase : (n : ℕ) →
          fst (iter' n) == n →
          fst (iter' (S n)) == S n
        inductiveCase m p = S= p -- or, as spelled out below
          -- fst (iter (0 , c0) cs' (S m)) =⟨ idp ⟩
          -- fst (cs' (iter (0 , c0) cs' m)) =⟨ idp ⟩
          -- fst (cs' (iter (0 , c0) cs' m)) =⟨ idp ⟩
          -- -- fst (cs' (fst (iter (0 , c0) cs' m) , _)) =⟨ idp ⟩ -- Agda loses track of some hidden
          -- -- fst (S (fst (iter (0 , c0) cs' m)) , _) =⟨ idp ⟩ -- type data here, thus double-protective comments
          -- S (fst (iter (0 , c0) cs' m)) =⟨ S= p ⟩
          -- S m ∎

      n'= : n' == n
      n'= = fstIter'= n

      cs= : ∀ {n m} {c : C} → n == m → cs n c == cs m c
      cs= idp = idp

-- recursor for Bool (used in next two exercises)
module RecBool where
  recBool : ∀ {i} {A : Type i} → A → A → Bool → A
  recBool x y true = x
  recBool x y false = y

module Ex1-5 where
  open RecBool
  {-
  Exercise 1.5. Show that if we define A + B :≡ ∑(x:2) rec2(U, A, B, x), then we can give a definition
  of indA+B for which the definitional equalities stated in §1.7 hold.
  -}
  _+_ : ∀ {i} → Type i → Type i → Type _
  A + B = Σ Bool (λ isA → recBool A B isA)

  inl-+ : ∀ {i} {A B : Type i} → A → A + B
  inl-+ a = true , a

  inr-+ : ∀ {i} {A B : Type i} → B → A + B
  inr-+ b = false , b

  ind-+ : ∀ {i j} {A B : Type i} (C : A + B → Type j) →
    ((a : A) → C (inl-+ a)) → ((b : B) → C (inr-+ b)) →
    (x : A + B) → C x
  ind-+ C g1 g2 (true , a) = g1 a
  ind-+ C g1 g2 (false , b) = g2 b

module Ex1-6 {i} {A B : Type i} where
  open RecBool
  open import FunExt
  {-
  Exercise 1.6. Show that if we define A × B :≡ ∏(x:2) rec2(U, A, B, x), then we can give a defini- tion of indA×B for which the definitional equalities stated in §1.5 hold propositionally (i.e. using equality types). (This requires the function extensionality axiom, which is introduced in §2.9.)
  -}

  _x_ : ∀ {i} → (A : Type i) → (B : Type i) → Type _
  A x B = Π Bool (λ ab → recBool A B ab)

  _,,_ : A → B → A x B
  _,,_ a b true = a
  _,,_ a b false = b

  fst-x : A x B → A
  fst-x ab = ab true

  snd-x : A x B → B
  snd-x ab = ab false

  uppt-x : (ab : (A x B)) → (fst-x ab ,, snd-x ab) == ab
  uppt-x ab = is-equiv.g fe h where -- (fst-x ab ,, snd-x ab) x == ab x
    fe = fun-ext ((fst-x ab ,, snd-x ab)) ab
    h : (c : Bool) → (fst-x ab ,, snd-x ab) c == ab c
    h true = idp
    h false = idp

  ind-x : ∀ {j} (C : A x B → Type j) →
    ((a : A) → (b : B) → C (a ,, b)) →
    (ab : A x B) → C ab
  ind-x C g ab = transport C (uppt-x ab) (g a b) where
    a = fst-x ab
    b = snd-x ab

module Ex1-7 where
  {-
  Exercise 1.7. Give an alternative derivation of ind′=A from ind=A which avoids the use of universes. (This is easiest
  using concepts from later chapters.)
  -}

  {-
  Section 1.12.2 "we can show that ind=A entails Lemmas2.3.1 and 3.11.8, and that these two principles imply ind′=A
  directly"
  -}

  {-
  Lemma 2.3.1 (Transport). Suppose that P is a type family over A and that p : x =A y. Then there is a function
    p∗ : P(x) → P(y).
  -}

  {-
  Lemma 3.11.8. For any A and any a : A, the type ∑(x:A)(a = x) is contractible.
  -}
  lemma-3-11-8 : ∀ {i} {A : Type i} (a : A) → is-contr (Σ A λ x → a == x)
  lemma-3-11-8 {_} {A} a = ((a , idp)) , λ { (x , p) → along {x} p } where
    along : {x : A}(p : a == x) → (a , idp) == (x , p)
    along idp = idp -- should really prove using ind== then ind='2 will indeed be based path induction from free
                    -- path induction

  ind='2 : ∀ {i j} {A : Type i} {a : A} →
    (D : (x : A) (p : a == x) → Type j) →
    D a idp →
    {x : A} (p : a == x) →
    D x p
  ind='2 {i} {j} {A} {a} D d {x} p = transport (λ { (x , p) → D x p}) (snd (lemma-3-11-8 a) (x , p)) d

module Ex1-14 where

  {- Exercise 1.14. Why do the induction principles for identity types not allow us to construct a
  function f : ∏(x:A) ∏(p:x=x)(p = reflx) with the defining equation f (x, reflx ) :≡ reflreflx ? -}

module Ex1-15 {i j} {A : Type i} {C : A → Type j} where
  {- Exercise 1.15. Show that indiscernability of identicals follows from path induction. -}

  f : ∀ {x y : A} (p : x == y) → C x → C y
  f idp = ind== (λ x y _ → C x → C y) (λ x → λ cx → cx) idp


module Example1 where

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

{-# BUILTIN NATURAL Nat #-}

data _==_ {l} {a : Set l} : a -> a -> Set l where
  refl : ∀{x} -> x == x

{-# BUILTIN EQUALITY _==_ #-}

cong : ∀{l} -> {A B : Set l} {x y : A} -> (f : A -> B) -> x == y -> f x == f y
cong f refl = refl

sym : ∀{l} {A : Set l} {x y : A} -> x == y -> y == x
sym refl = refl

_+_ : Nat -> Nat -> Nat
x + zero = x
x + suc y = suc (x + y)

_+₂_ : Nat -> Nat -> Nat
zero +₂ y = y
suc x +₂ y = {!!}

plus-right-identity : ∀{n} -> (n + 0) == n
plus-right-identity {n} = refl

plus-left-identity : (n : Nat) -> (0 + n) == n
plus-left-identity zero = refl
plus-left-identity (suc n) = cong suc (plus-left-identity n)

plus-comm : (m n : Nat) -> (m + n) == (n + m)
plus-comm m zero = sym (plus-left-identity m)
plus-comm m (suc n) = {!!}

plus-succ : ∀{m n} -> (m + suc n) == suc (m + n)
plus-succ = refl

data Vec (A : Set) : Nat -> Set where
  []  : Vec A 0
  _∷_ : ∀{n} -> A -> Vec A n -> Vec A (suc n)

replicate : ∀{A} -> (n : Nat) -> A -> Vec A n
replicate zero x = []
replicate (suc n) x = x ∷ replicate n x

data Pair (A B : Set) : Set where
  _,_ : A -> B -> Pair A B

zip : ∀{n A B} -> Vec A n -> Vec B n -> Vec (Pair A B) n
zip [] ys = []
zip {suc n} (x ∷ xs) (y ∷ ys) = replicate (suc n) (x , y)

map : ∀{n A B} -> (A -> B) -> Vec A n -> Vec B n
map f [] = []
map {suc n} f (x ∷ xs) = (f x) ∷ (map f xs) -- f x ∷ map f xs -- replicate (suc n) (f x)

id : {A : Set} -> A -> A
id x = x

map-id : ∀{n A} (xs : Vec A n) -> map id xs == xs
map-id [] = refl
map-id (x ∷ xs) = cong (_∷_ x) (map-id xs)

data Bool : Set where
  false true : Bool

not : Bool -> Bool
not false = true
not true = false

not-self-inverse : {x : Bool} -> not (not x) == x
not-self-inverse {false} = refl
not-self-inverse {true} = refl

data Empty : Set where

not-not-id : {x : Bool} -> not x == id x -> Empty
not-not-id {false} ()
not-not-id {true} ()


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

open import Level
open import Function renaming (id to id→; _∘_ to _●_) using ()

open import Categories.Category
open import Categories.Functor.Core public

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

Endofunctor : Category o ℓ e → Set _
Endofunctor C = Functor C C

id : ∀ {C : Category o ℓ e} → Functor C C
id {C = C} = record
  { F₀           = id→
  ; F₁           = id→
  ; identity     = Category.Equiv.refl C
  ; homomorphism = Category.Equiv.refl C
  ; F-resp-≈     = id→
  }

infixr 9 _∘F_

-- note that this definition could be shortened by inlining the definitions for
-- identity′ and homomorphism′, but the definitions below are simpler to understand.
_∘F_ : ∀ {C : Category o ℓ e} {D : Category o′ ℓ′ e′} {E : Category o″ ℓ″ e″}
    → Functor D E → Functor C D → Functor C E
_∘F_ {C = C} {D = D} {E = E} F G = record
  { F₀ = F.₀ ● G.₀
  ; F₁ = F.₁ ● G.₁
  ; identity = identity′
  ; homomorphism = homomorphism′
  ; F-resp-≈ =  F.F-resp-≈ ● G.F-resp-≈
  }
  where
  module C = Category C using (id)
  module D = Category D using (id)
  module E = Category E using (id; module HomReasoning)
  module F = Functor F
  module G = Functor G

  identity′ : ∀ {A} → E [ F.₁ (G.₁ (C.id {A})) ≈ E.id ]
  identity′ = begin
    F.₁ (G.₁ C.id) ≈⟨ F.F-resp-≈ G.identity ⟩
    F.₁ D.id       ≈⟨ F.identity ⟩
    E.id           ∎
    where open E.HomReasoning

  homomorphism′ : ∀ {X Y Z} {f : C [ X , Y ]} {g : C [ Y , Z ]}
                 → E [ F.₁ (G.₁ (C [ g ∘ f ])) ≈ E [ F.₁ (G.₁ g) ∘ F.₁ (G.₁ f) ] ]
  homomorphism′ {f = f} {g = g} = begin
    F.₁ (G.₁ (C [ g ∘ f ]))         ≈⟨ F.F-resp-≈ G.homomorphism ⟩
    F.₁ (D [ G.₁ g ∘ G.₁ f ])       ≈⟨ F.homomorphism ⟩
    E [ F.₁ (G.₁ g) ∘ F.₁ (G.₁ f) ] ∎
    where open E.HomReasoning


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

module Rewrite-with-doubly-indexed-equality where

open import Common.Level

infix 4 _≡_

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

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

sym : {A : Set}{x y : A} → x ≡ y → y ≡ x
sym refl = refl

data Nat : Set where
  zero : Nat
  suc  : Nat → Nat

infixl 6 _+_

_+_ : Nat → Nat → Nat
zero  + m = m
suc n + m = suc (n + m)

plus-zero : ∀ n → n + zero ≡ n
plus-zero zero    = refl
plus-zero (suc n) rewrite plus-zero n = refl

plus-suc : ∀ n m → n + suc m ≡ suc (n + m)
plus-suc zero    m = refl
plus-suc (suc n) m rewrite plus-suc n m = refl

-- Proving things about functions using rewrite
data IsRefl {A : Set}{x : A} : ∀ {y} → x ≡ y → Set where
  isRefl : IsRefl refl

plus-suc-isrefl : ∀ {n m} → IsRefl (plus-suc n m)
plus-suc-isrefl {zero } {m} = isRefl
plus-suc-isrefl {suc n} {m} rewrite plus-suc n m = isRefl

-- Multiple rewrites
com : ∀ n m → n + m ≡ m + n
com n zero    = plus-zero _
com n (suc m) rewrite plus-suc n m
                    | com n m
              = refl

-- rewrite followed by with
thm : ∀ a b c → a + (b + c) ≡ (c + b) + a
thm a b c rewrite com b c with c + b
... | cb = com a cb

data List A : Set where
  [] : List A
  _∷_ : (x : A)(xs : List A) → List A

infixr 30 _∷_ _++_

_++_ : ∀ {A} → List A → List A → List A
[]       ++ ys = ys
(x ∷ xs) ++ ys = x ∷ (xs ++ ys)

-- rewrite in parameterised module
module ListProps {A : Set} where

  append-nil : (xs : List A) → xs ++ [] ≡ xs
  append-nil []       = refl
  append-nil (x ∷ xs) rewrite append-nil xs = refl

  append-assoc : (as bs cs : List A) → (as ++ bs) ++ cs ≡ as ++ (bs ++ cs)
  append-assoc []       bs cs = refl
  append-assoc (a ∷ as) bs cs rewrite append-assoc as bs cs = refl

  -- With implicit arguments
  append-assoc′ : ∀ (as : List A) {bs cs} → (as ++ bs) ++ cs ≡ as ++ (bs ++ cs)
  append-assoc′ []       = refl
  append-assoc′ (a ∷ as) {bs}{cs} rewrite append-assoc′ as {bs} {cs} = refl

  reverse : List A → List A
  reverse []       = []
  reverse (x ∷ xs) = reverse xs ++ x ∷ []

  reverse-append : ∀ as bs → reverse (as ++ bs) ≡ reverse bs ++ reverse as
  reverse-append []       bs rewrite append-nil (reverse bs) = refl
  reverse-append (a ∷ as) bs rewrite reverse-append as bs
                          = append-assoc (reverse bs) _ _

  reverse-reverse : ∀ as → reverse (reverse as) ≡ as
  reverse-reverse []       = refl
  reverse-reverse (a ∷ as) rewrite reverse-append (reverse as) (a ∷ [])
                                 | reverse-reverse as
                           = refl

open ListProps

map : ∀ {A B} → (A → B) → List A → List B
map f []       = []
map f (x ∷ xs) = f x ∷ map f xs

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

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

map-id : ∀ {A} (xs : List A) → map id xs ≡ xs
map-id []       = refl
map-id (x ∷ xs) rewrite map-id xs = refl

map-compose : ∀ {A B C} (f : B → C)(g : A → B)(xs : List A) →
              map (f ∘ g) xs ≡ (map f ∘ map g) xs
map-compose f g []       = refl
map-compose f g (x ∷ xs) rewrite map-compose f g xs = refl

map-append : ∀ {A B} (f : A → B) (xs ys : List A) →
             map f (xs ++ ys) ≡ map f xs ++ map f ys
map-append f []       ys = refl
map-append f (x ∷ xs) ys rewrite map-append f xs ys = refl

map-reverse : ∀ {A B} (f : A → B) (xs : List A) →
              map f (reverse xs) ≡ reverse (map f xs)
map-reverse f []       = refl
map-reverse f (x ∷ xs) rewrite map-append f (reverse xs) (x ∷ [])
                             | map-reverse f xs
                       = refl

foldr : {A B : Set} → (A → B → B) → B → List A → B
foldr f z []       = z
foldr f z (x ∷ xs) = f x (foldr f z xs)

foldl : {A B : Set} → (B → A → B) → B → List A → B
foldl f z []       = z
foldl f z (x ∷ xs) = foldl f (f z x) xs

module FoldAssoc
  {A : Set}(_∙_ : A → A → A)
  (assoc : ∀ x y z → ((x ∙ y) ∙ z) ≡ (x ∙ (y ∙ z))) where

  smashr = foldr _∙_
  smashl = foldl _∙_

  foldr-append : ∀ ∅ z xs ys → (∀ x → (∅ ∙ x) ≡ x) →
                 smashr z (xs ++ ys) ≡ (smashr ∅ xs ∙ smashr z ys)
  foldr-append ∅ z []       ys idl = sym (idl _)
  foldr-append ∅ z (x ∷ xs) ys idl rewrite assoc x (smashr ∅ xs) (smashr z ys)
                                         | foldr-append ∅ z xs ys idl
                                   = refl

  foldl-plus : ∀ z₁ z₂ xs → smashl (z₁ ∙ z₂) xs ≡ (z₁ ∙ smashl z₂ xs)
  foldl-plus z₁ z₂ []       = refl
  foldl-plus z₁ z₂ (x ∷ xs) rewrite assoc z₁ z₂ x
                            = foldl-plus _ _ xs

  foldr=foldl : ∀ ∅ → (∀ x → (∅ ∙ x) ≡ (x ∙ ∅)) →
                ∀ xs → foldr _∙_ ∅ xs ≡ foldl _∙_ ∅ xs
  foldr=foldl ∅ id []       = refl
  foldr=foldl ∅ id (x ∷ xs) rewrite id x
                                  | foldl-plus x ∅ xs
                                  | foldr=foldl ∅ id xs
                            = refl

foldr-compose : ∀ {A B C : Set} (f : B → C → C) (z : C) (g : A → B) (xs : List A) →
                foldr (f ∘ g) z xs ≡ foldr f z (map g xs)
foldr-compose f z g []       = refl
foldr-compose f z g (x ∷ xs) rewrite foldr-compose f z g xs = refl

foldr-fusion : ∀ {A B C : Set} (f : B → C) (_⊕_ : A → B → B) (_⊗_ : A → C → C) (z : B) →
               (∀ x y → f (x ⊕ y) ≡ (x ⊗ f y)) →
               ∀ xs → f (foldr _⊕_ z xs) ≡ foldr _⊗_ (f z) xs
foldr-fusion f _⊕_ _⊗_ z distr []       = refl
foldr-fusion f _⊕_ _⊗_ z distr (x ∷ xs)
  rewrite sym (foldr-fusion f _⊕_ _⊗_ z distr xs)
  with    foldr _⊕_ z xs
...  |    y = distr x y


open import Data.Boolean
open import Type

module Data.BinaryTree.Heap {ℓ} {T : Type{ℓ}} (_≤?_ : T → T → Bool) where

import      Lvl
open import Data hiding (empty)
open import Data.BinaryTree

BinaryHeap = BinaryTree (Unit{Lvl.𝟎}) (T)

private variable ℓᵣ : Lvl.Level
private variable R : Type{ℓᵣ}

open import Data.Option
open import Data.Tuple as Tuple using (_⨯_ ; _,_)
open import Functional as Fn using (_∘_ ; _∘₂_ ; _$_)

merge : BinaryHeap → BinaryHeap → BinaryHeap
merge (Leaf <>)         (Leaf <>)      = Leaf <>
merge x@(Node _ _ _)    (Leaf <>)      = x
merge (Leaf <>)         y@(Node _ _ _) = y
merge x@(Node xa xl xr) y@(Node ya yl yr) with (xa ≤? ya) | (\_ → merge y xr) | (\_ → merge x yr)
... | 𝑇 | 𝑇-branch | _ = Node xa (𝑇-branch(<>{Lvl.𝟎})) xl
... | 𝐹 | _ | 𝐹-branch = Node ya (𝐹-branch(<>{Lvl.𝟎})) yl

insert : T → BinaryHeap → BinaryHeap
insert a = merge(singleton a)

pop : BinaryHeap → Option(T ⨯ BinaryHeap)
pop (Leaf <>)    = None
pop (Node a l r) = Some(a , merge l r)

{-# TERMINATING #-}
foldOrdered : (T → R → R) → R → BinaryHeap → R
foldOrdered(_▫_) id (Leaf <>)    = id
foldOrdered(_▫_) id (Node a l r) = a ▫ foldOrdered(_▫_) id (merge l r)

filterFold : (T → R → Option(R)) → R → BinaryHeap → (R ⨯ BinaryHeap)
filterFold (_▫_) r₀ (Leaf <>)    = (r₀ , Leaf <>)
filterFold (_▫_) r₀ (Node a l r)
  with (r₁ , il) ← filterFold (_▫_) r₀ l
  with (r₂ , ir) ← filterFold (_▫_) r₁ r
  with (a ▫ r₂)
... | Some r₃ = (r₃ , merge il ir)
... | None    = (r₂ , Node a il ir)

filter : (T → Bool) → BinaryHeap → BinaryHeap
filter f = Tuple.right ∘ filterFold (\{a <> → (\{𝐹 → None ; 𝑇 → Some <>}) $ f(a)}) (<>{Lvl.𝟎})

open import Data.List
import      Data.List.Functions as List

separate : (T → Bool) → BinaryHeap → (List(T) ⨯ BinaryHeap)
separate f = filterFold (\a l → (\{𝐹 → None ; 𝑇 → Some(a ⊰ l)}) $ f(a)) ∅

update : (T → Bool) → (T → T) → BinaryHeap → BinaryHeap
update p f a with (as , b) ← separate p a = List.foldᵣ insert b (List.map f as)


------------------------------------------------------------------------------
-- Propositional equality without using pattern matching on refl
------------------------------------------------------------------------------

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

module
  FOT.Common.FOL.Relation.Binary.PropositionalEquality.NoPatternMatchingOnRefl
  where

open import Common.FOL.FOL using ( D )

-- We add 3 to the fixities of the Agda standard library 0.8.1 (see
-- Relation/Binary/Core.agda).
infix 7 _≡_

------------------------------------------------------------------------------
-- The identity type on the universe of discourse.
data _≡_ (x : D) : D → Set where
  refl : x ≡ x

-- Elimination rule.
subst : (A : D → Set) → ∀ {x y} → x ≡ y → A x → A y
subst A refl Ax = Ax

-- Identity properties

sym : ∀ {x y} → x ≡ y → y ≡ x
sym {x} h = subst (λ y' → y' ≡ x) h refl

trans : ∀ {x y z} → x ≡ y → y ≡ z → x ≡ z
trans {x} h₁ h₂ = subst (_≡_ x) h₂ h₁

trans₂ : ∀ {w x y z} → w ≡ x → x ≡ y → y ≡ z → w ≡ z
trans₂ h₁ h₂ h₃ = trans (trans h₁ h₂) h₃

subst₂ : (A : D → D → Set) → ∀ {x₁ x₂ y₁ y₂} →
         x₁ ≡ y₁ → x₂ ≡ y₂ →
         A x₁ x₂ →
         A y₁ y₂
subst₂ A {x₁} {x₂} {y₁} {y₂} h₁ h₂ h₃ =
  subst (λ y₁' → A y₁' y₂) h₁ (subst (A x₁) h₂ h₃)

cong : (f : D → D) → ∀ {x y} → x ≡ y → f x ≡ f y
cong f {x} h = subst (λ x' → f x ≡ f x') h refl

cong₂ : (f : D → D → D) → ∀ {x₁ x₂ y₁ y₂} → x₁ ≡ y₁ → x₂ ≡ y₂ →
        f x₁ x₂ ≡ f y₁ y₂
cong₂ f {x₁} {x₂} {y₁} {y₂} h₁ h₂ =
  subst (λ x₁' → f x₁ x₂ ≡ f x₁' y₂)
        h₁
        (subst (λ x₂' → f x₁ x₂ ≡ f x₁ x₂') h₂ refl)


module List where

  import Prelude
  import Equiv
  import Datoid
  import Nat

  open Prelude
  open Equiv
  open Datoid
  open Nat

  data List (a : Set) : Set where
    nil  : List a
    _::_ : a -> List a -> List a

  map : {a b : Set} -> (a -> b) -> List a -> List b
  map f nil       = nil
  map f (x :: xs) = f x :: map f xs

  member : (a : Datoid) -> El a -> List (El a) -> Set
  member _ x nil       = Absurd
  member a x (y :: ys) = Either (rel' (datoidEq a) x y) (member a x ys)

  memberPreservesEq
    :  {a : Datoid}
    -> {x y : El a}
    -> datoidRel a x y
    -> (zs : List (El a))
    -> member a x zs
    -> member a y zs
  memberPreservesEq     xy nil       abs         = abs
  memberPreservesEq {a} xy (z :: zs) (left xz)   =
    left  (dTrans a (dSym a xy) xz)
  memberPreservesEq {a} xy (z :: zs) (right xzs) =
    right (memberPreservesEq {a} xy zs xzs)

  private
    noCopies' : (a : Datoid) -> (x y : El a) -> Dec (datoidRel a x y)
              -> Nat -> Nat
    noCopies' _ _ _ (left _)  n = suc n
    noCopies' _ _ _ (right _) n = n

  noCopies : (a : Datoid) -> El a -> List (El a) -> Nat
  noCopies a x nil       = zero
  noCopies a x (y :: ys) =
    noCopies' a x y (datoidDecRel a x y) (noCopies a x ys)

  NoDuplicates : (a : Datoid) -> List (El a) -> Set
  NoDuplicates _ nil      = Unit
  NoDuplicates a (x :: b) = Pair (Not (member a x b)) (NoDuplicates a b)

  private
    delete'
      : (a : Datoid)
      -> (x y : El a)
      -> Dec (datoidRel a x y)
      -> (ys delXYs : List (El a))
      -> List (El a)
    delete' _ _ _ (left _)  ys _      = ys
    delete' _ _ _ (right _) _  delXYs = delXYs

  -- Removes first occurrence if any.
  delete : (a : Datoid) -> El a -> List (El a) -> List (El a)
  delete a x nil       = nil
  delete a x (y :: ys) = delete' a x y (datoidDecRel a x y) ys (delete a x ys)

  private
    Perm : (a : Datoid) -> (xs ys : List (El a)) -> Set
    Perm a xs ys = forall z -> datoidRel natDatoid (noCopies a z xs)
                                                   (noCopies a z ys)

    refl' : {a : Datoid} -> (xs : List (El a)) -> Perm a xs xs
    refl' {a} xs = \z -> dRefl natDatoid {noCopies a z xs}

    sym' :  {a : Datoid} -> (xs ys : List (El a))
         -> Perm a xs ys -> Perm a ys xs
    sym' {a} xs ys xy =
      \z -> dSym natDatoid {noCopies a z xs} {noCopies a z ys} (xy z)

    trans' : {a : Datoid} -> (xs ys zs : List (El a))
           -> Perm a xs ys -> Perm a ys zs -> Perm a xs zs
    trans' {a} xs ys zs xy yz =
      \z -> dTrans natDatoid
                   {noCopies a z xs} {noCopies a z ys} {noCopies a z zs}
                   (xy z) (yz z)

    postulate
      dec' : {a : Datoid} -> (xs ys : List (El a))
             -> Either (Perm a xs ys) (Not (Perm a xs ys))

  Permutation : (a : Datoid) -> DecidableEquiv (List (El a))
  Permutation a = decEquiv (equiv (Perm a) (refl' {a}) (sym' {a}) (trans' {a})) (dec (dec' {a}))


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

open import LogicalFormulae
open import Setoids.Setoids
open import Groups.Definition
open import Groups.Homomorphisms.Definition
open import Groups.SymmetricGroups.Definition
open import Groups.Actions.Definition
open import Sets.EquivalenceRelations

module Groups.ActionIsSymmetry {a b c d : _} {A : Set a} {B : Set b} {S : Setoid {a} {c} A} {T : Setoid {b} {d} B} {_+_ : A → A → A} {G : Group S _+_} (gAction : GroupAction G T) where

open Group G
open GroupAction gAction

actionPermutation : (g : A) → SymmetryGroupElements T
actionPermutation g = sym {f = λ x → action g x} (record { inj = record { injective = inj ; wellDefined = actionWellDefined2 } ; surj = record { surjective = surj ; wellDefined = actionWellDefined2 } })
  where
    open Setoid T
    open Equivalence eq
    inj : {x y : B} → (Setoid._∼_ T (action g x) (action g y)) → Setoid._∼_ T x y
    inj {x} {y} gx=gy = transitive (symmetric identityAction) (transitive (transitive (symmetric (actionWellDefined1 (invLeft {g}))) (transitive (transitive associativeAction (transitive (actionWellDefined2 gx=gy) (symmetric associativeAction))) (actionWellDefined1 (invLeft {g})))) identityAction)
    surj : {x : B} → Sg B (λ a → action g a ∼ x)
    surj {x} = action (inverse g) x , transitive (symmetric associativeAction) (transitive (actionWellDefined1 invRight) identityAction)

actionPermutationMapIsHom : GroupHom G (symmetricGroup T) actionPermutation
GroupHom.groupHom actionPermutationMapIsHom = associativeAction
GroupHom.wellDefined actionPermutationMapIsHom x=y = actionWellDefined1 x=y


module Lec1Done where

-- the -- mark introduces a "comment to end of line"

------------------------------------------------------------------------------
-- some basic "logical" types
------------------------------------------------------------------------------

data Zero : Set where
  -- to give a value in a data, choose one constructor
  -- there are no constructors
  -- so that's impossible

record One : Set where
  -- to give a value in a record type, fill all its fields
  -- there are no fields
  -- so that's trivial
  --   (can we have a constructor, for convenience?)
  constructor <>

{-# COMPILE GHC One = data () (()) #-}

data _+_ (S : Set)(T : Set) : Set where -- "where" wants an indented block
  -- to offer a choice of constructors, list them with their types
  inl : S -> S + T     -- constructors can pack up stuff
  inr : T -> S + T
  -- in Haskell, this was called "Either S T"

{-
record _*_ (S : Set)(T : Set) : Set where
  field -- introduces a bunch of fields, listed with their types
    fst : S
    snd : T
  -- in Haskell, this was called "(S, T)"
-}

-- _*_ IS GENERALIZED BY SIGMA

record Sg (S : Set)(T : S -> Set) : Set where  -- Sg is short for "Sigma"
  constructor _,_
  field -- introduces a bunch of fields, listed with their types
    fst : S
    snd : T fst
open Sg public -- brings fst and snd into scope hereafter unto all inheritors
-- make _*_ from Sg ?
_*_ : Set -> Set -> Set
S * T = Sg S \ _ -> T
infixr 4 _*_ _,_

------------------------------------------------------------------------------
-- some simple proofs
------------------------------------------------------------------------------

comm-* : {A : Set}{B : Set} -> A * B -> B * A
comm-* record { fst = a ; snd = b } = record { fst = b ; snd = a }

assocLR-+ : {A B C : Set} -> (A + B) + C -> A + (B + C)
assocLR-+ (inl (inl a)) = inl a
assocLR-+ (inl (inr b)) = inr (inl b)
assocLR-+ (inr c)       = inr (inr c)

_$*_ : {A A' B B' : Set} -> (A -> A') -> (B -> B') -> A * B -> A' * B'
(f $* g) record { fst = a ; snd = b } = record { fst = f a ; snd = g b }

-- record syntax is rather ugly for small stuff; can we have constructors?

_$+_ : {A A' B B' : Set} -> (A -> A') -> (B -> B') -> A + B -> A' + B'
(f $+ g) (inl a) = inl (f a)
(f $+ g) (inr b) = inr (g b)

combinatorK : {A E : Set} -> A -> E -> A
combinatorK = \ a _ -> a

combinatorS : {S T E : Set} -> (E -> S -> T) -> (E -> S) -> E -> T
combinatorS = \ f s e -> (f e) (s e)

id : {X : Set} -> X -> X
-- id x = x -- is the easy way; let's do it a funny way to make a point
id = combinatorS combinatorK (combinatorK {_} {Zero})
--                          no choice for -^   ^^^^- could be anything

naughtE : {X : Set} -> Zero -> X
naughtE ()

-- standard composition: f << g is "f after g"
_<<_ : {X Y Z : Set} -> (Y -> Z) -> (X -> Y) -> (X -> Z)
(f << g) x = f (g x)

-- diagrammatic composition: f >> g is "f then g"
_>>_ : {X Y Z : Set} -> (X -> Y) -> (Y -> Z) -> (X -> Z)
                     --       ^^^^^^^^          dominoes!
(f >> g) x = g (f x)

-- infix application
_$_ : {S : Set}{T : S -> Set}(f : (x : S) -> T x)(s : S) -> T s
f $ s = f s
infixl 2 _$_

------------------------------------------------------------------------------
-- from logic to data
------------------------------------------------------------------------------

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

{-# BUILTIN NATURAL Nat #-}
--  ^^^^^^^^^^^^^^^^^^^       this pragma lets us use decimal notation

_+N_ : Nat -> Nat -> Nat
zero  +N y = y
suc x +N y = suc (x +N y)      -- there are other choices

four : Nat
four = 2 +N 2

------------------------------------------------------------------------------
-- and back to logic
------------------------------------------------------------------------------

data _==_ {X : Set} : X -> X -> Set where
  refl : (x : X) -> x == x           -- the relation that's "only reflexive"

{-# BUILTIN EQUALITY _==_ #-}  -- we'll see what that's for, later

_=$=_ : {X Y : Set}{f f' : X -> Y}{x x' : X} ->
        f == f' -> x == x' -> f x == f' x'
refl f =$= refl x = refl (f x)
infixl 2 _=$=_

zero-+N : (n : Nat) -> (zero +N n) == n
zero-+N n = refl n

+N-zero : (n : Nat) -> (n +N zero) == n
+N-zero zero     = refl zero
+N-zero (suc n)  = refl suc =$= +N-zero n

assocLR-+N : (x y z : Nat) -> ((x +N y) +N z) == (x +N (y +N z))
assocLR-+N zero    y z = refl (y +N z)
assocLR-+N (suc x) y z = refl suc =$= assocLR-+N x y z

------------------------------------------------------------------------------
-- computing types
------------------------------------------------------------------------------

_>=_ : Nat -> Nat -> Set
x     >= zero   = One
zero  >= suc y  = Zero
suc x >= suc y  = x >= y

refl->= : (n : Nat) -> n >= n
refl->= zero    = record {}
refl->= (suc n) = refl->= n

trans->= : (x y z : Nat) -> x >= y -> y >= z -> x >= z
trans->=      x       y  zero    x>=y y>=z = record {}
trans->=      x  zero    (suc z) x>=y ()
trans->= zero    (suc y) (suc z) ()   y>=z
trans->= (suc x) (suc y) (suc z) x>=y y>=z = trans->= x y z x>=y y>=z

------------------------------------------------------------------------------
-- construction by proof
------------------------------------------------------------------------------

{- -- MOVED UP TO REPLACE _*_
record Sg (S : Set)(T : S -> Set) : Set where  -- Sg is short for "Sigma"
  constructor _,_
  field -- introduces a bunch of fields, listed with their types
    fst : S
    snd : T fst
-- make _*_ from Sg ?
_*_ : Set -> Set -> Set
S * T = Sg S \ _ -> T
-}

difference : (m n : Nat) -> m >= n -> Sg Nat \ d -> m == (n +N d)
                                   --       (                    )
difference m       zero    m>=n = m , refl m
difference zero    (suc n) ()
difference (suc m) (suc n) m>=n with difference m n m>=n
difference (suc m) (suc n) m>=n | d , q = d , (refl suc =$= q)

tryMe      = difference 42 37 _
-- don'tTryMe = difference 37 42 {!!}

------------------------------------------------------------------------------
-- things to remember to say
------------------------------------------------------------------------------

-- why the single colon?

-- what's with all the spaces?

-- what's with identifiers mixing letters and symbols?

-- what's with all the braces?

-- what is Set?

-- are there any lowercase/uppercase conventions?

-- what's with all the underscores?
--   (i)   placeholders in mixfix operators
--   (ii)  don't care (on the left)
--   (iii) go figure (on the right)

-- why use arithmetic operators for types?

-- what's the difference between = and == ?

-- can't we leave out cases?

-- can't we loop?

-- the function type is both implication and universal quantification,
-- but why is it called Pi?

-- why is Sigma called Sigma?

-- B or nor B?


-- Andreas, 2015-07-07 Let _ pattern be instantiable by an inaccessible pattern

module _ where

open import Common.Equality

-- These used to work and should still work:

transV1 : ∀{A : Set} (a b c : A) → a ≡ b → b ≡ c → a ≡ c
transV1 _ ._ ._ refl refl = refl

transV2 : ∀{A : Set} (a b c : A) → a ≡ b → b ≡ c → a ≡ c
transV2 ._ _ ._ refl refl = refl

transV3 : ∀{A : Set} (a b c : A) → a ≡ b → b ≡ c → a ≡ c
transV3 ._ ._ _ refl refl = refl

transH : ∀{A : Set}{a b c : A} → a ≡ b → b ≡ c → a ≡ c
transH refl refl = refl

transH1 : ∀{A : Set}{a b c : A} → a ≡ b → b ≡ c → a ≡ c
transH1 {a = _}{b = ._}{c = ._} refl refl = refl

transH1' : ∀{A : Set}{a b c : A} → a ≡ b → b ≡ c → a ≡ c
transH1' {a = _} refl refl = refl

-- NEW: the following should also work:

transVNew : ∀{A : Set} (a b c : A) → a ≡ b → b ≡ c → a ≡ c
transVNew _ _ _ refl refl = refl

transHNew : ∀{A : Set}{a b c : A} → a ≡ b → b ≡ c → a ≡ c
transHNew {a = _}{b = _}{c = _} refl refl = refl


{- SAMPLE PROGRAMS IN SOURCE LANGUAGE -}

open import Preliminaries
open import Source
open import Pilot2
open import Translation
open import Interp
open import Preorder

module Samples where

  s2r : ∀ {Γ τ} → Γ Source.|- τ → (el ([ (⟨⟨ Γ ⟩⟩c) ]c ->p [ (|| τ ||) ]t))
  s2r p = interpE || p ||e

  -- this works
  {- dbl (n : nat) : nat = 2 * n -}
  dbl : ∀ {Γ} → Γ Source.|- (nat ->s nat)
  dbl = lam (rec (var i0) z (suc (suc (force (var (iS i0))))))

  -- this works
  {- add (m n : nat) : nat = m + n -}
  add : ∀ {Γ} → Γ Source.|- (nat ->s (nat ->s nat))
  add = lam (lam (rec (var (iS i0)) (var i0) (suc (force (var (iS i0))))))

  -- this works
  {- mult (m n : nat) : nat = m * n -}
  mult : ∀ {Γ} → Γ Source.|- (nat ->s (nat ->s nat))
  mult = lam (lam (rec (var (iS i0)) z (app (app add (var (iS (iS i0)))) (force (var (iS i0))))))

  -- this works
  {- pred (n : nat) : nat = predecessor of n -}
  pred : ∀ {Γ} → Γ Source.|- (nat ->s nat)
  pred = lam (rec (var i0) z (var i0))

  -- uhhhh
  fastfib : ∀ {Γ} → Γ Source.|- (nat ->s (nat ×s nat))
  fastfib =
    lam (rec (var i0) (prod z (suc z))
      (prod (split (force (var (iS i0))) (var (iS i0)))
        (app (app add (split (force (var (iS i0))) (var i0))) (split (force (var (iS i0))) (var (iS i0))))))

  -- hack : instead of having bool case analysis just do natural number recursion and return 1/0

  -- this works
  {- iszero (n : nat) : nat = z -> 1 | _ -> 0 -}
  isz : ∀ {Γ} → Γ Source.|- (nat ->s nat)
  isz = lam (rec (var i0) (suc z) z)

  -- this works
  {- leq (m n : nat) : nat = m ≤ n -}
  leq : ∀ {Γ} → Γ Source.|- (nat ->s (nat ->s nat))
  leq = lam (rec (var i0) (lam (suc z)) (lam (rec (var i0) z (app (force (var (iS (iS (iS (iS i0)))))) (var i0)))))

  -- this works
  {- eq (m n : nat) : nat = m = n -}
  eq : ∀ {Γ} → Γ Source.|- (nat ->s (nat ->s nat))
  eq = lam (rec (var i0) (lam (app isz (var i0))) (lam (rec (var i0) z (app (force (var (iS (iS (iS (iS i0)))))) (var i0)))))

  -- this works
  {- len (l : list τ) : nat = [] -> z | x :: xs -> 1 + len xs -}
  len : ∀ {Γ τ} → Γ Source.|- (list τ ->s nat)
  len = lam (listrec (var i0) z (suc (force (var (iS (iS i0))))))

  -- this works
  {- nth (n : nat) (l : list τ) : nat = [] -> 1 (return 1+len(l) if element not in list) | x :: xs -> if n = x then 0 else 1 + (nth n xs) -}
  nth : ∀ {Γ} → Γ Source.|- (list nat ->s (nat ->s nat))
  nth = lam (lam (listrec (var (iS i0)) (suc z) (rec (app (app eq (var (iS (iS (iS i0))))) (var i0)) (suc (force (var (iS (iS i0))))) z)))

  -- this works
  {- append (l1 l2 : list τ) : list τ = match l1 with [] -> l2 | x :: xs -> x :: (append xs ys) -}
  append : ∀ {Γ τ} → Γ Source.|- (list τ ->s (list τ ->s list τ))
  append = lam (lam (listrec (var (iS i0)) (var i0) (var i0 ::s force (var (iS (iS i0))))))

  -- this works
  {- rev (l : list τ) : list τ = [] -> [] | x :: xs -> append (rev xs) [x] -}
  rev : ∀ {Γ τ} → Γ Source.|- (list τ ->s list τ)
  rev = lam (listrec (var i0) nil (app (app append (force (var (iS (iS i0))))) (var i0 ::s nil)))

{-
  {- fast rev -}
  rev2piles : ∀ {Γ τ} → Γ Source.|- (list τ ->s (list τ ->s list τ))
  rev2piles = lam (lam (listrec (var (iS i0)) (var i0) (app (app rev2piles (var (iS i0))) (var i0 ::s var (iS (iS (iS i0)))))))
  fastrev : ∀ {Γ τ} → Γ Source.|- (list τ ->s list τ)
  fastrev = lam (app (app rev2piles (var i0)) nil)
-}

  -- this works
  {- insert (l : list nat) (el : nat) : list nat = [] -> [el] | x :: xs -> (leq el x -> el :: x :: xs | x :: (insert el xs)) -}
  insert : ∀ {Γ} → Γ Source.|- (list nat ->s (nat ->s list nat))
  insert = lam (lam (listrec (var (iS i0)) (var i0 ::s nil)
             (rec (app (app leq (var (iS (iS (iS i0))))) (var i0)) (var i0 ::s force (var (iS (iS i0))))
               (var (iS (iS (iS (iS (iS i0))))) ::s (var (iS (iS i0)) ::s var (iS (iS (iS i0))))))))

  -- this works
  {- insertion sort (l : list nat) : list nat = [] -> [] | x :: xs -> insert x (isort xs) -}
  isort : ∀ {Γ} → Γ Source.|- (list nat ->s list nat)
  isort = lam (listrec (var i0) nil (app (app insert (force (var (iS (iS i0))))) (var i0)))

  -- this works
  {- map (l : list τ) (f : τ → τ') : list τ = [] -> [] | x :: xs -> f x :: map f xs -}
  map : ∀ {Γ τ τ'} → Γ Source.|- ((τ ->s τ') ->s (list τ ->s list τ'))
  map = lam (lam (listrec (var i0) nil (app (var (iS (iS (iS (iS i0))))) (var i0) ::s force (var (iS (iS i0))))))

  dbl-trans : ∀ {Γ τ} → {!!} --el ([ (⟨⟨ Γ ⟩⟩c) ]c ->p [ (|| τ ||) ]t)
  dbl-trans {Γ} = {!|| dbl ||e!}

  example1 : ∀ {Γ τ} → {!!}
  example1 {Γ} {τ} = {!!} --copy and paste from this goal to the thing below
{-
  dbl-trans : ∀ {Γ} → ⟨⟨ Γ ⟩⟩c Pilot2.|- || nat ->s nat ||
  dbl-trans = prod 0C
                                     (lam
                                      (prod
                                       (plusC (l-proj (prod 0C (var i0)))
                                        (l-proj
                                         (rec (r-proj (prod 0C (var i0)))
                                          (prod (plusC 1C (l-proj (prod 0C z))) (r-proj (prod 0C z)))
                                          (prod
                                           (plusC 1C
                                            (l-proj
                                             (prod
                                              (l-proj
                                               (prod
                                                (l-proj
                                                 (prod
                                                  (plusC (l-proj (prod 0C (var (iS i0))))
                                                   (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                  (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                                (s
                                                 (r-proj
                                                  (prod
                                                   (plusC (l-proj (prod 0C (var (iS i0))))
                                                    (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                   (r-proj (r-proj (prod 0C (var (iS i0))))))))))
                                              (s
                                               (r-proj
                                                (prod
                                                 (l-proj
                                                  (prod
                                                   (plusC (l-proj (prod 0C (var (iS i0))))
                                                    (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                   (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                                 (s
                                                  (r-proj
                                                   (prod
                                                    (plusC (l-proj (prod 0C (var (iS i0))))
                                                     (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                    (r-proj (r-proj (prod 0C (var (iS i0))))))))))))))
                                           (r-proj
                                            (prod
                                             (l-proj
                                              (prod
                                               (l-proj
                                                (prod
                                                 (plusC (l-proj (prod 0C (var (iS i0))))
                                                  (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                 (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                               (s
                                                (r-proj
                                                 (prod
                                                  (plusC (l-proj (prod 0C (var (iS i0))))
                                                   (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                  (r-proj (r-proj (prod 0C (var (iS i0))))))))))
                                             (s
                                              (r-proj
                                               (prod
                                                (l-proj
                                                 (prod
                                                  (plusC (l-proj (prod 0C (var (iS i0))))
                                                   (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                  (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                                (s
                                                 (r-proj
                                                  (prod
                                                   (plusC (l-proj (prod 0C (var (iS i0))))
                                                    (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                   (r-proj (r-proj (prod 0C (var (iS i0)))))))))))))))))
                                       (r-proj
                                        (rec (r-proj (prod 0C (var i0)))
                                         (prod (plusC 1C (l-proj (prod 0C z))) (r-proj (prod 0C z)))
                                         (prod
                                          (plusC 1C
                                           (l-proj
                                            (prod
                                             (l-proj
                                              (prod
                                               (l-proj
                                                (prod
                                                 (plusC (l-proj (prod 0C (var (iS i0))))
                                                  (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                 (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                               (s
                                                (r-proj
                                                 (prod
                                                  (plusC (l-proj (prod 0C (var (iS i0))))
                                                   (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                  (r-proj (r-proj (prod 0C (var (iS i0))))))))))
                                             (s
                                              (r-proj
                                               (prod
                                                (l-proj
                                                 (prod
                                                  (plusC (l-proj (prod 0C (var (iS i0))))
                                                   (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                  (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                                (s
                                                 (r-proj
                                                  (prod
                                                   (plusC (l-proj (prod 0C (var (iS i0))))
                                                    (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                   (r-proj (r-proj (prod 0C (var (iS i0))))))))))))))
                                          (r-proj
                                           (prod
                                            (l-proj
                                             (prod
                                              (l-proj
                                               (prod
                                                (plusC (l-proj (prod 0C (var (iS i0))))
                                                 (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                              (s
                                               (r-proj
                                                (prod
                                                 (plusC (l-proj (prod 0C (var (iS i0))))
                                                  (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                 (r-proj (r-proj (prod 0C (var (iS i0))))))))))
                                            (s
                                             (r-proj
                                              (prod
                                               (l-proj
                                                (prod
                                                 (plusC (l-proj (prod 0C (var (iS i0))))
                                                  (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                 (r-proj (r-proj (prod 0C (var (iS i0)))))))
                                               (s
                                                (r-proj
                                                 (prod
                                                  (plusC (l-proj (prod 0C (var (iS i0))))
                                                   (l-proj (r-proj (prod 0C (var (iS i0))))))
                                                  (r-proj (r-proj (prod 0C (var (iS i0))))))))))))))))))

  aaa : ∀ {Γ} → interpE (r-proj (dbl-trans {Γ})) == {!!}
  aaa = {!!}

  interp-dbl-trans : ∀ {Γ} → Monotone (fst [ ⟨⟨ Γ ⟩⟩c ]c) (fst [ ⟨⟨ nat ->s nat ⟩⟩ ]t) (snd [ ⟨⟨ Γ ⟩⟩c ]c) (snd [ ⟨⟨ nat ->s nat ⟩⟩ ]t)
  interp-dbl-trans {Γ} = monotone
                           (λ x →
                              monotone
                              (λ p₁ →
                                 (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) p₁))
                              (λ a b c →
                                 nat-trans
                                 (fst (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) a))
                                 (fst (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) b))
                                 (fst (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) b))
                                 (fst
                                  (♭h-fix-args (monotone (λ x₁ → 1 , 0) (λ x₁ y z₁ → <> , <>))
                                   (monotone
                                    (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                    (λ x₁ y z₁ → fst (snd (fst z₁)) , snd (snd (fst z₁))))
                                   ((x , a) , a) ((x , b) , b) c))
                                 (fst
                                  (♭h-fix-el (monotone (λ x₁ → 1 , 0) (λ x₁ y z₁ → <> , <>))
                                   (monotone
                                    (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                    (λ x₁ y z₁ → fst (snd (fst z₁)) , snd (snd (fst z₁))))
                                   ((x , a) , a) ((x , b) , b)
                                   ((Preorder-str.refl (snd [ ⟨⟨ Γ ⟩⟩c ]c) x , c) , c)))
                                 ,
                                 ♭nat-trans
                                 (snd (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) a))
                                 (snd (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) b))
                                 (snd (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) b))
                                 (snd
                                  (♭h-fix-args (monotone (λ x₁ → 1 , 0) (λ x₁ y z₁ → <> , <>))
                                   (monotone
                                    (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                    (λ x₁ y z₁ → fst (snd (fst z₁)) , snd (snd (fst z₁))))
                                   ((x , a) , a) ((x , b) , b) c))
                                 (snd
                                  (♭h-fix-el (monotone (λ x₁ → 1 , 0) (λ x₁ y z₁ → <> , <>))
                                   (monotone
                                    (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                    (λ x₁ y z₁ → fst (snd (fst z₁)) , snd (snd (fst z₁))))
                                   ((x , a) , a) ((x , b) , b)
                                   ((Preorder-str.refl (snd [ ⟨⟨ Γ ⟩⟩c ]c) x , c) , c)))))
                           (λ x y z₁ w →
                              nat-trans
                              (fst (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) w))
                              (fst (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) w))
                              (fst (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) w))
                              (fst
                               (♭h-fix-args (monotone (λ x₁ → 1 , 0) (λ x₁ y₁ z₂ → <> , <>))
                                (monotone
                                 (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                 (λ x₁ y₁ z₂ → fst (snd (fst z₂)) , snd (snd (fst z₂))))
                                ((x , w) , w) ((y , w) , w) (♭nat-refl w)))
                              (fst
                               (♭h-fix-el (monotone (λ x₁ → 1 , 0) (λ x₁ y₁ z₂ → <> , <>))
                                (monotone
                                 (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                 (λ x₁ y₁ z₂ → fst (snd (fst z₂)) , snd (snd (fst z₂))))
                                ((x , w) , w) ((y , w) , w) ((z₁ , ♭nat-refl w) , ♭nat-refl w)))
                              ,
                              ♭nat-trans
                              (snd (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) w))
                              (snd (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) w))
                              (snd (natrec (1 , 0) (λ n x₂ → S (fst x₂) , S (S (snd x₂))) w))
                              (snd
                               (♭h-fix-args (monotone (λ x₁ → 1 , 0) (λ x₁ y₁ z₂ → <> , <>))
                                (monotone
                                 (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                 (λ x₁ y₁ z₂ → fst (snd (fst z₂)) , snd (snd (fst z₂))))
                                ((x , w) , w) ((y , w) , w) (♭nat-refl w)))
                              (snd
                               (♭h-fix-el (monotone (λ x₁ → 1 , 0) (λ x₁ y₁ z₂ → <> , <>))
                                (monotone
                                 (λ x₁ → S (fst (snd (fst x₁))) , S (S (snd (snd (fst x₁)))))
                                 (λ x₁ y₁ z₂ → fst (snd (fst z₂)) , snd (snd (fst z₂))))
                                ((x , w) , w) ((y , w) , w) ((z₁ , ♭nat-refl w) , ♭nat-refl w))))
-}

{-
  {- halve (l : list nat) : (list nat * list nat) = splits a list in half -}
  halve : ∀ {Γ} → Γ Source.|- (list nat ->s (list nat ×s list nat))
  halve = lam (listrec (var i0)
            (prod nil nil)
            (listrec (var (iS i0))
              (prod (var i0 ::s nil) nil)
              (prod (var (iS (iS (iS i0))) ::s split (force (var (iS (iS i0)))) (var i0)) (var i0 ::s split (force (var (iS (iS i0)))) (var (iS i0))))))

  test : ∀ {Γ τ} → {!!}
  test {Γ} = {!s2r (app halve (z ::s (z ::s (z ::s nil))))!}

  {- merge (l1 l2 : list nat) : list nat =
       match l1 with
       [] -> l2
       x :: xs ->
         match l2 with
           [] -> x :: xs
           y :: ys ->
             x <= y -> x :: merge xs l2
             _ -> y :: merge l1 ys -}
  merge : ∀ {Γ} → Γ Source.|- ((list nat ×s list nat) ->s list nat)
  merge = lam (listrec (split (var i0) (var i0))
            (split (var i0) (var (iS i0)))
            (listrec (split (var (iS (iS (iS i0)))) (var (iS i0)))
              (split (var (iS (iS (iS i0)))) (var i0))
              (rec (app (app leq (var (iS (iS (iS i0))))) (var i0))
                (var (iS (iS (iS i0))) ::s force (var (iS (iS (iS (iS (iS i0)))))))
                (var i0 ::s app merge (prod (split (var (iS (iS (iS (iS (iS (iS (iS (iS i0))))))))) (var (iS i0))) (var (iS (iS (iS i0)))))))))

  {- mergesort (l : list nat) : list nat -}
  msort : ∀ {Γ} → Γ Source.|- (list nat ->s list nat)
  msort = lam (listrec (var i0)
            nil
            (listrec (var (iS i0))
              (var i0 ::s nil)
              (app merge (prod
                (app msort (split (app halve (var (iS (iS (iS (iS (iS (iS i0)))))))) (var i0)))
                (app msort (split (app halve (var (iS (iS (iS (iS (iS (iS i0)))))))) (var (iS i0))))))))
-}


{-# OPTIONS --without-K --safe #-}
open import Categories.Category

{- Definition of SubCategory of a given Category C.

  Here a SubCategory is defined via
  - an index set I
  - a mapping I → Obj (not necessarily injective)
  - a proof (as a unary relation) that for all a, b : A, all arrows U a ⇒ U b belong to the SubCategory
    (note that this is 'backwards' from SubCategory at https://ncatlab.org/nlab/show/subcategory
     which would be
     (∀ {x y : Obj} (f : x ⇒ y) → R f → ∃ (A × B) (λ (a , b) → U a × U b))
     and that would be awkward to work with.
  - a proof that all objects pointed to by I have identity arrows that belong
  - a proof that composable arrows in the SubCategory are closed under composition

  It is simpler to package up all of that into a single record.
-}
module Categories.Category.SubCategory {o ℓ e} (C : Category o ℓ e) where

open Category C
open HomReasoning using (refl; sym; trans)

open import Level
open import Data.Product

private
  variable
    i : Level

record SubCat {ℓ′} (I : Set i) : Set (o ⊔ ℓ ⊔ i ⊔ suc ℓ′) where
  field
    U : I → Obj
    R : {a b : I} → U a ⇒ U b → Set ℓ′
    Rid : {a : I} → R (id {U a})
    _∘R_ : {a b c : I} {f : U b ⇒ U c} {g : U a ⇒ U b} → R f → R g → R (f ∘ g)

SubCategory : {ℓ′ : Level} {I : Set i} → SubCat {ℓ′ = ℓ′} I → Category _ _ _
SubCategory {I = I} sc = let open SubCat sc in record
  { Obj       = I
  ; _⇒_       = λ a b → Σ (U a ⇒ U b) R
  ; _≈_       = λ f g → proj₁ f ≈ proj₁ g
  ; id        = id , Rid
  ; _∘_       = zip _∘_ _∘R_
  ; assoc     = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv     = record -- need to expand this out, else the levels don't work out
    { refl  = refl
    ; sym   = sym
    ; trans = trans
    }
  ; ∘-resp-≈  = ∘-resp-≈
  }

FullSubCategory : ∀ {I : Set i} → (U : I → Obj) → Category _ _ _
FullSubCategory {I = I} U = record
  { Obj       = I
  ; _⇒_       = λ x y → U x ⇒ U y
  ; _≈_       = _≈_
  ; id        = id
  ; _∘_       = _∘_
  ; assoc     = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv     = equiv
  ; ∘-resp-≈  = ∘-resp-≈
  }


module MLib.Semirings where

open import MLib.Prelude
open Algebra using (Semiring)

module _ {ℓ p} (semiring : Semiring ℓ p) where
  open Semiring semiring renaming (Carrier to S)

  pow : ℕ → S → S
  pow Nat.zero x = 1#
  pow (Nat.suc n) x = x * pow n x

  powsum : ℕ → S → S
  powsum Nat.zero x = 1#
  powsum (Nat.suc n) x = pow (Nat.suc n) x + powsum n x

  Q-stable : ℕ → S → Set p
  Q-stable q x = powsum (1 Nat.+ q) x ≈ powsum q x


module #16 where

{-
  Show that addition of natural numbers is commutative: ∏(i,j:N)(i + j = j + i).
-}

open import Data.Nat
open import Relation.Binary.PropositionalEquality
open Relation.Binary.PropositionalEquality.≡-Reasoning

l-commut₀ : (n : ℕ) → n + 0 ≡ n
l-commut₀ zero = refl
l-commut₀ (suc n) = cong suc (l-commut₀ n)

suc-in-the-middle-with-you : ∀ m n → m + suc n ≡ suc (m + n)
suc-in-the-middle-with-you zero n = refl
suc-in-the-middle-with-you (suc m) n = cong suc (suc-in-the-middle-with-you m n)

+-commutative : (m n : ℕ) → m + n ≡ n + m
+-commutative zero n = sym (l-commut₀ n)
+-commutative (suc m) n =
  begin
    (suc m) + n
      ≡⟨ refl ⟩
    suc (m + n)
      ≡⟨ cong suc (+-commutative m n) ⟩
    suc (n + m)
      ≡⟨ sym (suc-in-the-middle-with-you n m) ⟩
    n + (suc m)
  ∎


{-

Part 3: Univalence and the SIP

• Univalence from ua and uaβ
• Transporting with ua
• The SIP as a consequence of ua

-}
{-# OPTIONS --cubical #-}
module Part3 where

open import Cubical.Core.Glue public
  using ( Glue ; glue ; unglue ; lineToEquiv )

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism

open import Part1
open import Part2

-- A key concept in HoTT/UF is univalence. As we have seen earlier in
-- the week this is assumed as an axiom in HoTT. In Cubical Agda it is
-- instead provable and has computational content. This means that
-- transporting with paths constructed using univalence reduces as
-- opposed to HoTT where they would be stuck. This simplifies some
-- proofs and make it possible to actually do concrete computations
-- using univalence.

-- The part of univalence which is most useful for our applications is
-- to be able to turn equivalences (written _≃_ and defined as a Σ-type
-- of a function and a proof that it has contractible fibers) into
-- paths:
ua : {A B : Type ℓ} → A ≃ B → A ≡ B
ua {A = A} {B = B} e i = Glue B (λ { (i = i0) → (A , e)
                                   ; (i = i1) → (B , idEquiv B) })

-- This term is defined using "Glue types" which were introduced in
-- the CCHM paper. We won't have time to go into details about them
-- today, but for practical applications one can usually forget about
-- them and use ua as a black box. There are multiple useful lemmas in
-- Cubical.Foundations.Univalence and Cubical.Foundations.Transport
-- which are helpful when reasoning about ua which don't require
-- knowing what Glue types are.

-- The important point is that transporting along the path constructed
-- using ua applies the function underlying the equivalence. This is
-- easily proved using transportRefl:
uaβ : (e : A ≃ B) (x : A) → transport (ua e) x ≡ fst e x
uaβ e x = transportRefl (equivFun e x)

-- Note that for concrete types this typically hold definitionally,
-- but for an arbitrary equivalence e we have to prove it.

-- The fact that we have both ua and uaβ suffices to be able to prove
-- the standard formulation of univalence. For details see:
--
--   Cubical.Foundations.Univalence

-- The standard way of constructing equivalences is to start with an
-- isomorphism and then improve it into an equivalence. The lemma in
-- the library which does this is
--
--   isoToEquiv : {A B : Type ℓ} → Iso A B → A ≃ B

-- Composing this with ua lets us directly turn isomorphisms into paths:
--
--   isoToPath : {A B : Type ℓ} → Iso A B → A ≡ B

-- Time for an example!

-- Booleans
data Bool : Type₀ where
  false true : Bool

not : Bool → Bool
not false = true
not true  = false

notPath : Bool ≡ Bool
notPath = isoToPath (iso not not rem rem)
  where
  rem : (b : Bool) → not (not b) ≡ b
  rem false = refl
  rem true  = refl

_ : transport notPath true ≡ false
_ = refl

-- Another example, integers:

open import Cubical.Data.Int hiding (_+_ ; +-assoc)

sucPath : Int ≡ Int
sucPath = isoToPath (iso sucInt predInt sucPred predSuc)

_ : transport sucPath (pos 0) ≡ pos 1
_ = refl

_ : transport (sucPath ∙ sucPath) (pos 0) ≡ pos 2
_ = refl

_ : transport (sym sucPath) (pos 0) ≡ negsuc 0
_ = refl

-------------------------------------------------------------------------
-- The structure identity principle

-- Combining subst and ua lets us transport any structure on A to get
-- a structure on an equivalent type B
substEquiv : (S : Type ℓ → Type ℓ') (e : A ≃ B) → S A → S B
substEquiv S e = subst S (ua e)

-- Example with binary numbers:

open import Cubical.Data.BinNat.BinNat renaming (ℕ≃binnat to ℕ≃Bin ; binnat to Bin)
open import Cubical.Data.Nat

T : Set → Set
T X = Σ[ _+_ ∈ (X → X → X) ] ((x y z : X) → x + (y + z) ≡ (x + y) + z)

TBin : T Bin
TBin = substEquiv T ℕ≃Bin (_+_ , +-assoc)

_+Bin_ : Bin → Bin → Bin
_+Bin_  = fst TBin

+Bin-assoc : (m n o : Bin) → m +Bin (n +Bin o) ≡ (m +Bin n) +Bin o
+Bin-assoc = snd TBin

-- This is however not always what we want as _+Bin_ will translate
-- its input to unary numbers, add, and then translate back. Instead
-- we want to use efficient addition on binary numbers, but get the
-- associativity proof for free. So what we really want is to
-- characterize the equality of T-structured types, i.e. we want a
-- proof that two types equipped with T-structures are equal if there
-- is a T-structure preserving equivalence between them. This is the
-- usual meaning of the *structure identity principle* (SIP). This
-- implies in particular that the type of paths of
-- monoids/groups/rings/R-modules/... is equivalent to the type of
-- monoid/group/ring/R-module/... preserving equivalences.

-- We formalize this and much more using a cubical version of the SIP in:
--
-- Internalizing Representation Independence with Univalence
-- Carlo Angiuli, Evan Cavallo, Anders Mörtberg, Max Zeuner
-- https://arxiv.org/abs/2009.05547
--
-- The binary numbers example with efficient addition is spelled out
-- in detail in Section 2.1.1 of:
--
-- https://www.doi.org/10.1017/S0956796821000034
-- (Can be downloaded from: https://staff.math.su.se/anders.mortberg/papers/cubicalagda2.pdf)


module _ where

open import Common.Prelude
open import Lib.Vec

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

sum : ∀ {n} → Vec Nat n → Nat
sum (x ∷ xs) = x + sum xs
sum [] = 0

foldl : ∀ {A}{B : Nat → Set} → (∀ {n} → B n → A → B (suc n)) → B 0 → ∀ {n} → Vec A n → B n
foldl {B = B} f z (x ∷ xs) = foldl {B = λ n → B (suc n)} f (f z x) xs
foldl f z [] = z

reverse : ∀ {A n} → Vec A n → Vec A n
reverse = foldl {B = Vec _} (λ xs x → x ∷ xs) []

downFrom : ∀ n → Vec Nat n
downFrom zero = []
downFrom (suc n) = n ∷ downFrom n

main : IO Unit
main = printNat (sum (reverse (downFrom 6000)))


{-# OPTIONS --sized-types #-}
module SList.Concatenation (A : Set) where

open import Data.List
open import List.Permutation.Base A
open import Size
open import SList

lemma-⊕-/ : {xs ys : List A}{x y : A} → xs / x ⟶ ys → unsize A (_⊕_ A (size A xs) (y ∙ snil)) / x ⟶ unsize A (_⊕_ A (size A ys) (y ∙ snil))
lemma-⊕-/ /head = /head
lemma-⊕-/ (/tail xs/x⟶xs') = /tail (lemma-⊕-/ xs/x⟶xs')

lemma-⊕∼ : {xs ys : List A}(x : A) → xs ∼ ys → (x ∷ xs) ∼ unsize A (_⊕_ A (size A ys) (x ∙ snil))
lemma-⊕∼ x ∼[] = ∼x /head /head ∼[]
lemma-⊕∼ x (∼x xs/x⟶xs' ys/x⟶ys' xs'∼ys') = ∼x (/tail xs/x⟶xs') (lemma-⊕-/ ys/x⟶ys') (lemma-⊕∼ x xs'∼ys')

lemma-size-unsize : {ι : Size}(x : A) → (xs : SList A {ι}) → (unsize A (_⊕_ A (size A (unsize A xs)) (x ∙ snil))) ∼ unsize A (_⊕_ A xs (x ∙ snil))
lemma-size-unsize x snil = ∼x /head /head ∼[]
lemma-size-unsize x (y ∙ ys) = ∼x /head  /head (lemma-size-unsize x ys)


module Nat where

open import Prelude
open import Star

Nat : Set
Nat = Star One _ _

zero : Nat
zero = ε

suc : Nat -> Nat
suc n = _ • n

infixl 50 _+_ _-_
infixl 60 _*_

_+_ : Nat -> Nat -> Nat
_+_ = _++_

_*_ : Nat -> Nat -> Nat
x * y = bind id (\ _ -> y) x

_-_ : Nat -> Nat -> Nat
n       - ε       = n
ε       - m       = ε
(_ • n) - (_ • m) = n - m

test : Nat
test = suc (suc zero) * suc (suc zero)


{-# OPTIONS --no-termination-check #-}

module Pi-reasoning where

open import Data.Empty
open import Data.Unit hiding (_≟_)
open import Data.Bool hiding (_≟_)
open import Data.Nat
open import Data.List
open import Data.Sum hiding (map)
open import Data.Product hiding (map)
open import Level hiding (suc)
open import Relation.Nullary
open import Relation.Binary
open import Algebra
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
open import Relation.Binary.PropositionalEquality as PropEq using(sym; trans)
import Algebra.FunctionProperties as FunctionProperties
-- open import Algebra.FunctionProperties
open import Algebra.Structures

open import Pi-abstract-machine

------------------------------------------------------------------------------
-- Decidable equality

-- normalize a type to a natural number

size : B → ℕ
size ZERO = 0
size ONE = 1
size (PLUS b₁ b₂) = size b₁ + size b₂
size (TIMES b₁ b₂) = size b₁ * size b₂

-- normalize a value to a number

normalize : {b : B} → VB b → ℕ
normalize {ZERO} ()
normalize {ONE} unitB = 0
normalize {PLUS b₁ b₂} (inlB v) = normalize {b₁} v
normalize {PLUS b₁ b₂} (inrB v) = size b₁ + normalize {b₂} v
normalize {TIMES b₁ b₂} (pairB v₁ v₂) = size b₂ * normalize {b₁} v₁ + normalize {b₂} v₂

-- decidable equality of our values: normalize and compare the
-- underlying natural numbers. This is justified by the fact that the
-- natural numbers are a model of commutative semirings.
-- Note that we can't compare at different types so easily, they have
-- to have the same size, something not worth dealing with right now
vb= : {b : B} → (v₁ : VB b) → (v₂ : VB b) → Set
vb= {b} v₁ v₂ = (normalize {b} v₁) ≡ (normalize {b} v₂)

vb-Equivalence : {b : B} → IsEquivalence (vb= {b})
vb-Equivalence = record
  { refl = refl
  ; sym = PropEq.sym
  ; trans = PropEq.trans }

vb== : {b : B} → Decidable {A = VB b} vb=
vb== {b} x y = (normalize {b} x) ≟ (normalize {b} y)

-- generate all values of a type

values : (b : B) → List (VB b)
values ZERO = []
values ONE = [ unitB ]
values (PLUS b₁ b₂) = map inlB (values b₁) ++ map inrB (values b₂)
values (TIMES b₁ b₂) = concatMap (λ v₁ → map (pairB v₁) (values b₂)) (values b₁)

-- B is a Setoid

VB-is-Setoid : {b : B} → Setoid Level.zero Level.zero
VB-is-Setoid {b} = record
  { Carrier = VB b
  ; _≈_ = vb=
  ; isEquivalence = vb-Equivalence
  }

-- equality of combinators:
-- two combinators are equal if they map equal values to equal values
-- best do this via proving that vb= generates a decidable equivalence
{-
⟺=bool : {b₁ b₂ : B} → (b₁ ⟺ b₂) → (b₁ ⟺ b₂) → Bool
⟺=bool {b₁} {b₂} f g =
  and (zipWith vb= (map (eval f) vs) (map (eval g) vs))
  where vs = values b₁

data _⟺=_ : {b₁ b₂ : B} → (b₁ ⟺ b₂) → (b₁ ⟺ b₂) → Set where
  id⟺= : {b₁ b₂ : B} → (f : b₁ ⟺ b₂) → (f ⟺= f)
  sym⟺= : {b₁ b₂ : B} → (f : b₁ ⟺ b₂) → (g : b₁ ⟺ b₂) → ( f ⟺= g ) → (g ⟺= f)

-- verifies that the given combinators relates the given values

data _s⟷_ : B → B → Set where
  sid⟷ : {b : B} {v₁ : VB b} {v₂ : VB b} {p : T (vb= (eval (iso id⟷) v₁) v₂)} → (b s⟷ b)

⟺=IsEquivalence : IsEquivalence _s⟷_
⟺=IsEquivalence = record {
    refl = srefl ;
    sym = {!!} ;
    trans = {!!}
  }
  where srefl : {b : B} {v : VB b} → b s⟷ b
        srefl {b} {v} = sid⟷ {b} {v} {v} {{!!}}

-- <-> : B -> B -> Set with constructors id : {b : B} -> (b <-> b)
-- IsEquivalence <->
--   refl = id

-- R= : B -> B -> Set with constructos
-}

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

{--
testT = PLUS ONE (PLUS ONE ONE)
test1 = normalize {testT} (inlB unitB)
test2 = normalize {testT} (inrB (inlB unitB))
test3 = normalize {testT} (inrB (inrB unitB))

testT = PLUS ZERO (PLUS ONE ONE)
test1 = normalize {testT} (inrB (inlB unitB))
test2 = normalize {testT} (inrB (inrB unitB))

testT = TIMES (PLUS ONE ONE) ZERO
test1 = size testT

testT = TIMES (PLUS ONE ONE) (PLUS ONE (PLUS ONE ONE))
test1 = normalize {testT} (pairB (inlB unitB) (inlB unitB))
test2 = normalize {testT} (pairB (inrB unitB) (inlB unitB))
test3 = normalize {testT} (pairB (inlB unitB) (inrB (inlB unitB)))
test4 = normalize {testT} (pairB (inrB unitB) (inrB (inlB unitB)))
test5 = normalize {testT} (pairB (inlB unitB) (inrB (inrB unitB)))
test6 = normalize {testT} (pairB (inrB unitB) (inrB (inrB unitB)))

BOOL = PLUS ONE ONE
test = ⟺= {BOOL} {BOOL} (iso swap₊ ◎ iso swap₊) (iso id⟷)

test1 : (iso swap₊) ⟺= (iso swap₊)
test1 = check
          {PLUS ONE ONE} {PLUS ONE ONE}
          (iso swap₊) (iso swap₊)
          tt

The following does NOT typecheck which is good. Agda rejected my
nonsense claim that id is equivalent to swap+

test2 : (iso swap₊) ⟺= (iso id⟷)
test2 = check
          {PLUS ONE ONE} {PLUS ONE ONE}
          (iso swap₊) (iso id⟷)
          tt

--}


module Numeral.Natural.Oper.Comparisons.Proofs where

open import Data.Boolean.Stmt
open import Data.Boolean
open import Logic.Propositional
open import Numeral.Natural
open import Numeral.Natural.Oper.Comparisons
open import Numeral.Natural.Oper.Proofs
open import Relator.Equals

[≤?]-𝟎 : ∀{n} → IsTrue(𝟎 ≤? n)
[≤?]-𝟎 = [⊤]-intro

[≤?]-𝐒 : ∀{n} → IsTrue(n ≤? 𝐒(n))
[≤?]-𝐒 {𝟎}   = [⊤]-intro
[≤?]-𝐒 {𝐒 n} = [≤?]-𝐒 {n}

[<?]-𝟎 : ∀{n} → IsTrue(𝟎 <? 𝐒(n))
[<?]-𝟎 {𝟎}   = [⊤]-intro
[<?]-𝟎 {𝐒 n} = [<?]-𝟎 {n}

[<?]-𝐒 : ∀{n} → IsTrue(n <? 𝐒(n))
[<?]-𝐒 {𝟎}   = [⊤]-intro
[<?]-𝐒 {𝐒 n} = [<?]-𝐒 {n}

[≤?]-reflexivity : ∀{n} → IsTrue(n ≤? n)
[≤?]-reflexivity {𝟎}    = [⊤]-intro
[≤?]-reflexivity {𝐒(n)} = [≤?]-reflexivity {n}

[<?]-positive : ∀{n} → (𝟎 <? n) ≡ positive?(n)
[<?]-positive {𝟎}    = [≡]-intro
[<?]-positive {𝐒(_)} = [≡]-intro
{-# REWRITE [<?]-positive #-}

[<?]-positive-any : ∀{m n} → ⦃ _ : IsTrue(m <? n) ⦄ → IsTrue(positive?(n))
[<?]-positive-any {𝟎}   {n}   ⦃ p ⦄ = p
[<?]-positive-any {𝐒 m} {𝐒 n} ⦃ p ⦄ = [⊤]-intro

[≤?]-positive : ∀{n} → (𝐒(𝟎) ≤? n) ≡ positive?(n)
[≤?]-positive {𝟎}    = [≡]-intro
[≤?]-positive {𝐒(_)} = [≡]-intro

[≢?]-positive : ∀{n} → (n ≢? 𝟎) ≡ positive?(n)
[≢?]-positive {𝟎}    = [≡]-intro
[≢?]-positive {𝐒(_)} = [≡]-intro

[<?]-to-[≤?] : ∀{a b} → ((a <? b) ≡ (𝐒(a) ≤? b))
[<?]-to-[≤?] {𝟎}   {𝟎}    = [≡]-intro
[<?]-to-[≤?] {𝟎}   {𝐒(_)} = [≡]-intro
[<?]-to-[≤?] {𝐒(_)}{𝟎}    = [≡]-intro
[<?]-to-[≤?] {𝐒(a)}{𝐒(b)} = [<?]-to-[≤?] {a}{b}

[≡?]-zero : ∀{n} → (n ≡? 𝟎) ≡ zero?(n)
[≡?]-zero {𝟎}    = [≡]-intro
[≡?]-zero {𝐒(_)} = [≡]-intro


module bool-thms where

open import bool
open import eq
open import sum
open import empty
open import level

~~-elim : ∀ (b : 𝔹) → ~ ~ b ≡ b
~~-elim tt = refl
~~-elim ff = refl

&&-idem : ∀ {b} → b && b ≡ b
&&-idem{tt} = refl
&&-idem{ff} = refl

||-idem : ∀{b} → b || b ≡ b
||-idem{tt} = refl
||-idem{ff} = refl

||≡ff₁ : ∀ {b1 b2} → b1 || b2 ≡ ff → ff ≡ b1
||≡ff₁ {ff} p = refl
||≡ff₁ {tt} p = sym p

||≡ff₂ : ∀ {b1 b2} → b1 || b2 ≡ ff → b2 ≡ ff
||≡ff₂ {tt} ()
||≡ff₂ {ff}{tt} ()
||≡ff₂ {ff}{ff} p = refl

||-tt : ∀ (b : 𝔹) → b || tt ≡ tt
||-tt tt = refl
||-tt ff = refl

||-cong₁ : ∀ {b1 b1' b2} → b1 ≡ b1' → b1 || b2 ≡ b1' || b2
||-cong₁ refl = refl

||-cong₂ : ∀ {b1 b2 b2'} → b2 ≡ b2' → b1 || b2 ≡ b1 || b2'
||-cong₂ p rewrite p = refl

ite-same : ∀{ℓ}{A : Set ℓ} →
           ∀(b : 𝔹) (x : A) →
           (if b then x else x) ≡ x
ite-same tt x = refl
ite-same ff x = refl

ite-arg : ∀{ℓ ℓ'}{A : Set ℓ}{B : Set ℓ'} → (f : A → B)(b : 𝔹)(x y : A) → (f (if b then x else y)) ≡ (if b then f x else f y)
ite-arg f tt x y = refl
ite-arg f ff x y = refl

𝔹-contra : ff ≡ tt → ∀{ℓ} {P : Set ℓ} → P
𝔹-contra ()

||-split : ∀ {b b' : 𝔹} → b || b' ≡ tt → b ≡ tt ⊎ b' ≡ tt
||-split{tt}{tt} p = inj₁ refl
||-split{tt}{ff} p = inj₁ refl
||-split{ff}{tt} p = inj₂ refl
||-split{ff}{ff} ()

𝔹-dec : ∀ (b : 𝔹) → b ≡ tt ⊎ b ≡ ff
𝔹-dec tt = inj₁ refl
𝔹-dec ff = inj₂ refl

&&-snd : {p1 p2 : 𝔹} → p1 && p2 ≡ tt → p2 ≡ tt
&&-snd{tt} p = p
&&-snd{ff} ()

&&-fst : {p1 p2 : 𝔹} → p1 && p2 ≡ tt → p1 ≡ tt
&&-fst{tt} p = refl
&&-fst{ff} ()

&&-combo : {p1 p2 : 𝔹} → p1 ≡ tt → p2 ≡ tt → p1 && p2 ≡ tt
&&-combo{tt} pr1 pr2 = pr2
&&-combo{ff} pr1 pr2 = 𝔹-contra pr1

&&-ff : ∀(b : 𝔹) → b && ff ≡ ff
&&-ff tt = refl
&&-ff ff = refl

ff≡tt : ff ≡ tt → ⊥ {lzero}
ff≡tt ()


module Numeral.Natural where

import      Lvl
open import Type

-- The set of natural numbers (0,..).
-- Positive integers including zero.
data ℕ : Type{Lvl.𝟎} where
  𝟎 : ℕ      -- Zero
  𝐒 : ℕ → ℕ -- Successor function (Intuitively: 𝐒(n) = n+1)
{-# BUILTIN NATURAL ℕ #-}

pattern 𝟏 = ℕ.𝐒(𝟎)
{-# DISPLAY ℕ.𝐒(𝟎) = 𝟏 #-}

-- Limited predecessor function
-- Intuitively: 𝐏(n) = max(0,n-1)
𝐏 : ℕ → ℕ
𝐏(𝟎)    = 𝟎
𝐏(𝐒(n)) = n


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

syntax PDiff | PD

type
  * : 0-ary

term
  zero  : * | 𝟘
  add   : *  *  ->  * | _⊕_ l20
  one   : * | 𝟙
  mult  : *  *  ->  * | _⊗_ l20
  neg   : *  ->  * | ⊖_ r50
  pd : *.*  *  ->  * | ∂_∣_

theory
  (𝟘U⊕ᴸ) a |> add (zero, a) = a
  (𝟘U⊕ᴿ) a |> add (a, zero) = a
  (⊕A) a b c |> add (add(a, b), c) = add (a, add(b, c))
  (⊕C) a b |> add(a, b) = add(b, a)
  (𝟙U⊗ᴸ) a |> mult (one, a) = a
  (𝟙U⊗ᴿ) a |> mult (a, one) = a
  (⊗A) a b c |> mult (mult(a, b), c) = mult (a, mult(b, c))
  (⊗D⊕ᴸ) a b c |> mult (a, add (b, c)) = add (mult(a, b), mult(a, c))
  (⊗D⊕ᴿ) a b c |> mult (add (a, b), c) = add (mult(a, c), mult(b, c))
  (𝟘X⊗ᴸ) a |> mult (zero, a) = zero
  (𝟘X⊗ᴿ) a |> mult (a, zero) = zero
  (⊖N⊕ᴸ) a |> add (neg (a), a) = zero
  (⊖N⊕ᴿ) a |> add (a, neg (a)) = zero
  (⊗C) a b |> mult(a, b) = mult(b, a)
  (∂⊕) a : * |> x : * |- d0 (add (x, a)) = one
  (∂⊗) a : * |> x : * |- d0 (mult(a, x)) = a
  (∂C) f : (*,*).* |> x : *  y : * |- d1 (d0 (f[x,y])) = d0 (d1 (f[x,y]))
  (∂Ch₂) f : (*,*).*  g h : *.* |> x : * |- d0 (f[g[x], h[x]]) = add (mult(pd(z. f[z, h[x]], g[x]), d0(g[x])), mult(pd(z. f[g[x], z], h[x]), d0(h[x])))
  (∂Ch₁) f g : *.* |> x : * |- d0 (f[g[x]]) = mult (pd (z. f[z], g[x]), d0(g[x]))
-}

module PDiff.Signature where

open import SOAS.Context

open import SOAS.Common

open import SOAS.Syntax.Signature *T public
open import SOAS.Syntax.Build *T public

-- Operator symbols
data PDₒ : Set where
  zeroₒ addₒ oneₒ multₒ negₒ pdₒ : PDₒ

-- Term signature
PD:Sig : Signature PDₒ
PD:Sig = sig λ
  {  zeroₒ   → ⟼₀ *
  ;  addₒ    → (⊢₀ *) , (⊢₀ *) ⟼₂ *
  ;  oneₒ    → ⟼₀ *
  ;  multₒ   → (⊢₀ *) , (⊢₀ *) ⟼₂ *
  ;  negₒ    → (⊢₀ *) ⟼₁ *
  ;  pdₒ  → (* ⊢₁ *) , (⊢₀ *) ⟼₂ *
  }

open Signature PD:Sig public


open import bool

module list-merge-sort (A : Set) (_<A_ : A → A → 𝔹) where

open import braun-tree A _<A_
open import eq
open import list
open import nat
open import nat-thms

merge : (l1 l2 : 𝕃 A) → 𝕃 A
merge [] ys = ys
merge xs [] = xs
merge (x :: xs) (y :: ys) with x <A y
merge (x :: xs) (y :: ys) | tt = x :: (merge xs (y :: ys))
merge (x :: xs) (y :: ys) | ff = y :: (merge (x :: xs) ys)

merge-sort-h : ∀{n : ℕ} → braun-tree' n → 𝕃 A
merge-sort-h (bt'-leaf a) = [ a ]
merge-sort-h (bt'-node l r p) = merge (merge-sort-h l) (merge-sort-h r)

merge-sort : 𝕃 A → 𝕃 A
merge-sort [] = []
merge-sort (a :: as) with 𝕃-to-braun-tree' a as
merge-sort (a :: as) | t = merge-sort-h t


{-# OPTIONS --without-K --safe #-}
module Categories.Category.Discrete where

open import Categories.Category

open import Function
open import Relation.Binary.PropositionalEquality as ≡

Discrete : ∀ {a} (A : Set a) → Category a a a
Discrete A = record
  { Obj       = A
  ; _⇒_       = _≡_
  ; _≈_       = _≡_
  ; id        = refl
  ; _∘_       = flip ≡.trans
  ; assoc     = λ {_ _ _ _ g} → sym (trans-assoc g)
  ; sym-assoc = λ {_ _ _ _ g} → trans-assoc g
  ; identityˡ = λ {_ _ f} → trans-reflʳ f
  ; identityʳ = refl
  ; identity² = refl
  ; equiv     = isEquivalence
  ; ∘-resp-≈  = λ where
    refl refl → refl
  }


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

module Data.Vec where

open import Data.Nat
open import Data.Fin using (Fin; zero; suc)
open import Data.List as List using (List)
open import Data.Product using (_×_; _,_)

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

infixr 5 _∷_

data Vec (a : Set) : ℕ → Set where
  []  : Vec a zero
  _∷_ : ∀ {n} (x : a) (xs : Vec a n) → Vec a (suc n)

infix 4 _∈_ _[_]=_

data _∈_ {a : Set} : a → {n : ℕ} → Vec a n → Set where
  here  : ∀ {n} {x}   {xs : Vec a n} → x ∈ x ∷ xs
  there : ∀ {n} {x y} {xs : Vec a n} (x∈xs : x ∈ xs) → x ∈ y ∷ xs

data _[_]=_ {a : Set} : {n : ℕ} → Vec a n → Fin n → a → Set where
  here  : ∀ {n}     {x}   {xs : Vec a n} → x ∷ xs [ zero ]= x
  there : ∀ {n} {i} {x y} {xs : Vec a n}
          (xs[i]=x : xs [ i ]= x) → y ∷ xs [ suc i ]= x

------------------------------------------------------------------------
-- Some operations

head : ∀ {a n} → Vec a (1 + n) → a
head (x ∷ xs) = x

tail : ∀ {a n} → Vec a (1 + n) → Vec a n
tail (x ∷ xs) = xs

[_] : ∀ {a} → a → Vec a 1
[ x ] = x ∷ []

infixr 5 _++_

_++_ : ∀ {a m n} → Vec a m → Vec a n → Vec a (m + n)
[]       ++ ys = ys
(x ∷ xs) ++ ys = x ∷ (xs ++ ys)

map : ∀ {a b n} → (a → b) → Vec a n → Vec b n
map f []       = []
map f (x ∷ xs) = f x ∷ map f xs

zipWith :  ∀ {a b c n}
        → (a → b → c) → Vec a n → Vec b n → Vec c n
zipWith _⊕_ []       []       = []
zipWith _⊕_ (x ∷ xs) (y ∷ ys) = (x ⊕ y) ∷ zipWith _⊕_ xs ys

zip : ∀ {a b n} → Vec a n → Vec b n → Vec (a × b) n
zip = zipWith _,_

replicate : ∀ {a n} → a → Vec a n
replicate {n = zero}  x = []
replicate {n = suc n} x = x ∷ replicate x

foldr :  ∀ {a} (b : ℕ → Set) {m}
      → (∀ {n} → a → b n → b (suc n))
      → b zero
      → Vec a m → b m
foldr b _⊕_ n []       = n
foldr b _⊕_ n (x ∷ xs) = x ⊕ foldr b _⊕_ n xs

foldr₁ :  ∀ {a : Set} {m}
       → (a → a → a) → Vec a (suc m) → a
foldr₁ _⊕_ (x ∷ [])     = x
foldr₁ _⊕_ (x ∷ y ∷ ys) = x ⊕ foldr₁ _⊕_ (y ∷ ys)

foldl :  ∀ {a : Set} (b : ℕ → Set) {m}
      → (∀ {n} → b n → a → b (suc n))
      → b zero
      → Vec a m → b m
foldl b _⊕_ n []       = n
foldl b _⊕_ n (x ∷ xs) = foldl (λ n → b (suc n)) _⊕_ (n ⊕ x) xs

foldl₁ :  ∀ {a : Set} {m}
       → (a → a → a) → Vec a (suc m) → a
foldl₁ _⊕_ (x ∷ xs) = foldl _ _⊕_ x xs

concat : ∀ {a m n} → Vec (Vec a m) n → Vec a (n * m)
concat []         = []
concat (xs ∷ xss) = xs ++ concat xss

infixr 5 _++'_

data SplitAt {a : Set} (m : ℕ) {n : ℕ} : Vec a (m + n) → Set where
  _++'_ : (xs : Vec a m) (ys : Vec a n) → SplitAt m (xs ++ ys)

splitAt : ∀ {a} m {n} (xs : Vec a (m + n)) → SplitAt m xs
splitAt zero    xs                = [] ++' xs
splitAt (suc m) (x ∷ xs)          with splitAt m xs
splitAt (suc m) (x ∷ .(ys ++ zs)) | ys ++' zs = (x ∷ ys) ++' zs

take : ∀ {a} m {n} → Vec a (m + n) → Vec a m
take m xs with splitAt m xs
take m .(xs ++ ys) | xs ++' ys = xs

drop : ∀ {a} m {n} → Vec a (m + n) → Vec a n
drop m xs with splitAt m xs
drop m .(xs ++ ys) | xs ++' ys = ys

data Group {a : Set} (n k : ℕ) : Vec a (n * k) → Set where
  concat' : (xss : Vec (Vec a k) n) → Group n k (concat xss)

group : ∀ {a} n k (xs : Vec a (n * k)) → Group n k xs
group zero    k []                  = concat' []
group (suc n) k xs                  with splitAt k xs
group (suc n) k .(ys ++ zs)         | ys ++' zs with group n k zs
group (suc n) k .(ys ++ concat zss) | ys ++' ._ | concat' zss = concat' (ys ∷ zss)

reverse : ∀ {a n} → Vec a n → Vec a n
reverse {a} = foldl (Vec a) (λ rev x → x ∷ rev) []

sum : ∀ {n} → Vec ℕ n → ℕ
sum = foldr _ _+_ 0

toList : ∀ {a n} → Vec a n → List a
toList []       = List.[]
toList (x ∷ xs) = List._∷_ x (toList xs)

fromList : ∀ {a} → (xs : List a) → Vec a (List.length xs)
fromList List.[]         = []
fromList (List._∷_ x xs) = x ∷ fromList xs

-- Snoc.

infixl 5 _∷ʳ_

_∷ʳ_ : ∀ {a n} → Vec a n → a → Vec a (1 + n)
[]       ∷ʳ y = [ y ]
(x ∷ xs) ∷ʳ y = x ∷ (xs ∷ʳ y)

infixl 5 _∷ʳ'_

data InitLast {a : Set} (n : ℕ) : Vec a (1 + n) → Set where
  _∷ʳ'_ : (xs : Vec a n) (x : a) → InitLast n (xs ∷ʳ x)

initLast : ∀ {a n} (xs : Vec a (1 + n)) → InitLast n xs
initLast {n = zero}  (x ∷ [])         = [] ∷ʳ' x
initLast {n = suc n} (x ∷ xs)         with initLast xs
initLast {n = suc n} (x ∷ .(ys ∷ʳ y)) | ys ∷ʳ' y = (x ∷ ys) ∷ʳ' y

init : ∀ {a n} → Vec a (1 + n) → Vec a n
init xs with initLast xs
init .(ys ∷ʳ y) | ys ∷ʳ' y = ys

last : ∀ {a n} → Vec a (1 + n) → a
last xs with initLast xs
last .(ys ∷ʳ y) | ys ∷ʳ' y = y

infixl 1 _>>=_

_>>=_ : ∀ {A B m n} → Vec A m → (A → Vec B n) → Vec B (m * n)
xs >>= f = concat (map f xs)

infixl 4 _⊛_

_⊛_ : ∀ {A B m n} → Vec (A → B) m → Vec A n → Vec B (m * n)
fs ⊛ xs = fs >>= λ f → map f xs

-- Interleaves the two vectors.

infixr 5 _⋎_

_⋎_ : ∀ {A m n} → Vec A m → Vec A n → Vec A (m +⋎ n)
[]       ⋎ ys = ys
(x ∷ xs) ⋎ ys = x ∷ (ys ⋎ xs)

lookup : ∀ {a n} → Fin n → Vec a n → a
lookup zero    (x ∷ xs) = x
lookup (suc i) (x ∷ xs) = lookup i xs

-- Update.

infixl 6 _[_]≔_

_[_]≔_ : ∀ {A n} → Vec A n → Fin n → A → Vec A n
[]       [ ()    ]≔ y
(x ∷ xs) [ zero  ]≔ y = y ∷ xs
(x ∷ xs) [ suc i ]≔ y = x ∷ xs [ i ]≔ y

-- Generates a vector containing all elements in Fin n. This function
-- is not placed in Data.Fin since Data.Vec depends on Data.Fin.

allFin : ∀ n → Vec (Fin n) n
allFin zero    = []
allFin (suc n) = zero ∷ map suc (allFin n)


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

module Categories.Functor.Continuous where

open import Level
open import Data.Product using (Σ)

open import Categories.Category
open import Categories.Functor
import Categories.Category.Construction.Cones as Con
import Categories.Diagram.Cone.Properties as Conₚ
import Categories.Diagram.Limit as Lim
import Categories.Morphism as Mor

private
  variable
    o ℓ e : Level
    C D E J : Category o ℓ e

-- G preserves the limit of F.
LimitPreserving : (G : Functor C D) {F : Functor J C} (L : Lim.Limit F) → Set _
LimitPreserving {C = C} {D = D} G {F} L = Σ (Lim.Limit (G ∘F F)) λ L′ → G.F₀ (Lim.Limit.apex L) ≅ Lim.Limit.apex L′
  where module F = Functor F
        module G = Functor G
        open Mor D

-- continuous functors preserves all limits.
Continuous : ∀ o ℓ e (G : Functor C D) → Set _
Continuous {C = C} o ℓ e G = ∀ {J : Category o ℓ e} {F : Functor J C} (L : Lim.Limit F) → LimitPreserving G L


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

open import lib.Basics
open import lib.NType2
open import lib.types.TLevel
open import lib.types.Sigma
open import lib.types.Pi

module lib.types.Groupoid where

record GroupoidStructure {i j} {El : Type i} (Arr : El → El → Type j)
  (_ : ∀ x y → has-level 0 (Arr x y)) : Type (lmax i j) where
  field
    id : ∀ {x} → Arr x x
    inv : ∀ {x y} → Arr x y → Arr y x
    comp : ∀ {x y z} → Arr x y → Arr y z → Arr x z
    unit-l : ∀ {x y} (a : Arr x y) → comp id a == a
    unit-r : ∀ {x y} (a : Arr x y) → comp a id == a
    assoc   : ∀ {x y z w} (a : Arr x y) (b : Arr y z) (c : Arr z w)
              → comp (comp a b) c == comp a (comp b c)
    inv-r    : ∀ {x y} (a : Arr x y) → (comp a (inv a)) == id
    inv-l    : ∀ {x y } (a : Arr x y) → (comp (inv a) a) == id

record Groupoid {i j} : Type (lsucc (lmax i j)) where
  constructor groupoid
  field
    El : Type i
    Arr : El → El → Type j
    Arr-level : ∀ x y → has-level 0 (Arr x y)
    groupoid-struct : GroupoidStructure Arr Arr-level


module Ints.Add.Comm where

open import Ints
open import Nats.Add.Comm

open import Equality

------------------------------------------------------------------------
-- internal stuffs

private

  a+b=b+a : ∀ a b → a + b ≡ b + a
  a+b=b+a (+ a) (+ b) = + (a :+: b)
    ≡⟨ cong +_ (nat-add-comm a b) ⟩ + (b :+: a) QED
  a+b=b+a (+    a  ) (-[1+ b ])
    rewrite nat-add-comm a b = refl
  a+b=b+a (-[1+ a ]) (+    b  )
    rewrite nat-add-comm a b = refl
  a+b=b+a (-[1+ a ]) (-[1+ b ])
    rewrite nat-add-comm a b = refl

------------------------------------------------------------------------
-- public aliases

int-add-comm : ∀ a b → a + b ≡ b + a
int-add-comm = a+b=b+a


{-# OPTIONS --without-K --safe #-}
module Categories.Functor.Hom where

-- The Hom Functor from C.op × C to Setoids,
-- the two 1-argument version fixing one object
-- and some notation for the version where the category must be made explicit

open import Data.Product
open import Function using () renaming (_∘_ to _∙_)

open import Categories.Category
open import Categories.Functor hiding (id)
open import Categories.Functor.Bifunctor
open import Categories.Category.Instance.Setoids
import Categories.Morphism.Reasoning as MR

open import Relation.Binary using (Setoid)

module Hom {o ℓ e} (C : Category o ℓ e) where
  open Category C
  open MR C

  Hom[-,-] : Bifunctor (Category.op C) C (Setoids ℓ e)
  Hom[-,-] = record
    { F₀           = F₀′
    ; F₁           = λ where
      (f , g) → record
        { _⟨$⟩_ = λ h → g ∘ h ∘ f
        ; cong  = ∘-resp-≈ʳ ∙ ∘-resp-≈ˡ
        }
    ; identity     = identity′
    ; homomorphism = homomorphism′
    ; F-resp-≈     = F-resp-≈′
    }
    where F₀′ : Obj × Obj → Setoid ℓ e
          F₀′ (A , B) = hom-setoid {A} {B}
          open HomReasoning

          identity′ : {A : Obj × Obj} {x y : uncurry _⇒_ A} → x ≈ y → id ∘ x ∘ id ≈ y
          identity′ {A} {x} {y} x≈y = begin
            id ∘ x ∘ id ≈⟨ identityˡ ⟩
            x ∘ id      ≈⟨ identityʳ ⟩
            x           ≈⟨ x≈y ⟩
            y           ∎

          homomorphism′ : ∀ {X Y Z : Σ Obj (λ x → Obj)}
                            {f : proj₁ Y ⇒ proj₁ X × proj₂ X ⇒ proj₂ Y}
                            {g : proj₁ Z ⇒ proj₁ Y × proj₂ Y ⇒ proj₂ Z}
                            {x y : proj₁ X ⇒ proj₂ X} →
                            x ≈ y →
                            (proj₂ g ∘ proj₂ f) ∘ x ∘ proj₁ f ∘ proj₁ g ≈
                            proj₂ g ∘ (proj₂ f ∘ y ∘ proj₁ f) ∘ proj₁ g
          homomorphism′ {f = f₁ , f₂} {g₁ , g₂} {x} {y} x≈y = begin
            (g₂ ∘ f₂) ∘ x ∘ f₁ ∘ g₁   ≈˘⟨ refl⟩∘⟨ assoc ⟩
            (g₂ ∘ f₂) ∘ (x ∘ f₁) ∘ g₁ ≈⟨ pullʳ (pullˡ (∘-resp-≈ʳ (∘-resp-≈ˡ x≈y))) ⟩
            g₂ ∘ (f₂ ∘ y ∘ f₁) ∘ g₁   ∎

          F-resp-≈′ : ∀ {A B : Σ Obj (λ x → Obj)}
                        {f g : Σ (proj₁ B ⇒ proj₁ A) (λ x → proj₂ A ⇒ proj₂ B)} →
                        Σ (proj₁ f ≈ proj₁ g) (λ x → proj₂ f ≈ proj₂ g) →
                        {x y : proj₁ A ⇒ proj₂ A} →
                        x ≈ y → proj₂ f ∘ x ∘ proj₁ f ≈ proj₂ g ∘ y ∘ proj₁ g
          F-resp-≈′ {f = f₁ , f₂} {g₁ , g₂} (f₁≈g₁ , f₂≈g₂) {x} {y} x≈y = begin
            f₂ ∘ x ∘ f₁ ≈⟨ f₂≈g₂ ⟩∘⟨ x≈y ⟩∘⟨ f₁≈g₁ ⟩
            g₂ ∘ y ∘ g₁ ∎

  open Functor Hom[-,-]
  open Equiv
  open HomReasoning

  Hom[_,-] : Obj → Functor C (Setoids ℓ e)
  Hom[_,-] = appˡ Hom[-,-]

  Hom[-,_] : Obj → Contravariant C (Setoids ℓ e)
  Hom[-,_] = appʳ Hom[-,-]

-- Notation for when the ambient Category must be specified explicitly.
module _ {o ℓ e} (C : Category o ℓ e) where
  open Category C
  open Hom C

  Hom[_][-,-] : Bifunctor (Category.op C) C (Setoids ℓ e)
  Hom[_][-,-] = Hom[-,-]

  Hom[_][_,-] : Obj → Functor C (Setoids ℓ e)
  Hom[_][_,-] B = Hom[ B ,-]

  Hom[_][-,_] : Obj → Contravariant C (Setoids ℓ e)
  Hom[_][-,_] B = Hom[-, B ]


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

open import Algebra

module Algebra.Props.DistributiveLattice
         (dl : DistributiveLattice)
         where

open DistributiveLattice dl
import Algebra.Props.Lattice as L; open L lattice public
open import Algebra.Structures
import Algebra.FunctionProperties as P; open P _≈_
import Relation.Binary.EqReasoning as EqR; open EqR setoid
open import Data.Function
open import Data.Product

∨-∧-distrib : _∨_ DistributesOver _∧_
∨-∧-distrib = ∨-∧-distribˡ , ∨-∧-distribʳ
  where
  ∨-∧-distribˡ : _∨_ DistributesOverˡ _∧_
  ∨-∧-distribˡ x y z = begin
    x ∨ y ∧ z          ≈⟨ ∨-comm _ _ ⟩
    y ∧ z ∨ x          ≈⟨ ∨-∧-distribʳ _ _ _ ⟩
    (y ∨ x) ∧ (z ∨ x)  ≈⟨ ∨-comm _ _ ⟨ ∧-pres-≈ ⟩ ∨-comm _ _ ⟩
    (x ∨ y) ∧ (x ∨ z)  ∎

∧-∨-distrib : _∧_ DistributesOver _∨_
∧-∨-distrib = ∧-∨-distribˡ , ∧-∨-distribʳ
  where
  ∧-∨-distribˡ : _∧_ DistributesOverˡ _∨_
  ∧-∨-distribˡ x y z = begin
    x ∧ (y ∨ z)                ≈⟨ sym (proj₂ absorptive _ _) ⟨ ∧-pres-≈ ⟩ refl ⟩
    (x ∧ (x ∨ y)) ∧ (y ∨ z)    ≈⟨ (refl ⟨ ∧-pres-≈ ⟩ ∨-comm _ _) ⟨ ∧-pres-≈ ⟩ refl ⟩
    (x ∧ (y ∨ x)) ∧ (y ∨ z)    ≈⟨ ∧-assoc _ _ _ ⟩
    x ∧ ((y ∨ x) ∧ (y ∨ z))    ≈⟨ refl ⟨ ∧-pres-≈ ⟩ sym (proj₁ ∨-∧-distrib _ _ _) ⟩
    x ∧ (y ∨ x ∧ z)            ≈⟨ sym (proj₁ absorptive _ _) ⟨ ∧-pres-≈ ⟩ refl ⟩
    (x ∨ x ∧ z) ∧ (y ∨ x ∧ z)  ≈⟨ sym $ proj₂ ∨-∧-distrib _ _ _ ⟩
    x ∧ y ∨ x ∧ z              ∎

  ∧-∨-distribʳ : _∧_ DistributesOverʳ _∨_
  ∧-∨-distribʳ x y z = begin
    (y ∨ z) ∧ x    ≈⟨ ∧-comm _ _ ⟩
    x ∧ (y ∨ z)    ≈⟨ ∧-∨-distribˡ _ _ _ ⟩
    x ∧ y ∨ x ∧ z  ≈⟨ ∧-comm _ _ ⟨ ∨-pres-≈ ⟩ ∧-comm _ _ ⟩
    y ∧ x ∨ z ∧ x  ∎

-- The dual construction is also a distributive lattice.

∧-∨-isDistributiveLattice : IsDistributiveLattice _≈_ _∧_ _∨_
∧-∨-isDistributiveLattice = record
  { isLattice    = ∧-∨-isLattice
  ; ∨-∧-distribʳ = proj₂ ∧-∨-distrib
  }

∧-∨-distributiveLattice : DistributiveLattice
∧-∨-distributiveLattice = record
  { _∧_                   = _∨_
  ; _∨_                   = _∧_
  ; isDistributiveLattice = ∧-∨-isDistributiveLattice
  }