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

open import Level
open import Categories.Category.Core using (Category)
open import Categories.Category
open import Categories.Monad

module Categories.Adjoint.Construction.Adjunctions {o ℓ e} {C : Category o ℓ e} (M : Monad C)  where

open Category C

open import Categories.Adjoint
open import Categories.Functor
open import Categories.Morphism
open import Categories.Functor.Properties
open import Categories.NaturalTransformation.Core
open import Categories.NaturalTransformation.NaturalIsomorphism -- using (_≃_; unitorʳ; unitorˡ)
open import Categories.Morphism.Reasoning as MR
open import Categories.Tactic.Category

-- three things:
-- 1. the category of adjunctions splitting a given Monad
-- 2. the proof that EM(M) is the terminal object here
-- 3. the proof that KL(M) is the initial object here

record SplitObj : Set (suc o ⊔ suc ℓ ⊔ suc e) where
  field
    D : Category o ℓ e
    F : Functor C D
    G : Functor D C
    adj : F ⊣ G
    eqM : G ∘F F ≃ Monad.F M

record Split⇒ (X Y : SplitObj) : Set (suc o ⊔ suc ℓ ⊔ suc e) where
  constructor Splitc⇒
  private
    module X = SplitObj X
    module Y = SplitObj Y
  field
    H : Functor X.D Y.D
    HF≃F' : H ∘F X.F ≃ Y.F
    G'H≃G : Y.G ∘F H ≃ X.G

Split : Monad C → Category _ _ _
Split M = record
  { Obj = SplitObj
  ; _⇒_ = Split⇒
  ; _≈_ = λ U V → Split⇒.H U ≃ Split⇒.H V
  ; id = split-id
  ; _∘_ = comp
  ; assoc = λ { {f = f} {g = g} {h = h} → associator (Split⇒.H f) (Split⇒.H g) (Split⇒.H h) }
  ; sym-assoc = λ { {f = f} {g = g} {h = h} → sym-associator (Split⇒.H f) (Split⇒.H g) (Split⇒.H h) }
  ; identityˡ = unitorˡ
  ; identityʳ = unitorʳ
  ; identity² = unitor²
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = _ⓘₕ_
  }
  where
  open NaturalTransformation
  split-id : {A : SplitObj} → Split⇒ A A
  split-id = record
    { H = Categories.Functor.id
    ; HF≃F' = unitorˡ
    ; G'H≃G = unitorʳ
    }
  comp : {A B X : SplitObj} → Split⇒ B X → Split⇒ A B → Split⇒ A X
  comp {A = A} {B = B} {X = X} (Splitc⇒ Hᵤ HF≃F'ᵤ G'H≃Gᵤ) (Splitc⇒ Hᵥ HF≃F'ᵥ G'H≃Gᵥ) = record
    { H = Hᵤ ∘F Hᵥ
    ; HF≃F' = HF≃F'ᵤ ⓘᵥ (Hᵤ ⓘˡ HF≃F'ᵥ) ⓘᵥ associator (SplitObj.F A) Hᵥ Hᵤ
    ; G'H≃G = G'H≃Gᵥ ⓘᵥ (G'H≃Gᵤ ⓘʳ Hᵥ) ⓘᵥ sym-associator Hᵥ Hᵤ (SplitObj.G X)
    }

open import Categories.Object.Terminal (Split M)
open import Categories.Object.Initial (Split M)
open import Categories.Category.Construction.EilenbergMoore
open import Categories.Category.Construction.Kleisli

EM-object : SplitObj
EM-object = record { D = {! EilenbergMoore M !}
                   ; F = {!   !}
                   ; G = {!   !}
                   ; adj = {!   !}
                   ; eqM = {!   !}
                   }

EM-terminal : IsTerminal EM-object
EM-terminal = {!   !}


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

open import Cubical.Core.Everything
open import Cubical.Relation.Binary
open import Cubical.Algebra.Semigroup

module Cubical.Algebra.Semigroup.Construct.Quotient {c ℓ} (S : Semigroup c) {R : Rel (Semigroup.Carrier S) ℓ} (isEq : IsEquivalence R)
                                                              (closed : Congruent₂ R (Semigroup._•_ S)) where

open import Cubical.Foundations.Prelude
open import Cubical.Algebra.Properties
open import Cubical.HITs.SetQuotients

open Semigroup S
open IsEquivalence isEq

import Cubical.Algebra.Magma.Construct.Quotient magma isEq closed as QMagma
open QMagma public hiding (M/R-isMagma; M/R)

•ᴿ-assoc : Associative _•ᴿ_
•ᴿ-assoc = elimProp3 (λ _ _ _ → squash/ _ _) (λ x y z → cong [_] (assoc x y z))

S/R-isSemigroup : IsSemigroup Carrier/R _•ᴿ_
S/R-isSemigroup = record
  { isMagma = QMagma.M/R-isMagma
  ; assoc   = •ᴿ-assoc
  }

S/R : Semigroup (ℓ-max c ℓ)
S/R = record { isSemigroup = S/R-isSemigroup }


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

open import lib.Basics
open import lib.types.Group
open import lib.types.Unit
open import lib.groups.SubgroupProp
open import lib.groups.Homomorphism
open import lib.groups.Isomorphism
open import lib.groups.Lift

module lib.groups.Unit where

Unit-group-structure : GroupStructure Unit
Unit-group-structure = record
  { ident = unit
  ; inv = λ _ → unit
  ; comp = λ _ _ → unit
  ; unit-l = λ _ → idp
  ; assoc = λ _ _ _ → idp
  ; inv-l = λ _ → idp
  }

Unit-group : Group lzero
Unit-group = group _ Unit-is-set Unit-group-structure

0ᴳ = Unit-group

abstract
  Unit-group-is-trivial : is-trivialᴳ Unit-group
  Unit-group-is-trivial = λ _ → idp

  iso-Unit-is-trivial : ∀ {i} {G : Group i} → G ≃ᴳ Unit-group → is-trivialᴳ G
  iso-Unit-is-trivial G-iso-0 = iso-preserves'-trivial G-iso-0 Unit-group-is-trivial

0ᴳ-is-trivial = Unit-group-is-trivial
iso-0ᴳ-is-trivial = iso-Unit-is-trivial

trivial-iso-Unit : ∀ {i} {G : Group i} → is-trivialᴳ G → G ≃ᴳ Unit-group
trivial-iso-Unit {G = G} G-triv = group-hom (λ _ → tt) (λ _ _ → idp) ,
  is-eq _ (λ _ → Group.ident G) (λ _ → idp) (λ _ → ! (G-triv _))

trivial-iso-0ᴳ = trivial-iso-Unit

{- the following should be replaced by [is-trivial] completely -}

abstract
  contr-iso-Unit : ∀ {i} (G : Group i) → is-contr (Group.El G) → G ≃ᴳ Unit-group
  contr-iso-Unit G pA = ≃-to-≃ᴳ (contr-equiv-Unit pA) (λ _ _ → idp)
contr-iso-0ᴳ = contr-iso-Unit

Unit-group-is-abelian : is-abelian 0ᴳ
Unit-group-is-abelian _ _ = idp

0ᴳ-is-abelian = Unit-group-is-abelian

Unit-abgroup : AbGroup₀
Unit-abgroup = Unit-group , Unit-group-is-abelian

0ᴳ-abgroup = Unit-abgroup

abstract
  hom₁-Unit-is-trivial : ∀ {i} (G : AbGroup i) → is-trivialᴳ (hom-group Unit-group G)
  hom₁-Unit-is-trivial G φ = group-hom= $ λ= λ _ → GroupHom.pres-ident φ

hom₁-0ᴳ-is-trivial = hom₁-Unit-is-trivial


------------------------------------------------------------------------------
-- Agda-Prop Library.
-- Theorems of ∧ connective.
------------------------------------------------------------------------------

open import Data.Nat using ( ℕ )

module Data.PropFormula.Theorems.Conjunction ( n : ℕ ) where

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

open import Data.PropFormula.Syntax n
open import Data.PropFormula.Theorems.Classical n

open import Function using ( _$_ ; _∘_  )

-------------------------------------------------------------------------- ∎

-- Theorem.
∧-assoc₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∧ (ψ ∧ γ)
  → Γ ⊢ (φ ∧ ψ) ∧ γ

-- Proof.
∧-assoc₁ Γ⊢φ∧ψ∧γ =
  ∧-intro
    (∧-intro
      (∧-proj₁  Γ⊢φ∧ψ∧γ)
      ((∧-proj₁ ∘ ∧-proj₂) Γ⊢φ∧ψ∧γ))
    ((∧-proj₂ ∘ ∧-proj₂) Γ⊢φ∧ψ∧γ)

-------------------------------------------------------------------------- ∎

-- Theorem.
∧-assoc₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∧ ψ) ∧ γ
  → Γ ⊢ φ ∧ (ψ ∧ γ)

-- Proof.
∧-assoc₂ Γ⊢φ∧ψ∧γ =
  ∧-intro
    (∧-proj₁ (∧-proj₁ Γ⊢φ∧ψ∧γ))
    (∧-intro
      (∧-proj₂ (∧-proj₁ Γ⊢φ∧ψ∧γ))
      (∧-proj₂ Γ⊢φ∧ψ∧γ))
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-assoc
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∧ (ψ ∧ γ)) ⇔ ((φ ∧ ψ) ∧ γ)

-- Proof.
∧-assoc {Γ} {φ}{ψ}{γ} =
  ⇔-intro
    (∧-assoc₁ (assume (φ ∧ (ψ ∧ γ))))
    (∧-assoc₂ (assume (φ ∧ ψ ∧ γ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-comm
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ∧ ψ
  → Γ ⊢ ψ ∧ φ

-- Proof.
∧-comm Γ⊢φ∧ψ =
  ∧-intro
    (∧-proj₂ Γ⊢φ∧ψ)
    (∧-proj₁ Γ⊢φ∧ψ)
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-dist₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∧ (ψ ∨ γ)
  → Γ ⊢ (φ ∧ ψ) ∨ (φ ∧ γ)

-- Proof.
∧-dist₁ {φ = φ}{ψ}{γ} Γ⊢φ∧ψ∨γ =
  ⊃-elim
    (⊃-intro $
      ∨-elim
        (∨-intro₁ (φ ∧ γ)
          (∧-intro
            (weaken ψ (∧-proj₁ Γ⊢φ∧ψ∨γ))
            (assume  ψ)))
        (∨-intro₂ (φ ∧ ψ)
          (∧-intro
            (weaken γ  (∧-proj₁ Γ⊢φ∧ψ∨γ))
            (assume  γ))))
    (∧-proj₂ Γ⊢φ∧ψ∨γ)
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-dist₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ∧ ψ) ∨ (φ ∧ γ)
  → Γ ⊢ φ ∧ (ψ ∨ γ)

-- Proof.
∧-dist₂ {φ = φ}{ψ}{γ} =
  ⊃-elim
    (⊃-intro
      (∨-elim
        (∧-intro
          (∧-proj₁
            (assume (φ ∧ ψ)))
          (∨-intro₁ γ
            (∧-proj₂
              (assume (φ ∧ ψ)))))
        (∧-intro
          (∧-proj₁
            (assume (φ ∧ γ)))
          (∨-intro₂ ψ
            (∧-proj₂
              (assume (φ ∧ γ)))))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-dist
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ∧ (ψ ∨ γ) ⇔ (φ ∧ ψ) ∨ (φ ∧ γ)

-- Proof.
∧-dist {φ = φ}{ψ}{γ} =
  ⇔-intro
    (∧-dist₁ (assume (φ ∧ (ψ ∨ γ))))
    (∧-dist₂ (assume (φ ∧ ψ ∨ (φ ∧ γ))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-dmorgan₁
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ (φ ∧ ψ)
  → Γ ⊢ ¬ φ ∨ ¬ ψ
¬∧-to-¬∨¬ = ∧-dmorgan₁

-- Proof.
∧-dmorgan₁ {Γ}{φ}{ψ} =
  ⊃-elim $
    ⊃-intro $
      RAA $
        ¬-elim
          (weaken (¬ (¬ φ ∨ ¬ ψ)) $
            assume $ ¬ (φ ∧ ψ))
          (∧-intro
            (RAA
              (¬-elim
                (weaken (¬ φ) $
                  assume {Γ = Γ , ¬ (φ ∧ ψ)} $ ¬ (¬ φ ∨ ¬ ψ))
                (∨-intro₁ (¬ ψ)
                  (assume {Γ = Γ , ¬ (φ ∧ ψ) , ¬ (¬ φ ∨ ¬ ψ)} $ ¬ φ))))
            (RAA
              (¬-elim {Γ = Γ , ¬ (φ ∧ ψ) , ¬ (¬ φ ∨ ¬ ψ) , ¬ ψ}
                (weaken (¬ ψ) $
                  assume {Γ = Γ , ¬ (φ ∧ ψ)} $ ¬ (¬ φ ∨ ¬ ψ))
                (∨-intro₂ (¬ φ)
                  (assume {Γ = Γ , ¬ (φ ∧ ψ) , ¬ (¬ φ ∨ ¬ ψ)} $ ¬ ψ )))))
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-dmorgan₂
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ φ ∨ ¬ ψ
  → Γ ⊢ ¬ (φ ∧ ψ)
¬∨¬-to-¬∧ = ∧-dmorgan₂

-- Proof.
∧-dmorgan₂ {Γ}{φ}{ψ} =
  ⊃-elim $
    ⊃-intro $
      ∨-elim {Γ = Γ}
        (¬-intro $
          ¬-elim
            (weaken (φ ∧ ψ) $
              assume (¬ φ))
            (∧-proj₁ $
              assume {Γ = Γ , ¬ φ} (φ ∧ ψ)))
        (¬-intro $
          ¬-elim
            (weaken (φ ∧ ψ) $ assume (¬ ψ))
          (∧-proj₂ $
            assume {Γ = Γ , ¬ ψ} (φ ∧ ψ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
∧-dmorgan
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ φ ∨ ¬ ψ ⇔ ¬ (φ ∧ ψ)
¬∨¬-⇔-¬∧ = ∧-dmorgan

-- Proof.
∧-dmorgan {Γ}{φ}{ψ} =
  ⇔-intro
    (∧-dmorgan₂
      (assume (¬ φ ∨ ¬ ψ)))
    (∧-dmorgan₁
      (assume (¬ (φ ∧ ψ))))
-------------------------------------------------------------------------- ∎

-- Theorem.
subst⊢∧₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ⊃ γ
  → Γ ⊢ φ ∧ ψ
  → Γ ⊢ γ ∧ ψ

-- Proof.
subst⊢∧₁ Γ⊢φ⊃ψ Γ⊢φ∧ψ =
  ∧-intro
    (⊃-elim (∧-proj₁ (∧-intro Γ⊢φ⊃ψ Γ⊢φ∧ψ)) (∧-proj₁ Γ⊢φ∧ψ))
    (∧-proj₂ Γ⊢φ∧ψ)
-------------------------------------------------------------------------- ∎

-- Theorem.
subst⊢∧₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ ψ ⊃ γ
  → Γ ⊢ φ ∧ ψ
  → Γ ⊢ φ ∧ γ

-- Proof.
subst⊢∧₂ {Γ}{φ}{ψ} Γ⊢φ⊃ψ Γ⊢φ∧ψ =
  ∧-intro
    (∧-proj₁ Γ⊢φ∧ψ)
    (⊃-elim Γ⊢φ⊃ψ (∧-proj₂ Γ⊢φ∧ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
¬∧-to-⊥
  : ∀ {Γ} {φ}
  → Γ ⊢ ¬ φ ∧ φ
  → Γ ⊢ ⊥

-- Proof.
¬∧-to-⊥ {Γ}{φ} Γ⊢φ = ¬-elim (∧-proj₁ Γ⊢φ) (∧-proj₂ Γ⊢φ)
-------------------------------------------------------------------------- ∎


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

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import LogicalFormulae
open import Sets.EquivalenceRelations
open import Setoids.Setoids

module Setoids.Intersection.Definition {a b : _} {A : Set a} (S : Setoid {a} {b} A) where

open Setoid S
open Equivalence eq
open import Setoids.Subset S

intersectionPredicate : {c d : _} {pred1 : A → Set c} {pred2 : A → Set d} (s1 : subset pred1) (s2 : subset pred2) → A → Set (c ⊔ d)
intersectionPredicate {pred1 = pred1} {pred2} s1 s2 a = pred1 a && pred2 a

intersection : {c d : _} {pred1 : A → Set c} {pred2 : A → Set d} (s1 : subset pred1) (s2 : subset pred2) → subset (intersectionPredicate s1 s2)
intersection s1 s2 {x1} {x2} x1=x2 (inS1 ,, inS2) = s1 x1=x2 inS1 ,, s2 x1=x2 inS2


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.CommRing.Instances.Polynomials.MultivariatePoly-Quotient where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Nat
open import Cubical.Data.Vec
open import Cubical.Data.Vec.OperationsNat
open import Cubical.Data.FinData

open import Cubical.Relation.Nullary

open import Cubical.Algebra.DirectSum.DirectSumHIT.Base
open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.FGIdeal
open import Cubical.Algebra.CommRing.QuotientRing
open import Cubical.Algebra.CommRing.Instances.Int renaming (ℤCommRing to ℤCR)

open import Cubical.Algebra.CommRing.Instances.Polynomials.MultivariatePoly
  renaming (PolyCommRing to A[X1,···,Xn] ; Poly to A[x1,···,xn])

private variable
  ℓ : Level

-----------------------------------------------------------------------------
-- General Nth polynome / quotient
-- Better to declare an alias

PolyCommRing-Quotient : (A : CommRing ℓ) → {n m : ℕ} → FinVec (fst (A[X1,···,Xn] A n)) m → CommRing ℓ
PolyCommRing-Quotient A {n} {m} v = A[X1,···,Xn] A n / genIdeal (A[X1,···,Xn] A n) v

-----------------------------------------------------------------------------
-- Notation in the general case and some real cases 1, 2, 3

module _
  (Ar@(A , Astr) : CommRing ℓ)
  (n : ℕ)
  where

  <Xkʲ> : (k j : ℕ) →  FinVec (A[x1,···,xn] Ar n) 1
  <Xkʲ> k j zero = base (genδℕ-Vec n k j 0) (CommRingStr.1r Astr)

  A[X1,···,Xn]/<Xkʲ> : (k j : ℕ) → CommRing ℓ
  A[X1,···,Xn]/<Xkʲ> k j = (A[X1,···,Xn] Ar n) / (genIdeal ((A[X1,···,Xn] Ar n)) (<Xkʲ> k j))

  A[x1,···,xn]/<xkʲ> : (k j : ℕ) → Type ℓ
  A[x1,···,xn]/<xkʲ> k j = fst (A[X1,···,Xn]/<Xkʲ> k j)

  <X1,···,Xn> : FinVec (A[x1,···,xn] Ar n) n
  <X1,···,Xn> = λ k → base (δℕ-Vec n (toℕ k)) (CommRingStr.1r Astr)

  A[X1,···,Xn]/<X1,···,Xn> : CommRing ℓ
  A[X1,···,Xn]/<X1,···,Xn> = (A[X1,···,Xn] Ar n) / (genIdeal ((A[X1,···,Xn] Ar n)) <X1,···,Xn>)

  A[x1,···,xn]/<x1,···,xn> : Type ℓ
  A[x1,···,xn]/<x1,···,xn> = fst A[X1,···,Xn]/<X1,···,Xn>


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

open import Cubical.Core.Everything

open import Cubical.Data.Prod.Base
open import Cubical.Data.Sigma

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

private
  variable
    ℓ ℓ' : Level
    A B  : Type ℓ

-- Swapping is an equivalence

swap : A × B → B × A
swap (x , y) = (y , x)

swap-invol : (xy : A × B) → swap (swap xy) ≡ xy
swap-invol (_ , _) = refl

isEquivSwap : (A : Type ℓ) (B : Type ℓ') → isEquiv (λ (xy : A × B) → swap xy)
isEquivSwap A B = isoToIsEquiv (iso swap swap swap-invol swap-invol)

swapEquiv : (A : Type ℓ) (B : Type ℓ') → A × B ≃ B × A
swapEquiv A B = (swap , isEquivSwap A B)

swapEq : (A : Type ℓ) (B : Type ℓ') → A × B ≡ B × A
swapEq A B = ua (swapEquiv A B)

private
  open import Cubical.Data.Nat

  -- As × is defined as a datatype this computes as expected
  -- (i.e. "C-c C-n test1" reduces to (2 , 1)). If × is implemented
  -- using Sigma this would be "transp (λ i → swapEq ℕ ℕ i) i0 (1 , 2)"
  test : ℕ × ℕ
  test = transp (λ i → swapEq ℕ ℕ i) i0 (1 , 2)

  testrefl : test ≡ (2 , 1)
  testrefl = refl

-- equivalence between the sigma-based definition and the inductive one
A×B≡A×ΣB : A × B ≡ A ×Σ B
A×B≡A×ΣB = isoToPath (iso (λ { (a , b) → (a , b)})
                          (λ { (a , b) → (a , b)})
                          (λ _ → refl)
                          (λ { (a , b) → refl }))

-- truncation for products
hLevelProd : (n : ℕ) → isOfHLevel n A → isOfHLevel n B → isOfHLevel n (A × B)
hLevelProd {A = A} {B = B} n h1 h2 =
  let h : isOfHLevel n (A ×Σ B)
      h = isOfHLevelΣ n h1 (λ _ → h2)
  in transport (λ i → isOfHLevel n (A×B≡A×ΣB {A = A} {B = B} (~ i))) h


------------------------------------------------------------------------------
-- From inductive PA to standard axiomatic PA
------------------------------------------------------------------------------

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

-- From the definition of PA using Agda data types and primitive
-- recursive functions for addition and multiplication, we can prove
-- the following axioms (see (Machover 1996, p. 263) and (Hájek and
-- Pudlák 1998, p. 28)):

-- PA₁. 0 ≠ n'
-- PA₂. m' = n' → m = n
-- PA₃. 0 + n = n
-- PA₄. m' + n = (m + n)'
-- PA₅. 0 * n = 0
-- PA₆. m' * n = n + (m * n)
-- Axiom of induction:
-- φ(0) → (∀n.φ(n) → φ(succ n)) → ∀n.φ(n), for any formulae φ

module PA.Inductive2Standard where

open import PA.Inductive.Base

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

PA₁ : ∀ {n} → zero ≢ succ n
PA₁ ()

PA₂ : ∀ {m n} → succ m ≡ succ n → m ≡ n
PA₂ refl = refl

PA₃ : ∀ n → zero + n ≡ n
PA₃ n = refl

PA₄ : ∀ m n → succ m + n ≡ succ (m + n)
PA₄ m n = refl

PA₅ : ∀ n → zero * n ≡ zero
PA₅ n = refl

PA₆ : ∀ m n → succ m * n ≡ n + m * n
PA₆ m n = refl

------------------------------------------------------------------------------
-- References
--
-- Machover, Moshé (1996). Set theory, Logic and their
-- Limitations. Cambridge University Press.

-- Hájek, Petr and Pudlák, Pavel (1998). Metamathematics of
-- First-Order Arithmetic. 2nd printing. Springer.


-- Andreas, 2016-02-09, issue and test case by Nisse

{-# OPTIONS --allow-unsolved-metas #-}

open import Common.Level

postulate
  _≡_  : ∀ {a} {A : Set a} → A → A → Set a
  refl : ∀ {a} {A : Set a} (x : A) → x ≡ x

record R a p : Set (lsuc (a ⊔ p)) where
  field
    elim : {A : Set a} (P : {x y : A} → x ≡ y → Set p) →
           (∀ x → P (refl x)) →
           ∀ {x y} (x≡y : x ≡ y) → P x≡y

module _ (eq : ∀ {a p} → R a p) where

  open module M {a p} = R (eq {a} {p})

  postulate

    elim-refl   : ∀ {a p} {A : Set a} (P : {x y : A} → x ≡ y → Set p)
                  (r : ∀ x → P (refl x)) {x} →
                  elim P r (refl x) ≡ r x
    cong        : ∀ {a b} {A : Set a} {B : Set b}
                  (f : A → B) {x y : A} → x ≡ y → f x ≡ f y
    subst       : ∀ {a p} {A : Set a} (P : A → Set p) {x y : A} →
                  x ≡ y → P x → P y
    subst-refl  : ∀ {a p} {A : Set a} (P : A → Set p) {x} (p : P x) →
                  subst P (refl x) p ≡ p
    trans       : ∀ {a} {A : Set a} {x y z : A} → x ≡ y → y ≡ z → x ≡ z
    cong₂       : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c}
                  (f : A → B → C) {x y : A} {u v : B} →
                  x ≡ y → u ≡ v → f y v ≡ f x u
    trans-reflʳ : ∀ {a} {A : Set a} {x y : A} (x≡y : x ≡ y) →
                  trans x≡y (refl y) ≡ x≡y
    trans-reflˡ : ∀ {a} {A : Set a} {x y : A} (x≡y : x ≡ y) →
                  trans (refl x) x≡y ≡ x≡y

  [subst≡]≡[trans≡trans] :
    ∀ {a} {A : Set a} {x y : A} {p : x ≡ y} {q : x ≡ x} {r : y ≡ y} →
    (subst (λ z → z ≡ z) p q ≡ r)
      ≡
    (trans q p ≡ trans p r)
  [subst≡]≡[trans≡trans] {p = p} {q} {r} = elim
    (λ {x y} p → {q : x ≡ x} {r : y ≡ y} →
                 (subst (λ z → z ≡ z) p q ≡ r)
                   ≡
                 (trans q p ≡ trans p r))
    (λ x {q r} →
       trans (cong (λ x → x ≡ _) (subst-refl (λ z → z ≡ z) _))
             (cong₂ _≡_ (trans-reflʳ _) (trans-reflˡ _)))
    p

  foo :
    ∀ {a} {A : Set a} {x : A} {q : x ≡ x} {r : x ≡ x} →
    [subst≡]≡[trans≡trans] {p = refl x} {q = q} {r = r} ≡
    [subst≡]≡[trans≡trans] {p = refl x} {q = q} {r = r}
  foo = elim-refl ? ?


module Vector where

infixr 10 _∷_

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

{-# BUILTIN NATURAL ℕ    #-}
{-# BUILTIN ZERO    zero #-}
{-# BUILTIN SUC     suc  #-}

infixl 6 _+_

_+_ : ℕ → ℕ → ℕ
0 + n = n
suc m + n = suc (m + n)

data Vec (A : Set) : ℕ → Set where
  []  : Vec A 0
  _∷_ : ∀ {n} → A → Vec A n → Vec A (suc n)

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

infix 4 _≡_

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

subst : {A : Set} → (P : A → Set) → ∀{x y} → x ≡ y → P x → P y
subst P refl p = p

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

vec : ∀ {A} (k : ℕ) → Set
vec {A} k = Vec A k

plus_zero : {n : ℕ} → n + 0 ≡ n
plus_zero {zero}  = refl
plus_zero {suc n} = cong suc plus_zero

plus_suc : {n : ℕ} → n + (suc 0) ≡ suc n
plus_suc {zero}  = refl
plus_suc {suc n} = cong suc (plus_suc {n})

reverse : ∀ {A n} → Vec A n → Vec A n
reverse []       = []
reverse {A} {suc n} (x ∷ xs) = subst vec (plus_suc {n}) (reverse xs ++ (x ∷ []))


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.Ideal where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Logic using ([_]; _∈_)

open import Cubical.Algebra.Ring

private
  variable
    ℓ : Level

module _ (R' : Ring {ℓ}) where

  open Ring R' renaming (Carrier to R)

  {- by default, 'ideal' means two-sided ideal -}
  record isIdeal (I : R → hProp ℓ) : Type ℓ where
    field
      +-closed : {x y : R} → x ∈ I → y ∈ I → (x + y) ∈ I
      -closed : {x : R} → x ∈ I → - x ∈ I
      0r-closed : 0r ∈ I
      ·-closedLeft : {x : R} → (r : R) → x ∈ I → r · x ∈ I
      ·-closedRight : {x : R} → (r : R) → x ∈ I → x · r ∈ I

  Ideal : Type _
  Ideal = Σ[ I ∈ (R → hProp ℓ) ] isIdeal I

  record isLeftIdeal (I : R → hProp ℓ) : Type ℓ where
    field
      +-closed : {x y : R} → x ∈ I → y ∈ I → (x + y) ∈ I
      -closed : {x : R} → x ∈ I → - x ∈ I
      0r-closed : 0r ∈ I
      ·-closedLeft : {x : R} → (r : R) → x ∈ I → r · x ∈ I

  record isRightIdeal (I : R → hProp ℓ) : Type ℓ where
    field
      +-closed : {x y : R} → x ∈ I → y ∈ I → (x + y) ∈ I
      -closed : {x : R} → x ∈ I → - x ∈ I
      0r-closed : 0r ∈ I
      ·-closedRight : {x : R} → (r : R) → x ∈ I → x · r ∈ I

  {- Examples of ideals -}
  zeroSubset : (x : R) → hProp ℓ
  zeroSubset x = (x ≡ 0r) , isSetRing R' _ _

  open Theory R'

  isIdealZeroIdeal : isIdeal zeroSubset
  isIdealZeroIdeal = record
                       { +-closed = λ x≡0 y≡0 → _ + _    ≡⟨ cong (λ u → u + _) x≡0 ⟩
                                                0r + _   ≡⟨ +-lid _ ⟩
                                                _        ≡⟨ y≡0 ⟩
                                                0r        ∎
                       ; -closed = λ x≡0 → - _ ≡⟨ cong (λ u → - u) x≡0 ⟩
                                           - 0r ≡⟨ 0-selfinverse ⟩
                                           0r ∎
                       ; 0r-closed = refl
                       ; ·-closedLeft = λ r x≡0 → r · _ ≡⟨ cong (λ u → r · u) x≡0 ⟩
                                                  r · 0r ≡⟨ 0-rightNullifies r  ⟩
                                                  0r ∎
                       ; ·-closedRight = λ r x≡0 → _ · r ≡⟨ cong (λ u → u · r) x≡0 ⟩
                                                   0r · r ≡⟨ 0-leftNullifies r ⟩
                                                   0r ∎
                       }

  zeroIdeal : Ideal
  zeroIdeal = zeroSubset , isIdealZeroIdeal


------------------------------------------------------------------------
-- The Agda standard library
--
-- Vectors where at least one element satisfies a given property
------------------------------------------------------------------------

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

module Data.Vec.Relation.Unary.Any {a} {A : Set a} where

open import Data.Empty
open import Data.Fin
open import Data.Nat using (zero; suc)
open import Data.Sum using (_⊎_; inj₁; inj₂; [_,_]′)
open import Data.Vec as Vec using (Vec; []; [_]; _∷_)
open import Data.Product as Prod using (∃; _,_)
open import Level using (_⊔_)
open import Relation.Nullary using (¬_; yes; no)
open import Relation.Nullary.Negation using (contradiction)
import Relation.Nullary.Decidable as Dec
open import Relation.Unary

------------------------------------------------------------------------
-- Any P xs means that at least one element in xs satisfies P.

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

------------------------------------------------------------------------
-- Operations on Any

module _ {p} {P : A → Set p} {n x} {xs : Vec A n} where

-- If the tail does not satisfy the predicate, then the head will.

  head : ¬ Any P xs → Any P (x ∷ xs) → P x
  head ¬pxs (here px)   = px
  head ¬pxs (there pxs) = contradiction pxs ¬pxs

-- If the head does not satisfy the predicate, then the tail will.
  tail : ¬ P x → Any P (x ∷ xs) → Any P xs
  tail ¬px (here  px)  = ⊥-elim (¬px px)
  tail ¬px (there pxs) = pxs

-- Convert back and forth with sum
  toSum : Any P (x ∷ xs) → P x ⊎ Any P xs
  toSum (here px)   = inj₁ px
  toSum (there pxs) = inj₂ pxs

  fromSum : P x ⊎ Any P xs → Any P (x ∷ xs)
  fromSum = [ here , there ]′

map : ∀ {p q} {P : A → Set p} {Q : A → Set q} →
      P ⊆ Q → ∀ {n} → Any P {n} ⊆ Any Q {n}
map g (here px)   = here (g px)
map g (there pxs) = there (map g pxs)

index : ∀ {p} {P : A → Set p} {n} {xs : Vec A n} → Any P xs → Fin n
index (here  px)  = zero
index (there pxs) = suc (index pxs)

-- If any element satisfies P, then P is satisfied.
satisfied : ∀ {p} {P : A → Set p} {n} {xs : Vec A n} → Any P xs → ∃ P
satisfied (here px)   = _ , px
satisfied (there pxs) = satisfied pxs

------------------------------------------------------------------------
-- Properties of predicates preserved by Any

module _ {p} {P : A → Set p} where

  any : Decidable P → ∀ {n} → Decidable (Any P {n})
  any P? []       = no λ()
  any P? (x ∷ xs) with P? x
  ... | yes px = yes (here px)
  ... | no ¬px = Dec.map′ there (tail ¬px) (any P? xs)

  satisfiable : Satisfiable P → ∀ {n} → Satisfiable (Any P {suc n})
  satisfiable (x , p) {zero}  = x ∷ [] , here p
  satisfiable (x , p) {suc n} = Prod.map (x ∷_) there (satisfiable (x , p))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Morphisms between algebraic structures
------------------------------------------------------------------------

module Algebra.Morphism where

open import Relation.Binary
open import Algebra
open import Algebra.FunctionProperties
import Algebra.Properties.Group as GroupP
open import Function
open import Data.Product
open import Level
import Relation.Binary.EqReasoning as EqR

------------------------------------------------------------------------
-- Basic definitions

module Definitions {f t ℓ}
                   (From : Set f) (To : Set t) (_≈_ : Rel To ℓ) where
  Morphism : Set _
  Morphism = From → To

  Homomorphic₀ : Morphism → From → To → Set _
  Homomorphic₀ ⟦_⟧ ∙ ∘ = ⟦ ∙ ⟧ ≈ ∘

  Homomorphic₁ : Morphism → Fun₁ From → Op₁ To → Set _
  Homomorphic₁ ⟦_⟧ ∙_ ∘_ = ∀ x → ⟦ ∙ x ⟧ ≈ ∘ ⟦ x ⟧

  Homomorphic₂ : Morphism → Fun₂ From → Op₂ To → Set _
  Homomorphic₂ ⟦_⟧ _∙_ _∘_ =
    ∀ x y → ⟦ x ∙ y ⟧ ≈ (⟦ x ⟧ ∘ ⟦ y ⟧)

------------------------------------------------------------------------
-- An example showing how a morphism type can be defined

-- Ring homomorphisms.

record _-Ring⟶_ {r₁ r₂ r₃ r₄}
                (From : Ring r₁ r₂) (To : Ring r₃ r₄) :
                Set (r₁ ⊔ r₂ ⊔ r₃ ⊔ r₄) where
  private
    module F = Ring From
    module T = Ring To
  open Definitions F.Carrier T.Carrier T._≈_

  field
    ⟦_⟧     : Morphism
    ⟦⟧-cong : ⟦_⟧ Preserves F._≈_ ⟶ T._≈_
    +-homo  : Homomorphic₂ ⟦_⟧ F._+_ T._+_
    *-homo  : Homomorphic₂ ⟦_⟧ F._*_ T._*_
    1-homo  : Homomorphic₀ ⟦_⟧ F.1#  T.1#

  open EqR T.setoid

  0-homo : Homomorphic₀ ⟦_⟧ F.0# T.0#
  0-homo =
    GroupP.left-identity-unique T.+-group ⟦ F.0# ⟧ ⟦ F.0# ⟧ (begin
      T._+_ ⟦ F.0# ⟧ ⟦ F.0# ⟧ ≈⟨ T.sym (+-homo F.0# F.0#) ⟩
      ⟦ F._+_ F.0# F.0# ⟧     ≈⟨ ⟦⟧-cong (proj₁ F.+-identity F.0#) ⟩
      ⟦ F.0# ⟧                ∎)

  -‿homo : Homomorphic₁ ⟦_⟧ F.-_ T.-_
  -‿homo x =
    GroupP.left-inverse-unique T.+-group ⟦ F.-_ x ⟧ ⟦ x ⟧ (begin
      T._+_ ⟦ F.-_ x ⟧ ⟦ x ⟧ ≈⟨ T.sym (+-homo (F.-_ x) x) ⟩
      ⟦ F._+_ (F.-_ x) x ⟧   ≈⟨ ⟦⟧-cong (proj₁ F.-‿inverse x) ⟩
      ⟦ F.0# ⟧               ≈⟨ 0-homo ⟩
      T.0#                   ∎)


module ExtInterface.Data.Product where

-- TODO: Write to Agda team about the lack of compilability of Sigma.
-- I assumed that the builtin flag would allow to compile Σ into (,)
-- but it doesn't. That's why this microfile exists

infixr 4 ⟨_,_⟩
infixr 2 _×_

data _×_ (A B : Set) : Set where
  ⟨_,_⟩ : A → B → A × B

{-# COMPILE GHC _×_ = data (,) ((,)) #-} -- Yeah, kinda abstract

proj₁ : ∀ {A B : Set} → A × B → A
proj₁ ⟨ x , y ⟩ = x

proj₂ : ∀ {A B : Set} → A × B → B
proj₂ ⟨ x , y ⟩ = y

map : ∀ {A B C D : Set}
    → (A → C) → (B → D) → A × B → C × D
map f g ⟨ x , y ⟩ = ⟨ f x , g y ⟩

map₁ : ∀ {A B C : Set}
     → (A → C) → A × B → C × B
map₁ f = map f (λ x → x)

map₂ : ∀ {A B D : Set}
     → (B → D) → A × B → A × D
map₂ g = map (λ x → x) g


module Membership where

open import Data.Sum
open import Data.List
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality

open import Data.List.Any as Any
open Any.Membership-≡

-- _∈_

∈-++-pos : ∀ {A : Set} {x : A} xs ys → x ∈ xs ++ ys
           → (x ∈ xs) ⊎ (x ∈ ys)
∈-++-pos [] ys x∈ = inj₂ x∈
∈-++-pos (x ∷ xs) ys (here refl) = inj₁ (here refl)
∈-++-pos (x ∷ xs) ys (there x∈) with ∈-++-pos xs ys x∈
... | inj₁ x∈xs = inj₁ (there x∈xs)
... | inj₂ x∈ys = inj₂ x∈ys

∈-++-r : ∀ {A : Set} {x : A} xs {ys}
           → x ∈ ys → x ∈ (xs ++ ys)
∈-++-r [] x∈ = x∈
∈-++-r (y ∷ xs) x∈ = there (∈-++-r xs x∈)

∈-++-l : ∀ {A : Set} {x : A} {xs} ys
           → x ∈ xs → x ∈ (xs ++ ys)
∈-++-l ys (here refl) = here refl
∈-++-l ys (there x∈) = there (∈-++-l ys x∈)

∈-++-weaken : ∀ {A : Set} {x : A} xs ys zs
              → x ∈ (xs ++ zs) → x ∈ (xs ++ ys ++ zs)
∈-++-weaken [] ys zs x∈ = ∈-++-r ys x∈
∈-++-weaken (x ∷ xs) ys zs (here refl) = here refl
∈-++-weaken (_ ∷ xs) ys zs (there x∈) = there (∈-++-weaken xs ys zs x∈)

-- _∉_

∉-++-l : ∀ {A : Set} {x : A} xs ys → x ∉ xs ++ ys → x ∉ xs
∉-++-l ._ ys x∉xs++ys (here eq) = x∉xs++ys (here eq)
∉-++-l .(x' ∷ xs) ys x∉xs++ys (there {x'} {xs} pxs) =
  ∉-++-l xs ys (λ p → x∉xs++ys (there p)) pxs

∉-++-r : ∀ {A : Set} {x : A} xs ys → x ∉ xs ++ ys → x ∉ ys
∉-++-r [] ys x∉xs++ys x∈ys = x∉xs++ys x∈ys
∉-++-r (x' ∷ xs) ys x∉xs++ys x∈ys = ∉-++-r xs ys (λ p → x∉xs++ys (there p)) x∈ys

postulate
  ∉-∷ : ∀ {A : Set} {y x : A} {xs}
        → ¬ (y ≡ x) → y ∉ xs → y ∉ (x ∷ xs)

∉-++-join : ∀ {A : Set} {x : A}
            → ∀ xs ys → x ∉ xs → x ∉ ys → x ∉ xs ++ ys
∉-++-join [] ys x∉xs x∉ys x∈ys = x∉ys x∈ys
∉-++-join (_ ∷ xs) ys x∉xs x∉ys (here refl) = x∉xs (here refl)
∉-++-join (_ ∷ xs) ys x∉xxs x∉ys (there pxs) = ∉-++-join xs ys (λ x∉xs → x∉xxs (there x∉xs)) x∉ys pxs

∉-++-weaken : ∀ {A : Set} {x : A} xs ys zs
              → x ∉ (xs ++ ys ++ zs) → x ∉ (xs ++ zs)
∉-++-weaken xs ys zs x∉xyz x∈xz = x∉xyz (∈-++-weaken xs ys zs x∈xz)

∉-∷-hd : ∀ {A : Set} {x y : A} xs → x ∉ (y ∷ xs) → ¬ (x ≡ y)
∉-∷-hd {x} {._} xs x∉ refl = x∉ (here refl)

∉-∷-tl : ∀ {A : Set} {x y : A} xs → x ∉ (y ∷ xs) → x ∉ xs
∉-∷-tl {x} {y} xs x∉ x∈ = x∉ (there x∈)


module BTree.Complete.Alternative.Correctness {A : Set} where

open import BTree {A}
open import BTree.Equality {A}
open import BTree.Equality.Properties {A}
open import BTree.Complete.Base {A}
open import BTree.Complete.Base.Properties {A}
open import BTree.Complete.Alternative {A} renaming (Complete to Complete' ; _⋘_ to _⋘'_ ; _⋙_ to _⋙'_ ; _⋗_ to _⋗'_)
open import Data.Sum renaming (_⊎_ to _∨_)

lemma-⋗'-⋗ : {l r : BTree} → l ⋗' r → l ⋗ r
lemma-⋗'-⋗ (⋗lf x) = ⋗lf x
lemma-⋗'-⋗ (⋗nd {r = r} {l' = l'} x x' _ l'≃r' l⋗'l') = ⋗nd x x' r l' (lemma-⋗-≃ (lemma-⋗'-⋗ l⋗'l') l'≃r')

lemma-⋙'-⋗ : {l r : BTree} → l ⋙' r → l ⋗ r
lemma-⋙'-⋗ (⋙lf x) = ⋗lf x
lemma-⋙'-⋗ (⋙rl {r = r} {l' = l'} x x' l≃r l'⋘'r' l⋗'r') = ⋗nd x x' r l' (lemma-⋗'-⋗ l⋗'r')
lemma-⋙'-⋗ (⋙rr {r = r} {l' = l'} x x' l≃r l'⋙'r' l≃l') = ⋗nd x x' r l' (lemma-≃-⋗ l≃l' (lemma-⋙'-⋗ l'⋙'r'))

lemma-⋘'-⋗ : {l r : BTree} → l ⋘' r → l ≃ r ∨ l ⋗ r
lemma-⋘'-⋗ lf⋘ = inj₁ ≃lf
lemma-⋘'-⋗ (ll⋘ {r = r} {l' = l'} x x' l⋘'r l'≃r' r≃l')
    with lemma-⋘'-⋗ l⋘'r
... | inj₁ l≃r = inj₁ (≃nd x x' l≃r (trans≃ l≃r r≃l') l'≃r')
... | inj₂ l⋗r = inj₂ (⋗nd x x' r l' (lemma-⋗-≃ l⋗r (trans≃ r≃l' l'≃r')))
lemma-⋘'-⋗ (lr⋘ {r = r} {l' = l'} x x' _ l'≃r' l⋗'l') = inj₂ (⋗nd x x' r l' (lemma-⋗-≃ (lemma-⋗'-⋗ l⋗'l') l'≃r'))

lemma-⋘'-⋘ : {l r : BTree} → l ⋘' r → l ⋘ r
lemma-⋘'-⋘ lf⋘ = eq⋘ ≃lf
lemma-⋘'-⋘ (ll⋘ {r = r} {l' = l'} x x' l⋘'r l'≃r' r≃l')
    with lemma-⋘'-⋗ l⋘'r
... | inj₁ l≃r = eq⋘ (≃nd x x' l≃r (trans≃ l≃r r≃l') l'≃r')
... | inj₂ l⋗r = gt⋘ (np⋗ x l⋗r) (p≃ x' l'≃r') (⋗nd x x' r l' (lemma-⋗-≃ l⋗r (trans≃ r≃l' l'≃r')))
lemma-⋘'-⋘ (lr⋘ {r = r} {l' = l'} x x' l⋙'r l'≃r' l⋗'l') = gt⋘ (np⋗ x (lemma-⋙'-⋗ l⋙'r)) (p≃ x' l'≃r') (⋗nd x x' r l' (lemma-⋗-≃ (lemma-⋗'-⋗ l⋗'l') l'≃r'))

lemma-⋙'-⋙ : {l r : BTree} → l ⋙' r → l ⋙ r
lemma-⋙'-⋙ l⋙'r = ⋙gt (lemma-⋙'-⋗ l⋙'r)

lemma-complete'-complete : {t : BTree} → Complete' t → Complete t
lemma-complete'-complete leaf = leaf
lemma-complete'-complete (left x c'l c'r l⋘'r) = left x (lemma-complete'-complete c'l) (lemma-complete'-complete c'r) (lemma-⋘'-⋘ l⋘'r)
lemma-complete'-complete (right x c'l c'r l⋙'r) = right x (lemma-complete'-complete c'l) (lemma-complete'-complete c'r) (lemma-⋙'-⋙ l⋙'r)


-- This document shows how to encode GADTs using `IFix`.

{-# OPTIONS --type-in-type #-}

module ScottVec where

-- The kind of church-encoded type-level natural numbers.
Nat = (Set -> Set) -> Set -> Set

zero : Nat
zero = λ f z -> z

suc : Nat -> Nat
suc = λ n f z -> f (n f z)

plus : Nat -> Nat -> Nat
plus = λ n m f z -> n f (m f z)

-- Our old friend.
{-# NO_POSITIVITY_CHECK #-}
record IFix {I : Set} (F : (I -> Set) -> I -> Set) (i : I) : Set where
  constructor wrap
  field unwrap : F (IFix F) i
open IFix

-- Scott-encoded vectors (a vector is a list with statically-known length).

-- As usually the pattern vector of a Scott-encoded data type encodes pattern-matching.
VecF : Set -> (Nat -> Set) -> Nat -> Set
VecF
  = λ A Rec n
  -> (R : Nat -> Set)                  -- The type of the result depends on the vector's length.
  -> (∀ p -> A -> Rec p -> R (suc p))  -- The encoded `cons` constructor.
  -> R zero                            -- The encoded `nil` constructor.
  -> R n

Vec : Set -> Nat -> Set
Vec = λ (A : Set) -> IFix (VecF A)

nil : ∀ A -> Vec A zero
nil = λ A -> wrap λ R f z -> z

cons : ∀ A n -> A -> Vec A n -> Vec A (suc n)
cons = λ A n x xs -> wrap λ R f z -> f n x xs

open import Data.Empty
open import Data.Unit.Base
open import Data.Nat.Base using (ℕ; _+_)

-- Type-safe `head`.
head : ∀ A n -> Vec A (suc n) -> A
head A n xs =
  unwrap
    xs
    (λ p -> p (λ _ -> A) ⊤)  -- `p (λ _ -> A) ⊤` returns `A` when `p` is `suc p'` for some `p'`
                             -- and `⊤` when `p` is `zero`
    (λ p x xs' -> x)         -- In the `cons` case `suc p (λ _ -> A) ⊤` reduces to `A`,
                             -- hence we return the list element of type `A`.
    tt                       -- In the `nil` case `zero (λ _ -> A) ⊤` reduces to `⊤`,
                             -- hence we return the only value of that type.

{- Note [Type-safe `tail`]
It's not obvious if type-safe `tail` can be implemented with this setup. This is because even
though `Vec` is Scott-encoded, the type-level natural are Church-encoded (obviously we could
have Scott-encoded type-level naturals in Agda just as well, but not in Zerepoch Core), which
makes `pred` hard, which makes `tail` non-trivial.

I did try using Scott-encoded naturals in Agda, that makes `tail` even more straightforward
than `head`:

    tail : ∀ A n -> Vec A (suc n) -> Vec A n
    tail A n xs =
      unwrap
        xs
        (λ p -> ((B : ℕ -> Set) -> B (pred p) -> B n) -> Vec A n)
        (λ p x xs' coe -> coe (Vec A) xs')
        (λ coe -> coe (Vec A) (nil A))
        (λ B x -> x)
-}

-- Here we pattern-match on `xs` and if it's non-empty, i.e. of type `Vec ℕ (suc p)` for some `p`,
-- then we also get access to a coercion function that allows us to coerce the second list from
-- `Vec ℕ n` to the same `Vec ℕ (suc p)` and call the type-safe `head` over it.
-- Note that we don't even need to encode the `n ~ suc p` and `n ~ zero` equality constraints in
-- the definition of `vecF` and can recover coercions along those constraints by adding the
-- `Vec ℕ n -> Vec ℕ p` argument to the motive.
sumHeadsOr0 : ∀ n -> Vec ℕ n -> Vec ℕ n -> ℕ
sumHeadsOr0 n xs ys =
  unwrap
    xs
    (λ p -> (Vec ℕ n -> Vec ℕ p) -> ℕ)
    (λ p i _ coe -> i + head ℕ p (coe ys))
    (λ _ -> 0)
    (λ x -> x)


module Lvl.Proofs where

open import Data.Tuple as Tuple using ()
open import Logic.Predicate
open import Lvl
open import Structure.Setoid
open import Structure.Function.Domain
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓₑ : Lvl.Level
private variable T : Type{ℓ}

instance
  LvlUp-equiv : ⦃ equiv : Equiv{ℓₑ}(T) ⦄ → Equiv(Lvl.Up{ℓ}(T))
  Equiv._≡_ (LvlUp-equiv {ℓ = ℓ}) (up x) (up y) = Lvl.Up{ℓ}(x ≡ y)
  Up.obj (Reflexivity.proof (Equivalence.reflexivity (Equiv.equivalence LvlUp-equiv))) = reflexivity(_≡_)
  Up.obj (Symmetry.proof (Equivalence.symmetry (Equiv.equivalence LvlUp-equiv)) (up p)) = symmetry(_≡_) p
  Up.obj (Transitivity.proof (Equivalence.transitivity (Equiv.equivalence LvlUp-equiv)) (up p) (up q)) = transitivity(_≡_) p q

instance
  LvlUpObj-bijective : ⦃ equiv : Equiv{ℓₑ}(T) ⦄ → Bijective(Lvl.Up.obj{ℓ}{T = T})
  Up.obj (∃.witness (Tuple.left (Bijective.proof LvlUpObj-bijective {y}))) = y
  ∃.proof (Tuple.left (Bijective.proof LvlUpObj-bijective)) = reflexivity(_≡_)
  Up.obj (Tuple.right (Bijective.proof LvlUpObj-bijective) p q) = transitivity(_≡_) p (symmetry(_≡_) q)


-- Andreas, 2011-04-15
-- {-# OPTIONS -v tc.meta:20 #-}
module SameMeta where

infix 10 _≡_

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

infixr 5 _×_ _,_

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

postulate A : Set

same : let X : A -> A -> A -> A × A
           X = _
       in {x y z : A} -> X x y y ≡ (x , y)
                       × X x x y ≡ X x y y
same = refl , refl
-- second equation triggers pruning of second variable of X
-- which makes the first equation linear and solvable


module Structure.Function.Linear where

import      Lvl
open import Logic
open import Logic.Propositional
open import Relator.Equals
open import Relator.Equals.Proofs
open import Type

-- TODO: Remove this
module _ {ℓ₁}{ℓ₂}{ℓ₃} {V₁ : Type{ℓ₁}} {V₂ : Type{ℓ₂}} {S : Type{ℓ₃}} where
  record LinearMap  (_+₁_ : V₁ → V₁ → V₁) (_⋅₁_ : S → V₁ → V₁)  (_+₂_ : V₂ → V₂ → V₂) (_⋅₂_ : S → V₂ → V₂) (f : V₁ → V₂) : Stmt{ℓ₁ Lvl.⊔ ℓ₂ Lvl.⊔ ℓ₃} where
    field
      additivity   : ∀{v₁ v₂ : V₁} → (f(v₁ +₁ v₂) ≡ f(v₁) +₂ f(v₂))
      homogeneity1 : ∀{s : S} → ∀{v : V₁} → (f(s ⋅₁ v) ≡ s ⋅₂ f(v))


module Sets.PredicateSet.Relations{ℓₗ}{ℓₒ} where

import      Lvl
open import Functional
open import Logic.Propositional{ℓₗ Lvl.⊔ ℓₒ}
open import Logic.Predicate{ℓₗ}{ℓₒ}
open import Numeral.Finite
open import Numeral.Natural
import      Relator.Equals
open import Sets.PredicateSet
open import Structure.Function.Domain

Empty : ∀{T} → PredSet{ℓₗ}{ℓₒ}(T) → Stmt
Empty(S) = (∀{x} → (x ∉' S)) where
  _∉'_ = _∉_ {ℓₗ}{ℓₒ}

NonEmpty : ∀{T} → PredSet{ℓₗ}{ℓₒ}(T) → Stmt
NonEmpty(S) = ∃(x ↦ (x ∈' S)) where
  _∈'_ = _∈_ {ℓₗ}{ℓₒ}


open import bool

module braun-tree{ℓ} (A : Set ℓ) (_<A_ : A → A → 𝔹) where

open import bool-thms
open import eq
open import list
open import nat
open import nat-thms
open import product
open import sum

-- the index n is the size of the tree (number of elements of type A)
data braun-tree : (n : ℕ) → Set ℓ where
  bt-empty : braun-tree 0
  bt-node : ∀ {n m : ℕ} →
            A → braun-tree n → braun-tree m →
            n ≡ m ∨ n ≡ suc m →
            braun-tree (suc (n + m))

{- we will keep smaller (_<A_) elements closer to the root of the Braun tree as we insert -}
bt-insert : ∀ {n : ℕ} → A → braun-tree n → braun-tree (suc n)

bt-insert a bt-empty = bt-node a bt-empty bt-empty (inj₁ refl)

bt-insert a (bt-node{n}{m} a' l r p)
  rewrite +comm n m with p | if a <A a' then (a , a') else (a' , a)
bt-insert a (bt-node{n}{m} a' l r _) | inj₁ p | (a1 , a2)
  rewrite p = (bt-node a1 (bt-insert a2 r) l (inj₂ refl))
bt-insert a (bt-node{n}{m} a' l r _) | inj₂ p | (a1 , a2) =
  (bt-node a1 (bt-insert a2 r) l (inj₁ (sym p)))

bt-replace-min : ∀{n : ℕ} → A → braun-tree (suc n) → braun-tree (suc n)
bt-replace-min a (bt-node _ bt-empty bt-empty u) = (bt-node a bt-empty bt-empty u)
bt-replace-min a (bt-node _ bt-empty (bt-node _ _ _ _) (inj₁ ()))
bt-replace-min a (bt-node _ bt-empty (bt-node _ _ _ _) (inj₂ ()))
bt-replace-min a (bt-node _ (bt-node _ _ _ _) bt-empty (inj₁ ()))
bt-replace-min a (bt-node a' (bt-node x l r u) bt-empty (inj₂ y)) with a <A x
bt-replace-min a (bt-node a' (bt-node x l r u) bt-empty (inj₂ y)) | tt = (bt-node a (bt-node x l r u) bt-empty (inj₂ y))
bt-replace-min a (bt-node a' (bt-node x l r u) bt-empty (inj₂ y)) | ff =
 (bt-node x (bt-replace-min a (bt-node x l r u)) bt-empty (inj₂ y))
bt-replace-min a (bt-node a' (bt-node x l r u) (bt-node x' l' r' u') v) with a <A x && a <A x'
bt-replace-min a (bt-node a' (bt-node x l r u) (bt-node x' l' r' u') v) | tt =
 (bt-node a (bt-node x l r u) (bt-node x' l' r' u') v)
bt-replace-min a (bt-node a' (bt-node x l r u) (bt-node x' l' r' u') v) | ff with x <A x'
bt-replace-min a (bt-node a' (bt-node x l r u) (bt-node x' l' r' u') v) | ff | tt =
 (bt-node x (bt-replace-min a (bt-node x l r u)) (bt-node x' l' r' u') v)
bt-replace-min a (bt-node a' (bt-node x l r u) (bt-node x' l' r' u') v) | ff | ff =
 (bt-node x' (bt-node x l r u) (bt-replace-min a (bt-node x' l' r' u')) v)

{- thanks to Matías Giovannini for the excellent post
     http://alaska-kamtchatka.blogspot.com/2010/02/braun-trees.html
   explaining how to do delete -}
bt-delete-min : ∀ {p : ℕ} → braun-tree (suc p) → braun-tree p
bt-delete-min (bt-node a bt-empty bt-empty u) = bt-empty
bt-delete-min (bt-node a bt-empty (bt-node _ _ _ _) (inj₁ ()))
bt-delete-min (bt-node a bt-empty (bt-node _ _ _ _) (inj₂ ()))
bt-delete-min (bt-node a (bt-node{n'}{m'} a' l' r' u') bt-empty u) rewrite +0 (n' + m') = bt-node a' l' r' u'
bt-delete-min (bt-node a
                (bt-node{n}{m} x l1 r1 u1)
                (bt-node{n'}{m'} x' l2 r2 u2) u)
  rewrite +suc(n + m)(n' + m') | +suc n (m + (n' + m'))
        | +comm(n + m)(n' + m') =
  if (x <A x') then
    (bt-node x (bt-node x' l2 r2 u2)
      (bt-delete-min (bt-node x l1 r1 u1)) (lem{n}{m}{n'}{m'} u))
  else
    (bt-node x' (bt-replace-min x (bt-node x' l2 r2 u2))
      (bt-delete-min (bt-node x l1 r1 u1)) (lem{n}{m}{n'}{m'} u))
  where lem : {n m n' m' : ℕ} → suc (n + m) ≡ suc (n' + m') ∨ suc (n + m) ≡ suc (suc (n' + m')) →
              suc (n' + m') ≡ n + m ∨ suc (n' + m') ≡ suc (n + m)
        lem{n}{m}{n'}{m'} (inj₁ x) = inj₂ (sym x)
        lem (inj₂ y) = inj₁ (sym (suc-inj y))

bt-remove-min : ∀ {p : ℕ} → braun-tree (suc p) → A × braun-tree p
bt-remove-min (bt-node a l r u) = a , bt-delete-min (bt-node a l r u)

----------------------------------------------------------------------
-- this version stores data at the leaves instead of at the nodes
----------------------------------------------------------------------
data braun-tree' : (n : ℕ) → Set ℓ where
  bt'-leaf : A → braun-tree' 1
  bt'-node : ∀ {n m : ℕ} →
            braun-tree' n → braun-tree' m →
            n =ℕ m ≡ tt ⊎ n =ℕ m + 1 ≡ tt →
            braun-tree' (n + m)

bt'-insert : ∀ {n : ℕ} → A → braun-tree' n → braun-tree' (suc n)
bt'-insert a (bt'-leaf a') = bt'-node (bt'-leaf a) (bt'-leaf a') (inj₁ refl)
bt'-insert a (bt'-node{n}{m} l r p) rewrite +comm n m with p
bt'-insert a (bt'-node{n}{m} l r p) | inj₁ p' rewrite =ℕ-to-≡{n} p' = (bt'-node (bt'-insert a r) l (inj₂ lem))
  where lem : suc m =ℕ m + 1 ≡ tt
        lem rewrite +comm m 1 = =ℕ-refl m
bt'-insert a (bt'-node{n}{m} l r p) | inj₂ p' = (bt'-node (bt'-insert a r) l (inj₁ (lem n m p')))
  where lem : ∀ n m → n =ℕ m + 1 ≡ tt → suc m =ℕ n ≡ tt
        lem n m p' rewrite =ℕ-to-≡{n} p' | +comm m 1 = =ℕ-refl m

𝕃-to-braun-tree' : A → (l : 𝕃 A) → braun-tree' (suc (length l))
𝕃-to-braun-tree' a [] = bt'-leaf a
𝕃-to-braun-tree' a (a' :: as) = bt'-insert a (𝕃-to-braun-tree' a' as)


{-# OPTIONS --without-K --safe #-}
open import Categories.Category using (Category; module Definitions)

-- Definition of the "Twisted" Functor between certain Functor Categories
module Categories.Functor.Instance.Twisted {o ℓ e o′ ℓ′ e′} (𝒞 : Category o ℓ e) (𝒟 : Category o′ ℓ′ e′) where

import Categories.Category.Construction.TwistedArrow as TW
open import Categories.Category.Product
open import Categories.Category.Construction.Functors
open import Categories.Functor
open import Categories.NaturalTransformation using (NaturalTransformation; ntHelper)

open import Data.Product using (_,_)

private
  module C = Category 𝒞

Twist : Functor (Product C.op 𝒞) 𝒟 → Functor (TW.TwistedArrow 𝒞) 𝒟
Twist F = record
  { F₀ = λ x → F₀ (dom x , cod x)
  ; F₁ = λ f → F₁ (dom⇐ f , cod⇒ f)
  ; identity = identity
  ; homomorphism = homomorphism
  ; F-resp-≈ = F-resp-≈
  }
  where
  open Functor F
  open TW.Morphism
  open TW.Morphism⇒

Twist′ : Functor (Product C.op 𝒞) 𝒟 → Functor (Category.op (TW.TwistedArrow C.op)) 𝒟
Twist′ F = record
  { F₀ = λ x → F₀ (dom x , cod x)
  ; F₁ = λ f → F₁ (dom⇐ f , cod⇒ f)
  ; identity = identity
  ; homomorphism = homomorphism
  ; F-resp-≈ = F-resp-≈
  }
  where
  open Functor F
  open TW.Morphism
  open TW.Morphism⇒

Twisted : Functor (Functors (Product C.op 𝒞) 𝒟) (Functors (TW.TwistedArrow 𝒞) 𝒟)
Twisted = record
  { F₀ = Twist
  ; F₁ = Nat
  ; identity = D.Equiv.refl
  ; homomorphism = D.Equiv.refl
  ; F-resp-≈ = λ f≈g → f≈g
  }
  where
  open TW.Morphism
  open TW.Morphism⇒
  module D = Category 𝒟
  Nat : {F G : Functor (Product C.op 𝒞) 𝒟} → NaturalTransformation F G → NaturalTransformation (Twist F) (Twist G)
  Nat nt = ntHelper record
    { η = λ x → η nt (dom x , cod x)
    ; commute = λ f → commute nt (dom⇐ f , cod⇒ f)
    }
    where
    open NaturalTransformation


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

open import Algebra

module Data.FingerTree.Reasoning
  {r m}
  (ℳ : Monoid r m)
  where

open Monoid ℳ renaming (Carrier to 𝓡)

open import Data.FingerTree.MonoidSolver ℳ using (solve-macro)

open import Data.Unit using (⊤)
open import Reflection using (TC; Term)

macro
  _↯ : Term → Term → TC ⊤
  _↯ = solve-macro

infixr 2 ∙≫_ ≪∙_
∙≫_ : ∀ {x y z} → x ≈ y → z ∙ x ≈ z ∙ y
∙≫_ = ∙-cong refl

≪∙_ : ∀ {x y z} → x ≈ y → x ∙ z ≈ y ∙ z
≪∙ x = ∙-cong x refl

infixl 1 _⍮_
_⍮_ : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z
_⍮_ = trans

infixl 1 trans⁻¹
trans⁻¹ : ∀ {x y z : 𝓡} → y ≈ z → x ≈ y → x ≈ z
trans⁻¹ x y = trans y x

syntax trans⁻¹ y≈z x≈y = x≈y ⍮′ y≈z

infixr 2 _↢_ ↣-syntax ↣↣-syntax
_↢_ : ∀ x {y} → x ≈ y → x ≈ y
_ ↢ x≈y = x≈y

↣-syntax : ∀ {x} y → x ≈ y → x ≈ y
↣-syntax _ x≈y = x≈y

syntax ↣-syntax y x≈y = x≈y ↣ y

↣↣-syntax : ∀ x y → x ≈ y → x ≈ y
↣↣-syntax _ _ x≈y = x≈y

syntax ↣↣-syntax x y x≈y = x ↣⟨ x≈y ⟩↣ y

infixl 6 _∙>_ _<∙_
_∙>_ : ∀ x {y z} → y ≈ z → x ∙ y ≈ x ∙ z
_ ∙> y≈z = ∙≫ y≈z

_<∙_ : ∀ {x y} → x ≈ y → ∀ z → x ∙ z ≈ y ∙ z
x≈y <∙ _ = ≪∙ x≈y


module Prelude.List.Properties where

open import Prelude.Function

open import Prelude.Bool
open import Prelude.Bool.Properties

open import Prelude.Nat
open import Prelude.Nat.Properties

open import Prelude.Semiring

open import Prelude.List.Base

open import Prelude.Decidable

open import Prelude.Monoid
open import Prelude.Semigroup

open import Prelude.Equality
open import Prelude.Equality.Inspect
open import Prelude.Variables

open import Prelude.Strict

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

foldl-map-fusion : ∀ {a b c} {A : Set a} {B : Set b} {C : Set c}
                     (f : C → B → C) (g : A → B) (z : C) (xs : List A) →
                     foldl f z (map g xs) ≡ foldl (λ x y → f x (g y)) z xs
foldl-map-fusion f g z [] = refl
foldl-map-fusion f g z (x ∷ xs)
  rewrite foldl-map-fusion f g (f z (g x)) xs
        = refl

foldl-assoc : ∀ {a} {A : Set a} (f : A → A → A) →
                (∀ x y z → f x (f y z) ≡ f (f x y) z) →
                ∀ y z xs → foldl f (f y z) xs ≡ f y (foldl f z xs)
foldl-assoc f assoc y z [] = refl
foldl-assoc f assoc y z (x ∷ xs)
  rewrite sym (assoc y z x)
        = foldl-assoc f assoc y (f z x) xs

foldl-foldr : ∀ {a} {A : Set a} (f : A → A → A) (z : A) →
                (∀ x y z → f x (f y z) ≡ f (f x y) z) →
                (∀ x → f z x ≡ x) → (∀ x → f x z ≡ x) →
                ∀ xs → foldl f z xs ≡ foldr f z xs
foldl-foldr f z assoc idl idr [] = refl
foldl-foldr f z assoc idl idr (x ∷ xs)
  rewrite sym (foldl-foldr f z assoc idl idr xs)
        | idl x ⟨≡⟩ʳ idr x
        = foldl-assoc f assoc x z xs

foldl!-foldl : ∀ {a b} {A : Set a} {B : Set b} (f : B → A → B) z (xs : List A) →
                 foldl! f z xs ≡ foldl f z xs
foldl!-foldl f z [] = refl
foldl!-foldl f z (x ∷ xs) = forceLemma (f z x) (λ z′ → foldl! f z′ xs) ⟨≡⟩
                            foldl!-foldl f (f z x) xs

-- Properties of _++_

map-++ : ∀ {a b} {A : Set a} {B : Set b} (f : A → B) (xs ys : List A) →
           map f (xs ++ ys) ≡ map f xs ++ map f ys
map-++ f []       ys = refl
map-++ f (x ∷ xs) ys = f x ∷_ $≡ map-++ f xs ys

product-++ : (xs ys : List Nat) → productR (xs ++ ys) ≡ productR xs * productR ys
product-++ []       ys = sym (add-zero-r _)
product-++ (x ∷ xs) ys = x *_ $≡ product-++ xs ys ⟨≡⟩ mul-assoc x _ _


module sum-thms where

open import eq
open import sum
open import list
open import product
open import negation

inj₁-inj : ∀{ℓ ℓ'}{A : Set ℓ}{B : Set ℓ'}{x : A}{x'} → inj₁{ℓ}{ℓ'}{A}{B} x ≡ inj₁ x' → x ≡ x'
inj₁-inj refl = refl

¬∨ : ∀ {A B : Set} → ¬ A ∧ ¬ B → ¬ (A ∨ B)
¬∨ (u , u') (inj₁ x) = u x
¬∨ (u , u') (inj₂ y) = u' y

¬Σ : ∀{A : Set}{B : A → Set} → (∀(x : A) → ¬ B x) → ¬ Σ A B
¬Σ p (a , b) = p a b

¬∧1 : ∀ {A B : Set} → ¬ A → ¬ (A ∧ B)
¬∧1 f (a , b) = f a

¬∧2 : ∀ {A B : Set} → ¬ B → ¬ (A ∧ B)
¬∧2 f (a , b) = f b

¬∀ : ∀{A : Set}{B : A → Set} → Σ A (λ x → ¬ B x) → ¬ ∀(x : A) → B x
¬∀ (a , b) f = b (f a)


mutual
  data ℕμ : Set where
    zero : ℕμ
    succ : ℕμ → ℕμ
    delay : ℕν → ℕμ

  record ℕν : Set where
    coinductive
    field step : ℕμ

open ℕν public

mutual
  data _≤μ_ : ℕμ → ℕμ → Set where
    -- Standard part of the order
    le₀ : (y : ℕμ) → zero ≤μ y
    leₛ : {x y : ℕμ} → x ≤μ y → succ x ≤μ succ y
    -- We can have finite delays on the left and on the right...
    le-stepₗ : {m : ℕν} {y : ℕμ} → step m ≤μ y → delay m ≤μ y
    le-stepᵣ : {x : ℕμ} {n : ℕν} → x ≤μ step n → x ≤μ delay n
    -- or an abritrary sequence of delays on both sides.
    le-step : {m n : ℕν} → m ≤ n → delay m ≤μ delay n

  record _≤_ (m n : ℕν) : Set where
    coinductive
    field step≤ : (step m) ≤μ (step m)

open _≤_ public

≤μ-refl : (x : ℕμ) → x ≤μ x
≤-refl : (m : ℕν) → m ≤ m

step≤ (≤-refl m) = ≤μ-refl (step m)

≤μ-refl zero = le₀ zero
≤μ-refl (succ x) = leₛ (≤μ-refl x)
≤μ-refl (delay x) = le-step (≤-refl x)


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

open import Categories.Category
open import Categories.Object.Zero

-- Kernels of morphisms.
-- https://ncatlab.org/nlab/show/kernel
module Categories.Object.Kernel {o ℓ e} {𝒞 : Category o ℓ e} (zero : Zero 𝒞) where

open import Level using (_⊔_)

open import Categories.Morphism 𝒞
open import Categories.Morphism.Reasoning 𝒞
  hiding (glue)

open Category 𝒞
open Zero zero

open HomReasoning

private
  variable
    A B X : Obj
    f h i j k : A ⇒ B

-- Note: We could define Kernels directly as equalizers or as pullbacks, but it seems somewhat
-- cleaner to just define them "as is" to avoid mucking about with extra morphisms that can
-- get in the way. For example, if we defined them as 'Pullback f !' then our 'p₂' projection
-- would _always_ be trivially equal to '¡ : K ⇒ zero'.

record IsKernel {A B K} (k : K ⇒ A) (f : A ⇒ B) : Set (o ⊔ ℓ ⊔ e) where
  field
    commute : f ∘ k ≈ zero⇒
    universal : ∀ {X} {h : X ⇒ A} → f ∘ h ≈ zero⇒ → X ⇒ K
    factors : ∀ {eq : f ∘ h ≈ zero⇒} → h ≈ k ∘ universal eq
    unique : ∀ {eq : f ∘ h ≈ zero⇒} → h ≈ k ∘ i → i ≈ universal eq

  universal-resp-≈ : ∀ {eq : f ∘ h ≈ zero⇒} {eq′ : f ∘ i ≈ zero⇒} →
    h ≈ i → universal eq ≈ universal eq′
  universal-resp-≈ h≈i = unique (⟺ h≈i ○ factors)

  universal-∘ : f ∘ k ∘ h ≈ zero⇒
  universal-∘ {h = h} = begin
    f ∘ k ∘ h ≈⟨ pullˡ commute ⟩
    zero⇒ ∘ h ≈⟨ pullʳ (⟺ (¡-unique (¡ ∘ h))) ⟩
    zero⇒ ∎

record Kernel {A B} (f : A ⇒ B) : Set (o ⊔ ℓ ⊔ e) where
  field
     {kernel} : Obj
     kernel⇒ : kernel ⇒ A
     isKernel : IsKernel kernel⇒ f

  open IsKernel isKernel public

IsKernel⇒Kernel : IsKernel k f → Kernel f
IsKernel⇒Kernel {k = k} isKernel = record
  { kernel⇒ = k
  ; isKernel = isKernel
  }


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

open import Algebra

module Algebra.Props.BooleanAlgebra (b : BooleanAlgebra) where

open BooleanAlgebra b
import Algebra.Props.DistributiveLattice as DL
open DL distributiveLattice public
open import Algebra.Structures
import Algebra.FunctionProperties as P; open P _≈_
import Relation.Binary.EqReasoning as EqR; open EqR setoid
open import Relation.Binary
open import Data.Function
open import Data.Product

------------------------------------------------------------------------
-- Some simple generalisations

∨-complement : Inverse ⊤ ¬_ _∨_
∨-complement = ∨-complementˡ , ∨-complementʳ
  where
  ∨-complementˡ : LeftInverse ⊤ ¬_ _∨_
  ∨-complementˡ x = begin
    ¬ x ∨ x    ≈⟨ ∨-comm _ _ ⟩
    x   ∨ ¬ x  ≈⟨ ∨-complementʳ _ ⟩
    ⊤          ∎

∧-complement : Inverse ⊥ ¬_ _∧_
∧-complement = ∧-complementˡ , ∧-complementʳ
  where
  ∧-complementˡ : LeftInverse ⊥ ¬_ _∧_
  ∧-complementˡ x = begin
    ¬ x ∧ x    ≈⟨ ∧-comm _ _ ⟩
    x   ∧ ¬ x  ≈⟨ ∧-complementʳ _ ⟩
    ⊥          ∎

------------------------------------------------------------------------
-- The dual construction is also a boolean algebra

∧-∨-isBooleanAlgebra : IsBooleanAlgebra _≈_ _∧_ _∨_ ¬_ ⊥ ⊤
∧-∨-isBooleanAlgebra = record
  { isDistributiveLattice = ∧-∨-isDistributiveLattice
  ; ∨-complementʳ         = proj₂ ∧-complement
  ; ∧-complementʳ         = proj₂ ∨-complement
  ; ¬-pres-≈              = ¬-pres-≈
  }

∧-∨-booleanAlgebra : BooleanAlgebra
∧-∨-booleanAlgebra = record
  { _∧_              = _∨_
  ; _∨_              = _∧_
  ; ⊤                = ⊥
  ; ⊥                = ⊤
  ; isBooleanAlgebra = ∧-∨-isBooleanAlgebra
  }

------------------------------------------------------------------------
-- (∨, ∧, ⊥, ⊤) is a commutative semiring

private

  ∧-identity : Identity ⊤ _∧_
  ∧-identity = (λ _ → ∧-comm _ _ ⟨ trans ⟩ x∧⊤=x _) , x∧⊤=x
    where
    x∧⊤=x : ∀ x → x ∧ ⊤ ≈ x
    x∧⊤=x x = begin
      x ∧ ⊤          ≈⟨ refl ⟨ ∧-pres-≈ ⟩ sym (proj₂ ∨-complement _) ⟩
      x ∧ (x ∨ ¬ x)  ≈⟨ proj₂ absorptive _ _ ⟩
      x              ∎

  ∨-identity : Identity ⊥ _∨_
  ∨-identity = (λ _ → ∨-comm _ _ ⟨ trans ⟩ x∨⊥=x _) , x∨⊥=x
    where
    x∨⊥=x : ∀ x → x ∨ ⊥ ≈ x
    x∨⊥=x x = begin
      x ∨ ⊥          ≈⟨ refl ⟨ ∨-pres-≈ ⟩ sym (proj₂ ∧-complement _) ⟩
      x ∨ x ∧ ¬ x    ≈⟨ proj₁ absorptive _ _ ⟩
      x              ∎

  ∧-zero : Zero ⊥ _∧_
  ∧-zero = (λ _ → ∧-comm _ _ ⟨ trans ⟩ x∧⊥=⊥ _) , x∧⊥=⊥
    where
    x∧⊥=⊥ : ∀ x → x ∧ ⊥ ≈ ⊥
    x∧⊥=⊥ x = begin
      x ∧ ⊥          ≈⟨ refl ⟨ ∧-pres-≈ ⟩ sym (proj₂ ∧-complement _) ⟩
      x ∧  x  ∧ ¬ x  ≈⟨ sym $ ∧-assoc _ _ _ ⟩
      (x ∧ x) ∧ ¬ x  ≈⟨ ∧-idempotent _ ⟨ ∧-pres-≈ ⟩ refl ⟩
      x       ∧ ¬ x  ≈⟨ proj₂ ∧-complement _ ⟩
      ⊥              ∎

∨-∧-isCommutativeSemiring : IsCommutativeSemiring _≈_ _∨_ _∧_ ⊥ ⊤
∨-∧-isCommutativeSemiring = record
  { isSemiring = record
    { isSemiringWithoutAnnihilatingZero = record
      { +-isCommutativeMonoid = record
        { isMonoid = record
          { isSemigroup = record
            { isEquivalence = isEquivalence
            ; assoc         = ∨-assoc
            ; ∙-pres-≈      = ∨-pres-≈
            }
          ; identity  = ∨-identity
          }
        ; comm  = ∨-comm
        }
      ; *-isMonoid = record
        { isSemigroup = record
          { isEquivalence = isEquivalence
          ; assoc         = ∧-assoc
          ; ∙-pres-≈      = ∧-pres-≈
          }
        ; identity = ∧-identity
        }
      ; distrib = ∧-∨-distrib
      }
    ; zero = ∧-zero
    }
  ; *-comm = ∧-comm
  }

∨-∧-commutativeSemiring : CommutativeSemiring
∨-∧-commutativeSemiring = record
  { _+_                   = _∨_
  ; _*_                   = _∧_
  ; 0#                    = ⊥
  ; 1#                    = ⊤
  ; isCommutativeSemiring = ∨-∧-isCommutativeSemiring
  }

------------------------------------------------------------------------
-- (∧, ∨, ⊤, ⊥) is a commutative semiring

private

  ∨-zero : Zero ⊤ _∨_
  ∨-zero = (λ _ → ∨-comm _ _ ⟨ trans ⟩ x∨⊤=⊤ _) , x∨⊤=⊤
    where
    x∨⊤=⊤ : ∀ x → x ∨ ⊤ ≈ ⊤
    x∨⊤=⊤ x = begin
      x ∨ ⊤          ≈⟨ refl ⟨ ∨-pres-≈ ⟩ sym (proj₂ ∨-complement _) ⟩
      x ∨  x  ∨ ¬ x  ≈⟨ sym $ ∨-assoc _ _ _ ⟩
      (x ∨ x) ∨ ¬ x  ≈⟨ ∨-idempotent _ ⟨ ∨-pres-≈ ⟩ refl ⟩
      x       ∨ ¬ x  ≈⟨ proj₂ ∨-complement _ ⟩
      ⊤              ∎

∧-∨-isCommutativeSemiring : IsCommutativeSemiring _≈_ _∧_ _∨_ ⊤ ⊥
∧-∨-isCommutativeSemiring = record
  { isSemiring = record
    { isSemiringWithoutAnnihilatingZero = record
      { +-isCommutativeMonoid = record
        { isMonoid = record
          { isSemigroup = record
            { isEquivalence = isEquivalence
            ; assoc         = ∧-assoc
            ; ∙-pres-≈      = ∧-pres-≈
            }
          ; identity  = ∧-identity
          }
        ; comm  = ∧-comm
        }
      ; *-isMonoid = record
        { isSemigroup = record
          { isEquivalence = isEquivalence
          ; assoc         = ∨-assoc
          ; ∙-pres-≈      = ∨-pres-≈
          }
        ; identity = ∨-identity
        }
      ; distrib = ∨-∧-distrib
      }
    ; zero = ∨-zero
    }
  ; *-comm = ∨-comm
  }

∧-∨-commutativeSemiring : CommutativeSemiring
∧-∨-commutativeSemiring =
  record { isCommutativeSemiring = ∧-∨-isCommutativeSemiring }

------------------------------------------------------------------------
-- Some other properties

-- I took the statement of this lemma (called Uniqueness of
-- Complements) from some course notes, "Boolean Algebra", written
-- by Gert Smolka.

private
  lemma : ∀ x y → x ∧ y ≈ ⊥ → x ∨ y ≈ ⊤ → ¬ x ≈ y
  lemma x y x∧y=⊥ x∨y=⊤ = begin
    ¬ x                ≈⟨ sym $ proj₂ ∧-identity _ ⟩
    ¬ x ∧ ⊤            ≈⟨ refl ⟨ ∧-pres-≈ ⟩ sym x∨y=⊤ ⟩
    ¬ x ∧ (x ∨ y)      ≈⟨ proj₁ ∧-∨-distrib _ _ _ ⟩
    ¬ x ∧ x ∨ ¬ x ∧ y  ≈⟨ proj₁ ∧-complement _ ⟨ ∨-pres-≈ ⟩ refl ⟩
    ⊥ ∨ ¬ x ∧ y        ≈⟨ sym x∧y=⊥ ⟨ ∨-pres-≈ ⟩ refl ⟩
    x ∧ y ∨ ¬ x ∧ y    ≈⟨ sym $ proj₂ ∧-∨-distrib _ _ _ ⟩
    (x ∨ ¬ x) ∧ y      ≈⟨ proj₂ ∨-complement _ ⟨ ∧-pres-≈ ⟩ refl ⟩
    ⊤ ∧ y              ≈⟨ proj₁ ∧-identity _ ⟩
    y                  ∎

¬⊥=⊤ : ¬ ⊥ ≈ ⊤
¬⊥=⊤ = lemma ⊥ ⊤ (proj₂ ∧-identity _) (proj₂ ∨-zero _)

¬⊤=⊥ : ¬ ⊤ ≈ ⊥
¬⊤=⊥ = lemma ⊤ ⊥ (proj₂ ∧-zero _) (proj₂ ∨-identity _)

¬-involutive : Involutive ¬_
¬-involutive x = lemma (¬ x) x (proj₁ ∧-complement _) (proj₁ ∨-complement _)

deMorgan₁ : ∀ x y → ¬ (x ∧ y) ≈ ¬ x ∨ ¬ y
deMorgan₁ x y = lemma (x ∧ y) (¬ x ∨ ¬ y) lem₁ lem₂
  where
  lem₁ = begin
    (x ∧ y) ∧ (¬ x ∨ ¬ y)          ≈⟨ proj₁ ∧-∨-distrib _ _ _ ⟩
    (x ∧ y) ∧ ¬ x ∨ (x ∧ y) ∧ ¬ y  ≈⟨ (∧-comm _ _ ⟨ ∧-pres-≈ ⟩ refl) ⟨ ∨-pres-≈ ⟩ refl ⟩
    (y ∧ x) ∧ ¬ x ∨ (x ∧ y) ∧ ¬ y  ≈⟨ ∧-assoc _ _ _ ⟨ ∨-pres-≈ ⟩ ∧-assoc _ _ _ ⟩
    y ∧ (x ∧ ¬ x) ∨ x ∧ (y ∧ ¬ y)  ≈⟨ (refl ⟨ ∧-pres-≈ ⟩ proj₂ ∧-complement _) ⟨ ∨-pres-≈ ⟩
                                      (refl ⟨ ∧-pres-≈ ⟩ proj₂ ∧-complement _) ⟩
    (y ∧ ⊥) ∨ (x ∧ ⊥)              ≈⟨ proj₂ ∧-zero _ ⟨ ∨-pres-≈ ⟩ proj₂ ∧-zero _ ⟩
    ⊥ ∨ ⊥                          ≈⟨ proj₂ ∨-identity _ ⟩
    ⊥                              ∎

  lem₃ = begin
    (x ∧ y) ∨ ¬ x          ≈⟨ proj₂ ∨-∧-distrib _ _ _ ⟩
    (x ∨ ¬ x) ∧ (y ∨ ¬ x)  ≈⟨ proj₂ ∨-complement _ ⟨ ∧-pres-≈ ⟩ refl ⟩
    ⊤ ∧ (y ∨ ¬ x)          ≈⟨ proj₁ ∧-identity _ ⟩
    y ∨ ¬ x                ≈⟨ ∨-comm _ _ ⟩
    ¬ x ∨ y                ∎

  lem₂ = begin
    (x ∧ y) ∨ (¬ x ∨ ¬ y)  ≈⟨ sym $ ∨-assoc _ _ _ ⟩
    ((x ∧ y) ∨ ¬ x) ∨ ¬ y  ≈⟨ lem₃ ⟨ ∨-pres-≈ ⟩ refl ⟩
    (¬ x ∨ y) ∨ ¬ y        ≈⟨ ∨-assoc _ _ _ ⟩
    ¬ x ∨ (y ∨ ¬ y)        ≈⟨ refl ⟨ ∨-pres-≈ ⟩ proj₂ ∨-complement _ ⟩
    ¬ x ∨ ⊤                ≈⟨ proj₂ ∨-zero _ ⟩
    ⊤                      ∎

deMorgan₂ : ∀ x y → ¬ (x ∨ y) ≈ ¬ x ∧ ¬ y
deMorgan₂ x y = begin
  ¬ (x ∨ y)          ≈⟨ ¬-pres-≈ $ sym (¬-involutive _) ⟨ ∨-pres-≈ ⟩
                                   sym (¬-involutive _) ⟩
  ¬ (¬ ¬ x ∨ ¬ ¬ y)  ≈⟨ ¬-pres-≈ $ sym $ deMorgan₁ _ _ ⟩
  ¬ ¬ (¬ x ∧ ¬ y)    ≈⟨ ¬-involutive _ ⟩
  ¬ x ∧ ¬ y          ∎

------------------------------------------------------------------------
-- (⊕, ∧, id, ⊥, ⊤) is a commutative ring

-- This construction is parameterised over the definition of xor.

module XorRing
  (xor : Op₂ carrier)
  (⊕-def : ∀ x y → xor x y ≈ (x ∨ y) ∧ ¬ (x ∧ y))
  where

  private
    infixl 6 _⊕_

    _⊕_ : Op₂ carrier
    _⊕_ = xor

  private
    helper : ∀ {x y u v} → x ≈ y → u ≈ v → x ∧ ¬ u ≈ y ∧ ¬ v
    helper x≈y u≈v = x≈y ⟨ ∧-pres-≈ ⟩ ¬-pres-≈ u≈v

  ⊕-¬-distribˡ : ∀ x y → ¬ (x ⊕ y) ≈ ¬ x ⊕ y
  ⊕-¬-distribˡ x y = begin
    ¬ (x ⊕ y)                              ≈⟨ ¬-pres-≈ $ ⊕-def _ _ ⟩
    ¬ ((x ∨ y) ∧ (¬ (x ∧ y)))              ≈⟨ ¬-pres-≈ (proj₂ ∧-∨-distrib _ _ _) ⟩
    ¬ ((x ∧ ¬ (x ∧ y)) ∨ (y ∧ ¬ (x ∧ y)))  ≈⟨ ¬-pres-≈ $
                                                refl ⟨ ∨-pres-≈ ⟩
                                                  (refl ⟨ ∧-pres-≈ ⟩
                                                     ¬-pres-≈ (∧-comm _ _)) ⟩
    ¬ ((x ∧ ¬ (x ∧ y)) ∨ (y ∧ ¬ (y ∧ x)))  ≈⟨ ¬-pres-≈ $ lem _ _ ⟨ ∨-pres-≈ ⟩ lem _ _ ⟩
    ¬ ((x ∧ ¬ y) ∨ (y ∧ ¬ x))              ≈⟨ deMorgan₂ _ _ ⟩
    ¬ (x ∧ ¬ y) ∧ ¬ (y ∧ ¬ x)              ≈⟨ deMorgan₁ _ _ ⟨ ∧-pres-≈ ⟩ refl ⟩
    (¬ x ∨ (¬ ¬ y)) ∧ ¬ (y ∧ ¬ x)          ≈⟨ helper (refl ⟨ ∨-pres-≈ ⟩ ¬-involutive _)
                                                     (∧-comm _ _) ⟩
    (¬ x ∨ y) ∧ ¬ (¬ x ∧ y)                ≈⟨ sym $ ⊕-def _ _ ⟩
    ¬ x ⊕ y                                ∎
    where
    lem : ∀ x y → x ∧ ¬ (x ∧ y) ≈ x ∧ ¬ y
    lem x y = begin
      x ∧ ¬ (x ∧ y)          ≈⟨ refl ⟨ ∧-pres-≈ ⟩ deMorgan₁ _ _ ⟩
      x ∧ (¬ x ∨ ¬ y)        ≈⟨ proj₁ ∧-∨-distrib _ _ _ ⟩
      (x ∧ ¬ x) ∨ (x ∧ ¬ y)  ≈⟨ proj₂ ∧-complement _ ⟨ ∨-pres-≈ ⟩ refl ⟩
      ⊥ ∨ (x ∧ ¬ y)          ≈⟨ proj₁ ∨-identity _ ⟩
      x ∧ ¬ y                ∎

  private
    ⊕-comm : Commutative _⊕_
    ⊕-comm x y = begin
      x ⊕ y                ≈⟨ ⊕-def _ _ ⟩
      (x ∨ y) ∧ ¬ (x ∧ y)  ≈⟨ helper (∨-comm _ _) (∧-comm _ _) ⟩
      (y ∨ x) ∧ ¬ (y ∧ x)  ≈⟨ sym $ ⊕-def _ _ ⟩
      y ⊕ x                ∎

  ⊕-¬-distribʳ : ∀ x y → ¬ (x ⊕ y) ≈ x ⊕ ¬ y
  ⊕-¬-distribʳ x y = begin
    ¬ (x ⊕ y)  ≈⟨ ¬-pres-≈ $ ⊕-comm _ _ ⟩
    ¬ (y ⊕ x)  ≈⟨ ⊕-¬-distribˡ _ _ ⟩
    ¬ y ⊕ x    ≈⟨ ⊕-comm _ _ ⟩
    x ⊕ ¬ y    ∎

  ⊕-annihilates-¬ : ∀ x y → x ⊕ y ≈ ¬ x ⊕ ¬ y
  ⊕-annihilates-¬ x y = begin
    x ⊕ y        ≈⟨ sym $ ¬-involutive _ ⟩
    ¬ ¬ (x ⊕ y)  ≈⟨ ¬-pres-≈ $ ⊕-¬-distribˡ _ _ ⟩
    ¬ (¬ x ⊕ y)  ≈⟨ ⊕-¬-distribʳ _ _ ⟩
    ¬ x ⊕ ¬ y    ∎

  private
    ⊕-pres : _⊕_ Preserves₂ _≈_ ⟶ _≈_ ⟶ _≈_
    ⊕-pres {x} {y} {u} {v} x≈y u≈v = begin
      x ⊕ u                ≈⟨ ⊕-def _ _ ⟩
      (x ∨ u) ∧ ¬ (x ∧ u)  ≈⟨ helper (x≈y ⟨ ∨-pres-≈ ⟩ u≈v)
                                     (x≈y ⟨ ∧-pres-≈ ⟩ u≈v) ⟩
      (y ∨ v) ∧ ¬ (y ∧ v)  ≈⟨ sym $ ⊕-def _ _ ⟩
      y ⊕ v                ∎

    ⊕-identity : Identity ⊥ _⊕_
    ⊕-identity = ⊥⊕x=x , (λ _ → ⊕-comm _ _ ⟨ trans ⟩ ⊥⊕x=x _)
      where
      ⊥⊕x=x : ∀ x → ⊥ ⊕ x ≈ x
      ⊥⊕x=x x = begin
        ⊥ ⊕ x                ≈⟨ ⊕-def _ _ ⟩
        (⊥ ∨ x) ∧ ¬ (⊥ ∧ x)  ≈⟨ helper (proj₁ ∨-identity _)
                                       (proj₁ ∧-zero _) ⟩
        x ∧ ¬ ⊥              ≈⟨ refl ⟨ ∧-pres-≈ ⟩ ¬⊥=⊤ ⟩
        x ∧ ⊤                ≈⟨ proj₂ ∧-identity _ ⟩
        x                    ∎

    ⊕-inverse : Inverse ⊥ id _⊕_
    ⊕-inverse = x⊕x=⊥ , (λ _ → ⊕-comm _ _ ⟨ trans ⟩ x⊕x=⊥ _)
      where
      x⊕x=⊥ : ∀ x → x ⊕ x ≈ ⊥
      x⊕x=⊥ x = begin
        x ⊕ x                ≈⟨ ⊕-def _ _ ⟩
        (x ∨ x) ∧ ¬ (x ∧ x)  ≈⟨ helper (∨-idempotent _)
                                       (∧-idempotent _) ⟩
        x ∧ ¬ x              ≈⟨ proj₂ ∧-complement _ ⟩
        ⊥                    ∎

    distrib-∧-⊕ : _∧_ DistributesOver _⊕_
    distrib-∧-⊕ = distˡ , distʳ
      where
      distˡ : _∧_ DistributesOverˡ _⊕_
      distˡ x y z = begin
        x ∧ (y ⊕ z)                ≈⟨ refl ⟨ ∧-pres-≈ ⟩ ⊕-def _ _ ⟩
        x ∧ ((y ∨ z) ∧ ¬ (y ∧ z))  ≈⟨ sym $ ∧-assoc _ _ _ ⟩
        (x ∧ (y ∨ z)) ∧ ¬ (y ∧ z)  ≈⟨ refl ⟨ ∧-pres-≈ ⟩ deMorgan₁ _ _ ⟩
        (x ∧ (y ∨ z)) ∧
        (¬ y ∨ ¬ z)                ≈⟨ sym $ proj₁ ∨-identity _ ⟩
        ⊥ ∨
        ((x ∧ (y ∨ z)) ∧
         (¬ y ∨ ¬ z))              ≈⟨ lem₃ ⟨ ∨-pres-≈ ⟩ refl ⟩
        ((x ∧ (y ∨ z)) ∧ ¬ x) ∨
        ((x ∧ (y ∨ z)) ∧
         (¬ y ∨ ¬ z))              ≈⟨ sym $ proj₁ ∧-∨-distrib _ _ _ ⟩
        (x ∧ (y ∨ z)) ∧
        (¬ x ∨ (¬ y ∨ ¬ z))        ≈⟨  refl ⟨ ∧-pres-≈ ⟩
                                      (refl ⟨ ∨-pres-≈ ⟩ sym (deMorgan₁ _ _)) ⟩
        (x ∧ (y ∨ z)) ∧
        (¬ x ∨ ¬ (y ∧ z))          ≈⟨ refl ⟨ ∧-pres-≈ ⟩ sym (deMorgan₁ _ _) ⟩
          (x ∧ (y ∨ z)) ∧
        ¬ (x ∧ (y ∧ z))            ≈⟨ helper refl lem₁ ⟩
          (x ∧ (y ∨ z)) ∧
        ¬ ((x ∧ y) ∧ (x ∧ z))      ≈⟨ proj₁ ∧-∨-distrib _ _ _ ⟨ ∧-pres-≈ ⟩
                                      refl ⟩
          ((x ∧ y) ∨ (x ∧ z)) ∧
        ¬ ((x ∧ y) ∧ (x ∧ z))      ≈⟨ sym $ ⊕-def _ _ ⟩
        (x ∧ y) ⊕ (x ∧ z)          ∎
        where
        lem₂ = begin
          x ∧ (y ∧ z)  ≈⟨ sym $ ∧-assoc _ _ _ ⟩
          (x ∧ y) ∧ z  ≈⟨ ∧-comm _ _ ⟨ ∧-pres-≈ ⟩ refl ⟩
          (y ∧ x) ∧ z  ≈⟨ ∧-assoc _ _ _ ⟩
          y ∧ (x ∧ z)  ∎

        lem₁ = begin
          x ∧ (y ∧ z)        ≈⟨ sym (∧-idempotent _) ⟨ ∧-pres-≈ ⟩ refl ⟩
          (x ∧ x) ∧ (y ∧ z)  ≈⟨ ∧-assoc _ _ _ ⟩
          x ∧ (x ∧ (y ∧ z))  ≈⟨ refl ⟨ ∧-pres-≈ ⟩ lem₂ ⟩
          x ∧ (y ∧ (x ∧ z))  ≈⟨ sym $ ∧-assoc _ _ _ ⟩
          (x ∧ y) ∧ (x ∧ z)  ∎

        lem₃ = begin
          ⊥                      ≈⟨ sym $ proj₂ ∧-zero _ ⟩
          (y ∨ z) ∧ ⊥            ≈⟨ refl ⟨ ∧-pres-≈ ⟩ sym (proj₂ ∧-complement _) ⟩
          (y ∨ z) ∧ (x ∧ ¬ x)    ≈⟨ sym $ ∧-assoc _ _ _ ⟩
          ((y ∨ z) ∧ x) ∧ ¬ x    ≈⟨ ∧-comm _ _ ⟨ ∧-pres-≈ ⟩ refl  ⟩
          (x ∧ (y ∨ z)) ∧ ¬ x    ∎

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

    lemma₂ : ∀ x y u v →
             (x ∧ y) ∨ (u ∧ v) ≈
             ((x ∨ u) ∧ (y ∨ u)) ∧
             ((x ∨ v) ∧ (y ∨ v))
    lemma₂ x y u v = begin
        (x ∧ y) ∨ (u ∧ v)              ≈⟨ proj₁ ∨-∧-distrib _ _ _ ⟩
        ((x ∧ y) ∨ u) ∧ ((x ∧ y) ∨ v)  ≈⟨ proj₂ ∨-∧-distrib _ _ _
                                            ⟨ ∧-pres-≈ ⟩
                                          proj₂ ∨-∧-distrib _ _ _ ⟩
        ((x ∨ u) ∧ (y ∨ u)) ∧
        ((x ∨ v) ∧ (y ∨ v))            ∎

    ⊕-assoc : Associative _⊕_
    ⊕-assoc x y z = sym $ begin
      x ⊕ (y ⊕ z)                                ≈⟨ refl ⟨ ⊕-pres ⟩ ⊕-def _ _ ⟩
      x ⊕ ((y ∨ z) ∧ ¬ (y ∧ z))                  ≈⟨ ⊕-def _ _ ⟩
        (x ∨ ((y ∨ z) ∧ ¬ (y ∧ z))) ∧
      ¬ (x ∧ ((y ∨ z) ∧ ¬ (y ∧ z)))              ≈⟨ lem₃ ⟨ ∧-pres-≈ ⟩ lem₄ ⟩
      (((x ∨ y) ∨ z) ∧ ((x ∨ ¬ y) ∨ ¬ z)) ∧
      (((¬ x ∨ ¬ y) ∨ z) ∧ ((¬ x ∨ y) ∨ ¬ z))    ≈⟨ ∧-assoc _ _ _ ⟩
      ((x ∨ y) ∨ z) ∧
      (((x ∨ ¬ y) ∨ ¬ z) ∧
       (((¬ x ∨ ¬ y) ∨ z) ∧ ((¬ x ∨ y) ∨ ¬ z)))  ≈⟨ refl ⟨ ∧-pres-≈ ⟩ lem₅ ⟩
      ((x ∨ y) ∨ z) ∧
      (((¬ x ∨ ¬ y) ∨ z) ∧
       (((x ∨ ¬ y) ∨ ¬ z) ∧ ((¬ x ∨ y) ∨ ¬ z)))  ≈⟨ sym $ ∧-assoc _ _ _ ⟩
      (((x ∨ y) ∨ z) ∧ ((¬ x ∨ ¬ y) ∨ z)) ∧
      (((x ∨ ¬ y) ∨ ¬ z) ∧ ((¬ x ∨ y) ∨ ¬ z))    ≈⟨ lem₁ ⟨ ∧-pres-≈ ⟩ lem₂ ⟩
        (((x ∨ y) ∧ ¬ (x ∧ y)) ∨ z) ∧
      ¬ (((x ∨ y) ∧ ¬ (x ∧ y)) ∧ z)              ≈⟨ sym $ ⊕-def _ _ ⟩
      ((x ∨ y) ∧ ¬ (x ∧ y)) ⊕ z                  ≈⟨ sym $ ⊕-def _ _ ⟨ ⊕-pres ⟩ refl ⟩
      (x ⊕ y) ⊕ z                                ∎
      where
      lem₁ = begin
        ((x ∨ y) ∨ z) ∧ ((¬ x ∨ ¬ y) ∨ z)  ≈⟨ sym $ proj₂ ∨-∧-distrib _ _ _ ⟩
        ((x ∨ y) ∧ (¬ x ∨ ¬ y)) ∨ z        ≈⟨ (refl ⟨ ∧-pres-≈ ⟩ sym (deMorgan₁ _ _))
                                                    ⟨ ∨-pres-≈ ⟩ refl ⟩
        ((x ∨ y) ∧ ¬ (x ∧ y)) ∨ z          ∎

      lem₂' = begin
        (x ∨ ¬ y) ∧ (¬ x ∨ y)              ≈⟨ sym $
                                                proj₁ ∧-identity _
                                                  ⟨ ∧-pres-≈ ⟩
                                                proj₂ ∧-identity _ ⟩
        (⊤ ∧ (x ∨ ¬ y)) ∧ ((¬ x ∨ y) ∧ ⊤)  ≈⟨ sym $
                                                (proj₁ ∨-complement _ ⟨ ∧-pres-≈ ⟩ ∨-comm _ _)
                                                  ⟨ ∧-pres-≈ ⟩
                                                (refl ⟨ ∧-pres-≈ ⟩ proj₁ ∨-complement _) ⟩
        ((¬ x ∨ x) ∧ (¬ y ∨ x)) ∧
        ((¬ x ∨ y) ∧ (¬ y ∨ y))            ≈⟨ sym $ lemma₂ _ _ _ _ ⟩
        (¬ x ∧ ¬ y) ∨ (x ∧ y)              ≈⟨ sym $ deMorgan₂ _ _ ⟨ ∨-pres-≈ ⟩ ¬-involutive _ ⟩
        ¬ (x ∨ y) ∨ ¬ ¬ (x ∧ y)            ≈⟨ sym (deMorgan₁ _ _) ⟩
        ¬ ((x ∨ y) ∧ ¬ (x ∧ y))            ∎

      lem₂ = begin
        ((x ∨ ¬ y) ∨ ¬ z) ∧ ((¬ x ∨ y) ∨ ¬ z)  ≈⟨ sym $ proj₂ ∨-∧-distrib _ _ _ ⟩
        ((x ∨ ¬ y) ∧ (¬ x ∨ y)) ∨ ¬ z          ≈⟨ lem₂' ⟨ ∨-pres-≈ ⟩ refl ⟩
        ¬ ((x ∨ y) ∧ ¬ (x ∧ y)) ∨ ¬ z          ≈⟨ sym $ deMorgan₁ _ _ ⟩
        ¬ (((x ∨ y) ∧ ¬ (x ∧ y)) ∧ z)          ∎

      lem₃ = begin
        x ∨ ((y ∨ z) ∧ ¬ (y ∧ z))          ≈⟨ refl ⟨ ∨-pres-≈ ⟩
                                                (refl ⟨ ∧-pres-≈ ⟩ deMorgan₁ _ _) ⟩
        x ∨ ((y ∨ z) ∧ (¬ y ∨ ¬ z))        ≈⟨ proj₁ ∨-∧-distrib _ _ _ ⟩
        (x ∨ (y ∨ z)) ∧ (x ∨ (¬ y ∨ ¬ z))  ≈⟨ sym (∨-assoc _ _ _) ⟨ ∧-pres-≈ ⟩
                                              sym (∨-assoc _ _ _) ⟩
        ((x ∨ y) ∨ z) ∧ ((x ∨ ¬ y) ∨ ¬ z)  ∎

      lem₄' = begin
        ¬ ((y ∨ z) ∧ ¬ (y ∧ z))    ≈⟨ deMorgan₁ _ _ ⟩
        ¬ (y ∨ z) ∨ ¬ ¬ (y ∧ z)    ≈⟨ deMorgan₂ _ _ ⟨ ∨-pres-≈ ⟩ ¬-involutive _ ⟩
        (¬ y ∧ ¬ z) ∨ (y ∧ z)      ≈⟨ lemma₂ _ _ _ _ ⟩
        ((¬ y ∨ y) ∧ (¬ z ∨ y)) ∧
        ((¬ y ∨ z) ∧ (¬ z ∨ z))    ≈⟨ (proj₁ ∨-complement _ ⟨ ∧-pres-≈ ⟩ ∨-comm _ _)
                                        ⟨ ∧-pres-≈ ⟩
                                      (refl ⟨ ∧-pres-≈ ⟩ proj₁ ∨-complement _) ⟩
        (⊤ ∧ (y ∨ ¬ z)) ∧
        ((¬ y ∨ z) ∧ ⊤)            ≈⟨ proj₁ ∧-identity _ ⟨ ∧-pres-≈ ⟩
                                      proj₂ ∧-identity _ ⟩
        (y ∨ ¬ z) ∧ (¬ y ∨ z)      ∎

      lem₄ = begin
        ¬ (x ∧ ((y ∨ z) ∧ ¬ (y ∧ z)))  ≈⟨ deMorgan₁ _ _ ⟩
        ¬ x ∨ ¬ ((y ∨ z) ∧ ¬ (y ∧ z))  ≈⟨ refl ⟨ ∨-pres-≈ ⟩ lem₄' ⟩
        ¬ x ∨ ((y ∨ ¬ z) ∧ (¬ y ∨ z))  ≈⟨ proj₁ ∨-∧-distrib _ _ _ ⟩
        (¬ x ∨ (y     ∨ ¬ z)) ∧
        (¬ x ∨ (¬ y ∨ z))              ≈⟨ sym (∨-assoc _ _ _) ⟨ ∧-pres-≈ ⟩
                                          sym (∨-assoc _ _ _) ⟩
        ((¬ x ∨ y)     ∨ ¬ z) ∧
        ((¬ x ∨ ¬ y) ∨ z)              ≈⟨ ∧-comm _ _ ⟩
        ((¬ x ∨ ¬ y) ∨ z) ∧
        ((¬ x ∨ y)     ∨ ¬ z)          ∎

      lem₅ = begin
        ((x ∨ ¬ y) ∨ ¬ z) ∧
        (((¬ x ∨ ¬ y) ∨ z) ∧ ((¬ x ∨ y) ∨ ¬ z))    ≈⟨ sym $ ∧-assoc _ _ _ ⟩
        (((x ∨ ¬ y) ∨ ¬ z) ∧ ((¬ x ∨ ¬ y) ∨ z)) ∧
        ((¬ x ∨ y) ∨ ¬ z)                          ≈⟨ ∧-comm _ _ ⟨ ∧-pres-≈ ⟩ refl ⟩
        (((¬ x ∨ ¬ y) ∨ z) ∧ ((x ∨ ¬ y) ∨ ¬ z)) ∧
        ((¬ x ∨ y) ∨ ¬ z)                          ≈⟨ ∧-assoc _ _ _ ⟩
        ((¬ x ∨ ¬ y) ∨ z) ∧
        (((x ∨ ¬ y) ∨ ¬ z) ∧ ((¬ x ∨ y) ∨ ¬ z))    ∎

  isCommutativeRing : IsCommutativeRing _≈_ _⊕_ _∧_ id ⊥ ⊤
  isCommutativeRing = record
    { isRing = record
      { +-isAbelianGroup = record
        { isGroup = record
          { isMonoid = record
            { isSemigroup = record
              { isEquivalence = isEquivalence
              ; assoc         = ⊕-assoc
              ; ∙-pres-≈      = ⊕-pres
              }
            ; identity = ⊕-identity
            }
          ; inverse   = ⊕-inverse
          ; ⁻¹-pres-≈ = id
          }
        ; comm = ⊕-comm
        }
      ; *-isMonoid = record
        { isSemigroup = record
          { isEquivalence = isEquivalence
          ; assoc         = ∧-assoc
          ; ∙-pres-≈      = ∧-pres-≈
          }
        ; identity = ∧-identity
        }
      ; distrib = distrib-∧-⊕
      }
    ; *-comm = ∧-comm
    }

  commutativeRing : CommutativeRing
  commutativeRing = record
    { _+_               = _⊕_
    ; _*_               = _∧_
    ; -_                = id
    ; 0#                = ⊥
    ; 1#                = ⊤
    ; isCommutativeRing = isCommutativeRing
    }

infixl 6 _⊕_

_⊕_ : Op₂ carrier
x ⊕ y = (x ∨ y) ∧ ¬ (x ∧ y)

module DefaultXorRing = XorRing _⊕_ (λ _ _ → refl)


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommMonoid.Properties where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Equiv.HalfAdjoint
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Transport
open import Cubical.Foundations.SIP

open import Cubical.Data.Sigma

open import Cubical.Structures.Axioms
open import Cubical.Structures.Auto
open import Cubical.Structures.Macro
open import Cubical.Algebra.Semigroup
open import Cubical.Algebra.Monoid
open import Cubical.Algebra.CommMonoid.Base

private
  variable
    ℓ : Level

module CommMonoidTheory (M' : CommMonoid ℓ) where
 open CommMonoidStr (snd M')
 private M = ⟨ M' ⟩

 commAssocl : (x y z : M) → x · (y · z) ≡ y · (x · z)
 commAssocl x y z = assoc x y z ∙∙ cong (_· z) (comm x y) ∙∙ sym (assoc y x z)

 commAssocr : (x y z : M) → x · y · z ≡ x · z · y
 commAssocr x y z = sym (assoc x y z) ∙∙ cong (x ·_) (comm y z) ∙∙ assoc x z y

 commAssocr2 : (x y z : M) → x · y · z ≡ z · y · x
 commAssocr2 x y z = commAssocr _ _ _ ∙∙ cong (_· y) (comm _ _) ∙∙ commAssocr _ _ _

 commAssocSwap : (x y z w : M) → (x · y) · (z · w) ≡ (x · z) · (y · w)
 commAssocSwap x y z w = assoc (x · y) z w ∙∙ cong (_· w) (commAssocr x y z)
                                               ∙∙ sym (assoc (x · z) y w)


module Nat.Binary where

open import Data.Bool hiding (_≤_; _<_; _<?_)
open import Data.Empty
open import Data.List
open import Data.List.Properties
open import Data.Maybe
open import Data.Product
open import Data.Sum
open import Function
open import Nat.Class
open import Nat.Unary
  using (ℕ₁)
  renaming
    (zero to zero₁
    ; succ to succ₁
    ; _+_ to _+₁_
    ; rec to rec₁
    ; ind to ind₁
    ; rec-succ to rec₁-succ
    )
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open ≡-Reasoning

data ℕ₂ : Set where
  zero : ℕ₂
  pos : List Bool → ℕ₂

ind :
  (P : ℕ₂ → Set) →
  P zero →
  P (pos []) →
  (∀ {b bs} → P (pos bs) → P (pos (b ∷ bs))) →
  ∀ n →
  P n
ind P z u d zero = z
ind P z u d (pos []) = u
ind P z u d (pos (b ∷ bs)) = d (ind P z u d (pos bs))

succ⁺ : List Bool → List Bool
succ⁺ [] = false ∷ []
succ⁺ (false ∷ bs) = true ∷ bs
succ⁺ (true ∷ bs) = false ∷ succ⁺ bs

succ : ℕ₂ → ℕ₂
succ zero = pos []
succ (pos bs) = pos (succ⁺ bs)

-- TODO: try to build a rec implementation that computes the
-- predecessor to do unary recursion on binary numbers!  However, the
-- predecessor function must return a proof showing that its result is
-- less than its argument, otherwise Agda doesn't know if the
-- recursion will terminate

digitsFold : {B : Set} → (B → Bool → Bool → B) → B → ℕ₂ → ℕ₂ → B
digitsFold f z zero zero =
  f z false false
digitsFold f z zero (pos []) =
  f z false true
digitsFold f z zero (pos (b₂ ∷ bs₂)) =
  digitsFold f (f z false b₂) zero (pos bs₂)
digitsFold f z (pos []) zero =
  f z true false
digitsFold f z (pos []) (pos []) =
  f z true true
digitsFold f z (pos []) (pos (b₂ ∷ bs₂)) =
  digitsFold f (f z true b₂) zero (pos bs₂)
digitsFold f z (pos (b₁ ∷ bs₁)) zero =
  digitsFold f (f z b₁ false) (pos bs₁) zero
digitsFold f z (pos (b₁ ∷ bs₁)) (pos []) =
  digitsFold f (f z b₁ true) (pos bs₁) zero
digitsFold f z (pos (b₁ ∷ bs₁)) (pos (b₂ ∷ bs₂)) =
  digitsFold f (f z b₁ b₂) (pos bs₁) (pos bs₂)

adder : List Bool × Bool → Bool → Bool → List Bool × Bool
adder (bs , c) a b = (c xor (a xor b)) ∷ bs , (c ∧ (a xor b)) ∨ (a ∧ b)

revNormalize : List Bool → ℕ₂ → ℕ₂
revNormalize [] acc = acc
revNormalize (b ∷ rxs) (pos acc) = revNormalize rxs (pos (b ∷ acc))
revNormalize (false ∷ rxs) zero = revNormalize rxs zero
revNormalize (true ∷ rxs) zero = revNormalize rxs (pos [])

_+_ : ℕ₂ → ℕ₂ → ℕ₂
m + n =
  let rs , c = digitsFold adder ([] , false) m n
   in revNormalize (c ∷ rs) zero

_ : zero + zero ≡ zero
_ =
  begin
    zero + zero
  ≡⟨⟩
    let rs , c = digitsFold adder ([] , false) zero zero
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs , c = adder ([] , false) false false
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs = (false xor (false xor false)) ∷ []
        c = (false ∧ (false xor false)) ∨ (false ∧ false)
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs = false ∷ []
        c = false
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
     revNormalize (false ∷ false ∷ []) zero
  ≡⟨⟩
     revNormalize (false ∷ []) zero
  ≡⟨⟩
     revNormalize [] zero
  ≡⟨⟩
    zero
  ∎

_ : zero + pos [] ≡ pos []
_ =
  begin
    zero + pos []
  ≡⟨⟩
    let rs , c = digitsFold adder ([] , false) zero (pos [])
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs , c = adder ([] , false) false true
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs = (false xor (false xor true)) ∷ []
        c = (false ∧ (false xor true)) ∨ (false ∧ true)
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs = true ∷ []
        c = false
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
     revNormalize (false ∷ true ∷ []) zero
  ≡⟨⟩
     revNormalize (true ∷ []) zero
  ≡⟨⟩
     revNormalize [] (pos [])
  ≡⟨⟩
    pos []
  ∎

_ : zero + pos (false ∷ []) ≡ pos (false ∷ [])
_ =
  begin
    zero + pos (false ∷ [])
  ≡⟨⟩
    let rs , c = digitsFold adder ([] , false) zero (pos (false ∷ []))
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs , c = digitsFold adder (adder ([] , false) false false) zero (pos [])
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let bs′ = (false xor (false xor false)) ∷ []
        c′ = (false ∧ (false xor false)) ∨ (false ∧ false)
        rs , c = digitsFold adder (bs′ , c′) zero (pos [])
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs , c = digitsFold adder (false ∷ [] , false) zero (pos [])
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs , c = adder (false ∷ [] , false) false true
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs = (false xor (false xor true)) ∷ false ∷ []
        c = (false ∧ (false xor true)) ∨ (false ∧ true)
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
     revNormalize (false ∷ true ∷ false ∷ []) zero
  ≡⟨⟩
     revNormalize (true ∷ false ∷ []) zero
  ≡⟨⟩
     revNormalize (false ∷ []) (pos [])
  ≡⟨⟩
     revNormalize [] (pos (false ∷ []))
  ≡⟨⟩
    pos (false ∷ [])
  ∎

-- TODO Expand more zero + n examples
reverse-∷ :
  {A : Set} {x : A} {xs : List A} →
  reverse (x ∷ xs) ≡ reverse xs ++ (x ∷ [])
reverse-∷ {A} {x} {xs} =
  begin
    reverse (x ∷ xs)
  ≡⟨⟩
    foldl (flip _∷_) [] (x ∷ xs)
  ≡⟨⟩
    foldl (flip _∷_) (flip _∷_ [] x) xs
  ≡⟨⟩
    foldl (flip _∷_) (x ∷ []) xs
  ≡⟨ {!!} ⟩
    foldl (flip _∷_) [] xs ++ (x ∷ [])
  ≡⟨⟩
    reverse xs ++ (x ∷ [])
  ∎

reverse-∷-++ :
  {A : Set} {x : A} {xs ys : List A} →
  reverse (x ∷ xs) ++ ys ≡ reverse xs ++ x ∷ ys
reverse-∷-++ {A} {x} {[]} {ys} = refl
reverse-∷-++ {A} {x} {x′ ∷ xs′} {ys} =
  begin
    reverse (x ∷ x′ ∷ xs′) ++ ys
  ≡⟨ {!!} ⟩
    reverse (x′ ∷ xs′) ++ x ∷ ys
  ∎

adder-ca-false : ∀ {b bs} → adder (bs , false) false b ≡ (b ∷ bs , false)
adder-ca-false = refl

digitsFold-adder-zeroˡ-carry :
  ∀ {bs n} → proj₂ (digitsFold adder (bs , false) zero n) ≡ false
digitsFold-adder-zeroˡ-carry {n = zero} = refl
digitsFold-adder-zeroˡ-carry {n = pos []} = refl
digitsFold-adder-zeroˡ-carry {n = pos (b ∷ bs₂)} =
  digitsFold-adder-zeroˡ-carry {n = pos bs₂}

digitsFold-adder-zeroˡ-sum :
  ∀ {rs bs} →
  proj₁ (digitsFold adder (rs , false) zero (pos bs)) ≡ true ∷ reverse bs ++ rs
digitsFold-adder-zeroˡ-sum {rs} {[]} = refl
digitsFold-adder-zeroˡ-sum {rs} {b ∷ bs} =
  begin
    proj₁ (digitsFold adder (rs , false) zero (pos (b ∷ bs)))
  ≡⟨⟩
    proj₁ (digitsFold adder (b ∷ rs , false) zero (pos bs))
  ≡⟨ digitsFold-adder-zeroˡ-sum {b ∷ rs} {bs} ⟩
    true ∷ reverse bs ++ b ∷ rs
  ≡⟨ {!!} ⟩
    true ∷ reverse (b ∷ bs) ++ rs
  ∎

+-identityˡ : ∀ {n} → zero + n ≡ n
+-identityˡ {zero} = refl
+-identityˡ {pos []} = refl
+-identityˡ {pos (b ∷ bs)} =
  begin
    zero + pos (b ∷ bs)
  ≡⟨⟩
    let rs , c = digitsFold adder ([] , false) zero (pos (b ∷ bs))
     in revNormalize (c ∷ rs) zero
  ≡⟨⟩
    let rs , c = digitsFold adder (adder ([] , false) false b) zero (pos bs)
     in revNormalize (c ∷ rs) zero
  ≡⟨ {!!} ⟩
    pos (b ∷ bs)
  ∎

fromℕ₁ : ℕ₁ → ℕ₂
fromℕ₁ = rec₁ zero succ

fromℕ₁-succ : ∀ n → fromℕ₁ (succ₁ n) ≡ succ (fromℕ₁ n)
fromℕ₁-succ zero₁ = refl
fromℕ₁-succ (succ₁ n) =
  begin
    fromℕ₁ (succ₁ (succ₁ n))
  ≡⟨⟩
    rec₁ zero succ (succ₁ (succ₁ n))
  ≡⟨⟩
    rec₁ (succ zero) succ (succ₁ n)
  ≡⟨ rec₁-succ zero succ (succ₁ n) ⟩
    succ (rec₁ zero succ (succ₁ n))
  ≡⟨⟩
    succ (fromℕ₁ (succ₁ n))
  ∎

-- TODO Prove this using algebraic laws!
fromℕ₁-comm-+ : ∀ {m n} → fromℕ₁ (m +₁ n) ≡ fromℕ₁ m + fromℕ₁ n
fromℕ₁-comm-+ {zero₁} {zero₁} = refl
fromℕ₁-comm-+ {zero₁} {succ₁ n} =
  begin
    fromℕ₁ (zero₁ +₁ succ₁ n)
  ≡⟨⟩
    fromℕ₁ (succ₁ n)
  ≡⟨ fromℕ₁-succ n ⟩
    succ (fromℕ₁ n)
  ≡⟨ {!!} ⟩
    zero + succ (fromℕ₁ n)
  ≡⟨ cong (zero +_) (sym (fromℕ₁-succ n)) ⟩
    fromℕ₁ zero₁ + fromℕ₁ (succ₁ n)
  ∎
fromℕ₁-comm-+ {succ₁ m} {n} = {!!}

toℕ₁⁺ : List Bool → ℕ₁
toℕ₁⁺ [] = succ₁ zero₁
toℕ₁⁺ (b ∷ bs) = (if b then succ₁ else id) (toℕ₁⁺ bs +₁ toℕ₁⁺ bs)

toℕ₁ : ℕ₂ → ℕ₁
toℕ₁ zero = zero₁
toℕ₁ (pos bs) = toℕ₁⁺ bs

toℕ₁⁺-succ : ∀ bs → toℕ₁⁺ (succ⁺ bs) ≡ succ₁ (toℕ₁⁺ bs)
toℕ₁⁺-succ [] = refl
toℕ₁⁺-succ (false ∷ bs) = refl
toℕ₁⁺-succ (true ∷ bs) =
  begin
    toℕ₁⁺ (succ⁺ (true ∷ bs))
  ≡⟨⟩
    toℕ₁⁺ (false ∷ succ⁺ bs)
  ≡⟨⟩
    toℕ₁⁺ (succ⁺ bs) +₁ toℕ₁⁺ (succ⁺ bs)
  ≡⟨ cong (λ x → x +₁ toℕ₁⁺ (succ⁺ bs)) (toℕ₁⁺-succ bs) ⟩
    succ₁ (toℕ₁⁺ bs) +₁ toℕ₁⁺ (succ⁺ bs)
  ≡⟨ cong (λ x → succ₁ (toℕ₁⁺ bs) +₁ x) (toℕ₁⁺-succ bs) ⟩
    succ₁ (toℕ₁⁺ bs) +₁ succ₁ (toℕ₁⁺ bs)
  ≡⟨ +-succˡ {m = toℕ₁⁺ bs}  ⟩
    succ₁ (toℕ₁⁺ bs +₁ succ₁ (toℕ₁⁺ bs))
  ≡⟨ cong succ₁ (+-succʳ {m = toℕ₁⁺ bs}) ⟩
    succ₁ (succ₁ (toℕ₁⁺ bs +₁ toℕ₁⁺ bs))
  ≡⟨⟩
    succ₁ (toℕ₁⁺ (true ∷ bs))
  ∎

toℕ₁-succ : ∀ n → toℕ₁ (succ n) ≡ succ₁ (toℕ₁ n)
toℕ₁-succ zero = refl
toℕ₁-succ (pos bs) = toℕ₁⁺-succ bs

fromToℕ₁ : ∀ n → toℕ₁ (fromℕ₁ n) ≡ n
fromToℕ₁ zero₁ = refl
fromToℕ₁ (succ₁ n) =
  begin
    toℕ₁ (fromℕ₁ (succ₁ n))
  ≡⟨ cong toℕ₁ (fromℕ₁-succ n) ⟩
    toℕ₁ (succ (fromℕ₁ n))
  ≡⟨ toℕ₁-succ (fromℕ₁ n) ⟩
    succ₁ (toℕ₁ (fromℕ₁ n))
  ≡⟨ cong succ₁ (fromToℕ₁ n) ⟩
    succ₁ n
  ∎

toFromℕ₁⁺ : ∀ bs → fromℕ₁ (toℕ₁⁺ bs) ≡ pos bs
toFromℕ₁⁺ [] = refl
toFromℕ₁⁺ (false ∷ bs) =
  begin
    fromℕ₁ (toℕ₁⁺ (false ∷ bs))
  ≡⟨⟩
    fromℕ₁ (toℕ₁⁺ bs +₁ toℕ₁⁺ bs)
    -- TODO Fill in these holes!
  ≡⟨ {!!} ⟩
    fromℕ₁ (toℕ₁⁺ bs) + fromℕ₁ (toℕ₁⁺ bs)
  ≡⟨ cong (λ x → x + fromℕ₁ (toℕ₁⁺ bs)) (toFromℕ₁⁺ bs) ⟩
    pos bs + fromℕ₁ (toℕ₁⁺ bs)
  ≡⟨ cong (λ x → pos bs + x) (toFromℕ₁⁺ bs) ⟩
    pos bs + pos bs
  ≡⟨ {!!} ⟩
    pos (false ∷ bs)
  ∎
toFromℕ₁⁺ (true ∷ bs) = {!!}

toFromℕ₁ : ∀ n → fromℕ₁ (toℕ₁ n) ≡ n
toFromℕ₁ zero = refl
toFromℕ₁ (pos bs) = toFromℕ₁⁺ bs

instance
  Nat-ℕ₂ : Nat ℕ₂
  Nat-ℕ₂ = record
    { zero = zero
    ; succ = succ
    ; ind = {!!}
    ; _+_ = _+_
    ; +-zero = +-identityˡ
    ; +-succ = {!!}
    }


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

open import LogicalFormulae
open import Groups.Definition
open import Rings.Definition
open import Setoids.Setoids

open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)

module Fields.Fields where

record Field {m n : _} {A : Set m} {S : Setoid {m} {n} A} {_+_ : A → A → A} {_*_ : A → A → A} (R : Ring S _+_ _*_) : Set (lsuc m ⊔ n) where
  open Ring R
  open Group additiveGroup
  open Setoid S
  field
    allInvertible : (a : A) → ((a ∼ Group.0G (Ring.additiveGroup R)) → False) → Sg A (λ t → t * a ∼ 1R)
    nontrivial : (0R ∼ 1R) → False
  0F : A
  0F = Ring.0R R

record Field' {m n : _} : Set (lsuc m ⊔ lsuc n) where
  field
    A : Set m
    S : Setoid {m} {n} A
    _+_ : A → A → A
    _*_ : A → A → A
    R : Ring S _+_ _*_
    isField : Field R

encapsulateField : {m n : _} {A : Set m} {S : Setoid {m} {n} A} {_+_ : A → A → A} {_*_ : A → A → A} {R : Ring S _+_ _*_} (F : Field R) → Field'
encapsulateField {A = A} {S = S} {_+_} {_*_} {R} F = record { A = A ; S = S ; _+_ = _+_ ; _*_ = _*_ ; R = R ; isField = F }

{-
record OrderedField {n} {A : Set n} {R : Ring A} (F : Field R) : Set (lsuc n) where
  open Field F
  field
    ord : TotalOrder A
  open TotalOrder ord
  open Ring R
  field
    productPos : {a b : A} → (0R < a) → (0R < b) → (0R < (a * b))
    orderRespectsAddition : {a b c : A} → (a < b) → (a + c) < (b + c)

-}


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

module Fragment.Examples.Semigroup.Arith.Reasoning where

open import Fragment.Examples.Semigroup.Arith.Base

+-direct : ∀ {m n} → (m + 2) + (3 + n) ≡ m + (5 + n)
+-direct {m} {n} = begin
    (m + 2) + (3 + n)
  ≡⟨ fragment SemigroupFrex +-semigroup ⟩
    m + (5 + n)
  ∎

open import Data.Nat.Properties using (*-distribˡ-+)

+-inner : ∀ {m n k} → k * (m + 2) + k * (3 + n) ≡ k * (m + 5 + n)
+-inner {m} {n} {k} = begin
    k * (m + 2) + k * (3 + n)
  ≡⟨ sym (*-distribˡ-+ k (m + 2) (3 + n)) ⟩
    k * ((m + 2) + (3 + n))
  ≡⟨ cong (k *_) (fragment SemigroupFrex +-semigroup) ⟩
    k * (m + 5 + n)
  ∎


module Morphism where

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

open import Prelude
open import Structure

module _ {A B : Set} (σᴬ : Struct A) (σᴮ : Struct B) where
  open Struct σᴬ renaming (ℜ to ℜᴬ ; 𝔉 to 𝔉ᴬ)
  open Struct σᴮ renaming (ℜ to ℜᴮ ; 𝔉 to 𝔉ᴮ)

  record Hom : Set where
    field
      f : A → B
      ℜ-f : {n : ℕ} → (aⁿ : A ^ n) → ℜᴬ aⁿ → ℜᴮ (𝔉ⁿ f n aⁿ)
      𝔉-f : {n : ℕ} → (aⁿ : A ^ n) → f (𝔉ᴬ aⁿ) ≡ 𝔉ᴮ (𝔉ⁿ f n aⁿ)

  record Embed : Set where
    field
      h : Hom
    open Hom h
    field
      f-inj : ∀ {x y} → f x ≡ f y → x ≡ y
      ℜ-f' : {n : ℕ} → (aⁿ : A ^ n) → ℜᴮ (𝔉ⁿ f n aⁿ) → ℜᴬ aⁿ

  record Iso : Set where
    field
      e : Embed
    open Embed e
    open Hom h
    field
      g : B → A
      g-inj : ∀ {x y} → g x ≡ g y → x ≡ y
      f-g : ∀ {x} → f (g x) ≡ x
      g-f : ∀ {x} → g (f x) ≡ x

module _ {A : Set} (σᴬ : Struct A) where
  Endo : Set
  Endo = Hom σᴬ σᴬ

  Auto : Set
  Auto = Iso σᴬ σᴬ

module Examples {A B : Set} (σᴬ : Struct A) (σᴮ : Struct B) (h : Hom σᴬ σᴮ) where
  open Struct σᴬ renaming (ℜ to ℜᴬ ; 𝔉 to 𝔉ᴬ)
  open Struct σᴮ renaming (ℜ to ℜᴮ ; 𝔉 to 𝔉ᴮ)
  open Hom h

  hrel : A → A → Set
  hrel x y = f x ≡ f y

  open import Relation.Binary

  hrel-equiv : IsEquivalence hrel
  hrel-equiv = record { refl = refl ; sym = sym ; trans = trans }

  module _ {x y : A} where
    hrel-cong : hrel x y → hrel (𝔉ᴬ (x , tt)) (𝔉ᴬ (y , tt))
    hrel-cong p = trans (𝔉-f (x , tt)) (sym (trans (𝔉-f (y , tt)) (sym (cong (λ b → 𝔉ᴮ (b , tt)) p))))


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

open import Setoids.Setoids
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Groups.Definition

module Groups.Abelian.Definition where

record AbelianGroup {a} {b} {A : Set a} {S : Setoid {a} {b} A} {_·_ : A → A → A} (G : Group S _·_) : Set (lsuc a ⊔ b) where
  open Setoid S
  field
    commutative : {a b : A} → (a · b) ∼ (b · a)


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

open import PathOperations
open import Types

p·p⁻¹ : {a b : A} (p : a ≡ b) → p · p ⁻¹ ≡ refl
p·p⁻¹ = J (λ _ _ p → p · p ⁻¹ ≡ refl) (λ _ → refl) _ _

p⁻¹·p : {a b : A} (p : a ≡ b) → p ⁻¹ · p ≡ refl
p⁻¹·p = J (λ _ _ p → p ⁻¹ · p ≡ refl) (λ _ → refl) _ _

p·id : {a b : A} (p : a ≡ b) → p · refl ≡ p
p·id = J (λ _ _ p → p · refl ≡ p) (λ _ → refl) _ _

id·p : {a b : A} (p : a ≡ b) → refl · p ≡ p
id·p _ = refl

p·q·r : {a b c d : A} (p : a ≡ b) (q : b ≡ c) (r : c ≡ d) →
  p · (q · r) ≡ (p · q) · r
p·q·r {c = c} {d = d} = J
  (λ _ b p → (q : b ≡ c) (r : c ≡ d) → p · q · r ≡ (p · q) · r)
  (λ b → J
    (λ _ c q → (r : c ≡ d) → refl · q · r ≡ (refl · q) · r)
    (λ _ _ → refl) _ _)
  _ _


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

syntax UTLC | Λ

type
  * : 0-ary

term
  app  : *  *  ->  * | _$_ l20
  lam  : *.*  ->  * | ƛ_ r10

theory
  (ƛβ) b : *.*  a : * |> app (lam (x.b[x]), a) = b[a]
  (ƛη) f : *          |> lam (x.app (f, x))    = f
  (lβ) b : *.*  a : * |> letd (a, x. b) = b[a]
-}

module UTLC.Signature where

open import SOAS.Context

open import SOAS.Common

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

-- Operator symbols
data Λₒ : Set where
  appₒ lamₒ : Λₒ

-- Term signature
Λ:Sig : Signature Λₒ
Λ:Sig = sig λ
  {  appₒ   → (⊢₀ *) , (⊢₀ *) ⟼₂ *
  ;  lamₒ   → (* ⊢₁ *) ⟼₁ *
  }

open Signature Λ:Sig public


{-# OPTIONS --universe-polymorphism #-}
module NoBlockOnLevel where

open import Common.Level

infixr 0 _,_
record ∃ {a b} {A : Set a} (B : A → Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    proj₁ : A
    proj₂ : B proj₁

open ∃

BSetoid : ∀ c → Set (lsuc c)
BSetoid c = Set c

infixr 0 _⟶_

postulate
  _⟶_ : ∀ {f t} → BSetoid f → BSetoid t → Set (f ⊔ t)
  →-to-⟶ : ∀ {a b} {A : Set a} {B : BSetoid b} →
           (A → B) → A ⟶ B

postulate
  a b p : Level
  A : Set a
  B : Set b
  P : A → B → Set p

-- This will leave unsolved metas if we give up on an unsolved level constraint
-- when checking argument spines. Since we can't match on levels it's safe to keep
-- checking later constraints even if they depend on the unsolved levels.
f : (∃ λ x → ∃ λ y → P x y) ⟶ (∃ λ y → ∃ λ x → P x y)
f = →-to-⟶ λ p → proj₁ (proj₂ p) , proj₁ p , proj₂ (proj₂ p)


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

module TypeTheory.HoTT.Relation.Nullary.Negation.Properties where

-- agda-stdlib
open import Axiom.Extensionality.Propositional
open import Level renaming (zero to lzero)
open import Relation.Nullary

-- agda-misc
open import TypeTheory.HoTT.Base
open import TypeTheory.HoTT.Data.Empty.Properties
open import TypeTheory.HoTT.Function.Properties

private
  variable
    a b : Level
    A : Set a
    B : Set b

module _ {a} {A : Set a} where
  isProp-¬ : Extensionality a lzero → isProp (¬ A)
  isProp-¬ ext = isProp-→ ext isProp-⊥


-- Operations on natural number

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

-- agad-stdlib
open import Relation.Binary.PropositionalEquality

module TypeTheory.Nat.Operations
  {a}
  -- | Type of natural number
  (N : Set a)
  -- | Zero
  (zero : N)
  -- | Successor function
  (suc : N → N)
  -- | Induction principle
  (ind : ∀ {p} (P : N → Set p) → P zero → (∀ k → P k → P (suc k)) → ∀ n → P n)
  -- | Computation rule for `zero`
  (ind-base : ∀ {p} (P : N → Set p) P-base P-step →
              ind P P-base P-step zero ≡ P-base)
  -- | Computation rule for `suc`
  (ind-step : ∀ {p} (P : N → Set p) P-base P-step n →
              ind P P-base P-step (suc n) ≡ P-step n (ind P P-base P-step n))
  where

-- agda-stdlib
open import Level renaming (zero to lzero; suc to lsuc)
open import Data.Empty
open import Data.Bool using (Bool; true; false)
open import Data.Product
open import Function.Base

private
  variable
    A : Set a

-- recursion
rec : ∀ {l} {A : Set l} → A → (A → A) → N → A
rec {l} {A} z s n = ind (λ _ → A) z (λ k x → s x) n

caseNat : ∀ {l} {A : Set l} (z s : A) → N → A
caseNat {A = A} z s n = ind (λ x → A) z (λ k x → s) n

-- predcessor
pred : N → N
pred n = ind (λ _ → N) zero (λ k x → k) n

-- Arithematic
infixl 6 _+_ _∸_
infixl 7 _*_
infix 4 _≤_ _≰_ _≥_ _≱_ _<_ _≮_ _>_ _≯_
infix 5 _≤ᵇ_

-- Addition
_+_ : N → N → N
m + n = rec n suc m

-- Multiplication
_*_ : N → N → N
m * n = rec zero (λ k → n + k) m

-- Monus
_∸_ : N → N → N
m ∸ n = rec m pred n

-- Order
_≤_ : N → N → Set a
m ≤ n = ∃ λ o → m + o ≡ n

_≰_ : N → N → Set a
m ≰ n = m ≤ n → ⊥

_≥_ : N → N → Set a
m ≥ n = n ≤ m

_≱_ : N → N → Set a
m ≱ n = m ≥ n → ⊥

_<_ : N → N → Set a
m < n = suc m ≤ n

_≮_ : N → N → Set a
m ≮ n = m < n → ⊥

_>_ : N → N → Set a
m > n = n < m

_≯_ : N → N → Set a
m ≯ n = m > n → ⊥

-- Order (Bool)
_≤ᵇ_ : N → N → Bool
m ≤ᵇ n = caseNat true false (m ∸ n)

-- induction
ind2 :
  ∀ {p} (P : N → N → Set p) →
  P zero zero →
  (∀ m n → P m n → P m (suc n)) →
  (∀ m n → P m n → P (suc m) n) →
  ∀ m n → P m n
ind2 P Pzz Pmn→Pms Pmn→Psn m n = ind
  (λ o → P o n)
  (ind (λ p → P zero p) Pzz (λ k Pzk → Pmn→Pms zero k Pzk) n)
  (λ k Pkn → ind (λ r → P (suc k) r) (P[k,0] (suc k))
    (λ k₁ P[1+k,k₁] → Pmn→Pms (suc k) k₁ P[1+k,k₁]) n)
  m
  where
  P[k,0] : ∀ k → P k zero
  P[k,0] = λ k → ind (λ x → P x zero) Pzz (λ k₂ → Pmn→Psn k₂ zero) k

-- Constant
one : N
one = suc zero


module Lambda where

open import Prelude

-- Simply typed λ-calculus

infixr 70 _⟶_

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

Ctx : Set
Ctx = List Type

infixl 80 _•_
infix  20  ƛ_

data Term : Ctx -> Type -> Set where
  vz  : forall {Γ τ  } -> Term (Γ , τ) τ
  wk  : forall {Γ σ τ} -> Term Γ τ -> Term (Γ , σ) τ
  _•_ : forall {Γ σ τ} -> Term Γ (σ ⟶ τ) -> Term Γ σ -> Term Γ τ
  ƛ_  : forall {Γ σ τ} -> Term (Γ , σ) τ -> Term Γ (σ ⟶ τ)

Terms : Ctx -> Ctx -> Set
Terms Γ Δ = All (Term Γ) Δ

infixl 60 _◄_
infixr 70 _⇒_

_⇒_ : Ctx -> Type -> Type
ε       ⇒ τ = τ
(Δ , σ) ⇒ τ = Δ ⇒ σ ⟶ τ

infixl 80 _•ˢ_

_•ˢ_ : {Γ Δ : Ctx}{τ : Type} -> Term Γ (Δ ⇒ τ) -> Terms Γ Δ -> Term Γ τ
t •ˢ ∅        = t
t •ˢ (us ◄ u) = t •ˢ us • u

Var : Ctx -> Type -> Set
Var Γ τ = τ ∈ Γ

var : forall {Γ τ} -> Var Γ τ -> Term Γ τ
var hd     = vz
var (tl x) = wk (var x)

vzero : forall {Γ τ} -> Var (Γ , τ) τ
vzero = hd

vsuc : forall {Γ σ τ} -> Var Γ τ -> Var (Γ , σ) τ
vsuc = tl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Lifting of two predicates
------------------------------------------------------------------------

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

module Data.Product.Relation.Unary.All where

open import Level
open import Data.Product
open import Function.Base
open import Relation.Unary

private
  variable
    a b p q : Level
    A : Set a
    B : Set b

All : (A → Set p) → (B → Set q) → (A × B → Set (p ⊔ q))
All P Q (a , b) = P a × Q b


open import Auto
open import Algebra
open import Data.List using (_∷_; [];_++_;List)
open import Data.Nat using (ℕ; suc; zero; _+_)
open import Data.Product using (_×_; ∃₂; proj₁; proj₂)
open import Relation.Binary.PropositionalEquality as PropEq using (_≡_; refl; cong; sym)
open import Reflection

module Auto.Example.Even where

  private
    n+0≡n : ∀ n → n + 0 ≡ n
    n+0≡n zero    = refl
    n+0≡n (suc n) = cong suc (n+0≡n n)

    m+1+n≡1+m+n : ∀ m n → m + suc n ≡ suc (m + n)
    m+1+n≡1+m+n zero    n = refl
    m+1+n≡1+m+n (suc m) n = cong suc (m+1+n≡1+m+n m n)

  data Even  : ℕ →  Set where
    isEven0  : Even 0
    isEven+2 : ∀ {n} → Even n → Even (suc (suc n))

  even+ : ∀ {n m} → Even n → Even m → Even (n + m)
  even+  isEven0      e2 = e2
  even+ (isEven+2 e1) e2 = isEven+2 (even+ e1 e2)

  isEven-2 : ∀ {n} → Even (2 + n) → Even n
  isEven-2 (isEven+2 n) = n

  simple : ∀ {n} → Even n → Even (n + 2)
  simple e =  even+ e (isEven+2 isEven0)

  rules : HintDB
  rules = [] << quote isEven0
             << quote isEven+2
             << quote even+

  test₁ : Even 4
  test₁ = tactic (auto 5 rules)

  test₂ : ∀ {n} → Even n → Even (n + 2)
  test₂ = tactic (auto 5 rules)

  test₃ : ∀ {n} → Even n → Even (4 + n)
  test₃ = tactic (auto 5 rules)

  test₄ : ∀ {n} → Even n → Even (n + 2)
  test₄ = tactic (auto 5 rules)

  -- attempting to prove an impossible goal (e.g. evenness of n + 3
  -- for all n) will result in searchSpaceExhausted
  goal₁ = quoteTerm (∀ {n} → Even n → Even (n + 3))
  fail₁ : unquote (auto 5 rules goal₁) ≡ throw searchSpaceExhausted
  fail₁ = refl

  -- attempting to convert an unsupported expression (e.g. a lambda
  -- term) will result in unsupportedSyntax
  goal₂ = quoteTerm (∃₂ λ m n → Even (m + n))
  fail₂ : unquote (auto 5 rules goal₂) ≡ throw unsupportedSyntax
  fail₂ = refl


module 08-higherOrder where

explicitize : ∀ {A : Set} {B : A → Set} → ({{x : A}} → B x) → (x : A) → B x
explicitize f x = f {{x}}

implicitize : ∀ {A : Set} {B : A → Set} → ((x : A) → B x) → {{x : A}} → B x
implicitize f {{x}} = f x

data T : Set where
  tt : T

test = explicitize (λ {{t : T}} → t)


module Inductive.Examples.Sum where

open import Inductive
open import Tuple

open import Data.Fin
open import Data.Product
open import Data.List
open import Data.Vec

_⊎_ : Set → Set → Set
A ⊎ B = Inductive (((A ∷ []) , []) ∷ (((B ∷ []) , []) ∷ []))

inl : {A B : Set} → A → A ⊎ B
inl a = construct zero (a ∷ []) []

inr : {A B : Set} → B → A ⊎ B
inr b = construct (suc zero) (b ∷ []) []

case : {A B C : Set} → A ⊎ B → (A → C) → (B → C) → C
case x f g = rec (f ∷ (g ∷ [])) x


------------------------------------------------------------------------
-- The Agda standard library
--
-- An inductive definition of the sublist relation for types which have
-- a decidable equality. This is commonly known as Order Preserving
-- Embeddings (OPE).
------------------------------------------------------------------------

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

open import Relation.Binary
open import Agda.Builtin.Equality using (_≡_)

module Data.List.Relation.Binary.Sublist.DecPropositional
  {a} {A : Set a} (_≟_ : Decidable {A = A} _≡_)
  where

open import Data.List.Relation.Binary.Equality.DecPropositional _≟_
  using (_≡?_)
import Data.List.Relation.Binary.Sublist.DecSetoid as DecSetoidSublist
import Data.List.Relation.Binary.Sublist.Propositional as PropositionalSublist
open import Relation.Binary.PropositionalEquality

------------------------------------------------------------------------
-- Re-export core definitions and operations

open PropositionalSublist {A = A} public
open DecSetoidSublist (decSetoid _≟_) using (_⊆?_) public

------------------------------------------------------------------------
-- Additional relational properties

⊆-isDecPartialOrder : IsDecPartialOrder _≡_ _⊆_
⊆-isDecPartialOrder = record
  { isPartialOrder = ⊆-isPartialOrder
  ; _≟_            = _≡?_
  ; _≤?_           = _⊆?_
  }

⊆-decPoset : DecPoset a a a
⊆-decPoset = record
  { isDecPartialOrder = ⊆-isDecPartialOrder
  }