-- 2010-09-06 Andreas

module IrrelevantApplication where

-- an unknown function that does not use its second argument

postulate
  f : {A B : Set} -> A -> .B -> A

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

-- the second argument is irrelevant for equality

proofIrr : {A : Set}{x y z : A} -> f x y == f x z
proofIrr = refl

-- irrelevant arguments (like x) may appear as arguments to irrelevant func.s

id : {A B : Set} -> (.A -> B) -> .A -> B
id g x = g x

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

-- t = pId id

record Prod (A B : Set) : Set where
  constructor _,_
  field
    fst : A
    snd : B

-- matching an irrelevant record is ok as long as fields are use irrelevantly
irrElim : {A B C : Set} → .(Prod A B) → (.A → .B → C) → C
irrElim (a , b) f = f a b

lemma : {A B C : Set}(a : A)(b : B)(f : .A -> .B -> C) -> irrElim (a , b) f == f a b
lemma a b f = refl


module Numeral.Finite.Conversions where

import      Lvl
open import Data using (Empty ; Unit ; <>)
open import Data.Boolean using (Bool ; 𝐹 ; 𝑇)
open import Data.Tuple using (_,_)
open import Logic.Propositional using (_↔_)
open import Numeral.Finite
open import Numeral.Natural
open import Syntax.Number

private variable ℓ : Lvl.Level

empty : 𝕟(0) ↔ Empty{ℓ}
empty = (\()) , (\())

unit : 𝕟(1) ↔ Unit{ℓ}
unit = (\{<> → 0}) , (\{𝟎 → <>})

bool : 𝕟(2) ↔ Bool
bool = (\{𝐹 → 0 ; 𝑇 → 1}) , (\{𝟎 → 𝐹 ; (𝐒(𝟎)) → 𝑇})


------------------------------------------------------------------------
-- The Agda standard library
--
-- Some derivable properties
------------------------------------------------------------------------

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

open import Algebra

module Algebra.Properties.AbelianGroup
  {a ℓ} (G : AbelianGroup a ℓ) where

open AbelianGroup G
open import Function
open import Relation.Binary.Reasoning.Setoid setoid

------------------------------------------------------------------------
-- Publicly re-export group properties

open import Algebra.Properties.Group group public

------------------------------------------------------------------------
-- Properties of abelian groups

xyx⁻¹≈y : ∀ x y → x ∙ y ∙ x ⁻¹ ≈ y
xyx⁻¹≈y x y = begin
  x ∙ y ∙ x ⁻¹    ≈⟨ ∙-congʳ $ comm _ _ ⟩
  y ∙ x ∙ x ⁻¹    ≈⟨ assoc _ _ _ ⟩
  y ∙ (x ∙ x ⁻¹)  ≈⟨ ∙-congˡ $ inverseʳ _ ⟩
  y ∙ ε           ≈⟨ identityʳ _ ⟩
  y               ∎

⁻¹-∙-comm : ∀ x y → x ⁻¹ ∙ y ⁻¹ ≈ (x ∙ y) ⁻¹
⁻¹-∙-comm x y = begin
  x ⁻¹ ∙ y ⁻¹ ≈˘⟨ ⁻¹-anti-homo-∙ y x ⟩
  (y ∙ x) ⁻¹  ≈⟨ ⁻¹-cong $ comm y x ⟩
  (x ∙ y) ⁻¹  ∎


import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; sym; trans; cong; cong₂; _≢_)
open import Data.Empty using (⊥; ⊥-elim)
open import Data.List using (List; []; _∷_; takeWhile; dropWhile)
open import Data.List.All using (All; []; _∷_)
open import Data.Bool using (Bool; true; false; T)
open import Data.Unit using (⊤; tt)
open import Relation.Nullary using (¬_)
open import Function using (_∘_)

module TakeDropBool (A : Set) (P : A → Bool) where

  Head : ∀ {A : Set} → (A → Bool) → List A → Set
  Head P []       =  ⊥
  Head P (c ∷ s)  =  T (P c)

  data Book (x : A) (b : Bool) : Set where
    book : P x ≡ b → Book x b

  boo : ∀ (x : A) → Book x (P x)
  boo x = book refl

  dropWhile-lemma : ∀ (s : List A) → ¬ Head P (dropWhile P s)
  dropWhile-lemma []                                         =  λ()
  dropWhile-lemma (c ∷ s) with P c    | boo c
  ...                        | true   | _                    =  dropWhile-lemma s
  ...                        | false  | book eq  rewrite eq  =  λ()

  takeWhile-lemma : ∀ (s : List A) → All (T ∘ P) (takeWhile P s)
  takeWhile-lemma []                                         =  []
  takeWhile-lemma (c ∷ s) with P c    | boo c
  ...                        | false  | _                    =  []
  ...                        | true   | book eq rewrite eq   =  {! tt!} ∷ takeWhile-lemma s

{-  Hole 0
Goal: T (P c)
————————————————————————————————————————————————————————————
s  : List A
eq : P c ≡ true
c  : A
P  : A → Bool
A  : Set
-}


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

module Categories.Functor.Instance.01-Truncation where

-- (0,1)-trucation of categories as a functor from Cats to Posets.
--
-- This is the right-adjoint of the inclusion functor from Posets to
-- Cats (see Categories.Functor.Adjoint.Instance.01-Truncation)

open import Level using (_⊔_)
open import Function using (flip)
open import Data.Product as Prod using (_,_; _×_)
open import Relation.Binary.OrderMorphism using (_⇒-Poset_)
open import Relation.Binary using (Poset)

open import Categories.Category using (Category; _[_≈_])
open import Categories.Functor hiding (id)
open import Categories.Category using (Category)
open import Categories.Category.Instance.Cats using (Cats)
open import Categories.Category.Instance.Posets using (Posets)
import Categories.Morphism as Morphism
open import Categories.NaturalTransformation.NaturalIsomorphism
  using (NaturalIsomorphism; _≃_)

Trunc : ∀ {o ℓ e} → Functor (Cats o ℓ e) (Posets o ℓ ℓ)
Trunc {o} {ℓ} {e} = record
   { F₀           = Trunc₀
   ; F₁           = Trunc₁
   ; identity     = λ {C} → id C , id C
   ; homomorphism = λ {_ _ C} → id C , id C
   ; F-resp-≈     = TruncRespNI
   }
   where
     open Functor

     -- The choice of _≈_ below may seem a bit arbitrary.  The
     -- rationale is as follows:
     --
     -- Since we are defining an Agda stdlib-style poset, we have to
     -- pick an equality on the carrier set, i.e. on objects.  But
     -- objects do not come with an equality in this library (that's
     -- considered evil), so we pick isomorphism.  In a poset, any
     -- pair of morphisms f : X ⇒ Y and g : Y ⇒ X constitute an
     -- isomorphism between X and Y.  Hence the definition of _≈_.

     Trunc₀ : Category o ℓ e → Poset o ℓ ℓ
     Trunc₀ C = record
       { Carrier           = Obj
       ; _≈_               = λ x y → x ⇒ y × y ⇒ x
       ; _≤_               = _⇒_
       ; isPartialOrder    = record
         { isPreorder      = record
           { isEquivalence = record
             { refl        = id , id
             ; sym         = Prod.swap
             ; trans       = Prod.zip (flip _∘_) _∘_
             }
           ; reflexive     = Prod.proj₁
           ; trans         = flip _∘_
           }
         ; antisym         = _,_
         }
       }
       where open Category C

     Trunc₁ : ∀ {C D} → Functor C D → Trunc₀ C ⇒-Poset Trunc₀ D
     Trunc₁ F = record { fun = F₀ F ; monotone = F₁ F }

     TruncRespNI : ∀ {C D : Category o ℓ e} {F G : Functor C D} →
                   F ≃ G → Posets o ℓ ℓ [ Trunc₁ F ≈ Trunc₁ G ]
     TruncRespNI μ {X} = ⇒.η X , ⇐.η X
       where open NaturalIsomorphism μ

     open Category


{- Binary natural numbers (Anders Mörtberg, Jan. 2019)

This file defines two representations of binary numbers. We prove that
they are equivalent to unary numbers and univalence is then used to
transport both programs and properties between the representations.
This is an example of how having computational univalence can be
useful for practical programming.

The first definition is [Binℕ] and the numbers are essentially lists
of 0/1 with no trailing zeroes (in little-endian format). The main
definitions and examples are:

- Equivalence between Binℕ and ℕ ([Binℕ≃ℕ]) with an equality obtained
  using univalence ([Binℕ≡ℕ]).

- Addition on Binℕ defined by transporting addition on ℕ to Binℕ
  ([_+Binℕ_]) along Binℕ≡ℕ together with a proof that addition on Binℕ
  is associative obtained by transporting the proof for ℕ ([+Binℕ-assoc]).

- Functions testing whether a binary number is odd or even in O(1)
  ([oddBinℕ], [evenBinℕ]) and the corresponding functions for ℕ
  obtained by transport. Proof that odd numbers are not even
  transported from Binℕ to ℕ ([oddℕnotEvenℕ]).

- An example of the structure identity principle for natural number
  structures ([NatImpl]). We first prove that Binℕ≡ℕ lifts to natural
  number structures ([NatImplℕ≡Binℕ]) and we then use this to
  transport "+-suc : m + suc n ≡ suc (m + n)" from ℕ to Binℕ ([+Binℕ-suc]).

- An example of program/data refinement using the structure identity
  principle where we transport a property that is infeasible to prove
  by computation for ℕ ([propDoubleℕ]):

      2^20 * 2^10 = 2^5 * (2^15 * 2^10)

  from the corresponding result on Binℕ which is proved instantly by
  refl ([propDoubleBinℕ]).

These examples are inspired from an old cubicaltt formalization:

https://github.com/mortberg/cubicaltt/blob/master/examples/binnat.ctt

which itself is based on an even older cubical formalization (from 2014):

https://github.com/simhu/cubical/blob/master/examples/binnat.cub

The second representation is more non-standard and inspired by:

https://github.com/RedPRL/redtt/blob/master/library/cool/nats.red

Only some of the experiments have been done for this representation,
but it has the virtue of being a bit simpler to prove equivalent to
ℕ. The same representation can be found in:

http://www.cs.bham.ac.uk/~mhe/agda-new/BinaryNaturals.html

-}
{-# OPTIONS --cubical --no-exact-split --safe #-}
module Cubical.Data.BinNat.BinNat where

open import Cubical.Core.Glue

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Isomorphism

open import Cubical.Data.Nat
open import Cubical.Data.Bool
open import Cubical.Data.Empty

open import Cubical.Relation.Nullary

-- Positive binary numbers
data Pos : Type₀ where
  pos1 : Pos
  x0   : Pos → Pos
  x1   : Pos → Pos

sucPos : Pos → Pos
sucPos pos1    = x0 pos1
sucPos (x0 ps) = x1 ps
sucPos (x1 ps) = x0 (sucPos ps)

Pos→ℕ : Pos → ℕ
Pos→ℕ pos1    = suc zero
Pos→ℕ (x0 ps) = doubleℕ (Pos→ℕ ps)
Pos→ℕ (x1 ps) = suc (doubleℕ (Pos→ℕ ps))

posInd : {P : Pos → Type₀} → P pos1 → ((p : Pos) → P p → P (sucPos p)) → (p : Pos) → P p
posInd {P} h1 hs ps = f ps
  where
  H : (p : Pos) → P (x0 p) → P (x0 (sucPos p))
  H p hx0p = hs (x1 p) (hs (x0 p) hx0p)

  f : (ps : Pos) → P ps
  f pos1    = h1
  f (x0 ps) = posInd (hs pos1 h1) H ps
  f (x1 ps) = hs (x0 ps) (posInd (hs pos1 h1) H ps)

Pos→ℕsucPos : (p : Pos) → Pos→ℕ (sucPos p) ≡ suc (Pos→ℕ p)
Pos→ℕsucPos pos1   = refl
Pos→ℕsucPos (x0 p) = refl
Pos→ℕsucPos (x1 p) = λ i → doubleℕ (Pos→ℕsucPos p i)

zero≠Pos→ℕ : (p : Pos) → ¬ (zero ≡ Pos→ℕ p)
zero≠Pos→ℕ p = posInd (λ prf → znots prf) hs p
  where
  hs : (p : Pos) → ¬ (zero ≡ Pos→ℕ p) → zero ≡ Pos→ℕ (sucPos p) → ⊥
  hs p neq ieq = ⊥-elim (znots (ieq ∙ (Pos→ℕsucPos p)))

ℕ→Pos : ℕ → Pos
ℕ→Pos zero          = pos1
ℕ→Pos (suc zero)    = pos1
ℕ→Pos (suc (suc n)) = sucPos (ℕ→Pos (suc n))

ℕ→PosSuc : ∀ n → ¬ (zero ≡ n) → ℕ→Pos (suc n) ≡ sucPos (ℕ→Pos n)
ℕ→PosSuc zero neq    = ⊥-elim (neq refl)
ℕ→PosSuc (suc n) neq = refl

Pos→ℕ→Pos : (p : Pos) → ℕ→Pos (Pos→ℕ p) ≡ p
Pos→ℕ→Pos p = posInd refl hs p
  where
  hs : (p : Pos) → ℕ→Pos (Pos→ℕ p) ≡ p → ℕ→Pos (Pos→ℕ (sucPos p)) ≡ sucPos p
  hs p hp =
    ℕ→Pos (Pos→ℕ (sucPos p)) ≡⟨ cong ℕ→Pos (Pos→ℕsucPos p) ⟩
    ℕ→Pos (suc (Pos→ℕ p))    ≡⟨ ℕ→PosSuc (Pos→ℕ p) (zero≠Pos→ℕ p) ⟩
    sucPos (ℕ→Pos (Pos→ℕ p)) ≡⟨ cong sucPos hp ⟩
    sucPos p ∎

ℕ→Pos→ℕ : (n : ℕ) → Pos→ℕ (ℕ→Pos (suc n)) ≡ suc n
ℕ→Pos→ℕ zero    = refl
ℕ→Pos→ℕ (suc n) =
  Pos→ℕ (sucPos (ℕ→Pos (suc n))) ≡⟨ Pos→ℕsucPos (ℕ→Pos (suc n)) ⟩
  suc (Pos→ℕ (ℕ→Pos (suc n)))    ≡⟨ cong suc (ℕ→Pos→ℕ n) ⟩
  suc (suc n) ∎

-- Binary numbers
data Binℕ : Type₀ where
  binℕ0   : Binℕ
  binℕpos : Pos → Binℕ

ℕ→Binℕ : ℕ → Binℕ
ℕ→Binℕ zero    = binℕ0
ℕ→Binℕ (suc n) = binℕpos (ℕ→Pos (suc n))

Binℕ→ℕ : Binℕ → ℕ
Binℕ→ℕ binℕ0       = zero
Binℕ→ℕ (binℕpos x) = Pos→ℕ x

ℕ→Binℕ→ℕ : (n : ℕ) → Binℕ→ℕ (ℕ→Binℕ n) ≡ n
ℕ→Binℕ→ℕ zero          = refl
ℕ→Binℕ→ℕ (suc zero)    = refl
ℕ→Binℕ→ℕ (suc (suc n)) =
    Pos→ℕ (sucPos (ℕ→Pos (suc n))) ≡⟨ Pos→ℕsucPos (ℕ→Pos (suc n)) ⟩
    suc (Pos→ℕ (ℕ→Pos (suc n)))    ≡⟨ cong suc (ℕ→Binℕ→ℕ (suc n)) ⟩
    suc (suc n) ∎

Binℕ→ℕ→Binℕ : (n : Binℕ) → ℕ→Binℕ (Binℕ→ℕ n) ≡ n
Binℕ→ℕ→Binℕ binℕ0 = refl
Binℕ→ℕ→Binℕ (binℕpos p) = posInd refl (λ p _ → rem p) p
  where
  rem : (p : Pos) → ℕ→Binℕ (Pos→ℕ (sucPos p)) ≡ binℕpos (sucPos p)
  rem p =
    ℕ→Binℕ (Pos→ℕ (sucPos p))       ≡⟨ cong ℕ→Binℕ (Pos→ℕsucPos p) ⟩
    binℕpos (ℕ→Pos (suc (Pos→ℕ p))) ≡⟨ cong binℕpos ((ℕ→PosSuc (Pos→ℕ p) (zero≠Pos→ℕ p)) ∙
                                                              (cong sucPos (Pos→ℕ→Pos p))) ⟩
    binℕpos (sucPos p) ∎

Binℕ≃ℕ : Binℕ ≃ ℕ
Binℕ≃ℕ = isoToEquiv (iso Binℕ→ℕ ℕ→Binℕ ℕ→Binℕ→ℕ Binℕ→ℕ→Binℕ)

-- Use univalence (in fact only "ua") to get an equality from the
-- above equivalence
Binℕ≡ℕ : Binℕ ≡ ℕ
Binℕ≡ℕ = ua Binℕ≃ℕ

sucBinℕ : Binℕ → Binℕ
sucBinℕ binℕ0       = binℕpos pos1
sucBinℕ (binℕpos x) = binℕpos (sucPos x)

Binℕ→ℕsuc : (x : Binℕ) → suc (Binℕ→ℕ x) ≡ Binℕ→ℕ (sucBinℕ x)
Binℕ→ℕsuc binℕ0       = refl
Binℕ→ℕsuc (binℕpos x) = sym (Pos→ℕsucPos x)

-- We can transport addition on ℕ to Binℕ
_+Binℕ_ : Binℕ → Binℕ → Binℕ
_+Binℕ_ = transport (λ i → Binℕ≡ℕ (~ i) → Binℕ≡ℕ (~ i) → Binℕ≡ℕ (~ i)) _+_

-- Test: 4 + 1 = 5
private
  _ : binℕpos (x0 (x0 pos1)) +Binℕ binℕpos pos1 ≡ binℕpos (x1 (x0 pos1))
  _ = refl

-- It is easy to test if binary numbers are odd
oddBinℕ : Binℕ → Bool
oddBinℕ binℕ0            = false
oddBinℕ (binℕpos pos1)   = true
oddBinℕ (binℕpos (x0 _)) = false
oddBinℕ (binℕpos (x1 _)) = true

evenBinℕ : Binℕ → Bool
evenBinℕ n = oddBinℕ (sucBinℕ n)

-- And prove the following property (without induction)
oddBinℕnotEvenBinℕ : (n : Binℕ) → oddBinℕ n ≡ not (evenBinℕ n)
oddBinℕnotEvenBinℕ binℕ0            = refl
oddBinℕnotEvenBinℕ (binℕpos pos1)   = refl
oddBinℕnotEvenBinℕ (binℕpos (x0 x)) = refl
oddBinℕnotEvenBinℕ (binℕpos (x1 x)) = refl

-- It is also easy to define and prove the property for unary numbers,
-- however the definition uses recursion and the proof induction
private
  oddn : ℕ → Bool
  oddn zero    = true
  oddn (suc x) = not (oddn x)

  evenn : ℕ → Bool
  evenn n = not (oddn n)

  oddnSuc : (n : ℕ) → oddn n ≡ not (evenn n)
  oddnSuc zero    = refl
  oddnSuc (suc n) = cong not (oddnSuc n)

-- So what we can do instead is to transport the odd test from Binℕ to
-- ℕ along the equality
oddℕ : ℕ → Bool
oddℕ = transport (λ i → Binℕ≡ℕ i → Bool) oddBinℕ

evenℕ : ℕ → Bool
evenℕ = transport (λ i → Binℕ≡ℕ i → Bool) evenBinℕ

-- We can then also transport the property
oddℕnotEvenℕ : (n : ℕ) → oddℕ n ≡ not (evenℕ n)
oddℕnotEvenℕ =
  let -- We first build a path from oddBinℕ to oddℕ. When i=1 this is
      -- "transp (λ j → Binℕ≡ℕ j → Bool) i0 oddBinℕ" (i.e. oddℕ)
      oddp : PathP (λ i → Binℕ≡ℕ i → Bool) oddBinℕ oddℕ
      oddp i = transp (λ j → Binℕ≡ℕ (i ∧ j) → Bool) (~ i) oddBinℕ

      -- We then build a path from evenBinℕ to evenℕ
      evenp : PathP (λ i → Binℕ≡ℕ i → Bool) evenBinℕ evenℕ
      evenp i = transp (λ j → Binℕ≡ℕ (i ∧ j) → Bool) (~ i) evenBinℕ
  in -- Then transport oddBinℕnotEvenBinℕ in a suitable equality type
     -- When i=0 this is "(n : Binℕ) → oddBinℕ n ≡ not (evenBinℕ n)"
     -- When i=1 this is "(n : ℕ) → oddℕ n ≡ not (evenℕ n)"
     transport (λ i → (n : Binℕ≡ℕ i) → oddp i n ≡ not (evenp i n)) oddBinℕnotEvenBinℕ

-- We can do the same for natural numbers:

-- First construct the path
addp : PathP (λ i → Binℕ≡ℕ (~ i) → Binℕ≡ℕ (~ i) → Binℕ≡ℕ (~ i)) _+_ _+Binℕ_
addp i = transp (λ j → Binℕ≡ℕ (~ i ∨ ~ j) → Binℕ≡ℕ (~ i ∨ ~ j) → Binℕ≡ℕ (~ i ∨ ~ j)) (~ i) _+_

-- Then transport associativity:
+Binℕ-assoc : ∀ m n o → m +Binℕ (n +Binℕ o) ≡ (m +Binℕ n) +Binℕ o
+Binℕ-assoc =
  transport (λ i → (m n o : Binℕ≡ℕ (~ i))
                 → addp i m (addp i n o) ≡ addp i (addp i m n) o) +-assoc

-- We can also define what it means to be an implementation of natural
-- numbers and use this to transport properties between different
-- implementation of natural numbers. This can be seen as a special
-- case of the structure identity principle: any property that holds
-- for one structure also holds for an equivalent one.

-- An implementation of natural numbers (i.e. a "natural number
-- structure") has a zero and successor.
record NatImpl (A : Type₀) : Type₀ where
  field
    z : A
    s : A → A
open NatImpl

NatImplℕ : NatImpl ℕ
z NatImplℕ = zero
s NatImplℕ = suc

NatImplBinℕ : NatImpl Binℕ
z NatImplBinℕ = binℕ0
s NatImplBinℕ = sucBinℕ

-- Using the equality between binary and unary numbers we can get an
-- equality between the two implementations of the NatImpl interface
NatImplℕ≡Binℕ : PathP (λ i → NatImpl (Binℕ≡ℕ (~ i))) NatImplℕ NatImplBinℕ
z (NatImplℕ≡Binℕ i) = transp (λ j → Binℕ≡ℕ (~ i ∨ ~ j)) (~ i) zero
s (NatImplℕ≡Binℕ i) =
  λ x → glue (λ { (i = i0) → suc x
                ; (i = i1) → sucBinℕ x })
             -- We need to do use and hcomp to do and endpoint
             -- correction as "suc (unglue x)" connects "suc x"
             -- with "suc (Binℕ→ℕ x)" along i (which makes sense as
             -- x varies from ℕ to Binℕ along i), but we need
             -- something from "suc x" to "Binℕ→ℕ (sucBinℕ x)" for
             -- the glue to be well-formed
             (hcomp (λ j → λ { (i = i0) → suc x
                             ; (i = i1) → Binℕ→ℕsuc x j })
                    (suc (unglue (i ∨ ~ i) x)))

-- We then use this to transport +-suc from unary to binary numbers
+Binℕ-suc : ∀ m n → m +Binℕ sucBinℕ n ≡ sucBinℕ (m +Binℕ n)
+Binℕ-suc =
  transport (λ i → (m n : Binℕ≡ℕ (~ i))
                 → addp i m (NatImplℕ≡Binℕ i .s n) ≡ NatImplℕ≡Binℕ i .s (addp i m n)) +-suc

-- Doubling experiment: we define a notion of "doubling structure" and
-- transport a proof that is proved directly using refl for binary
-- numbers to unary numbers. This is an example of program/data
-- refinement: we can use univalence to prove properties about
-- inefficient data-structures using efficient ones.

-- Doubling structures
record Double {ℓ} (A : Type ℓ) : Type (ℓ-suc ℓ) where
  field
    -- doubling function computing 2 * x
    double : A → A
    -- element to double
    elt : A
open Double

-- Compute: 2^n * x
doubles : ∀ {ℓ} {A : Type ℓ} (D : Double A) → ℕ → A → A
doubles D n x = iter n (double D) x

Doubleℕ : Double ℕ
double Doubleℕ = doubleℕ
elt Doubleℕ    = n1024
  where
  -- 1024 = 2^8 * 2^2 = 2^10
  n1024 : ℕ
  n1024 = doublesℕ 8 4

-- The doubling operation on binary numbers is O(1), while for unary
-- numbers it is O(n). What is of course even more problematic is that
-- we cannot handle very big unary natural numbers, but with binary
-- there is no problem to represent very big numbers
doubleBinℕ : Binℕ → Binℕ
doubleBinℕ binℕ0       = binℕ0
doubleBinℕ (binℕpos x) = binℕpos (x0 x)

DoubleBinℕ : Double Binℕ
double DoubleBinℕ = doubleBinℕ
elt DoubleBinℕ    = bin1024
  where
  -- 1024 = 2^10 = 10000000000₂
  bin1024 : Binℕ
  bin1024 = binℕpos (x0 (x0 (x0 (x0 (x0 (x0 (x0 (x0 (x0 (x0 pos1))))))))))

-- As these function don't commute strictly we have to prove it
-- separately and insert it in the proof of DoubleBinℕ≡Doubleℕ below
-- (just like we had to in NatImplℕ≡NatImplBinℕ
Binℕ→ℕdouble : (x : Binℕ) → doubleℕ (Binℕ→ℕ x) ≡ Binℕ→ℕ (doubleBinℕ x)
Binℕ→ℕdouble binℕ0       = refl
Binℕ→ℕdouble (binℕpos x) = refl

-- We use the equality between Binℕ and ℕ to get an equality of
-- doubling structures
DoubleBinℕ≡Doubleℕ : PathP (λ i → Double (Binℕ≡ℕ i)) DoubleBinℕ Doubleℕ
double (DoubleBinℕ≡Doubleℕ i) =
  λ x → glue (λ { (i = i0) → doubleBinℕ x
                ; (i = i1) → doubleℕ x })
             (hcomp (λ j → λ { (i = i0) → Binℕ→ℕdouble x j
                             ; (i = i1) → doubleℕ x })
                    (doubleℕ (unglue (i ∨ ~ i) x)))
elt (DoubleBinℕ≡Doubleℕ i) = transp (λ j → Binℕ≡ℕ (i ∨ ~ j)) i (Doubleℕ .elt)

-- We can now use transport to prove a property that is too slow to
-- check with unary numbers. We define the property we want to check
-- as a record so that Agda does not try to unfold it eagerly.
record propDouble {ℓ} {A : Type ℓ} (D : Double A) : Type ℓ where
  field
  -- 2^20 * e = 2^5 * (2^15 * e)
    proof : doubles D 20 (elt D) ≡ doubles D 5 (doubles D 15 (elt D))
open propDouble

-- The property we want to prove takes too long to typecheck for ℕ:
-- propDoubleℕ : propDouble Doubleℕ
-- propDoubleℕ = refl

-- With binary numbers it is instant
propDoubleBinℕ : propDouble DoubleBinℕ
proof propDoubleBinℕ = refl

-- By transporting the proof along the equality we then get it for
-- unary numbers
propDoubleℕ : propDouble Doubleℕ
propDoubleℕ = transport (λ i → propDouble (DoubleBinℕ≡Doubleℕ i)) propDoubleBinℕ

--------------------------------------------------------------------------------
--
-- Alternative encoding of binary natural numbers inspired by:
-- https://github.com/RedPRL/redtt/blob/master/library/cool/nats.red
--
-- This representation makes the equivalence with ℕ a bit easier to
-- prove, but the doubling example wouldn't work as nicely as we
-- cannot define it as an O(1) operation

data binnat : Type₀ where
  zero     : binnat            -- 0
  consOdd  : binnat → binnat   -- 2^n + 1
  consEven : binnat → binnat   -- 2^{n+1}

binnat→ℕ : binnat → ℕ
binnat→ℕ zero         = 0
binnat→ℕ (consOdd n)  = suc (doubleℕ (binnat→ℕ n))
binnat→ℕ (consEven n) = suc (suc (doubleℕ (binnat→ℕ n)))

suc-binnat : binnat → binnat
suc-binnat zero         = consOdd zero
suc-binnat (consOdd n)  = consEven n
suc-binnat (consEven n) = consOdd (suc-binnat n)

ℕ→binnat : ℕ → binnat
ℕ→binnat zero    = zero
ℕ→binnat (suc n) = suc-binnat (ℕ→binnat n)

binnat→ℕ-suc : (n : binnat) → binnat→ℕ (suc-binnat n) ≡ suc (binnat→ℕ n)
binnat→ℕ-suc zero         = refl
binnat→ℕ-suc (consOdd n)  = refl
binnat→ℕ-suc (consEven n) = λ i → suc (doubleℕ (binnat→ℕ-suc n i))

ℕ→binnat→ℕ : (n : ℕ) → binnat→ℕ (ℕ→binnat n) ≡ n
ℕ→binnat→ℕ zero    = refl
ℕ→binnat→ℕ (suc n) = (binnat→ℕ-suc (ℕ→binnat n)) ∙ (cong suc (ℕ→binnat→ℕ n))

suc-ℕ→binnat-double : (n : ℕ) → suc-binnat (ℕ→binnat (doubleℕ n)) ≡ consOdd (ℕ→binnat n)
suc-ℕ→binnat-double zero    = refl
suc-ℕ→binnat-double (suc n) = λ i → suc-binnat (suc-binnat (suc-ℕ→binnat-double n i))

binnat→ℕ→binnat : (n : binnat) → ℕ→binnat (binnat→ℕ n) ≡ n
binnat→ℕ→binnat zero        = refl
binnat→ℕ→binnat (consOdd n) =
           (suc-ℕ→binnat-double (binnat→ℕ n)) ∙ (cong consOdd (binnat→ℕ→binnat n))
binnat→ℕ→binnat (consEven n) =
           (λ i → suc-binnat (suc-ℕ→binnat-double (binnat→ℕ n) i)) ∙ (cong consEven (binnat→ℕ→binnat n))

ℕ≃binnat : ℕ ≃ binnat
ℕ≃binnat = isoToEquiv (iso ℕ→binnat binnat→ℕ binnat→ℕ→binnat ℕ→binnat→ℕ)

ℕ≡binnat : ℕ ≡ binnat
ℕ≡binnat = ua ℕ≃binnat

-- We can transport addition on ℕ to binnat
_+binnat_ : binnat → binnat → binnat
_+binnat_ = transport (λ i → ℕ≡binnat i → ℕ≡binnat i → ℕ≡binnat i) _+_

-- Test: 4 + 1 = 5
_ : consEven (consOdd zero) +binnat consOdd zero ≡ consOdd (consEven zero)
_ = refl

oddbinnat : binnat → Bool
oddbinnat zero         = false
oddbinnat (consOdd _)  = true
oddbinnat (consEven _) = false

oddℕ' : ℕ → Bool
oddℕ' = transport (λ i → ℕ≡binnat (~ i) → Bool) oddbinnat

-- The NatImpl example for this representation of binary numbers
private
  NatImplbinnat : NatImpl binnat
  z NatImplbinnat = zero
  s NatImplbinnat = suc-binnat

  -- Note that the s case is a bit simpler as no end-point correction
  -- is necessary (things commute strictly)
  NatImplℕ≡NatImplbinnat : PathP (λ i → NatImpl (ℕ≡binnat i)) NatImplℕ NatImplbinnat
  z (NatImplℕ≡NatImplbinnat i) = transp (λ j → ℕ≡binnat (i ∨ ~ j)) i zero
  s (NatImplℕ≡NatImplbinnat i) =
    λ x → glue (λ { (i = i0) → suc x
                  ; (i = i1) → suc-binnat x })
               (suc-binnat (unglue (i ∨ ~ i) x))

  oddSuc : (n : binnat) → oddbinnat n ≡ not (oddbinnat (suc-binnat n))
  oddSuc zero         = refl
  oddSuc (consOdd _)  = refl
  oddSuc (consEven _) = refl

  oddℕSuc' : (n : ℕ) → oddℕ' n ≡ not (oddℕ' (suc n))
  oddℕSuc' =
    let eq : (i : I) → ℕ≡binnat (~ i) → Bool
        eq i = transp (λ j → ℕ≡binnat (~ i ∨ ~ j) → Bool) (~ i) oddbinnat
    in transport
         (λ i → (n : ℕ≡binnat (~ i)) → eq i n ≡ not (eq i (NatImplℕ≡NatImplbinnat (~ i) .NatImpl.s n)))
         oddSuc


{-# OPTIONS --without-K #-}
-- Finite types and paths between them
module FT where

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

infixl 10 _◎_

------------------------------------------------------------------------------
-- Finite types

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

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

------------------------------------------------------------------------------
-- Generalized paths are pi-combinators

data _⇛_ : FT → FT → Set where
  -- additive structure
  unite₊⇛  : { b : FT } → PLUS ZERO b ⇛ b

  uniti₊⇛  : { b : FT } → b ⇛ PLUS ZERO b
  swap₊⇛   : { b₁ b₂ : FT } → PLUS b₁ b₂ ⇛ PLUS b₂ b₁
  assocl₊⇛ : { b₁ b₂ b₃ : FT } → PLUS b₁ (PLUS b₂ b₃) ⇛ PLUS (PLUS b₁ b₂) b₃
  assocr₊⇛ : { b₁ b₂ b₃ : FT } → PLUS (PLUS b₁ b₂) b₃ ⇛ PLUS b₁ (PLUS b₂ b₃)
  -- multiplicative structure
  unite⋆⇛  : { b : FT } → TIMES ONE b ⇛ b
  uniti⋆⇛  : { b : FT } → b ⇛ TIMES ONE b
  swap⋆⇛   : { b₁ b₂ : FT } → TIMES b₁ b₂ ⇛ TIMES b₂ b₁
  assocl⋆⇛ : { b₁ b₂ b₃ : FT } →
             TIMES b₁ (TIMES b₂ b₃) ⇛ TIMES (TIMES b₁ b₂) b₃
  assocr⋆⇛ : { b₁ b₂ b₃ : FT } →
             TIMES (TIMES b₁ b₂) b₃ ⇛ TIMES b₁ (TIMES b₂ b₃)
  -- distributivity
  distz⇛   : { b : FT } → TIMES ZERO b ⇛ ZERO
  factorz⇛ : { b : FT } → ZERO ⇛ TIMES ZERO b
  dist⇛    : { b₁ b₂ b₃ : FT } →
            TIMES (PLUS b₁ b₂) b₃ ⇛ PLUS (TIMES b₁ b₃) (TIMES b₂ b₃)
  factor⇛  : { b₁ b₂ b₃ : FT } →
            PLUS (TIMES b₁ b₃) (TIMES b₂ b₃) ⇛ TIMES (PLUS b₁ b₂) b₃
  -- congruence
  id⇛    : { b : FT } → b ⇛ b
  sym⇛   : { b₁ b₂ : FT } → (b₁ ⇛ b₂) → (b₂ ⇛ b₁)
  _◎_    : { b₁ b₂ b₃ : FT } → (b₁ ⇛ b₂) → (b₂ ⇛ b₃) → (b₁ ⇛ b₃)
  _⊕_    : { b₁ b₂ b₃ b₄ : FT } →
           (b₁ ⇛ b₃) → (b₂ ⇛ b₄) → (PLUS b₁ b₂ ⇛ PLUS b₃ b₄)
  _⊗_    : { b₁ b₂ b₃ b₄ : FT } →
           (b₁ ⇛ b₃) → (b₂ ⇛ b₄) → (TIMES b₁ b₂ ⇛ TIMES b₃ b₄)

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

-- An alternative interpretation, as a Functor.
⟦_⟧′ : FT → (Set → Set)
⟦ ZERO ⟧′ A = ⊥
⟦ ONE ⟧′ A = A
⟦ PLUS B₁ B₂ ⟧′ A = ⟦ B₁ ⟧′ A ⊎ ⟦ B₂ ⟧′ A
⟦ TIMES B₁ B₂ ⟧′ A = ⟦ B₁ ⟧′ A × ⟦ B₂ ⟧′ A

{--

Not used

BOOL-FT : Set
BOOL-FT = ⟦ PLUS ONE ONE ⟧

true-FT : BOOL-FT
true-FT = inj₁ tt

false-FT : BOOL-FT
false-FT = inj₂ tt

witness : (B : FT) → Maybe ⟦ B ⟧
witness ZERO = nothing
witness ONE = just tt
witness (PLUS B₁ B₂) with witness B₁ | witness B₂
... | nothing | nothing = nothing
... | nothing | just b  = just (inj₂ b)
... | just b  | _ = just (inj₁ b)
witness (TIMES B₁ B₂) with witness B₁ | witness B₂
... | nothing | _ = nothing
... | just b  | nothing = nothing
... | just b₁ | just b₂ = just (b₁ , b₂)

elems : (B : FT) → List ⟦ B ⟧
elems ZERO          = []
elems ONE           = [ tt ]
elems (PLUS B₁ B₂)  = map inj₁ (elems B₁) ++ map inj₂ (elems B₂)
elems (TIMES B₁ B₂) = concatMap
                        (λ e₁ → map (λ e₂ → (e₁ , e₂)) (elems B₂))
                        (elems B₁)

expandF : {B₁ B₂ : FT} → (⟦ B₁ ⟧ → ⟦ B₂ ⟧) → List (⟦ B₁ ⟧ × ⟦ B₂ ⟧)
expandF {B₁} {B₂} f = map (λ e → (e , f e)) (elems B₁)

test0 : List ((⊤ × BOOL-FT) × BOOL-FT)
test0 = expandF (unite⋆ {BOOL-FT})

--}


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

open import Setoids.Setoids
open import Groups.Definition
open import Sets.EquivalenceRelations

module Groups.Lemmas {a b : _} {A : Set a} {_·_ : A → A → A} {S : Setoid {a} {b} A} (G : Group S _·_) where

open Setoid S
open Group G
open Equivalence eq

groupsHaveLeftCancellation : (x y z : A) → (x · y) ∼ (x · z) → y ∼ z
groupsHaveLeftCancellation x y z pr = o
  where
  _^-1 = inverse
  j : ((x ^-1) · x) · y ∼ (x ^-1) · (x · z)
  j = transitive (symmetric (+Associative {x ^-1} {x} {y})) (+WellDefined ~refl pr)
  k : ((x ^-1) · x) · y ∼ ((x ^-1) · x) · z
  k = transitive j +Associative
  l : 0G · y ∼ ((x ^-1) · x) · z
  l = transitive (+WellDefined (symmetric invLeft) ~refl) k
  m : 0G · y ∼ 0G · z
  m = transitive l (+WellDefined invLeft ~refl)
  n : y ∼ 0G · z
  n = transitive (symmetric identLeft) m
  o : y ∼ z
  o = transitive n identLeft

groupsHaveRightCancellation : (x y z : A) → (y · x) ∼ (z · x) → y ∼ z
groupsHaveRightCancellation x y z pr = transitive m identRight
  where
  _^-1 = inverse
  i : (y · x) · (x ^-1) ∼ (z · x) · (x ^-1)
  i = +WellDefined pr ~refl
  j : y · (x · (x ^-1)) ∼ (z · x) · (x ^-1)
  j = transitive +Associative i
  j' : y · 0G ∼ (z · x) · (x ^-1)
  j' = transitive (+WellDefined ~refl (symmetric invRight)) j
  k : y ∼ (z · x) · (x ^-1)
  k = transitive (symmetric identRight) j'
  l : y ∼ z · (x · (x ^-1))
  l = transitive k (symmetric +Associative)
  m : y ∼ z · 0G
  m = transitive l (+WellDefined ~refl invRight)

rightInversesAreUnique : {x : A} → {y : A} → (y · x) ∼ 0G → y ∼ (inverse x)
rightInversesAreUnique {x} {y} f = transitive i (transitive j (transitive k (transitive l m)))
  where
  _^-1 = inverse
  i : y ∼ y · 0G
  j : y · 0G ∼ y · (x · (x ^-1))
  k : y · (x · (x ^-1)) ∼ (y · x) · (x ^-1)
  l : (y · x) · (x ^-1) ∼ 0G · (x ^-1)
  m : 0G · (x ^-1) ∼ x ^-1
  i = symmetric identRight
  j = +WellDefined ~refl (symmetric invRight)
  k = +Associative
  l = +WellDefined f ~refl
  m = identLeft

leftInversesAreUnique : {x : A} → {y : A} → (x · y) ∼ 0G → y ∼ (inverse x)
leftInversesAreUnique {x} {y} f = rightInversesAreUnique {x} {y} l
  where
    _^-1 = inverse
    i : y · (x · y) ∼ y · 0G
    i' : y · (x · y) ∼ y
    j : (y · x) · y ∼ y
    k : (y · x) · y ∼ 0G · y
    l : y · x ∼ 0G
    i = +WellDefined ~refl f
    i' = transitive i identRight
    j = transitive (symmetric +Associative) i'
    k = transitive j (symmetric identLeft)
    l = groupsHaveRightCancellation y (y · x) 0G k

invTwice : (x : A) → (inverse (inverse x)) ∼ x
invTwice x = symmetric (rightInversesAreUnique {x ^-1} {x} invRight)
  where
  _^-1 = inverse

replaceGroupOp : {a b c d w x y z : A} → (Setoid._∼_ S a c) → (Setoid._∼_ S b d) → (Setoid._∼_ S w y) → (Setoid._∼_ S x z) → Setoid._∼_ S (a · b) (w · x) → Setoid._∼_ S (c · d) (y · z)
replaceGroupOp a~c b~d w~y x~z pr = transitive (symmetric (+WellDefined a~c b~d)) (transitive pr (+WellDefined w~y x~z))

replaceGroupOpRight : {a b c x : A} → (Setoid._∼_ S a (b · c)) → (Setoid._∼_ S c x) → (Setoid._∼_ S a (b · x))
replaceGroupOpRight a~bc c~x = transitive a~bc (+WellDefined reflexive c~x)

inverseWellDefined : {x y : A} → (x ∼ y) → (inverse x) ∼ (inverse y)
inverseWellDefined {x} {y} x~y = groupsHaveRightCancellation x (inverse x) (inverse y) q
  where
    p : inverse x · x ∼ inverse y · y
    p = transitive invLeft (symmetric invLeft)
    q : inverse x · x ∼ inverse y · x
    q = replaceGroupOpRight {_·_ (inverse x) x} {inverse y} {y} {x} p (symmetric x~y)

transferToRight : {a b : A} → (a · (inverse b)) ∼ 0G → a ∼ b
transferToRight {a} {b} ab-1 = transitive (symmetric (invTwice a)) (transitive u (invTwice b))
  where
    t : inverse a ∼ inverse b
    t = symmetric (leftInversesAreUnique ab-1)
    u : inverse (inverse a) ∼ inverse (inverse b)
    u = inverseWellDefined t

transferToRight' : {a b : A} → (a · b) ∼ 0G → a ∼ (inverse b)
transferToRight' {a} {b} ab-1 = transferToRight lemma
  where
    lemma : a · (inverse (inverse b)) ∼ 0G
    lemma = transitive (+WellDefined reflexive (invTwice b)) ab-1

transferToRight'' : {a b : A} → Setoid._∼_ S a b → (a · (inverse b)) ∼ 0G
transferToRight'' {a} {b} a~b = transitive (+WellDefined a~b reflexive) invRight

invInv : {x : A} → (inverse (inverse x)) ∼ x
invInv {x} = symmetric (transferToRight' invRight)

invIdent : (inverse 0G) ∼ 0G
invIdent = symmetric (transferToRight' identLeft)

swapInv : {x y : A} → (inverse x) ∼ y → x ∼ (inverse y)
swapInv {x} {y} -x=y = transitive (symmetric invInv) (inverseWellDefined -x=y)

identityIsUnique : (e : A) → ((b : A) → ((b · e) ∼ b)) → (e ∼ 0G)
identityIsUnique thing fb = transitive (symmetric identLeft) (fb 0G)

invContravariant : {x y : A} → (Setoid._∼_ S (Group.inverse G (x · y)) ((Group.inverse G y) · (Group.inverse G x)))
invContravariant {x} {y} = ans
  where
    _^-1 = inverse
    otherInv = (y ^-1) · (x ^-1)
    many+Associatives : x · ((y · (y ^-1)) · (x ^-1)) ∼ (x · y) · ((y ^-1) · (x ^-1))
    oneMult : (x · y) · otherInv ∼ x · (x ^-1)
    many+Associatives = transitive +Associative (transitive (+WellDefined +Associative reflexive) (symmetric +Associative))
    oneMult = symmetric (transitive (+WellDefined reflexive (transitive (symmetric identLeft) (+WellDefined (symmetric invRight) reflexive))) many+Associatives)
    otherInvIsInverse : (x · y) · otherInv ∼ 0G
    otherInvIsInverse = transitive oneMult invRight
    ans : (x · y) ^-1 ∼ (y ^-1) · (x ^-1)
    ans = symmetric (leftInversesAreUnique otherInvIsInverse)

equalsDoubleImpliesZero : {x : A} → (x · x) ∼ x → x ∼ 0G
equalsDoubleImpliesZero 2x=x = transitive (symmetric identLeft) (transitive (+WellDefined (symmetric invLeft) reflexive) (transitive (symmetric +Associative) (transitive (+WellDefined reflexive 2x=x) invLeft)))

invZeroImpliesZero : {x : A} → (inverse x) ∼ 0G → x ∼ 0G
invZeroImpliesZero {x} pr = transitive (symmetric (invTwice x)) (transitive (inverseWellDefined pr) invIdent)


module x09-747Decidable-hc where

-- Library

import Relation.Binary.PropositionalEquality as Eq
open        Eq                        using (_≡_; cong; refl; sym) -- added sym
open        Eq.≡-Reasoning
open import Data.Nat                  using (ℕ; zero; suc; _≤_; z≤n; s≤s)
open import Data.Product              using (_×_; proj₁; proj₂) renaming (_,_ to ⟨_,_⟩)
open import Data.Sum                  using (_⊎_; inj₁; inj₂)
open import Relation.Nullary          using (¬_)
open import Relation.Nullary.Negation using () renaming (contradiction to ¬¬-intro)
open import Data.Unit                 using (⊤; tt)
open import Data.Empty                using (⊥; ⊥-elim)

-- Copied from 747Isomorphism.
record _⇔_ (A B : Set) : Set where
  field
    to   : A → B
    from : B → A
open _⇔_

-- Copied from 747Relations.

infix 4 _<_

data _<_ : ℕ → ℕ → Set where

  z<s : ∀ {n : ℕ}
      ------------
    → zero < suc n

  s<s : ∀ {m n : ℕ}
    → m < n
      -------------
    → suc m < suc n

-- examples proving inequalities and their negations

2≤4 : 2 ≤ 4
2≤4 = s≤s (s≤s z≤n)

¬4≤2 : ¬ (4 ≤ 2)
¬4≤2 = λ { (s≤s (s≤s ())) }

-- Bool values (computation)
data Bool : Set where
  true  : Bool
  false : Bool

-- Boolean comparison function
infix 4 _≤ᵇ_

_≤ᵇ_ : ℕ → ℕ → Bool
zero  ≤ᵇ zero  = true
zero  ≤ᵇ suc n = true
suc m ≤ᵇ zero  = false
suc m ≤ᵇ suc n = m ≤ᵇ n

-- PLFA steps through these computations using equational reasoning.

_ : (2 ≤ᵇ 4) ≡ true
_ =  refl

_ : (4 ≤ᵇ 2) ≡ false
_ = refl

-- Relating evidence and computation.

-- Boolean types (evidence)
T : Bool → Set
T true  = ⊤
T false = ⊥

T→≡ : ∀ (b : Bool) → T b → b ≡ true
T→≡ true tt = refl

≡→T : ∀ {b : Bool} → b ≡ true → T b
≡→T refl = tt

≤ᵇ→≤ : ∀ (m n : ℕ) → T (m ≤ᵇ n) → m ≤ n
≤ᵇ→≤  zero    zero   t = z≤n
≤ᵇ→≤  zero   (suc n) t = z≤n
≤ᵇ→≤ (suc m) (suc n) t = s≤s (≤ᵇ→≤ m n t)

≤→≤ᵇ : ∀ {m n : ℕ} → m ≤ n → T (m ≤ᵇ n)
≤→≤ᵇ {zero} {zero}      z≤n  = tt
≤→≤ᵇ {zero} {suc n}     z≤n  = tt
≤→≤ᵇ               (s≤s m≤n) = ≤→≤ᵇ m≤n

-- computation and evidence

data Dec (A : Set) : Set where
  yes :   A → Dec A
  no  : ¬ A → Dec A

inv-s≤s : ∀ {m n : ℕ}
  → suc m ≤ suc n
    -------------
  →     m ≤     n
inv-s≤s (s≤s m≤n) = m≤n

-- helpers for defining _≤?_ (use these so examples will normalize)

¬s≤z : ∀ {m : ℕ} → ¬ (suc m ≤ zero)
¬s≤z = λ ()

¬s≤s : ∀ {m n : ℕ} → ¬ (m ≤ n) → ¬ (suc m ≤ suc n)
¬s≤s  {zero}  {zero} ¬m≤n = λ        sucm≤sucn  → ¬m≤n z≤n
¬s≤s  {zero} {suc n} ¬m≤n = λ        sucm≤sucn  → ¬m≤n z≤n
¬s≤s {suc m}  {zero} ¬m≤n = λ { (s≤s sucm≤sucn) → ¬m≤n sucm≤sucn }
¬s≤s {suc m} {suc n} ¬m≤n = λ { (s≤s sucm≤sucn) → ¬m≤n (s≤s (inv-s≤s sucm≤sucn)) }

-- Decidable ≤.

_≤?_ : ∀ (m n : ℕ) → Dec (m ≤ n)
zero  ≤? zero  = yes z≤n
zero  ≤? suc n = yes z≤n
suc m ≤? zero  = no (λ ())
suc m ≤? suc n
  with m ≤? n
... | yes  m≤n = yes (s≤s  m≤n)
... | no  ¬m≤n = no (¬s≤s ¬m≤n)

-- can also evaluate the LHS of these tests via C-c C-n
_ : 2 ≤? 4 ≡ yes (s≤s  (s≤s  z≤n))
_ = refl
_ : 4 ≤? 2 ≡ no (¬s≤s (¬s≤s ¬s≤z))
_ = refl

-- 747/PLFA exercise: DecLT (3 point)
-- Decidable strict equality.
-- You will need these helper functions as we did above.

¬z<z : ¬ (zero < zero)
¬z<z = λ ()

¬s<s : ∀ {m n : ℕ} → ¬ (m < n) → ¬ (suc m < suc n)
¬s<s ¬m<n = λ { (s<s sucm<sucn) → ¬m<n sucm<sucn }

¬s<z : ∀ {n : ℕ} → ¬ (suc n < zero)
¬s<z = λ ()

_<?_ : ∀ (m n : ℕ) → Dec (m < n)
zero  <? zero  = no (λ ())
zero  <? suc n = yes z<s
suc m <? zero  = no (λ ())
suc m <? suc n
  with m <? n
... | yes m<n = yes (s<s  m<n)
... | no ¬m<n = no (¬s<s ¬m<n)

_ : 2 <? 4 ≡ yes (s<s (s<s (z<s)))
_ = refl
_ : 4 <? 2 ≡ no (¬s<s (¬s<s ¬s<z))
_ = refl
_ : 3 <? 3 ≡ no (¬s<s (¬s<s (¬s<s ¬z<z)))
_ = refl

-- 747/PLFA exercise: DecNatEq (3 points)
-- Decidable equality for natural numbers.

_≡ℕ?_ : ∀ (m n : ℕ) → Dec (m ≡ n)
zero  ≡ℕ? zero  = yes refl
zero  ≡ℕ? suc n = no (λ ())
suc m ≡ℕ? zero  = no (λ ())
suc m ≡ℕ? suc n
  with m ≡ℕ? n
... | yes m≡n = yes (cong suc m≡n)
... | no ¬m≡n = no  λ { refl → ¬m≡n refl }

-- reusing ≤ᵇ and proofs of equivalence with ≤ to decide ≤
_≤?′_ : ∀ (m n : ℕ) → Dec (m ≤ n)
m ≤?′ n with m ≤ᵇ n | ≤ᵇ→≤ m n | ≤→≤ᵇ {m} {n}
... | true  | ⊤→m≤n |     _ = yes (⊤→m≤n tt)
... | false |     _ | m≤n→⊥ = no  (λ m≤n → m≤n→⊥ m≤n)

-- extract Bool value from Dec (aka "isYes")
⌊_⌋ : ∀ {A : Set} → Dec A → Bool
⌊ yes x ⌋ = true
⌊ no  x ⌋ = false

_≤ᵇ′_ : ℕ → ℕ → Bool
m ≤ᵇ′ n  =  ⌊ m ≤? n ⌋

-- If D is Dec A, then T ⌊ D ⌋ is inhabited exactly when A is inhabited.
toWitness : ∀ {A : Set} {D : Dec A} → T ⌊ D ⌋ → A
toWitness {_} {yes a} t = a

fromWitness : ∀ {A : Set} {D : Dec A} → A → T ⌊ D ⌋
fromWitness {_} {yes _} _ = tt
fromWitness {_} {no ¬A} a = ¬A a

-- handle "no" witnesses
isNo : ∀ {A : Set} → Dec A → Bool
isNo (yes _) = false
isNo (no  _) = true

toWitnessFalse : ∀ {A : Set} {D : Dec A} → T (isNo D) → ¬ A
toWitnessFalse {_} {no ¬A} _ = ¬A

fromWitnessFalse : ∀ {A : Set} {D : Dec A} → ¬ A → T (isNo D)
fromWitnessFalse {_} {yes A } ¬A = ¬A A
fromWitnessFalse {A} {no ¬A'} ¬A = tt

-- Agda standard library definitions for use of these.

True  : ∀ {A : Set} → (D : Dec A) → Set
True  Q = T ⌊ Q ⌋

False : ∀ {A : Set} → (D : Dec A) → Set
False Q = T (isNo Q)

-- concrete examples

≤ᵇ′→≤ : ∀ {m n : ℕ} → T (m ≤ᵇ′ n) → m ≤ n
≤ᵇ′→≤  =  toWitness

≤→≤ᵇ′ : ∀ {m n : ℕ} → m ≤ n → T (m ≤ᵇ′ n)
≤→≤ᵇ′  =  fromWitness

-- BEST PRACTICE : use Decidables instead of Booleans.

-- Logical connectives for Decidables.

infixr 6 _∧_

_∧_ : Bool → Bool → Bool
true  ∧ true  = true
true  ∧ false = false
false ∧     _ = false

infixr 6 _×-dec_

_×-dec_ : ∀ {A B : Set} → Dec A → Dec B → Dec (A × B)
yes A ×-dec yes B = yes ⟨ A , B ⟩
yes _ ×-dec no ¬B = no (λ A×B → ¬B (proj₂ A×B))
no ¬A ×-dec yes B = no (λ A×B → ¬A (proj₁ A×B))
no  _ ×-dec no ¬B = no (λ A×B → ¬B (proj₂ A×B))

infixr 5 _∨_

_∨_ : Bool → Bool → Bool
true  ∨ _ = true
false ∨ r = r

infixr 5 _⊎-dec_

_⊎-dec_ : ∀ {A B : Set} → Dec A → Dec B → Dec (A ⊎ B)
yes A ⊎-dec yes _ = yes (inj₁ A)
yes A ⊎-dec no  _ = yes (inj₁ A)
no  _ ⊎-dec yes B = yes (inj₂ B)
no ¬A ⊎-dec no ¬B = no (λ { (inj₁ A) → ¬A A ; (inj₂ B) → ¬B B } )

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

¬? : ∀ {A : Set} → Dec A → Dec (¬ A)
¬? (yes A) = no (λ ¬A → ¬A A)
¬? (no ¬A) = yes ¬A

-- Boolean version of implication
_⊃_ : Bool → Bool → Bool
true  ⊃ true  = true
true  ⊃ false = false
false ⊃ _     = true

_→-dec_ : ∀ {A B : Set} → Dec A → Dec B → Dec (A → B)
_     →-dec yes B = yes (λ   _ →  B)
no ¬A →-dec     _ = yes (λ A   → ⊥-elim (¬A A))
yes A →-dec no ¬B = no  (λ A→B → ¬B (A→B A))

-- 747/PLFA exercise: ErasBoolDec (3 points)
-- Erasure relates boolean and decidable operations.

∧-× : ∀ {A B : Set} (x : Dec A) (y : Dec B) → ⌊ x ⌋ ∧ ⌊ y ⌋ ≡ ⌊ x ×-dec y ⌋
∧-× (no  _) (no  _) = refl
∧-× (no  _) (yes _) = refl
∧-× (yes _) (no  _) = refl
∧-× (yes _) (yes _) = refl -- true ≡ true

∨-× : ∀ {A B : Set} (x : Dec A) (y : Dec B) → ⌊ x ⌋ ∨ ⌊ y ⌋ ≡ ⌊ x ⊎-dec y ⌋
∨-× (no  _) (no  _) = refl
∨-× (no  _) (yes _) = refl
∨-× (yes _) (no  _) = refl
∨-× (yes _) (yes _) = refl

not-¬ : ∀ {A : Set} (x : Dec A) → not ⌊ x ⌋ ≡ ⌊ ¬? x ⌋
not-¬ (yes _) = refl
not-¬ (no  _) = refl

-- 747/PLFA exercise: iff-erasure.

_iff_ : Bool → Bool → Bool
true  iff true  = true
false iff false = true
true  iff false = false
false iff true  = false

_⇔-dec_ : ∀ {A B : Set} → Dec A → Dec B → Dec (A ⇔ B)
yes A ⇔-dec yes B = yes (record { to = λ _ → B ; from = λ _ → A })
yes A ⇔-dec no ¬B = no  (λ A⇔B → ¬B (to   A⇔B A))
no ¬A ⇔-dec yes B = no  (λ A⇔B → ¬A (from A⇔B B))
no ¬A ⇔-dec no ¬B = yes (record { to = λ A → ⊥-elim (¬A A) ; from = λ B → ⊥-elim (¬B B) })

iff-⇔ : ∀ {A B : Set} (x : Dec A) (y : Dec B) → ⌊ x ⌋ iff ⌊ y ⌋ ≡ ⌊ x ⇔-dec y ⌋
iff-⇔ (yes _) (yes _) = refl
iff-⇔ (yes _) (no  _) = refl
iff-⇔ (no  _) (yes _) = refl
iff-⇔ (no ¬A) (no ¬B) = refl

-- proof by reflection (i.g., get Agda to construct proofs at compile time)

-- guarded version of monus
minus : (m n : ℕ) (n≤m : n ≤ m) → ℕ
minus      m   zero          _  = m
minus (suc m) (suc n) (s≤s m≤n) = minus m n m≤n

-- but must provide proofs
_ : minus 5 3 (s≤s (s≤s (s≤s z≤n))) ≡ 2
_ = refl

-- Agda will fill in an implicit record type if it can fill in all fields.
-- Since ⊤ is defined as a record type with no fields...
-- then Agda will compute a value of type True (n ≤? m).
_-_ : (m n : ℕ) {n≤m : True (n ≤? m)} → ℕ
_-_ m n {n≤m} = minus m n (toWitness n≤m)

-- then proofs no longer needed
_ : 5 - 3 ≡ 2
_ = refl

-- Use above to get Agda to compute parts of proofs that would be annoying for us to provide.


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

open import Relation.Binary using (Poset)

-- A thin (or posetal) category is a category with at most one
-- morphism between any pair of objects.
--
-- As explained on nLab:
--
--   "Up to isomorphism, a thin category is the same thing as a
--   proset. Up to equivalence, a thin category is the same thing as a
--   poset. (...) (It is really a question of whether you're working
--   with strict categories, which are classified up to isomorphism,
--   or categories as such, which are classified up to equivalence.)"
--
--                  -- https://ncatlab.org/nlab/show/thin+category
--
-- Since
--
--  1. posets in the standard library are defined up to an underlying
--     equivalence/setoid, but
--
--  2. categories in this library do not have a notion of equality on
--     objects (i.e. objects are considered up to isomorphism),
--
-- it makes sense to work with the weaker notion of thin categories
-- here, i.e. those generated by posets rather than prosets.  This
-- means that the core of a thin category (the groupoid consisting of
-- all its isomorphism) is just the setoid underlying the
-- corresponding poset.
--
-- (See Categories.Adjoint.Instance.PosetCore for more details.)

module Categories.Category.Construction.Thin {o ℓ₁ ℓ₂} e (P : Poset o ℓ₁ ℓ₂) where

open import Level
open import Data.Unit using (⊤)
open import Function using (flip)

open import Categories.Category
import Categories.Morphism as Morphism

open Poset P

Thin : Category o ℓ₂ e
Thin = record
  { Obj       = Carrier
  ; _⇒_       = _≤_
  ; _≈_       = λ _ _ → Lift e ⊤
  ; id        = refl
  ; _∘_       = flip trans
  ; assoc     = _
  ; identityˡ = _
  ; identityʳ = _
  ; equiv     = _
  ; ∘-resp-≈  = _
  }

module EqIsIso where

  open Category Thin hiding (_≈_)
  open Morphism Thin using (_≅_)
  open _≅_

  -- Equivalent elements of |P| are isomorphic objects of |Thin P|.

  ≈⇒≅ : ∀ {x y : Obj} → x ≈ y → x ≅ y
  ≈⇒≅ x≈y = record
    { from = reflexive x≈y
    ; to   = reflexive (Eq.sym x≈y)
    }

  ≅⇒≈ : ∀ {x y : Obj} → x ≅ y → x ≈ y
  ≅⇒≈ x≅y = antisym (from x≅y) (to x≅y)


module Numeral.Natural.Relation.Order.Decidable where

open import Functional
open import Logic.IntroInstances
open import Logic.Propositional.Theorems
open import Numeral.Natural
open import Numeral.Natural.Oper.Comparisons
open import Numeral.Natural.Oper.Proofs
open import Numeral.Natural.Relation.Order
open import Numeral.Natural.Relation.Order.Proofs
open import Relator.Equals
open import Relator.Equals.Proofs
open import Structure.Function.Domain
open import Type.Properties.Decidable

instance
  [≡]-decider : Decider(2)(_≡_)(_≡?_)
  [≡]-decider {𝟎}  {𝟎}   = true [≡]-intro
  [≡]-decider {𝟎}  {𝐒 y} = false \()
  [≡]-decider {𝐒 x}{𝟎}   = false \()
  [≡]-decider {𝐒 x}{𝐒 y} = step{f = id} (true ∘ [≡]-with(𝐒)) (false ∘ contrapositiveᵣ (injective(𝐒))) ([≡]-decider {x}{y})

instance
  [≤]-decider : Decider(2)(_≤_)(_≤?_)
  [≤]-decider {𝟎} {𝟎} = true [≤]-minimum
  [≤]-decider {𝟎} {𝐒 y} = true [≤]-minimum
  [≤]-decider {𝐒 x} {𝟎} = false \()
  [≤]-decider {𝐒 x} {𝐒 y} = step{f = id} (true ∘ \p → [≤]-with-[𝐒] ⦃ p ⦄) (false ∘ contrapositiveᵣ [≤]-without-[𝐒]) ([≤]-decider {x}{y})

[<]-decider : Decider(2)(_<_)(_<?_)
[<]-decider {𝟎} {𝟎} = false (λ ())
[<]-decider {𝟎} {𝐒 y} = true (succ min)
[<]-decider {𝐒 x} {𝟎} = false (λ ())
[<]-decider {𝐒 x} {𝐒 y} = step{f = id} (true ∘ succ) (false ∘ contrapositiveᵣ [≤]-without-[𝐒]) ([<]-decider {x} {y})


module Thesis.SIRelBigStep.Syntax where

open import Data.Nat

open import Thesis.SIRelBigStep.Types public

data Primitive : (τ : Type) → Set where
  succ : Primitive (nat ⇒ nat)
  add : Primitive (pair nat nat ⇒ nat)

data Const : (τ : Type) → Set where
  lit : (n : ℕ) → Const nat

data Term (Γ : Context) (τ : Type) : Set
-- Source values
data SVal (Γ : Context) : (τ : Type) → Set where
  var : ∀ {τ} →
    (x : Var Γ τ) →
    SVal Γ τ
  abs : ∀ {σ τ}
    (t : Term (σ • Γ) τ) →
    SVal Γ (σ ⇒ τ)
  cons : ∀ {τ1 τ2}
    (sv1 : SVal Γ τ1)
    (sv2 : SVal Γ τ2) →
    SVal Γ (pair τ1 τ2)
  const : ∀ {τ} → (c : Const τ) → SVal Γ τ

data Term (Γ : Context) (τ : Type) where
  val :
    SVal Γ τ →
    Term Γ τ
  primapp : ∀ {σ}
    (p : Primitive (σ ⇒ τ)) →
    (sv : SVal Γ σ) →
    Term Γ τ
  -- we use de Bruijn indices, so we don't need binding occurrences.
  app : ∀ {σ}
    (vs : SVal Γ (σ ⇒ τ)) →
    (vt : SVal Γ σ) →
    Term Γ τ
  lett : ∀ {σ}
    (s : Term Γ σ) →
    (t : Term (σ • Γ) τ) →
    Term Γ τ


module Dave.Algebra.Naturals.Exponentiation where
  open import Dave.Algebra.Naturals.Definition
  open import Dave.Algebra.Naturals.Multiplication
  open import Dave.Algebra.Naturals.Addition

  _^_ : ℕ → ℕ → ℕ
  a ^ zero = 1
  a ^ suc b = (a ^ b) * a

  infixl 8 _^_

  ^-distribₗ : ∀ (m n p : ℕ) → m ^ (n + p) ≡ (m ^ n) * (m ^ p)
  ^-distribₗ m zero p = refl
  ^-distribₗ m (suc n) p = begin
    m ^ (suc n + p) ≡⟨⟩
    m ^ suc (n + p) ≡⟨⟩
    (m ^ (n + p)) * m ≡⟨ cong (λ a → a * m) (^-distribₗ m n p) ⟩
    (m ^ n) * (m ^ p) * m ≡⟨ IsCommutativeMonoid.swap021 ℕ-*-IsCommutativeMonoid (m ^ n) (m ^ p) m ⟩
    (m ^ n) * m * (m ^ p) ≡⟨⟩
    (m ^ n) * (m ^ 1) * (m ^ p) ≡⟨ cong (λ a → a * (m ^ p)) (sym (^-distribₗ m n 1)) ⟩
    (m ^ (n + 1)) * (m ^ p) ≡⟨ cong (λ a → (m ^ a) * (m ^ p)) (+-add1ᵣ n) ⟩
    (m ^ suc n) * (m ^ p) ∎

  ^-distribᵣ : ∀ (m n p : ℕ) → (m * n) ^ p ≡ (m ^ p) * (n ^ p)
  ^-distribᵣ m n zero = refl
  ^-distribᵣ m n (suc p) = begin
     (m * n) ^ suc p ≡⟨⟩
     ((m * n) ^ p) * (m * n) ≡⟨ cong (λ a → a * (m * n)) (^-distribᵣ m n p) ⟩
     (m ^ p) * (n ^ p) * (m * n) ≡⟨ sym (IsSemigroup.assoc ℕ-*-IsSemigroup ((m ^ p) * (n ^ p)) m n) ⟩
     (m ^ p) * (n ^ p) * m * n ≡⟨ cong (λ a → a * n) (IsCommutativeMonoid.swap021 ℕ-*-IsCommutativeMonoid (m ^ p) (n ^ p) m) ⟩
     (m ^ p) * m * (n ^ p) * n ≡⟨ IsSemigroup.assoc ℕ-*-IsSemigroup ((m ^ p) * m) (n ^ p) n ⟩
     (m ^ p) * m * ((n ^ p) * n) ≡⟨⟩
     (m ^ p) * (m ^ 1) * ((n ^ p) * (n ^ 1)) ≡⟨ cong (λ a → a * ((n ^ p) * (n ^ 1))) (sym (^-distribₗ m p 1)) ⟩
     (m ^ (p + 1)) * ((n ^ p) * (n ^ 1)) ≡⟨ cong (λ a → (m ^ a) * ((n ^ p) * (n ^ 1))) (+-add1ᵣ p) ⟩
     (m ^ suc p) * ((n ^ p) * (n ^ 1)) ≡⟨ cong (λ a → (m ^ suc p) * a) (sym (^-distribₗ n p 1)) ⟩
     (m ^ suc p) * (n ^ (p + 1)) ≡⟨ cong (λ a → (m ^ suc p) * (n ^ a)) (+-add1ᵣ p) ⟩
     (m ^ suc p) * (n ^ suc p) ∎

  ^-*-assoc : ∀ (m n p : ℕ) → (m ^ n) ^ p ≡ m ^ (n * p)
  ^-*-assoc m n zero = sym (begin
    m ^ (n * zero) ≡⟨ cong (λ a → m ^ a) (*-zero n) ⟩
    1 ∎)
  ^-*-assoc m n (suc p) = begin
    (m ^ n) ^ suc p ≡⟨⟩
    (m ^ n) ^ p * (m ^ n) ≡⟨ cong (λ a → a * (m ^ n)) (^-*-assoc m n p) ⟩
    m ^ (n * p) * (m ^ n) ≡⟨ sym (^-distribₗ m (n * p) n) ⟩
    m ^ (n * p + n) ≡⟨ cong (λ a → m ^ a ) (sym (*-distrib1ᵣ-+ₗ n p)) ⟩
    m ^ (n * (p + 1)) ≡⟨ cong (λ a → m ^ (n * a)) (+-add1ᵣ p) ⟩
    m ^ (n * suc p) ∎


module Nat where

open import Base

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

_=N_ : Nat -> Nat -> Set
zero  =N zero  = True
zero  =N suc _ = False
suc _ =N zero  = False
suc n =N suc m = n =N m

refN : Refl _=N_
refN {zero}  = T
refN {suc n} = refN {n}

symN : Sym _=N_
symN {zero}{zero} p   = p
symN {suc n}{suc m} p = symN {n}{m} p
symN {zero}{suc _} ()
symN {suc _}{zero} ()

transN : Trans _=N_
transN {zero }{zero }{zero } p _ = p
transN {suc n}{suc m}{suc l} p q = transN {n}{m}{l} p q
transN {zero }{zero }{suc _} _ ()
transN {zero }{suc _}{_    } () _
transN {suc _}{zero }{_    } () _
transN {suc _}{suc _}{zero } _ ()


{-# OPTIONS --copattern --safe --no-sized-types --guardedness #-}
module Copattern where

open import Data.Nat
open import Relation.Binary.PropositionalEquality

record Stream (A : Set) : Set where
  coinductive
  field
    head : A
    tail : Stream A
open Stream public

-- | Bisimulation as equality
record _==_ {A : Set} (x : Stream A) (y : Stream A) : Set where
  coinductive
  field
    refl-head : head x ≡ head y
    refl-tail : tail x == tail y
open _==_ public

module Introduction where

  ones : Stream ℕ
  head ones = suc zero
  tail ones = ones

  repeat : {A : Set} -> A -> Stream A
  repeat {A} a = P
    where
    P : Stream A
    head P = a
    tail P = repeat a

  even : ∀ {A} -> Stream A -> Stream A
  even {A} a = P
    where
    P : Stream A
    head P = head a
    tail P = even (tail (tail a))

  odd : ∀ {A} -> Stream A -> Stream A
  odd {A} a = P
    where
    P : Stream A
    head P = head (tail a)
    tail P = odd (tail (tail a))

module Bisimulation where
  open Introduction

  refl′ : ∀ {A} -> (a : Stream A) -> a == a
  refl′ a = f
    where
    f : a == a
    refl-head f = refl
    refl-tail f = refl′ (tail a)

  oddEven : ∀ {A} -> (a : Stream A) -> odd a == even (tail a)
  oddEven a = f
    where
    f : odd a == even (tail a)
    refl-head f = refl
    refl-tail f = oddEven ((tail (tail a)))

module Merge where
  open Bisimulation
  open Introduction

  merge : ∀ {A} -> Stream A -> Stream A -> Stream A
  merge {A} a b = ma
    where
    ma : Stream A
    head ma = head a
    tail ma = merge b (tail a)

  -- Merge! Even! Odd!
  moe : ∀ {A} -> (a : Stream A) -> (merge (even a) (odd a) == a)
  moe a = f
    where
    f : merge (even a) (odd a) == a
    refl-head f = refl
    refl-head (refl-tail f) = refl
    refl-tail (refl-tail f) = moe (tail (tail a))


module plfa.part1.Induction where

import Relation.Binary.PropositionalEquality as Eq

open Eq using (_≡_; refl; cong; sym)
open Eq.≡-Reasoning
open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_)

+-assoc : ∀ (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-assoc zero n p =
  begin
    (zero + n) + p
  ≡⟨⟩
    n + p
  ≡⟨⟩
    zero + (n + p)
  ∎
+-assoc (suc m) n p =
  begin
    (suc m + n) + p
  ≡⟨⟩
    suc (m + n) + p
  ≡⟨⟩
    suc ((m + n) + p)

  -- A relation is said to be a congruence for
  -- a given function if it is preserved by applying that function

  -- If e is evidence that x ≡ y,
  -- then cong f e is evidence that f x ≡ f y,
  -- for any function f

  -- The correspondence between proof by induction and
  -- definition by recursion is one of the most appealing
  -- aspects of Agda

  -- cong : ∀ (f : A → B) {x y} → x ≡ y → f x ≡ f y
  --           ^- suc               ^- (m + n) + p ≡ m + (n + p)
  -- ----------------------------------------------------------- (=> implies)
  -- suc ((m + n) + p) ≡ suc (m + (n + p))

  ≡⟨ cong suc (+-assoc m n p) ⟩
    suc (m + (n + p))

  -- cong : ∀ (f : A → B) {x y} → x ≡ y → f x ≡ f y
  -- cong f refl = refl

  ≡⟨⟩
    suc m + (n + p)
  ∎

+-assoc-2 : ∀ (n p : ℕ) → (2 + n) + p ≡ 2 + (n + p)
+-assoc-2 n p =
  begin
    (2 + n) + p
  ≡⟨⟩
    suc (1 + n) + p
  ≡⟨⟩
    suc ((1 + n) + p)
  ≡⟨ cong suc (+-assoc-1 n p) ⟩
    suc (1 + (n + p))
  ≡⟨⟩
    2 + (n + p)
  ∎
  where
  +-assoc-1 : ∀ (n p : ℕ) -> (1 + n) + p ≡ 1 + (n + p)
  +-assoc-1 n p =
    begin
      (1 + n) + p
    ≡⟨⟩
      suc (0 + n) + p
    ≡⟨⟩
      suc ((0 + n) + p)
    ≡⟨ cong suc (+-assoc-0 n p) ⟩
      suc (0 + (n + p))
    ≡⟨⟩
      1 + (n + p)
    ∎
    where
      +-assoc-0 : ∀ (n p : ℕ) → (0 + n) + p ≡ 0 + (n + p)
      +-assoc-0 n p =
        begin
          (0 + n) + p
        ≡⟨⟩
          n + p
        ≡⟨⟩
          0 + (n + p)
        ∎

+-identityᴿ : ∀ (m : ℕ) → m + zero ≡ m
+-identityᴿ zero =
  begin
    zero + zero
  ≡⟨⟩
    zero
  ∎
+-identityᴿ (suc m) =
  begin
    suc m + zero
  ≡⟨⟩
    suc (m + zero)
  ≡⟨ cong suc (+-identityᴿ m) ⟩
    suc m
  ∎

+-suc : ∀ (m n : ℕ) → m + suc n ≡ suc (m + n)
+-suc zero n =
  begin
    zero + suc n
  ≡⟨⟩
    suc n
  ≡⟨⟩
    suc (zero + n)
  ∎
+-suc (suc m) n =
  begin
    suc m + suc n
  ≡⟨⟩
    suc (m + suc n)
  ≡⟨ cong suc (+-suc m n) ⟩
    suc (suc (m + n))
  ≡⟨⟩
    suc (suc m + n)
  ∎

+-comm : ∀ (m n : ℕ) → m + n ≡ n + m
+-comm m zero =
  begin
    m + zero
  ≡⟨ +-identityᴿ m ⟩
    m
  ≡⟨⟩
    zero + m
  ∎
+-comm m (suc n) =
  begin
    m + suc n
  ≡⟨ +-suc m n ⟩
    suc (m + n)
  ≡⟨ cong suc (+-comm m n) ⟩
    suc (n + m)
  ≡⟨⟩
    suc n + m
  ∎

-- Rearranging

-- We can apply associativity to
-- rearrange parentheses however we like

+-rearrange
  : ∀ (m n p q : ℕ)
  → (m + n) + (p + q) ≡ m + (n + p) + q
+-rearrange m n p q =
  begin
    (m + n) + (p + q)
  ≡⟨ +-assoc m n (p + q) ⟩
    m + (n + (p + q))

  ≡⟨ cong (m +_) (sym (+-assoc n p q)) ⟩
    m + ((n + p) + q)

  -- +-assoc : (m + n) + p ≡ m + (n + p)
  -- sym (+-assoc) : m + (n + p) ≡ (m + n) + p

  ≡⟨ sym (+-assoc m (n + p) q) ⟩
    (m + (n + p)) + q
  ∎

-- Associativity with rewrite

-- Rewriting avoids not only chains of
-- equations but also the need to invoke cong

+-assoc' : ∀ (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-assoc' zero n p                           = refl
+-assoc' (suc m) n p rewrite +-assoc' m n p = refl

+-identity' : ∀ (n : ℕ) → n + zero ≡ n
+-identity' zero = refl
+-identity' (suc n) rewrite +-identity' n = refl

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

+-comm' : ∀ (m n : ℕ) → m + n ≡ n + m
+-comm' m zero rewrite +-identity' m = refl
+-comm' m (suc n) rewrite +-suc' m n | +-comm' m n = refl

-- Building proofs interactively

+-assoc'' : ∀ (m n p : ℕ) → (m + n) + p ≡ m + (n + p)
+-assoc'' zero n p = refl
+-assoc'' (suc m) n p rewrite +-assoc'' m n p = refl

-- Exercise

-- Note:
-- sym -- rewrites the left side of the Goal

+-swap : ∀ (m n p : ℕ) → m + (n + p) ≡ n + (m + p)
+-swap zero n p = refl
+-swap (suc m) n p rewrite
    +-assoc'' m n p
  | +-suc n (m + p)
  | +-swap m n p
  = refl

-- (suc m + n) * p ≡ suc m * p + n * p
-- p + (m * p + n * p) ≡ p + m * p + n * p

*-distrib-+ : ∀ (m n p : ℕ) → (m + n) * p ≡ m * p + n * p
*-distrib-+ zero n p = refl
*-distrib-+ (suc m) n p rewrite
    *-distrib-+ m n p
  | sym (+-assoc p (m * p) (n * p))
  = refl

-- (n + m * n) * p ≡ n * p + m * (n * p)

*-assoc : ∀ (m n p : ℕ) → (m * n) * p ≡ m * (n * p)
*-assoc zero n p = refl
*-assoc (suc m) n p rewrite
    *-distrib-+ n (m * n) p
  | *-assoc m n p
  = refl


{-# OPTIONS --cubical --safe --postfix-projections #-}

module Cardinality.Finite.Structure where

open import Prelude
open import Data.Fin
open import Data.Nat
open import Data.Nat.Properties

private
  variable
    n m : ℕ

liftˡ : ∀ n m → Fin m → Fin (n + m)
liftˡ zero    m x = x
liftˡ (suc n) m x = fs (liftˡ n m x)

liftʳ : ∀ n m → Fin n → Fin (n + m)
liftʳ (suc n) m f0     = f0
liftʳ (suc n) m (fs x) = fs (liftʳ n m x)

mapl : (A → B) → A ⊎ C → B ⊎ C
mapl f (inl x) = inl (f x)
mapl f (inr x) = inr x

fin-sum-to : ∀ n m → Fin n ⊎ Fin m → Fin (n + m)
fin-sum-to n m = either (liftʳ n m) (liftˡ n m)

fin-sum-from : ∀ n m → Fin (n + m) →  Fin n ⊎ Fin m
fin-sum-from zero    m x      = inr x
fin-sum-from (suc n) m f0     = inl f0
fin-sum-from (suc n) m (fs x) = mapl fs (fin-sum-from n m x)

mapl-distrib : ∀ {a b c d} {A : Type a} {B : Type b} {C : Type c} {D : Type d} (xs : A ⊎ B) (h : A → C) (f : C → D) (g : B → D) → either′ f g (mapl h xs) ≡ either′ (f ∘′ h) g xs
mapl-distrib (inl x) h f g = refl
mapl-distrib (inr x) h f g = refl

either-distrib : ∀ {d} {D : Type d} (f : A → C) (g : B → C) (h : C → D) (xs : A ⊎ B) → either′ (h ∘ f) (h ∘ g) xs ≡ h (either′ f g xs)
either-distrib f g h (inl x) = refl
either-distrib f g h (inr x) = refl

open import Path.Reasoning

fin-sum-to-from : ∀ n m x → fin-sum-to n m (fin-sum-from n m x) ≡ x
fin-sum-to-from zero    m x      = refl
fin-sum-to-from (suc n) m f0     = refl
fin-sum-to-from (suc n) m (fs x) =
  fin-sum-to (suc n) m (mapl fs (fin-sum-from n m x)) ≡⟨ mapl-distrib (fin-sum-from n m x) fs (liftʳ (suc n) m) (liftˡ (suc n) m) ⟩
  either (liftʳ (suc n) m ∘ fs) (liftˡ (suc n) m) (fin-sum-from n m x) ≡⟨⟩
  either (fs ∘ liftʳ n m) (fs ∘ liftˡ n m) (fin-sum-from n m x) ≡⟨ either-distrib (liftʳ n m) (liftˡ n m) fs (fin-sum-from n m x) ⟩
  fs (either (liftʳ n m) (liftˡ n m) (fin-sum-from n m x)) ≡⟨ cong fs (fin-sum-to-from n m x) ⟩
  fs x ∎

-- fin-sum-from-to : ∀ n m x → fin-sum-from n m (fin-sum-to n m x) ≡ x
-- fin-sum-from-to n m (inl x) = {!!}
-- fin-sum-from-to n m (inr x) = {!!}

-- fin-sum : ∀ n m → Fin n ⊎ Fin m ⇔ Fin (n + m)
-- fin-sum n m .fun = fin-sum-to n m
-- fin-sum n m .inv = fin-sum-from n m
-- fin-sum n m .rightInv = fin-sum-to-from n m
-- fin-sum n m .leftInv = fin-sum-from-to n m


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

open import Setoids.Setoids
open import Sets.EquivalenceRelations
open import Rings.Definition
open import Rings.Homomorphisms.Definition
open import Rings.Ideals.Definition

module Rings.Homomorphisms.Kernel {a b c d : _} {A : Set a} {B : Set c} {S : Setoid {a} {b} A} {T : Setoid {c} {d} B} {_+1_ _*1_ : A → A → A} {_+2_ _*2_ : B → B → B} {R1 : Ring S _+1_ _*1_} {R2 : Ring T _+2_ _*2_} {f : A → B} (fHom : RingHom R1 R2 f) where

open import Groups.Homomorphisms.Kernel (RingHom.groupHom fHom)

ringKernelIsIdeal : Ideal R1 groupKernelPred
Ideal.isSubgroup ringKernelIsIdeal = groupKernelIsSubgroup
Ideal.accumulatesTimes ringKernelIsIdeal {x} {y} fx=0 = transitive (RingHom.ringHom fHom) (transitive (Ring.*WellDefined R2 fx=0 reflexive) (transitive (Ring.*Commutative R2) (Ring.timesZero R2)))
  where
    open Setoid T
    open Equivalence eq


-- modular arithmetic

module nat-mod where

open import eq
open import nat

infix 8 _≡_mod_

data _≡_mod_ : ℕ → ℕ → ℕ → Set where
  mod-refl : ∀ {n k} → n ≡ n mod k
  mod-add : ∀ {n m k} → n ≡ m mod k → n + k ≡ m mod k
  mod-symm : ∀ {n m k} → n ≡ m mod k → m ≡ n mod k

{-
mod-trans : ∀ {n m o k} → n ≡ m mod k → m ≡ o mod k → n ≡ o mod k
mod-trans{n}{.n}{o}{k} mod-refl p2 = {!!}
mod-trans}{m}{o}{k} (mod-add p1) p2 = {!!}
mod-trans{n}{m}{o}{k} (mod-symm p1) p2 = {!!}
-}


------------------------------------------------------------------------
-- The delay monad, defined coinductively, with a sized type parameter
------------------------------------------------------------------------

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

module Delay-monad.Sized where

open import Equality.Propositional
open import Prelude
open import Prelude.Size

open import Bijection equality-with-J using (_↔_)

-- The delay monad.

mutual

  data Delay {a} (A : Size → Type a) (i : Size) : Type a where
    now   : A i        → Delay A i
    later : Delay′ A i → Delay A i

  record Delay′ {a} (A : Size → Type a) (i : Size) : Type a where
    coinductive
    field
      force : {j : Size< i} → Delay A j

open Delay′ public

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

  mutual

    -- A non-terminating computation.

    never : ∀ {i} → Delay A i
    never = later never′

    never′ : ∀ {i} → Delay′ A i
    force never′ = never

  -- Removes a later constructor, if possible.

  drop-later : Delay A ∞ → Delay A ∞
  drop-later (now   x) = now x
  drop-later (later x) = force x

  -- An unfolding lemma for Delay.

  Delay↔ : ∀ {i} → Delay A i ↔ A i ⊎ Delay′ A i
  Delay↔ = record
    { surjection = record
      { logical-equivalence = record
        { to   = λ { (now x) → inj₁ x; (later x) → inj₂ x }
        ; from = [ now , later ]
        }
      ; right-inverse-of = [ (λ _ → refl) , (λ _ → refl) ]
      }
    ; left-inverse-of = λ { (now _) → refl; (later _) → refl }
    }


module Oscar.Data.Proposextensequality where

open import Oscar.Data.Proposequality using (_≡_)
open import Oscar.Level

infix 4 _≡̇_
_≡̇_ : ∀ {a} {A : Set a} {b} {B : A → Set b} → ((x : A) → B x) → ((x : A) → B x) → Set (a ⊔ b)
f ≡̇ g = ∀ x → f x ≡ g x


open import Formalization.PredicateLogic.Signature

module Formalization.PredicateLogic.Classical.Semantics (𝔏 : Signature) {ℓₘ} where
open Signature(𝔏)

import      Lvl
open import Data.Boolean
open import Data.ListSized
open import Numeral.Natural
open import Type

private variable args : ℕ

-- Model.
-- A model decides which propositional constants that are true or false.
-- A model in a signature `s` consists of a domain and interpretations of the function/relation symbols in `s`.
-- Also called: Structure.
-- Note: A model satisfying `∀{args} → Empty(Prop args)` is called an "Algebraic structure".
record Model : Type{ℓₚ Lvl.⊔ ℓₒ Lvl.⊔ Lvl.𝐒(ℓₘ)} where
  field
    Domain : Type{ℓₘ}                                  -- The type of objects that quantifications quantifies over and functions/relations maps from.
    function : Obj(args) → List(Domain)(args) → Domain -- Maps Obj to a n-ary function (n=0 is interpreted as a constant).
    relation : Prop(args) → List(Domain)(args) → Bool  -- Maps Prop to a boolean n-ary relation (n=0 is interpreted as a proposition).

open Model public using() renaming (Domain to dom ; function to fn ; relation to rel)


module Computability.Data.Nat.Iteration where

open import Computability.Prelude

iterate : {A : Set} → (A → A) → ℕ → A → A
iterate f zero a = a
iterate f (suc n) a = f (iterate f n a)

split-iterate : {A : Set}(f : A → A)(a : A)
              → (n m : ℕ)
              → iterate f (n + m) a ≡ iterate f n (iterate f m a)
split-iterate f a zero m = refl
split-iterate f a (suc n) m rewrite split-iterate f a n m = refl


-- Sets represented by unary predicates (Like restricted comprehension)
-- Similiar to BoolSet, but instead using the builtin constructive logic with levels.
module Sets.PredicateSet where

import      Lvl
open import Data hiding (Empty)
open import Data.Boolean
open import Data.Boolean.Stmt
open import Functional
open import Function.Proofs
open import Logic
open import Logic.Propositional
open import Logic.Propositional.Theorems
open import Logic.Predicate
-- open import Relator.Equals.Proofs.Equiv
open import Structure.Setoid using (Equiv) renaming (_≡_ to _≡ₛ_)
open import Data.Any
open import Structure.Function.Domain
open import Type

module _ where
  -- A set of objects of a certain type.
  -- This is represented by a predicate.
  -- Note: This is only a "set" within a certain type, so everything PredSet(T) is actually a subset of T (if T were a set (the set of all objects with type T)). Or in other words: PredSet(T) is supposed to represent the set {x. x: T}, and then (S ∈ PredSet(T)) essentially means that S when interpreted as a set of objects is a subset of {x. x: T}.
  PredSet : ∀{ℓ ℓₒ} → Type{ℓₒ} → Type{Lvl.𝐒(ℓ) Lvl.⊔ ℓₒ}
  PredSet{ℓ}{ℓₒ} (T) = (T → Stmt{ℓ})

  private variable ℓ ℓ₁ ℓ₂ ℓ₃ ℓ₄ ℓₒ ℓₑ : Lvl.Level
  private variable T A B : Type{ℓₒ}

  -- The statement of whether an element is in a set
  -- TODO: Maybe define this using a equivalence relation instead? (Alternatively a Setoid: x ∈ S = ∃(y ↦ (x ≡_T y) ∧ S(y)))
  _∈_ : T → PredSet{ℓ}(T) → Stmt
  _∈_ = apply -- (x ∈ S) = S(x)

  _∉_ : T → PredSet{ℓ}(T) → Stmt
  _∉_ = (¬_) ∘₂ (_∈_) -- (x ∉ S) = ¬(x ∈ S)

  _∋_ : PredSet{ℓ}(T) → T → Stmt
  _∋_ = swap(_∈_) -- (S ∋ x) = (x ∈ S)

  _∌_ : PredSet{ℓ}(T) → T → Stmt
  _∌_ = (¬_) ∘₂ (_∋_) -- (S ∌ x) = ¬(S ∋ x)

  module BoundedQuantifiers {T : Type{ℓₒ}} where
    ∀ₛ : PredSet{ℓ}(T) → (T → Stmt{ℓ₁}) → Stmt{ℓ Lvl.⊔ ℓ₁ Lvl.⊔ ℓₒ}
    ∀ₛ(S) P = ∀{elem : T} → (elem ∈ S) → P(elem)

    ∃ₛ : PredSet{ℓ}(T) → (T → Stmt{ℓ₁}) → Stmt{ℓ Lvl.⊔ ℓ₁ Lvl.⊔ ℓₒ}
    ∃ₛ(S) P = ∃(elem ↦ (elem ∈ S) ∧ P(elem))

  -- An empty set
  ∅ : PredSet{ℓ}(T)
  ∅ = const(Data.Empty)

  -- An universal set
  -- Note: It is only within a certain type, so 𝐔{T} is not actually a subset of everything. It is the greatest subset of subsets of T.
  𝐔 : PredSet{ℓ}(T)
  𝐔 = const(Unit)

  -- A singleton set (a set with only one element)
  •_ : ⦃ Equiv{ℓₑ}(T) ⦄ → T → PredSet(T)
  •_ = (_≡ₛ_)

  -- An union of two sets
  _∪_ : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → PredSet(T)
  _∪_ S₁ S₂ x = (S₁(x) ∨ S₂(x))

  -- An union of a set and a singleton set
  _∪•_ : ∀{ℓ}{T : Type{ℓ}} → PredSet{ℓ₁}(T) → Type{ℓ} → PredSet(T)
  _∪•_ S P x = (S(x) ∨ P)

  -- An intersection of two sets
  _∩_ : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → PredSet(T)
  _∩_ S₁ S₂ x = (S₁(x) ∧ S₂(x))

  -- A complement of a set
  ∁_ : PredSet{ℓ}(T) → PredSet(T)
  ∁_ = (¬_) ∘_ -- ∁_ S x = (¬ S(x))

  _∖_ : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → PredSet(T)
  _∖_ S₁ S₂ = (S₁ ∩ (∁ S₂))

  filter : (T → Bool) → PredSet{ℓ}(T) → PredSet(T)
  filter f S x = (S(x) ∧ IsTrue(f(x)))

  -- TODO: Maybe move these to a separate "Relations" file?

  -- A statement of whether a set is a subset of a set
  _⊆_ : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → Stmt
  _⊆_ S₁ S₂ = (∀{x} → (x ∈ S₁) → (x ∈ S₂))

  -- A statement of whether a set is a superset of a set
  _⊇_ : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → Stmt
  _⊇_ S₁ S₂ = (∀{x} → (x ∈ S₁) ← (x ∈ S₂))

  -- A statement of whether a set is equivalent to a set
  _≡_ : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → Stmt
  _≡_ S₁ S₂ = (∀{x} → (x ∈ S₁) ↔ (x ∈ S₂))

  Empty : PredSet{ℓ}(T) → Stmt
  Empty(S) = ∀{x} → ¬(x ∈ S)

  NonEmpty : PredSet{ℓ}(T) → Stmt
  NonEmpty(S) = ∃(_∈ S)

  Disjoint : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → Stmt
  Disjoint S₁ S₂ = ((S₁ ∩ S₂) ⊆ (∅ {Lvl.𝟎}))

  Overlapping : PredSet{ℓ₁}(T) → PredSet{ℓ₂}(T) → Stmt
  Overlapping S₁ S₂ = ∃(S₁ ∩ S₂)

  ⋃_ : PredSet{ℓ₁}(PredSet{ℓ₂}(T)) → PredSet(T)
  ⋃_ S x = ∃(s ↦ (s ∈ S) ∧ (x ∈ s))

  ⋂_ : PredSet{ℓ₁}(PredSet{ℓ₂}(T)) → PredSet(T)
  ⋂_ S x = ∀{s} → (s ∈ S) → (x ∈ s)

  ℘ : PredSet{ℓ₁}(T) → PredSet(PredSet{ℓ₁}(T))
  ℘ S x = x ⊆ S

  unapply : ⦃ Equiv{ℓₑ}(B) ⦄ → (f : A → B) → B → PredSet(A)
  unapply f(y) x = f(x) ≡ₛ y

  map : ⦃ Equiv{ℓₑ}(B) ⦄ → (f : A → B) → PredSet{ℓ}(A) → PredSet(B)
  map f(S) y = Overlapping(S)(unapply f(y))

  unmap : (f : A → B) → PredSet{ℓ}(B) → PredSet(A)
  unmap f(y) x = f(x) ∈ y

  ⊶ : ⦃ Equiv{ℓₑ}(B) ⦄ → (f : A → B) → PredSet(B)
  ⊶ f y = ∃(unapply f(y))

  module _ where -- TODO: These proofs should be generalized somewhere else?
    open import Structure.Relator.Equivalence
    open import Structure.Relator.Properties

    private variable S a b c a₁ a₂ b₁ b₂ : PredSet{ℓ}(T)
    private variable S₁ : PredSet{ℓ₁}(T)
    private variable S₂ : PredSet{ℓ₂}(T)

    instance
      [≡]-reflexivity : Reflexivity(_≡_ {ℓ₁}{ℓ₂}{T})
      Reflexivity.proof [≡]-reflexivity = [↔]-reflexivity

    instance
      [≡]-symmetry : Symmetry(_≡_ {ℓ₁}{ℓ₂}{T})
      Symmetry.proof [≡]-symmetry {x} {y} xy = [↔]-symmetry xy

    instance
      [≡]-transitivity : Transitivity(_≡_ {ℓ₁}{ℓ₂}{T})
      Transitivity.proof [≡]-transitivity {x} {y} {z} xy yz = [↔]-transitivity xy yz

    instance
      [≡]-equivalence : Equivalence(_≡_ {ℓ₁}{ℓ₂}{T})
      [≡]-equivalence = intro

    {- TODO: Cannot be an instance of Equiv because of level issues, but see a solution in ExtensionalPredicateSet
    instance
      [≡]-equiv : Equiv(PredSet{ℓ}(T))
      Equiv._≡_ [≡]-equiv = {!_≡_!}
      Equiv.equivalence [≡]-equiv = {![≡]-equivalence!}
    -}

    [≡]-to-[⊆] : (S₁ ≡ S₂) → (S₁ ⊆ S₂)
    [≡]-to-[⊆] S₁S₂ {x} = [↔]-to-[→] (S₁S₂{x})

    [≡]-to-[⊇] : (S₁ ≡ S₂) → (S₁ ⊇ S₂)
    [≡]-to-[⊇] S₁S₂ {x} = [↔]-to-[←] (S₁S₂{x})

    [⊆]-of-[∪]ₗ : (S₁ ⊆ (S₁ ∪ S₂))
    [⊆]-of-[∪]ₗ = [∨]-introₗ

    [⊆]-of-[∪]ᵣ : (S₂ ⊆ (S₁ ∪ S₂))
    [⊆]-of-[∪]ᵣ = [∨]-introᵣ

    [∪]-of-subset : (S₁ ⊆ S₂) → ((S₁ ∪ S₂) ≡ S₂)
    [∪]-of-subset S₁S₂ = [↔]-intro [∨]-introᵣ ([∨]-elim S₁S₂ id)

    [⊆]-min : (∅ {ℓ} ⊆ S)
    [⊆]-min = empty

    [⊆]-max : (S ⊆ 𝐔 {ℓ})
    [⊆]-max = const <>

    [∅]-containment : ∀{x : T} → ¬(x ∈ ∅ {ℓ})
    [∅]-containment = empty

    [𝐔]-containment : ∀{x : T} → (x ∈ 𝐔 {ℓ})
    [𝐔]-containment = <>

    map-containmentₗ : ⦃ equiv-B : Equiv{ℓₑ}(B) ⦄ → ∀{x : A}{f : A → B} → (f(x) ∈ map ⦃ equiv-B ⦄ f(S)) ← (x ∈ S)
    map-containmentₗ {x = x} = (xS ↦ [∃]-intro x ⦃ [↔]-intro xS (reflexivity(_≡ₛ_)) ⦄)

    -- map-containmentᵣ : ⦃ _ : Relation(S) ⦄ → ∀{f : A → B} → ⦃ _ : Injective(f) ⦄ → ∀{x : A} → (f(x) ∈ map f(S)) → (x ∈ S)
    -- map-containmentᵣ {x = x} = ([∃]-intro a ⦃ [∧]-intro p q ⦄) ↦ {!!}

    [⋂]-of-[∅] : ((⋂_ {ℓ₁}{ℓ₂} ∅) ≡ 𝐔 {ℓ₃}{ℓ₄}{T})
    [⋂]-of-[∅] = [↔]-intro (const empty) (const <>)

    [⋂]-of-[𝐔] : ((⋂_ {ℓ₁}{ℓ₂} 𝐔) ≡ ∅ {ℓ₂}{ℓ₃}{T})
    [⋂]-of-[𝐔] {ℓ₁}{ℓ₂}{ℓ₃}{T} = [↔]-intro empty (inters ↦ inters([𝐔]-containment{x = ∅ {ℓ₂}{ℓ₃}{T}}))

    [⋃]-of-[∅] : ((⋃_ {ℓ₁}{ℓ₂} ∅) ≡ ∅ {ℓ₁}{ℓ₃}{T})
    [⋃]-of-[∅] = [↔]-intro empty (([∃]-intro s ⦃ [∧]-intro p _ ⦄) ↦ p)

    [⋃]-of-[𝐔] : ((⋃_ {ℓ₁}{ℓ₂} 𝐔) ≡ 𝐔 {ℓ₃}{ℓ₄}{T})
    [⋃]-of-[𝐔] {ℓ₁}{ℓ₂}{ℓ₃}{T} = [↔]-intro (const ([∃]-intro 𝐔 ⦃ [↔]-intro <> <> ⦄)) (const <>)

    LvlUp-set-equality : (Lvl.Up{ℓ} ∘ S ≡ S)
    LvlUp-set-equality = [↔]-intro Lvl.up Lvl.Up.obj

    -- Disjoint-irreflexivity : ⦃ _ : NonEmpty(_) ⦄ → Irreflexivity(Disjoint{ℓ₁}{ℓ₂}{T})
    -- Irreflexivity.proof Disjoint-irreflexivity p = {!!}

    -- TODO: Duplicated in SetLike
    -- SetType : PredSet{ℓ}(T) → Stmt
    -- SetType(s) = ∃(_∈ s)

    {-
    choice : ∀{S : PredSet{ℓ₁}(PredSet{ℓ₂}(T))} → ∃{Obj = SetType(S) → SetType(⋃ S)}(f ↦ ∀{([∃]-intro x) : SetType(S)} → ([∃]-witness(f([∃]-intro x)) ∈ x))
    ∃.witness (∃.witness (choice {S = S}) ([∃]-intro f ⦃ proof ⦄)) = {!!}
    ∃.proof   (∃.witness (choice {S = S}) ([∃]-intro f ⦃ proof ⦄)) = {!!}
    ∃.proof              (choice {S = S}) {[∃]-intro f ⦃ proof ⦄}  = {!!}
    -}

    [∪]-subset : (a ⊆ c) → (b ⊆ c) → ((a ∪ b) ⊆ c)
    [∪]-subset ac bc = [∨]-elim ac bc

    [∪]-subset2 : (a₁ ⊆ a₂) → (b₁ ⊆ b₂) → ((a₁ ∪ b₁) ⊆ (a₂ ∪ b₂))
    [∪]-subset2 aa bb = [∨]-elim2 aa bb

    [∖][∪]-is-[∪]ᵣ : (((a ∖ b) ∪ b) ⊆ (a ∪ b))
    [∖][∪]-is-[∪]ᵣ {a = A}{b = B}{x = x} = [∨]-elim ([∨]-introₗ ∘ [∧]-elimₗ) [∨]-introᵣ

    open import Logic.Classical
    [∖][∪]-is-[∪] : ⦃ ∀{x} → Classical(b(x)) ⦄ → (((a ∖ b) ∪ b) ≡ (a ∪ b))
    [∖][∪]-is-[∪] {b = B}{a = A}{x = x} = [↔]-intro
      ([∨]-elim (Ax ↦ [∨]-elim2 ([∧]-intro Ax) id ([∨]-symmetry(excluded-middle(B(x))))) [∨]-introᵣ)
      ([∖][∪]-is-[∪]ᵣ {a = A}{b = B})

    [∪][∖]-invertᵣ-[⊆] : (a ⊆ (b ∪ c)) → ((a ∖ c) ⊆ b)
    [∪][∖]-invertᵣ-[⊆] abc ([∧]-intro a nc) = [∨]-elim id ([⊥]-elim ∘ nc) (abc a)


-- This file comes from the agda class taught by Peter Selinger at
-- Dalhousie University. It contains a bonus question that
-- propostional logic is complete. We will show this in
-- Kalmar.agda.

-- The course link:
-- https://www.mathstat.dal.ca/~selinger/agda-lectures/

{-# OPTIONS --without-K --safe #-}
module PropLogic where

-- We replace the library (lib-3.0) used in class by the stdlib v2.

-- open import Equality
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl ; inspect ; [_] ; trans ; sym)

-- open import Nat
open import Data.Nat using (ℕ)

-- open import Bool
-- renaming and defining new connectives.
open import Data.Bool renaming(_∧_ to _and_ ; _∨_ to _or_) using (Bool ; not ; true ; false)
_implies_ : Bool → Bool → Bool
_implies_ a b = not a or b

-- ----------------------------------------------------------------------
-- * Introduction

-- In this project, we will formalize boolean (i.e., classical)
-- propositional logic, define a notion of derivation for the logic,
-- give a semantics for the logic in terms of truth tables (also known
-- as boolean assignments), and prove the soundness of the derivation
-- rules.

-- ----------------------------------------------------------------------
-- * The set of boolean formulas

-- We start by defining the set of boolean formulas. Informally, the
-- formulas are given by the following grammar:
--
-- Formula   A, B ::= P n | A ∧ B | A ⇒ B | ⊥ | ⊤
--
-- Here, n is a natural number, and P n is the nth propositional
-- symbol. A ∧ B is conjunction, A ⇒ B is implication, ⊥ is "false"
-- (also known as a contradiction), and ⊤ is "true". You can type
-- these symbols in Emacs Agda mode using \and, \=>, \bot, and \top.
-- Note that the symbol "⊤" is not the same as the capital letter "T".

-- The set of propositional formulas as an Agda datatype:
data Formula : Set where
  P : ℕ -> Formula
  _∧_ : Formula -> Formula -> Formula
  _⇒_ : Formula -> Formula -> Formula
  ⊥ : Formula
  ⊤ : Formula
  ¬_ : Formula -> Formula
  _∨_ : Formula -> Formula -> Formula

infixl 9 _∧_
infixr 8 _⇒_

infix 20 ¬_

-- ----------------------------------------------------------------------
-- * Derivations

-- The most important concept in logic is that of derivability.
-- "Derivation" is another word for "proof". If A₁, ..., Aₙ, B are
-- formulas, we write
--
--       A₁, ..., Aₙ ⊢
--
-- for the statement "the conclusion B can be derived from hypotheses
-- A₁, ..., Aₙ". The symbol "⊢" is pronounced "entails", and we also
-- say "A₁, ..., Aₙ entails B". You can type this symbol as \|- or
-- \entails or \vdash.
--
-- Although entailment "⊢" is somewhat similar to implication "⇒",
-- there is an important difference: an implication A ⇒ B is a
-- formula, so it could be used, for example, as a hypothesis or as a
-- conclusion in a proof. Entailment is a statement about formulas,
-- i.e., entailment means: there exists a proof of B from hypotheses
-- A₁, ..., Aₙ. Entailment is not itself a formula; it is a statement
-- about provability. Thus, entailment is a relation between a list of
-- formulas (the hypotheses) and a formula (the conclusion).

-- ----------------------------------------------------------------------
-- ** Contexts

-- Before we can formalize entailment, we must formalize lists of
-- formulas. A list of formulas is also called a "context". We
-- write Γ for an arbitrary context.
--
-- Because a context can also be empty (a list of zero formulas), it
-- is convenient to use the empty context as the base case. Then we
-- can give the following recursive definition of contexts: a context
-- is either the empty context, or it is obtained from another context
-- by adding one more formula on the right. In other words, we have
-- the following grammar for contexts:
--
-- Context   Γ ::= Empty | Γ, A
--
-- We formalize this as the following Agda datatype:

{-
data Context : Set where
  Empty : Context
  _,_ : Context -> Formula -> Context
-}

-- Modification: We use List Formula as Context instead of defining a
-- new data type. This will ease the proof using list with distinct
-- elements.

open import Data.List using (List ; _∷_ ; [])
Context = List Formula

infixl 7 _,_

-- _,_ : Context -> Formula -> Context
-- _,_ Γ F = F ∷ Γ

pattern _,_ Γ F = F ∷ Γ
pattern Empty = []

-- We also need the membership relation for contexts. We define the
-- relation A ∈ Γ to mean "the formula A is in the context Γ". The
-- symbol ∈ is typed as \in. The membership relation can be
-- recursively defined as follows: the empty context has no members,
-- and the members of the context Γ, A are A and the members of
-- Γ. More formally, we have the following inference rules for
-- membership:
--
-- ───────────── (∈-base)
--   A ∈ Γ, A
--
--
--     A ∈ Γ
-- ───────────── (∈-step)
--   A ∈ Γ, B
--
-- An inference rule, written with a horizontal line, has the
-- following meaning: if the (zero or more) statements above the line
-- are true, then the statement below the line is true. The statements
-- above the line are called the hypotheses of the inference rule, and
-- the statement below the line is called the conclusion. The name of
-- each inference rule (e.g., ∈-base, ∈-step) is written next to the
-- rule. We use inference rules to define a relation, in this case the
-- relation "∈". By definition, "∈" is the smallest relation
-- satisfying (∈-base) and (∈-step).
--
-- We can directly translate these inference rules into Agda as
-- follows:
data _∈_ : Formula -> Context -> Set where
  ∈-base : ∀ {A : Formula} {Γ : Context} -> A ∈ (Γ , A)
  ∈-step : ∀ {A B : Formula} {Γ : Context} -> A ∈ Γ -> A ∈ (Γ , B)

infixl 6 _∈_

-- ----------------------------------------------------------------------
-- ** Entailment

-- We now define the entailment relation. Recall that the entailment
-- relation Γ ⊢ A is supposed to mean "the conclusion A can be derived
-- from the hypotheses Γ". Defining entailment therefore requires us
-- to formalize what we mean by "derived". In other words, we have to
-- give a set of proof rules for propositional logic. Let me first
-- write these proof rules in the style of inference rules. These
-- rules define the relation "⊢":
--
--    A ∈ Γ
-- ───────────── (assump)
--    Γ ⊢ A
--
--
--   Γ ⊢ A   Γ ⊢ B
-- ───────────────── (∧-intro)
--     Γ ⊢ A ∧ B
--
--
--     Γ ⊢ A ∧ B
-- ───────────────── (∧-elim1)
--       Γ ⊢ A
--
--
--     Γ ⊢ A ∧ B
-- ───────────────── (∧-elim2)
--       Γ ⊢ B
--
--
--     Γ, A ⊢ B
-- ───────────────── (⇒-intro)
--     Γ ⊢ A ⇒ B
--
--
--     Γ ⊢ A ⇒ B    Γ ⊢ A
-- ────────────────────────── (⇒-elim)
--           Γ ⊢ B
--
--
--     Γ ⊢ (A ⇒ ⊥) ⇒ ⊥
-- ────────────────────── (double-negation)
--         Γ ⊢ A
--
--
-- ───────────────── (⊤-intro)
--      Γ ⊢ ⊤
--
--
-- The rule (assump) states that if A is one of the hypotheses, then A
-- can be derived from the hypothesis. Basically A is true "by assumption".
--
-- Several of the connectives, such as ∧ and ⇒, have an "introduction"
-- rule, which shows how to prove such a formula, and an "elimination"
-- rule, which shows how such a formula can be used to prove other
-- things. The rule (∧-intro) states that to prove A ∧ B from the
-- given hypotheses Γ, we must separately derive A and B. Conversely,
-- the rules (∧-elim1) and (∧-elim2) state that if we have already
-- derived A ∧ B, then we may separately conclude A and B. The rule
-- (⇒-intro) states that to prove an implication A ⇒ B, we must assume
-- A and prove B. In other words, to derive A ⇒ B from some hypotheses
-- Γ, we must prove B from the hypotheses Γ, A. The rule (⇒-elim) is
-- also known by its Latin name "modus ponens". It states that if we
-- can prove A ⇒ B and we can also prove A, then we may conclude
-- B. The rule (double-negation) is required to make the logic
-- classical.  Informally, it states that we can prove A by assuming
-- "not A" and deriving a contradiction. Remember that "not A" is an
-- abbreviation for (A ⇒ ⊥). The rule (⊤-intro) states that ⊤ can be
-- derived from any set of hypotheses.

-- We can directly translate the above rules into Agda:
data _⊢_ : Context -> Formula -> Set where
  assump : ∀ {Γ A} -> A ∈ Γ -> Γ ⊢ A
  ∧-intro : ∀ {Γ A B} -> Γ ⊢ A -> Γ ⊢ B -> Γ ⊢ A ∧ B
  ∧-elim1 : ∀ {Γ A B} -> Γ ⊢ A ∧ B -> Γ ⊢ A
  ∧-elim2 : ∀ {Γ A B} -> Γ ⊢ A ∧ B -> Γ ⊢ B
  ⇒-intro : ∀ {Γ A B} -> Γ , A ⊢ B -> Γ ⊢ A ⇒ B
  ⇒-elim : ∀ {Γ A B} -> Γ ⊢ A ⇒ B -> Γ ⊢ A -> Γ ⊢ B
  double-negation : ∀ {Γ A} -> Γ ⊢ (A ⇒ ⊥) ⇒ ⊥ -> Γ ⊢ A
  ⊤-intro : ∀ {Γ} -> Γ ⊢ ⊤
  ¬-intro : ∀ {Γ A} -> Γ , A ⊢ ⊥ -> Γ ⊢ ¬ A
  ¬-elim : ∀ {Γ A} -> Γ  ⊢ ¬ A -> Γ ⊢ A -> Γ ⊢ ⊥
  ∨-intro1 : ∀ {Γ A B} -> Γ ⊢ A -> Γ ⊢ A ∨ B
  ∨-intro2 : ∀ {Γ A B} -> Γ ⊢ B  -> Γ ⊢ A ∨ B
  ∨-elim : ∀ {Γ A B C} -> Γ ⊢ A ∨ B -> Γ , A ⊢ C -> Γ , B ⊢ C -> Γ ⊢ C

infixl 4 _⊢_

-- ----------------------------------------------------------------------
-- ** Weakening

-- "Weakening" is the property that if we add additional hypotheses to
-- an entailment (or change their order), the entailment remains valid.
-- More formally, weakening states that Γ ⊢ A and Γ ⊆ Γ' implies Γ' ⊢ A.
--
-- To state weakening, we must first define ⊆. The symbol ⊆ can be
-- typed as \subseteq or \seq.
_⊆_ : Context -> Context -> Set
Γ ⊆ Γ' = ∀ {A} -> A ∈ Γ -> A ∈ Γ'

infix 4 _⊆_

-- We need a lemma about ⊆:
lemma-⊆ : ∀ {Γ Γ' A} -> Γ ⊆ Γ' -> Γ , A ⊆ Γ' , A
lemma-⊆ hyp ∈-base = ∈-base
lemma-⊆ hyp (∈-step hyp2) = ∈-step (hyp hyp2)

lemma-⊆-ext : ∀ {Γ Γ' A} -> Γ ⊆ Γ' -> Γ ⊆ Γ' , A
lemma-⊆-ext hyp =  \ x -> ∈-step (hyp x)

lemma-⊆-ext' : ∀ {Γ A} -> Γ ⊆ Γ , A
lemma-⊆-ext' = lemma-⊆-ext \ x -> x

lemma-⊆-empty : ∀ {Γ} -> Empty ⊆ Γ
lemma-⊆-empty {Γ} {A} ()

-- Prove the weakening lemma:
weakening : ∀ {Γ Γ' A} -> Γ ⊢ A -> Γ ⊆ Γ' -> Γ' ⊢ A
weakening (assump x) w = assump (w x)
weakening (∧-intro d d₁) w = ∧-intro (weakening d w) (weakening d₁ w)
weakening (∧-elim1 d) w = ∧-elim1 (weakening d w )
weakening (∧-elim2 d) w = ∧-elim2 (weakening d w)
weakening (⇒-intro d) w = ⇒-intro (weakening d (lemma-⊆ w) )
weakening (⇒-elim d d₁) w = ⇒-elim (weakening d w ) (weakening d₁ w )
weakening (double-negation d) w = double-negation (weakening d w )
weakening ⊤-intro w = ⊤-intro
weakening (¬-intro d) w  = ¬-intro (weakening d (lemma-⊆ w ) )
weakening (¬-elim d e) w  = ( ¬-elim (weakening d w) (weakening e w))
weakening (∨-intro1 d) w  = ∨-intro1 (weakening d w)
weakening (∨-intro2 d) w  = ∨-intro2 (weakening d w)
weakening (∨-elim d e f) w  = ∨-elim (weakening d w) (weakening e (lemma-⊆ w)) (weakening f (lemma-⊆ w) )

-- ----------------------------------------------------------------------
-- ** A derived rule

-- Classical logic also has the following rule, called "ex falsum
-- quodlibet" or "⊥-elim".
--
--       Γ ⊢ ⊥
-- ───────────────── (⊥-elim)
--       Γ ⊢ A
--
-- The reason we did not include it in the proof rules in the above
-- definition of entailment it that it follows from the other rules.
-- Prove it:

⊥-elim : ∀ {Γ A} -> Γ ⊢ ⊥ -> Γ ⊢ A
⊥-elim {Γ} {A} a = double-negation a-⊥-⊥  where
  a-⊥ : Γ ⊢ A ⇒ ⊥
  a-⊥ = ⇒-intro (weakening a (lemma-⊆-ext \ x -> x ) )
  a-⊥-⊥ : Γ ⊢ (A ⇒ ⊥) ⇒ ⊥
  a-⊥-⊥ = ⇒-intro (weakening a (lemma-⊆-ext \ x -> x ) )

-- ----------------------------------------------------------------------
-- * Truth table semantics

-- ----------------------------------------------------------------------
-- ** Truth assignments and interpretation

-- Remember that the atomic formulas of our logic are P 0, P 1, P 2,
-- and so on. The fundamental concept of truth tables is that of a
-- "truth assignment", namely a map from atomic formulas to {T, F}.
-- We usually denote a truth assignment by the letter ρ = \rho.
--
-- We formalize truth assignments as maps from ℕ to Bool.
Assign : Set
Assign = ℕ -> Bool

-- Given a truth assignment ρ, we can compute the truth value (true or
-- false) of any given formula. This is called the "interpretation" of
-- formulas.

interp : Assign -> Formula -> Bool
interp ρ (P x) = ρ x
interp ρ (A ∧ B) = (interp ρ A) and (interp ρ B)
interp ρ (A ⇒ B) = (interp ρ A) implies (interp ρ B)
interp ρ ⊥ = false
interp ρ ⊤ = true
interp ρ (¬ A) = not (interp ρ A)
interp ρ (A ∨ B) = (interp ρ A) or (interp ρ B)

-- Lift the interp function to a list of formulas. We interpret a list
-- of formulas basically as the conjunction of the formulas, i.e., the
-- interpretation of Γ is true if and only if the interpretation of
-- all A ∈ Γ is true.
interps : Assign -> Context -> Bool
interps ρ Empty = true
interps ρ (c , x) = interps ρ c and interp ρ x

-- ----------------------------------------------------------------------
-- ** Semantic entailment

-- We write Γ ⊧ A if under all assignments ρ, if Γ is true, then A is true.
-- A special case is when the context Γ is empty. In that case, ⊧ A means
-- that A is true under all assignments, i.e., A is a tautology.
--
-- The symbol ⊧ is pronounced "models", and you can type it as
-- \models. Please note that this is a different symbol from ⊨ = \|=.
-- We use the symbol "⊧" here.
--
-- The relation ⊧ is also called "semantic entailment" or "truth table
-- entailment", to distinguish it from ⊢, which is "syntactic
-- entailment" or "derivability".

-- We define semantic entailment.
-- _⊧_ : Context -> Formula -> Set
-- Γ ⊧ A = ∀ ρ -> interps ρ Γ ≡ true -> interp ρ A ≡ true

_⊧_ : Context -> Formula -> Set
Γ ⊧ f = (ρ : Assign) -> interps ρ Γ ≡ true -> interp ρ f ≡ true

infix 4 _⊧_

-- ----------------------------------------------------------------------
-- * Soundness

-- ----------------------------------------------------------------------
-- ** Lemmas about the boolean connectives

-- The boolean functions 'and' and 'implies' are defined in Bool.agda.
-- We need to know certain ones of their properties.

boolCases : {p : Set} -> (a : Bool) -> (a ≡ true -> p) -> (a ≡ false -> p) -> p
boolCases true t f = t refl
boolCases false t f = f refl

lemma-and-1 : {a b : Bool} -> a ≡ true -> b ≡ true -> a and b ≡ true
lemma-and-1 refl refl = refl

lemma-and-2 : {a b : Bool} -> a and b ≡ true -> a ≡ true
lemma-and-2 {true} {true} abt = refl

lemma-and-3 : {a b : Bool} -> a and b ≡ true -> b ≡ true
lemma-and-3 {true} {true} abt = refl

lemma-imp-1 : {a b : Bool} -> a implies b ≡ true -> a ≡ true -> b ≡ true
lemma-imp-1 {true} {true} aibt at = refl

lemma-imp-2 : {a b : Bool} -> a ≡ false -> a implies b ≡ true
lemma-imp-2 {false} {true} af = refl
lemma-imp-2 {false} {false} af = refl

lemma-imp-3 : {a b : Bool} -> b ≡ true -> a implies b ≡ true
lemma-imp-3 {true} {true} bt = refl
lemma-imp-3 {false} {b} bt = refl

lemma-doubleNeg : {a : Bool} -> (a implies false) implies false ≡ true -> a ≡ true
lemma-doubleNeg {true} a-f-ft = refl

lemma-not-true : {a : Bool} -> a ≡ true -> not a ≡ false
lemma-not-true {.true} refl = refl

lemma-not-false : {a : Bool} -> a ≡ false -> not a ≡ true
lemma-not-false {.false} refl = refl

lemma-or-1 : {a b : Bool} -> a ≡ true -> a or b ≡ true
lemma-or-1 refl  = refl

lemma-or-2 : {a b : Bool} -> b ≡ true -> a or b ≡ true
lemma-or-2 {true} {.true} refl = refl
lemma-or-2 {false} {.true} refl = refl

-- like boolCases
lemma-or-3 : {C : Set} ->  {a b : Bool} -> a or b ≡ true -> (a ≡ true -> C) -> (b ≡ true -> C) -> C
lemma-or-3 {a = true} {true} aob ca cb = ca aob
lemma-or-3 {a = false} {true} aob ca cb = cb aob
lemma-or-3 {a = true} {false} aob ca cb = ca refl

-- ----------------------------------------------------------------------
-- ** Lemmas about the soundness of individual rules

-- Each of the proof rules of logic has a semantic counterpart.

lemma-assump : ∀ Γ A -> A ∈ Γ -> Γ ⊧ A
lemma-assump (._ , A) A ∈-base = \ ρ ct -> lemma-and-3 ct
lemma-assump (Γ' , B) A (∈-step hyp) = \ ρ ct -> (lemma-assump Γ' A hyp) ρ (lemma-and-2 ct )

lemma-∧-intro : ∀ Γ A B -> Γ ⊧ A -> Γ ⊧ B -> Γ ⊧ A ∧ B
lemma-∧-intro Γ A B as bs = \ ρ ct -> lemma-and-1 (as ρ ct ) (bs ρ ct )

lemma-∧-elim1 : ∀ Γ A B -> Γ ⊧ A ∧ B -> Γ ⊧ A
lemma-∧-elim1 Γ A B abs = \ ρ ct -> lemma-and-2 (abs ρ ct)

lemma-∧-elim2 : ∀ Γ A B -> Γ ⊧ A ∧ B -> Γ ⊧ B
lemma-∧-elim2 Γ A B abs = \ ρ ct -> lemma-and-3 (abs ρ ct)

lemma-⇒-intro' : ∀ Γ A B -> Γ , A ⊧ B -> Γ ⊧ A ⇒ B
lemma-⇒-intro' Γ A B a-bs ρ ct  = boolCases (interp ρ A) (\ x -> lemma-imp-3 (a-bs ρ (lemma-and-1 ct x ) ) ) \ x -> lemma-imp-2 x

lemma-⇒-intro : ∀ Γ A B -> Γ , A ⊧ B -> Γ ⊧ A ⇒ B
lemma-⇒-intro Γ A B a-bs ρ ct with (interp ρ) A | inspect (interp ρ) A
lemma-⇒-intro Γ A B a-bs ρ ct | true | [ eq ] = lemma-imp-3 (a-bs ρ (lemma-and-1 ct eq ) )
lemma-⇒-intro Γ A B a-bs ρ ct | false | [ eq ] = refl

lemma-⇒-elim : ∀ Γ A B -> Γ ⊧ A ⇒ B -> Γ ⊧ A -> Γ ⊧ B
lemma-⇒-elim Γ A B a-bs as = \ ρ ct -> lemma-imp-1 (a-bs ρ ct ) (as ρ ct )

lemma-double-negation : ∀ Γ A -> Γ ⊧ (A ⇒ ⊥) ⇒ ⊥ -> Γ ⊧ A
lemma-double-negation Γ A anns = \ ρ ct -> lemma-doubleNeg (anns ρ ct )

lemma-⊤-intro : ∀ Γ -> Γ ⊧ ⊤
lemma-⊤-intro Γ = \ ρ ct -> refl

lemma-¬-intro : ∀ Γ A -> Γ , A ⊧ ⊥ -> Γ ⊧ ¬ A
lemma-¬-intro Γ A a ρ ct with (interp ρ) A | inspect (interp ρ) A
lemma-¬-intro Γ A a ρ ct | true  | [ eq ] = a ρ (lemma-and-1 ct eq)
lemma-¬-intro Γ A a ρ ct | false | [ eq ] = refl

lemma-¬-intro' : ∀ Γ A -> Γ , A ⊧ ⊥ -> Γ ⊧ ¬ A
lemma-¬-intro' Γ A a ρ ct = boolCases (interp ρ A) (\x -> trans (lemma-not-true x ) (a ρ (lemma-and-1 ct x ) )  ) \ x -> lemma-not-false x

lemma-¬-elim : ∀ Γ A -> Γ ⊧ ¬ A -> Γ ⊧  A -> Γ ⊧ ⊥
lemma-¬-elim Γ A a b ρ ct = boolCases (interp ρ A) (\ x -> trans (sym (lemma-not-true x)) (a ρ ct )   ) \ x -> trans (sym  x ) (b ρ ct)

lemma-∨-intro1 : ∀ Γ A B -> Γ ⊧  A -> Γ ⊧  A ∨ B
lemma-∨-intro1 Γ A B a ρ ct = lemma-or-1 (a ρ ct )

lemma-∨-intro2 : ∀ Γ A B -> Γ ⊧  B -> Γ ⊧  A ∨ B
lemma-∨-intro2 Γ A B a ρ ct = lemma-or-2 (a ρ ct )

lemma-∨-elim : ∀ Γ A B C -> Γ ⊧  A ∨ B -> Γ , A ⊧  C -> Γ , B ⊧  C -> Γ ⊧ C
lemma-∨-elim Γ A B C aob ac bc ρ ct = lemma-or-3 (aob ρ ct) (\ x -> ac ρ (lemma-and-1 ct x )  ) \ x -> bc ρ (lemma-and-1 ct x )

-- ----------------------------------------------------------------------
-- * The soundness theorem of boolean logic.

-- Soundness states that Γ ⊢ A implies Γ ⊧ A, or equivalently:
-- everything that is provable is valid.

-- 4. Prove the soundness of boolean logic.
sound : ∀ Γ A -> Γ ⊢ A -> Γ ⊧ A
sound Γ A (assump x) = lemma-assump Γ A x
sound Γ (A ∧ B) (∧-intro hyp hyp₁) = lemma-∧-intro Γ A B (sound Γ A hyp) (sound Γ B hyp₁)
sound Γ A (∧-elim1 {B = B} hyp) = lemma-∧-elim1 Γ A B (sound Γ  (A ∧ B) hyp )
sound Γ A (∧-elim2 {A = A₁} hyp) = lemma-∧-elim2 Γ  A₁ A ((sound Γ  (A₁ ∧ A) hyp ) )
sound Γ (A ⇒ B) (⇒-intro hyp) = lemma-⇒-intro Γ A B (sound (Γ , A) B   hyp )
sound Γ A (⇒-elim {A = A₁} hyp hyp₁) = lemma-⇒-elim Γ A₁ A (sound Γ (A₁ ⇒ A) hyp ) (sound Γ A₁ hyp₁)
sound Γ A (double-negation hyp) = lemma-double-negation Γ A (sound Γ ((A ⇒ ⊥) ⇒ ⊥) hyp)
sound Γ .⊤ ⊤-intro = λ ρ _ → refl
sound Γ (¬ A) (¬-intro x) = lemma-¬-intro' Γ  A ((sound (Γ , A) ⊥  x ) )
sound Γ .⊥ (¬-elim {A = A} x y) = lemma-¬-elim Γ A (sound Γ (¬ A) x) (sound Γ  A y)
sound Γ (A ∨ B) (∨-intro1 x) = lemma-∨-intro1 Γ A B (sound Γ A x )
sound Γ (A ∨ B) (∨-intro2 x) = lemma-∨-intro2 Γ A B (sound Γ B x )
sound Γ C (∨-elim {A = A} {B} x y z) = lemma-∨-elim Γ A B C (sound Γ (A ∨ B) x ) (sound (Γ , A) C y ) (sound (Γ , B) C z)

-- ----------------------------------------------------------------------
-- * Bonus questions and beyond

-- If you feel adventurous, add more connectives, such as ¬A or A ∨ B,
-- to the boolean logic.
--
-- The proof rules are:
--
--    Γ, A ⊢ ⊥
-- ────────────── (¬-intro)
--     Γ ⊢ ¬A
--
--
--    Γ ⊢ ¬A     Γ ⊢ A
-- ───────────────────── (¬-elim)
--         Γ ⊢ ⊥
--
--
--      Γ ⊢ A
-- ───────────── (∨-intro1)
--    Γ ⊢ A ∨ B
--
--
--      Γ ⊢ B
-- ───────────── (∨-intro2)
--    Γ ⊢ A ∨ B
--
--
--    Γ ⊢ A ∨ B    Γ, A ⊢ C    Γ, B ⊢ C
-- ─────────────────────────────────────── (∨-elim)
--                  Γ ⊢ C

-- If you feel extremely ambitious, try to prove completeness, the
-- converse of soundness. Completeness is the statement that Γ ⊧ A
-- implies Γ ⊢ A.
--
-- Warning: this is extremely difficult and far more work than
-- soundness. Neither Frank nor I have been able to finish a proof of
-- completeness.

-- Actually, Peter had finished the proof a couple of days later using
-- conjunction normal form.

completeness-property : Set
completeness-property = ∀ Γ A -> Γ ⊧ A -> Γ ⊢ A

-- Now, I (Bian) will prove this property in Kalmar.agda using
-- Kalmar's lemma.


------------------------------------------------------------------------
-- The Agda standard library
--
-- N-ary products
------------------------------------------------------------------------

-- Vectors (as in Data.Vec) also represent n-ary products, so what is
-- the point of this module? The n-ary products below are intended to
-- be used with a fixed n, in which case the nil constructor can be
-- avoided: pairs are represented as pairs (x , y), not as triples
-- (x , y , unit).

module Data.Product.N-ary where

open import Data.Nat
open import Data.Product
open import Data.Unit
open import Data.Vec
open import Function.Inverse
open import Level using (Lift; lift)
open import Relation.Binary.PropositionalEquality as P using (_≡_)

-- N-ary product.

infix 8 _^_

_^_ : ∀ {ℓ} → Set ℓ → ℕ → Set ℓ
A ^ 0           = Lift ⊤
A ^ 1           = A
A ^ suc (suc n) = A × A ^ suc n

-- Conversions.

↔Vec : ∀ {a} {A : Set a} n → A ^ n ↔ Vec A n
↔Vec n = record
  { to         = P.→-to-⟶ (toVec n)
  ; from       = P.→-to-⟶ fromVec
  ; inverse-of = record
    { left-inverse-of  = fromVec∘toVec n
    ; right-inverse-of = toVec∘fromVec
    }
  }
  where
  toVec : ∀ {a} {A : Set a} n → A ^ n → Vec A n
  toVec 0             (lift tt) = []
  toVec 1             x         = [ x ]
  toVec (suc (suc n)) (x , xs)  = x ∷ toVec _ xs

  fromVec : ∀ {a} {A : Set a} {n} → Vec A n → A ^ n
  fromVec []           = lift tt
  fromVec (x ∷ [])     = x
  fromVec (x ∷ y ∷ xs) = (x , fromVec (y ∷ xs))

  fromVec∘toVec : ∀ {a} {A : Set a} n (xs : A ^ n) →
                  fromVec (toVec n xs) ≡ xs
  fromVec∘toVec 0                   (lift tt)    = P.refl
  fromVec∘toVec 1                   x            = P.refl
  fromVec∘toVec 2                   (x , y)      = P.refl
  fromVec∘toVec (suc (suc (suc n))) (x , y , xs) =
    P.cong (_,_ x) (fromVec∘toVec (suc (suc n)) (y , xs))

  toVec∘fromVec : ∀ {a} {A : Set a} {n} (xs : Vec A n) →
                  toVec n (fromVec xs) ≡ xs
  toVec∘fromVec []           = P.refl
  toVec∘fromVec (x ∷ [])     = P.refl
  toVec∘fromVec (x ∷ y ∷ xs) = P.cong (_∷_ x) (toVec∘fromVec (y ∷ xs))


{-# OPTIONS --cubical --safe #-}
module Cubical.HITs.ListedFiniteSet.Base where

open import Cubical.Core.Everything
open import Cubical.Foundations.Logic
open import Cubical.Foundations.Everything

private
  variable
    A : Type₀

infixr 20 _∷_
infix 30 _∈_

data LFSet (A : Type₀) : Type₀ where
  []    : LFSet A
  _∷_   : (x : A) → (xs : LFSet A) → LFSet A
  dup   : ∀ x xs   → x ∷ x ∷ xs ≡ x ∷ xs
  comm  : ∀ x y xs → x ∷ y ∷ xs ≡ y ∷ x ∷ xs
  trunc : isSet (LFSet A)

-- Membership.
--
-- Doing some proofs with equational reasoning adds an extra "_∙ refl"
-- at the end.
-- We might want to avoid it, or come up with a more clever equational reasoning.
_∈_ : A → LFSet A → hProp
z ∈ []                  = ⊥
z ∈ (y ∷ xs)            = (z ≡ₚ y) ⊔ (z ∈ xs)
z ∈ dup x xs i          = proof i
  where
    -- proof : z ∈ (x ∷ x ∷ xs) ≡ z ∈ (x ∷ xs)
    proof = z ≡ₚ x  ⊔ (z ≡ₚ x ⊔ z ∈ xs) ≡⟨ ⊔-assoc (z ≡ₚ x) (z ≡ₚ x) (z ∈ xs) ⟩
            (z ≡ₚ x ⊔ z ≡ₚ x) ⊔ z ∈ xs  ≡⟨ cong (_⊔ (z ∈ xs)) (⊔-idem (z ≡ₚ x)) ⟩
            z ≡ₚ x            ⊔ z ∈ xs  ∎
z ∈ comm x y xs i       = proof i
  where
    -- proof : z ∈ (x ∷ y ∷ xs) ≡ z ∈ (y ∷ x ∷ xs)
    proof = z ≡ₚ x  ⊔ (z ≡ₚ y ⊔ z ∈ xs) ≡⟨ ⊔-assoc (z ≡ₚ x) (z ≡ₚ y) (z ∈ xs) ⟩
            (z ≡ₚ x ⊔ z ≡ₚ y) ⊔ z ∈ xs  ≡⟨ cong (_⊔ (z ∈ xs)) (⊔-comm (z ≡ₚ x) (z ≡ₚ y)) ⟩
            (z ≡ₚ y ⊔ z ≡ₚ x) ⊔ z ∈ xs  ≡⟨ sym (⊔-assoc (z ≡ₚ y) (z ≡ₚ x) (z ∈ xs)) ⟩
            z ≡ₚ y  ⊔ (z ≡ₚ x ⊔ z ∈ xs) ∎

x ∈ trunc xs ys p q i j = isSetHProp (x ∈ xs) (x ∈ ys) (cong (x ∈_) p) (cong (x ∈_) q) i j


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Relation.Nullary.HLevels where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Function
open import Cubical.Functions.Fixpoint

open import Cubical.Relation.Nullary

private
  variable
    ℓ : Level
    A : Type ℓ

isPropPopulated : isProp (Populated A)
isPropPopulated = isPropΠ λ x → 2-Constant→isPropFixpoint (x .fst) (x .snd)

isPropHSeparated : isProp (HSeparated A)
isPropHSeparated f g i x y a = HSeparated→isSet f x y (f x y a) (g x y a) i

isPropCollapsible≡ : isProp (Collapsible≡ A)
isPropCollapsible≡ {A = A} f = (isPropΠ2 λ x y → isPropCollapsiblePointwise) f where
  sA : isSet A
  sA = Collapsible≡→isSet f
  gA : isGroupoid A
  gA = isSet→isGroupoid sA
  isPropCollapsiblePointwise : ∀ {x y} → isProp (Collapsible (x ≡ y))
  isPropCollapsiblePointwise {x} {y} (a , ca) (b , cb) = λ i → endoFunction i , endoFunctionIsConstant i where
    endoFunction : a ≡ b
    endoFunction = funExt λ p → sA _ _ (a p) (b p)
    isProp2-Constant : (k : I) → isProp (2-Constant (endoFunction k))
    isProp2-Constant k = isPropΠ2 λ r s → gA x y (endoFunction k r) (endoFunction k s)
    endoFunctionIsConstant : PathP (λ i → 2-Constant (endoFunction i)) ca cb
    endoFunctionIsConstant = isProp→PathP isProp2-Constant ca cb


module Relations where

open import Agda.Builtin.Equality
open import Natural

data _≤_ : ℕ → ℕ → Set where
    z≤n : ∀ {n : ℕ} →
        zero ≤ n
    s≤s : ∀ {m n : ℕ} →
        m ≤ n →
        (suc m) ≤ (suc n)

infix 4 _≤_

-- example : finished by auto mode
_ : 3 ≤ 5
_ = s≤s {2} {4} (s≤s {1} {3} (s≤s {0} {2} (z≤n {2})))

inv-s≤s : ∀ {m n : ℕ}
    → suc m ≤ suc n
    → m ≤ n
inv-s≤s (s≤s m≤n) = m≤n

inv-z≤n : ∀ {m : ℕ}
    → m ≤ zero
    → m ≡ zero
inv-z≤n z≤n = refl


module BBHeap.Order.Properties {A : Set}(_≤_ : A → A → Set) where

open import BBHeap _≤_
open import BBHeap.Order _≤_ renaming (Acc to Accₕ ; acc to accₕ)
open import Data.Nat
open import Induction.Nat
open import Induction.WellFounded

ii-acc : ∀ {b} {h} → Acc _<′_ (# {b} h) → Accₕ h
ii-acc (acc rs) = accₕ (λ h' #h'<′#h → ii-acc (rs (# h') #h'<′#h))

≺-wf : ∀ {b} h → Accₕ {b} h
≺-wf = λ h → ii-acc (<-well-founded (# h))