{-
  Finitely presented algebras.
  An R-algebra A is finitely presented, if there merely is an exact sequence
  of R-modules:

    (f₁,⋯,fₘ) → R[X₁,⋯,Xₙ] → A → 0

  (where f₁,⋯,fₘ ∈ R[X₁,⋯,Xₙ])
-}
{-# OPTIONS --safe #-}
module Cubical.Algebra.CommAlgebra.FPAlgebra where

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

open import Cubical.Data.FinData
open import Cubical.Data.Nat
open import Cubical.Data.Vec
open import Cubical.Data.Sigma
open import Cubical.Data.Empty

open import Cubical.HITs.PropositionalTruncation

open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.FGIdeal using (inclOfFGIdeal)
open import Cubical.Algebra.CommAlgebra
open import Cubical.Algebra.CommAlgebra.FreeCommAlgebra
  renaming (inducedHom to freeInducedHom)
open import Cubical.Algebra.CommAlgebra.QuotientAlgebra
  renaming (inducedHom to quotientInducedHom)
open import Cubical.Algebra.CommAlgebra.Ideal
open import Cubical.Algebra.CommAlgebra.FGIdeal
open import Cubical.Algebra.CommAlgebra.Instances.Initial
open import Cubical.Algebra.CommAlgebra.Instances.Unit
  renaming (UnitCommAlgebra to TerminalCAlg)
open import Cubical.Algebra.CommAlgebra.Kernel
open import Cubical.Algebra.Algebra.Properties
open import Cubical.Algebra.Algebra

private
  variable
    ℓ : Level

module _ {R : CommRing ℓ} where
  open Construction using (var)
  Polynomials : (n : ℕ) → CommAlgebra R ℓ
  Polynomials n = R [ Fin n ]

  evPoly : {n : ℕ} (A : CommAlgebra R ℓ) → ⟨ Polynomials n ⟩ → FinVec ⟨ A ⟩ n → ⟨ A ⟩
  evPoly A P values = fst (freeInducedHom A values) P

  evPolyPoly : {n : ℕ} (P : ⟨ Polynomials n ⟩) → evPoly (Polynomials n) P var ≡ P
  evPolyPoly {n = n} P = cong (λ u → fst u P) (inducedHomVar R (Fin n))

  evPolyHomomorphic : {n : ℕ} (A B : CommAlgebra R ℓ) (f : CommAlgebraHom A B)
                     → (P : ⟨ Polynomials n ⟩) → (values : FinVec ⟨ A ⟩ n)
                     → (fst f) (evPoly A P values) ≡ evPoly B P (fst f ∘ values)
  evPolyHomomorphic A B f P values =
    (fst f) (evPoly A P values)                         ≡⟨ refl ⟩
    (fst f) (fst (freeInducedHom A values) P)           ≡⟨ refl ⟩
    fst (f ∘a freeInducedHom A values) P                ≡⟨ cong (λ u → fst u P) (natIndHomR f values) ⟩
    fst (freeInducedHom B (fst f ∘ values)) P           ≡⟨ refl ⟩
    evPoly B P (fst f ∘ values) ∎
    where open AlgebraHoms

  module _ {m : ℕ} (n : ℕ) (relation : FinVec ⟨ Polynomials n ⟩ m) where
    open CommAlgebraStr using (0a)
    open Cubical.Algebra.Algebra.Properties.AlgebraHoms

    relationsIdeal = generatedIdeal (Polynomials n) relation

    abstract
      {-
        The following definitions are abstract because of type checking speed
        problems - complete unfolding of FPAlgebra is triggered otherwise.
        This also means, the where blocks contain more type declarations than usual.
      -}
      FPAlgebra : CommAlgebra R ℓ
      FPAlgebra = Polynomials n / relationsIdeal

      modRelations : CommAlgebraHom (Polynomials n) (Polynomials n / relationsIdeal)
      modRelations = quotientHom (Polynomials n) relationsIdeal

      generator : (i : Fin n) → ⟨ FPAlgebra ⟩
      generator = fst modRelations ∘ var

      relationsHold : (i : Fin m) → evPoly FPAlgebra (relation i) generator ≡ 0a (snd FPAlgebra)
      relationsHold i =
        evPoly FPAlgebra (relation i) generator
        ≡⟨ sym (evPolyHomomorphic (Polynomials n) FPAlgebra modRelations (relation i) var) ⟩
        fst modRelations (evPoly (Polynomials n) (relation i) var)
        ≡⟨ cong (λ u → fst modRelations u) (evPolyPoly (relation i)) ⟩
        fst modRelations (relation i)
        ≡⟨ isZeroFromIdeal {R = R}
                           {A = (Polynomials n)}
                           {I = relationsIdeal}
                           (relation i)
                           (incInIdeal (Polynomials n) relation i ) ⟩
        0a (snd FPAlgebra) ∎

      inducedHom :
        (A : CommAlgebra R ℓ)
        (values : FinVec ⟨ A ⟩ n)
        (relationsHold : (i : Fin m) → evPoly A (relation i) values ≡ 0a (snd A))
        → CommAlgebraHom FPAlgebra A
      inducedHom A values relationsHold =
        quotientInducedHom
          (Polynomials n)
          relationsIdeal
          A
          freeHom
          isInKernel
        where
          freeHom : CommAlgebraHom (Polynomials n) A
          freeHom = freeInducedHom A values
          isInKernel :   fst (generatedIdeal (Polynomials n) relation)
                       ⊆ fst (kernel (Polynomials n) A freeHom)
          isInKernel = inclOfFGIdeal
                         (CommAlgebra→CommRing (Polynomials n))
                         relation
                         (kernel (Polynomials n) A freeHom)
                         relationsHold

      inducedHomOnGenerators :
        (A : CommAlgebra R ℓ)
        (values : FinVec ⟨ A ⟩ n)
        (relationsHold : (i : Fin m) → evPoly A (relation i) values ≡ 0a (snd A))
        (i : Fin n)
        → fst (inducedHom A values relationsHold) (generator i) ≡ values i
      inducedHomOnGenerators _ _ _ _ = refl

      unique :
             {A : CommAlgebra R ℓ}
             (values : FinVec ⟨ A ⟩ n)
             (relationsHold : (i : Fin m) → evPoly A (relation i) values ≡ 0a (snd A))
             (f : CommAlgebraHom FPAlgebra A)
             → ((i : Fin n) → fst f (generator i) ≡ values i)
             → inducedHom A values relationsHold ≡ f
      unique {A = A} values relationsHold f hasCorrectValues =
        injectivePrecomp
          (Polynomials n)
          relationsIdeal
          A
          (inducedHom A values relationsHold)
          f
          (sym (
           f'     ≡⟨ sym (inv f') ⟩
           freeInducedHom A (evaluateAt A f')    ≡⟨ cong (freeInducedHom A)
                                                         (funExt hasCorrectValues) ⟩
           freeInducedHom A values               ≡⟨ cong (freeInducedHom A) refl ⟩
           freeInducedHom A (evaluateAt A iHom') ≡⟨ inv iHom' ⟩
           iHom' ∎))
        where
          {-
                     Poly n
                      |    \
            modRelations    f'
                      ↓      ↘
                 FPAlgebra ─f→ A
          -}
          f' iHom' : CommAlgebraHom (Polynomials n) A
          f' = compAlgebraHom modRelations f
          iHom' = compAlgebraHom modRelations (inducedHom A values relationsHold)

          inv : retract (Iso.fun (homMapIso {I = Fin n} A)) (Iso.inv (homMapIso A))
          inv = Iso.leftInv (homMapIso {R = R} {I = Fin n} A)

      universal :
             (A : CommAlgebra R ℓ)
             (values : FinVec ⟨ A ⟩ n)
             (relationsHold : (i : Fin m) → evPoly A (relation i) values ≡ 0a (snd A))
             → isContr (Σ[ f ∈ CommAlgebraHom FPAlgebra A ] ((i : Fin n) → fst f (generator i) ≡ values i))
      universal A values relationsHold =
        ( (inducedHom A values relationsHold)
          , (inducedHomOnGenerators A values relationsHold) )
        , λ {(f , mapsValues)
            → Σ≡Prop (λ _ → isPropΠ (λ _ → isSetCommAlgebra A _ _))
                     (unique values relationsHold f mapsValues)}

      {- ∀ A : Comm-R-Algebra,
         ∀ J : Finitely-generated-Ideal,
         Hom(R[I]/J,A) is isomorphic to the Set of roots of the generators of J
      -}

      zeroLocus : (A : CommAlgebra R ℓ) → Type ℓ
      zeroLocus A = Σ[ v ∈ FinVec ⟨ A ⟩ n ] ((i : Fin m) → evPoly A (relation i) v ≡ 0a (snd A))

      inducedHomFP : (A : CommAlgebra R ℓ) →
                      zeroLocus A → CommAlgebraHom FPAlgebra A
      inducedHomFP A d = inducedHom A (fst d) (snd d)

      evaluateAtFP : {A : CommAlgebra R ℓ} →
                      CommAlgebraHom FPAlgebra A → zeroLocus A
      evaluateAtFP {A} f = value ,
        λ i →  evPoly A (relation i) value                            ≡⟨ step1 (relation i) ⟩
            fst compHom (evPoly (Polynomials n) (relation i) var)  ≡⟨ refl ⟩
            (fst f) ((fst modRelations)
                       (evPoly (Polynomials n) (relation i) var))  ≡⟨ cong
                                                                       (fst f)
                                                                       (evPolyHomomorphic
                                                                         (Polynomials n)
                                                                         FPAlgebra
                                                                         modRelations
                                                                         (relation i) var) ⟩
            (fst f) (evPoly FPAlgebra (relation i) generator)      ≡⟨ cong (fst f) (relationsHold i) ⟩
            (fst f) (0a (snd FPAlgebra))                           ≡⟨ IsAlgebraHom.pres0 (snd f) ⟩
            0a (snd A) ∎
        where
          compHom : CommAlgebraHom (Polynomials n) A
          compHom = CommAlgebraHoms.compCommAlgebraHom (Polynomials n) FPAlgebra A modRelations f
          value : FinVec ⟨ A ⟩ n
          value = (Iso.fun (homMapIso A)) compHom
          step1 : (x : ⟨ Polynomials n ⟩) → evPoly A x value ≡ fst compHom (evPoly (Polynomials n) x var)
          step1 x = sym (evPolyHomomorphic (Polynomials n) A compHom x var)

      FPHomIso : {A : CommAlgebra R ℓ} →
                  Iso (CommAlgebraHom FPAlgebra A) (zeroLocus A)
      Iso.fun FPHomIso = evaluateAtFP
      Iso.inv FPHomIso = inducedHomFP _
      Iso.rightInv (FPHomIso {A}) =
        λ b → Σ≡Prop
                (λ x → isPropΠ
                  (λ i → isSetCommAlgebra A
                          (evPoly A (relation i) x)
                          (0a (snd A))))
                refl
      Iso.leftInv (FPHomIso {A}) =
        λ a → Σ≡Prop (λ f → isPropIsCommAlgebraHom {ℓ} {R} {ℓ} {ℓ} {FPAlgebra} {A} f)
                 λ i → fst (unique {A}
                             (fst (evaluateAtFP {A} a))
                             (snd (evaluateAtFP a))
                             a
                             (λ j → refl)
                             i)

      homMapPathFP : (A : CommAlgebra R ℓ)→ CommAlgebraHom FPAlgebra A ≡ zeroLocus A
      homMapPathFP A = isoToPath (FPHomIso {A})

      isSetZeroLocus : (A : CommAlgebra R ℓ) → isSet (zeroLocus A)
      isSetZeroLocus A =  J (λ y _ → isSet y)
                       (isSetAlgebraHom (CommAlgebra→Algebra FPAlgebra) (CommAlgebra→Algebra A))
                       (homMapPathFP A)

  record FinitePresentation (A : CommAlgebra R ℓ) : Type ℓ where
    field
      n : ℕ
      m : ℕ
      relations : FinVec ⟨ Polynomials n ⟩ m
      equiv : CommAlgebraEquiv (FPAlgebra n relations) A

  isFPAlgebra : (A : CommAlgebra R ℓ) → Type _
  isFPAlgebra A = ∥ FinitePresentation A ∥₁

  isFPAlgebraIsProp : {A : CommAlgebra R ℓ} → isProp (isFPAlgebra A)
  isFPAlgebraIsProp = isPropPropTrunc

module Instances (R : CommRing ℓ) where
  open FinitePresentation

  {- Every (multivariate) polynomial algebra is finitely presented -}
  module _ (n : ℕ) where
    private
      A : CommAlgebra R ℓ
      A = Polynomials n

      emptyGen : FinVec (fst A) 0
      emptyGen = λ ()

      B : CommAlgebra R ℓ
      B = FPAlgebra n emptyGen

    polynomialAlgFP : FinitePresentation A
    FinitePresentation.n polynomialAlgFP = n
    m polynomialAlgFP = 0
    relations polynomialAlgFP = emptyGen
    equiv polynomialAlgFP =
      -- Idea: A and B enjoy the same universal property.
      toAAsEquiv , snd toA
      where
        toA : CommAlgebraHom B A
        toA = inducedHom n emptyGen A Construction.var (λ ())
        fromA : CommAlgebraHom A B
        fromA = freeInducedHom B (generator _ _)
        open AlgebraHoms
        inverse1 : fromA ∘a toA ≡ idAlgebraHom _
        inverse1 =
          fromA ∘a toA
            ≡⟨ sym (unique _ _ _ _ _ (λ i → cong (fst fromA) (
                 fst toA (generator n emptyGen i)
                   ≡⟨ inducedHomOnGenerators _ _ _ _ _ _ ⟩
                 Construction.var i
                   ∎))) ⟩
          inducedHom n emptyGen B (generator _ _) (relationsHold _ _)
            ≡⟨ unique _ _ _ _ _ (λ i → refl) ⟩
          idAlgebraHom _
            ∎
        inverse2 : toA ∘a fromA ≡ idAlgebraHom _
        inverse2 = isoFunInjective (homMapIso A) _ _ (
          evaluateAt A (toA ∘a fromA)   ≡⟨ sym (naturalEvR {A = B} {B = A} toA fromA) ⟩
          fst toA ∘ evaluateAt B fromA  ≡⟨ refl ⟩
          fst toA ∘ generator _ _       ≡⟨ funExt (inducedHomOnGenerators _ _ _ _ _)⟩
          Construction.var              ∎)
        toAAsEquiv : ⟨ B ⟩ ≃ ⟨ A ⟩
        toAAsEquiv = isoToEquiv (iso (fst toA)
                                     (fst fromA)
                                     (λ a i → fst (inverse2 i) a)
                                     (λ b i → fst (inverse1 i) b))

  {- The initial R-algebra is finitely presented -}
  private
    R[⊥] : CommAlgebra R ℓ
    R[⊥] = Polynomials 0

    emptyGen : FinVec (fst R[⊥]) 0
    emptyGen = λ ()

    R[⊥]/⟨0⟩ : CommAlgebra R ℓ
    R[⊥]/⟨0⟩ = FPAlgebra 0 emptyGen

  R[⊥]/⟨0⟩IsInitial : (B : CommAlgebra R ℓ)
                     → isContr (CommAlgebraHom R[⊥]/⟨0⟩ B)
  R[⊥]/⟨0⟩IsInitial B = iHom , uniqueness
    where
      iHom : CommAlgebraHom R[⊥]/⟨0⟩ B
      iHom = inducedHom 0 emptyGen B (λ ()) (λ ())
      uniqueness : (f : CommAlgebraHom R[⊥]/⟨0⟩ B) →
                   iHom ≡ f
      uniqueness f = unique 0 emptyGen {A = B} (λ ()) (λ ()) f (λ ())

  initialCAlgFP : FinitePresentation (initialCAlg R)
  n initialCAlgFP = 0
  m initialCAlgFP = 0
  relations initialCAlgFP = emptyGen
  equiv initialCAlgFP =
    equivByInitiality R R[⊥]/⟨0⟩ R[⊥]/⟨0⟩IsInitial

  {- The terminal R-algebra is finitely presented -}
  private
    unitGen : FinVec (fst R[⊥]) 1
    unitGen zero = 1a
      where open CommAlgebraStr (snd R[⊥])

    R[⊥]/⟨1⟩ : CommAlgebra R ℓ
    R[⊥]/⟨1⟩ = FPAlgebra 0 unitGen

  terminalCAlgFP : FinitePresentation (TerminalCAlg R)
  n terminalCAlgFP = 0
  m terminalCAlgFP = 1
  relations terminalCAlgFP = unitGen
  equiv terminalCAlgFP = equivFrom1≡0 R R[⊥]/⟨1⟩
                                      (sym (⋆-lid 1a) ∙ relationsHold 0 unitGen zero)
    where open CommAlgebraStr (snd R[⊥]/⟨1⟩)

  {-
    Quotients of the base ring by principal ideals are finitely presented.
  -}
  module _ (x : ⟨ R ⟩) where
    ⟨x⟩ : IdealsIn (initialCAlg R)
    ⟨x⟩ = generatedIdeal (initialCAlg R) (replicateFinVec 1 x)

    R/⟨x⟩ = (initialCAlg R) / ⟨x⟩

    open CommAlgebraStr ⦃...⦄
    private
      relation : FinVec ⟨ Polynomials {R = R} 0 ⟩ 1
      relation = replicateFinVec 1 (Construction.const x)

      B = FPAlgebra 0 relation

      π = quotientHom (initialCAlg R) ⟨x⟩
      instance
        _ = snd R/⟨x⟩
        _ = snd (initialCAlg R)
        _ = snd B

      πx≡0 : π $a x ≡ 0a
      πx≡0 = isZeroFromIdeal {A = initialCAlg R} {I = ⟨x⟩} x
               (incInIdeal (initialCAlg R) (replicateFinVec 1 x) zero)

    R/⟨x⟩FP : FinitePresentation R/⟨x⟩
    n R/⟨x⟩FP = 0
    m R/⟨x⟩FP = 1
    relations R/⟨x⟩FP = relation
    equiv R/⟨x⟩FP = (isoToEquiv (iso (fst toA) (fst fromA)
                                    (λ a i → toFrom i $a a)
                                    λ a i → fromTo i $a a))
                   , (snd toA)
      where
        toA : CommAlgebraHom B R/⟨x⟩
        toA = inducedHom 0 relation R/⟨x⟩ (λ ()) relation-holds
          where
            vals : FinVec ⟨ R/⟨x⟩ ⟩ 0
            vals ()
            vals' : FinVec ⟨ initialCAlg R ⟩ 0
            vals' ()
            relation-holds = λ zero →
              evPoly R/⟨x⟩ (relation zero) (λ ())    ≡⟨ sym
                                                       (evPolyHomomorphic
                                                         (initialCAlg R)
                                                          R/⟨x⟩
                                                          π
                                                          (Construction.const x)
                                                          vals') ⟩
              π $a (evPoly (initialCAlg R)
                           (Construction.const x)
                           vals')                   ≡⟨ cong (π $a_) (·Rid x) ⟩
              π $a x                                ≡⟨ πx≡0 ⟩
              0a                               ∎
        {-
            R ─→   R/⟨x⟩
          id↓       ↓ ∃!
            R ─→   R[⊥]/⟨const x⟩
        -}
        fromA : CommAlgebraHom R/⟨x⟩ B
        fromA =
          quotientInducedHom
            (initialCAlg R)
            ⟨x⟩
            B
            (initialMap R B)
            (inclOfFGIdeal
              (CommAlgebra→CommRing (initialCAlg R))
              (replicateFinVec 1 x)
              (kernel (initialCAlg R) B (initialMap R B))
              λ {Fin.zero → relationsHold 0 relation Fin.zero})

        open AlgebraHoms

        fromTo : fromA ∘a toA ≡ idCAlgHom B
        fromTo = cong fst
          (isContr→isProp (universal 0 relation B (λ ()) (relationsHold 0 relation))
                          (fromA ∘a toA , (λ ()))
                          (idCAlgHom B , (λ ())))

        toFrom : toA ∘a fromA ≡ idCAlgHom R/⟨x⟩
        toFrom = injectivePrecomp (initialCAlg R) ⟨x⟩ R/⟨x⟩ (toA ∘a fromA) (idCAlgHom R/⟨x⟩)
                   (isContr→isProp (initialityContr R R/⟨x⟩) _ _)


module ListsWithIrrelevantProofs where

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

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

{-# BUILTIN NATURAL ℕ    #-}

postulate
  _≤_ : ℕ → ℕ → Set
  p1 : 0 ≤ 1
  p2 : 0 ≤ 1

-- descending lists indexed by upper bound for largest element

data SList (bound : ℕ) : Set where
  []    : SList bound
  scons : (head : ℕ) →
          .(head ≤ bound) →   -- irrelevant proof, dotted non-dependent domain
          (tail : SList head) →
          SList bound

l1 : SList 1
l1 = scons 0 p1 []

l2 : SList 1
l2 = scons 0 p2 []

-- proofs in list are irrelevant

l1≡l2 : l1 ≡ l2
l1≡l2 = refl


module Setoid where

module Logic where

  infix 4 _/\_
  -- infix 2 _\/_

  data True : Set where
    tt : True

  data False : Set where

  data _/\_ (P Q : Set) : Set where
    andI : P -> Q -> P /\ Q

--   Not allowed if we have proof irrelevance
--   data _\/_ (P Q : Set) : Set where
--     orIL : P -> P \/ Q
--     orIR : Q -> P \/ Q

module Setoid where

  data Setoid : Set1 where
    setoid : (A     : Set)
          -> (_==_  : A -> A -> Set)
          -> (refl  : (x : A) -> x == x)
          -> (sym   : (x y : A) -> x == y -> y == x)
          -> (trans : (x y z : A) -> x == y -> y == z -> x == z)
          -> Setoid

  El : Setoid -> Set
  El (setoid A _ _ _ _) = A

  module Projections where

    eq : (A : Setoid) -> El A -> El A -> Set
    eq (setoid _ e _ _ _) = e

    refl : (A : Setoid) -> {x : El A} -> eq A x x
    refl (setoid _ _ r _ _) = r _

    sym : (A : Setoid) -> {x y : El A} -> (h : eq A x y) -> eq A y x
    sym (setoid _ _ _ s _) = s _ _

    trans : (A : Setoid) -> {x y z : El A} -> eq A x y -> eq A y z -> eq A x z
    trans (setoid _ _ _ _ t) = t _ _ _

  module Equality (A : Setoid) where

    infix 6 _==_

    _==_ : El A -> El A -> Set
    _==_ = Projections.eq A

    refl : {x : El A} -> x == x
    refl = Projections.refl A

    sym : {x y : El A} -> x == y -> y == x
    sym = Projections.sym A

    trans : {x y z : El A} -> x == y -> y == z -> x == z
    trans = Projections.trans A

module EqChain (A : Setoid.Setoid) where

  infixl 5 _===_ _=-=_
  infix  8 _since_

  open Setoid
  private open module EqA = Equality A

  eqProof>_ : (x : El A) -> x == x
  eqProof> x = refl

  _=-=_ : (x : El A) -> {y : El A} -> x == y -> x == y
  x =-= eq = eq

  _===_ : {x y z : El A} -> x == y -> y == z -> x == z
  _===_ = trans

  _since_ : {x : El A} -> (y : El A) -> x == y -> x == y
  _ since eq = eq

module Fun where

  open Logic
  open Setoid

  infixr 10 _=>_ _==>_

  open Setoid.Projections using (eq)

  data _=>_ (A B : Setoid) : Set where
    lam : (f : El A -> El B)
       -> ({x y : El A} -> eq A x y
                         -> eq B (f x) (f y)
          )
       -> A => B

  app : {A B : Setoid} -> (A => B) -> El A -> El B
  app (lam f _) = f

  cong : {A B : Setoid} -> (f : A => B) -> {x y : El A} ->
         eq A x y -> eq B (app f x) (app f y)
  cong (lam _ resp) = resp

  data EqFun {A B : Setoid}(f g : A => B) : Set where
    eqFunI : ({x y : El A} -> eq A x y -> eq B (app f x) (app g y)) ->
             EqFun f g

  eqFunE : {A B : Setoid} -> {f g : A => B} -> {x y : El A} ->
           EqFun f g -> eq A x y -> eq B (app f x) (app g y)
  eqFunE (eqFunI h) = h

  _==>_ : Setoid -> Setoid -> Setoid
  A ==> B = setoid (A => B) EqFun r s t
    where
      module Proof where
        open module EqChainB = EqChain B
        module EqA = Equality A
        open module EqB = Equality B

        -- either abstract or --proof-irrelevance needed
        -- (we don't want to compare the proofs for equality)
        -- abstract
        r : (f : A => B) -> EqFun f f
        r f = eqFunI (\xy -> cong f xy)

        s : (f g : A => B) -> EqFun f g -> EqFun g f
        s f g fg =
          eqFunI (\{x}{y} xy ->
            app g x =-= app g y  since  cong g xy
                    === app f x  since  sym (eqFunE fg xy)
                    === app f y  since  cong f xy
          )

        t : (f g h : A => B) -> EqFun f g -> EqFun g h -> EqFun f h
        t f g h fg gh =
          eqFunI (\{x}{y} xy ->
            app f x =-= app g y  since  eqFunE fg xy
                    === app g x  since  cong g (EqA.sym xy)
                    === app h y  since  eqFunE gh xy
          )
      open Proof

  infixl 100 _$_
  _$_ : {A B : Setoid} -> El (A ==> B) -> El A -> El B
  _$_ = app

  lam2 : {A B C : Setoid} ->
         (f : El A -> El B -> El C) ->
         ({x x' : El A} -> eq A x x' ->
          {y y' : El B} -> eq B y y' -> eq C (f x y) (f x' y')
         ) -> El (A ==> B ==> C)
  lam2 {A} f h = lam (\x -> lam (\y -> f x y)
                                (\y -> h EqA.refl y))
                     (\x -> eqFunI (\y -> h x y))
    where
      module EqA = Equality A

  lam3 : {A B C D : Setoid} ->
         (f : El A -> El B -> El C -> El D) ->
         ({x x' : El A} -> eq A x x' ->
          {y y' : El B} -> eq B y y' ->
          {z z' : El C} -> eq C z z' -> eq D (f x y z) (f x' y' z')
         ) -> El (A ==> B ==> C ==> D)
  lam3 {A} f h =
    lam (\x -> lam2 (\y z -> f x y z)
                    (\y z -> h EqA.refl y z))
        (\x -> eqFunI (\y -> eqFunI (\z -> h x y z)))
    where
      module EqA = Equality A

  eta : {A B : Setoid} -> (f : El (A ==> B)) ->
        eq (A ==> B) f (lam (\x -> f $ x) (\xy -> cong f xy))
  eta f = eqFunI (\xy -> cong f xy)

  id : {A : Setoid} -> El (A ==> A)
  id = lam (\x -> x) (\x -> x)

  {- Now it looks okay. But it's incredibly slow!  Proof irrelevance makes it
     go fast again...  The problem is equality checking of (function type)
     setoids which without proof irrelevance checks equality of the proof that
     EqFun is an equivalence relation.  It's not clear why using lam3 involves
     so many more equality checks than using lam. Making the proofs abstract
     makes the problem go away.
  -}
  compose : {A B C : Setoid} -> El ((B ==> C) ==> (A ==> B) ==> (A ==> C))
  compose =
    lam3 (\f g x -> f $ (g $ x))
         (\f g x -> eqFunE f (eqFunE g x))

  _∘_ : {A B C : Setoid} -> El (B ==> C) -> El (A ==> B) -> El (A ==> C)
  f ∘ g = compose $ f $ g

  const : {A B : Setoid} -> El (A ==> B ==> A)
  const = lam2 (\x y -> x) (\x y -> x)

module Nat where

  open Logic
  open Setoid
  open Fun

  infixl 10 _+_

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

  module NatSetoid where

    eqNat : Nat -> Nat -> Set
    eqNat zero     zero   = True
    eqNat zero    (suc _) = False
    eqNat (suc _)  zero   = False
    eqNat (suc n) (suc m) = eqNat n m

    data EqNat (n m : Nat) : Set where
      eqnat : eqNat n m -> EqNat n m

    uneqnat : {n m : Nat} -> EqNat n m -> eqNat n m
    uneqnat (eqnat x) = x

    r : (x : Nat) -> eqNat x x
    r zero    = tt
    r (suc n) = r n

    -- reflexivity of EqNat
    rf : (n : Nat) -> EqNat n n
    rf = \ x -> eqnat (r x)

    s : (x y : Nat) -> eqNat x y -> eqNat y x
    s  zero    zero   _ = tt
    s (suc n) (suc m) h = s n m h
    s zero    (suc _) ()
    s (suc _) zero    ()

    -- symmetry of EqNat
    sy : (x y : Nat) -> EqNat x y -> EqNat y x
    sy = \x y h -> eqnat (s x y (uneqnat h))

    t : (x y z : Nat) -> eqNat x y -> eqNat y z -> eqNat x z
    t  zero    zero    z      xy yz = yz
    t (suc x) (suc y) (suc z) xy yz = t x y z xy yz
    t  zero   (suc _)  _      () _
    t (suc _)  zero    _      () _
    t (suc _) (suc _)  zero   _  ()

    -- transitivity of EqNat
    tr : (x y z : Nat) -> EqNat x y -> EqNat y z -> EqNat x z
    tr = \x y z xy yz -> eqnat (t x y z (uneqnat xy) (uneqnat yz))

  NAT : Setoid
  NAT = setoid Nat NatSetoid.EqNat NatSetoid.rf NatSetoid.sy NatSetoid.tr

  _+_ : Nat -> Nat -> Nat
  zero  + m = m
  suc n + m = suc (n + m)

  plus : El (NAT ==> NAT ==> NAT)
  plus = lam2 (\n m -> n + m) eqPlus
    where
      module EqNat = Equality NAT
      open EqNat
      open NatSetoid

      eqPlus : {n n' : Nat} -> n == n' -> {m m' : Nat} -> m == m' -> n + m == n' + m'
      eqPlus {zero}  {zero}    _  mm = mm
      eqPlus {suc n} {suc n'}  (eqnat nn) {m}{m'} (eqnat mm) =
        eqnat (uneqnat (eqPlus{n}{n'} (eqnat nn)
                              {m}{m'} (eqnat mm)
              )        )
      eqPlus {zero}  {suc _}  (eqnat ())  _
      eqPlus {suc _} {zero}   (eqnat ())  _

module List where

  open Logic
  open Setoid

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

  LIST : Setoid -> Setoid
  LIST A = setoid (List (El A)) eqList r s t
    where
      module EqA = Equality A
      open EqA

      eqList : List (El A) -> List (El A) -> Set
      eqList nil        nil      = True
      eqList nil       (_ :: _)  = False
      eqList (_ :: _)   nil      = False
      eqList (x :: xs) (y :: ys) = x == y /\ eqList xs ys

      r : (x : List (El A)) -> eqList x x
      r  nil      = tt
      r (x :: xs) = andI refl (r xs)

      s : (x y : List (El A)) -> eqList x y -> eqList y x
      s  nil       nil       h            = h
      s (x :: xs) (y :: ys) (andI xy xys) = andI (sym xy) (s xs ys xys)
      s  nil      (_ :: _)  ()
      s (_ :: _)   nil      ()

      t : (x y z : List (El A)) -> eqList x y -> eqList y z -> eqList x z
      t  nil       nil       zs        _             h            = h
      t (x :: xs) (y :: ys) (z :: zs) (andI xy xys) (andI yz yzs) =
        andI (trans xy yz) (t xs ys zs xys yzs)
      t  nil      (_ :: _)  _          () _
      t (_ :: _)   nil      _          () _
      t (_ :: _)  (_ :: _)  nil        _  ()

open Fun


open import Agda.Builtin.Sigma
open import Agda.Builtin.Equality

infix 1 Σ-syntax
Σ-syntax = Σ
syntax Σ-syntax A (λ x → B) = Σ[ x ∈ A ] B

pair : Set → Set
pair A = Σ[ _ ∈ A ] A

diag : Set → Set
diag A = Σ[ p ∈ pair A ] fst p ≡ snd p

diag-pat : Set → Set
diag-pat A = Σ[ p@(x , y) ∈ pair A ] p ≡ (y , x)

-- WAS: parse error for p@(x , y)
-- WANT: success


module Categories where

infix 10 _≡_

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

trans : forall {A B C}{a : A}{b : B}{c : C} -> a ≡ b -> b ≡ c -> a ≡ c
trans refl p = p

sym : forall {A B}{a : A}{b : B} -> a ≡ b -> b ≡ a
sym refl = refl

resp : forall {A}{B : A -> Set}{a a' : A} ->
       (f : (a : A) -> B a) -> a ≡ a' -> f a ≡ f a'
resp f refl = refl

record Cat : Set1 where
  field Obj : Set
        Hom : Obj -> Obj -> Set
        id : forall X -> Hom X X
        _○_ : forall {X Y Z} -> Hom Y Z -> Hom X Y -> Hom X Z
        idl : forall {X Y}{f : Hom X Y} -> id Y ○ f ≡ f
        idr : forall {X Y}{f : Hom X Y} -> f ○ id X ≡ f
        assoc : forall {W X Y Z}{f : Hom W X}{g : Hom X Y}{h : Hom Y Z} ->
                (h ○ g) ○ f ≡ h ○ (g ○ f)

open Cat

record Functor (C D : Cat) : Set where
  field Fun : Obj C -> Obj D
        map : forall {X Y} -> (Hom C X Y) -> Hom D (Fun X) (Fun Y)
        mapid : forall {X} -> map (id C X) ≡ id D (Fun X)
        map○ : forall {X Y Z}{f : Hom C X Y}{g : Hom C Y Z} ->
               map (_○_ C g f) ≡ _○_ D (map g) (map f)

open Functor

idF : forall C -> Functor C C
idF C = record {Fun = \x -> x; map = \x -> x; mapid = refl; map○ = refl}

_•_ : forall {C D E} -> Functor D E -> Functor C D -> Functor C E
F • G = record {Fun = \X -> Fun F (Fun G X);
                 map = \f -> map F (map G f);
                 mapid = trans (resp (\x -> map F x) (mapid G)) (mapid F);
                 map○ = trans (resp (\x -> map F x) (map○ G)) (map○ F)}

record Nat {C D : Cat} (F G : Functor C D) : Set where
  field η : (X : Obj C) -> Hom D (Fun F X) (Fun G X)
        law : {X Y : Obj C}{f : Hom C X Y} ->
              _○_ D (η Y) (map F f) ≡ _○_ D (map G f) (η X)

open Nat

_▪_ : forall {C D : Cat}{F G H : Functor C D} -> Nat G H -> Nat F G -> Nat F H
_▪_ {D = D} A B =
  record {
    η = \X -> _○_ D (η A X) (η B X);
    law = \{X}{Y} ->
      trans (assoc D)
            (trans (resp (\f -> _○_ D (η A Y) f) (law B))
                   (trans (sym (assoc D))
                          (trans (resp (\g -> _○_ D g (η B X)) (law A))
                                 (assoc D))))
  }


{-# OPTIONS --without-K #-}
open import HoTT.Base
open import HoTT.Identity

module HoTT.Transport.Identity where

open variables

unitₗᵣ : {x y : A} {p : x == y} → p == refl ∙ p ∙ refl
unitₗᵣ {p = refl} = refl

-- Theorem 2.11.3
transport= : {a a' : A} (f g : A → B) (p : a == a') (q : f a == g a) →
             transport (λ x → f x == g x) p q == ap f p ⁻¹ ∙ q ∙ ap g p
transport= _ _ refl _ = unitₗᵣ

transport=-id-const : (a : A) {x₁ x₂ : A} (p : x₁ == x₂) (q : x₁ == a) →
                      transport (λ x → x == a) p q == p ⁻¹ ∙ q
transport=-id-const _ refl refl = refl

transport=-const-id : {x₁ x₂ : A} (p : x₁ == x₂) (a : A) (q : a == x₁) →
                      transport (λ x → a == x) p q == q ∙ p
transport=-const-id refl _ refl = refl

transport=-id-id : {x₁ x₂ : A} (p : x₁ == x₂) (q : x₁ == x₁) →
                   transport (λ x → x == x) p q == p ⁻¹ ∙ q ∙ p
transport=-id-id refl _ = unitₗᵣ

transport=-constₗ : (b : B) (f : A → B) {x₁ x₂ : A} (p : x₁ == x₂) (q : b == f x₁) →
                    transport (λ x → b == f x) p q == q ∙ ap f p
transport=-constₗ _ _ refl _ = unitᵣ

transport=-constᵣ : (f : A → B) (b : B) {x₁ x₂ : A} (p : x₁ == x₂) (q : f x₁ == b) →
                    transport (λ x → f x == b) p q == ap f p ⁻¹ ∙ q
transport=-constᵣ _ _ refl refl = refl

transport=-idₗ : (f : A → A) {x₁ x₂ : A} (p : x₁ == x₂) (q : x₁ == f x₁) →
                 transport (λ x → x == f x) p q == p ⁻¹ ∙ q ∙ ap f p
transport=-idₗ _ refl _ = unitₗᵣ

transport=-idᵣ : (f : A → A) {x₁ x₂ : A} (p : x₁ == x₂) (q : f x₁ == x₁) →
                 transport (λ x → f x == x) p q == ap f p ⁻¹ ∙ q ∙ p
transport=-idᵣ _ refl _ = unitₗᵣ


module x02-842Induction-hc-2 where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; cong; sym)
open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; _∎)
open import Data.Nat using (ℕ; zero; suc; _+_; _*_; _∸_)

-- PLFA coverage of identity, associativity, commutativity, distributivity.

-- + associative
_ : (3 + 4) + 5 ≡ 3 + (4 + 5)
_ =
  begin
  (3 + 4) + 5 ≡⟨⟩
       7  + 5 ≡⟨⟩
           12 ≡⟨⟩
   3 +  9     ≡⟨⟩
   3 + (4 + 5)
  ∎

+-identityᴸ : ∀ (m : ℕ) → zero + m ≡ m
+-identityᴸ m = refl

0+ : ∀ (m : ℕ) → zero + m ≡ m
0+ = +-identityᴸ

+-identityʳ : ∀ (m : ℕ) → m + zero ≡ m
+-identityʳ  zero   = refl
+-identityʳ (suc m) = cong suc (+-identityʳ m)

+0 : ∀ (m : ℕ) → m + zero ≡ m
+0 = +-identityʳ

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

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

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

-- without using induction/recursion
+-swap : ∀ (m n p : ℕ) → (m + n) + p ≡ n + (m + p)
+-swap  zero   n p = refl
+-swap (suc m) n p      -- suc  m + n + p  ≡ n + (suc m + p)
                        -- suc (m + n + p) ≡ n + suc (m + p)
  rewrite
    +-suc n (m + p)     -- suc (m + n + p) ≡ suc (n + (m + p))
  | sym (+-assoc n m p) -- suc (m + n + p) ≡ suc (n +  m + p)
  | +-comm m n          -- suc (n + m + p) ≡ suc (n +  m + p)
  = refl

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

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

*0 : ∀ (m : ℕ) → m * zero ≡ zero
*0  zero   = refl
*0 (suc m) = *0 m

*1 : ∀ (m : ℕ) → m * suc zero ≡ m
*1  zero   = refl
*1 (suc m) = cong suc (*1 m)

-- like addition, helper lemmas needed
*-comm : ∀ (m n : ℕ) → m * n ≡ n * m
*-comm  zero   n rewrite *0 n = refl
*-comm  m   zero rewrite *0 m = refl
*-comm (suc m) (suc n)        -- suc       m * suc n   ≡ suc      n * suc m
                              -- suc (n +  m * suc n)  ≡ suc (m + n * suc m)
  rewrite
    *-comm m (suc n)          -- suc (n + (m + n * m)) ≡ suc (m +  n * suc m)
  | *-comm n (suc m)          -- suc (n + (m + n * m)) ≡ suc (m + (n + m * n))
  | sym (+-assoc n m (n * m)) -- suc (n +  m + n * m)  ≡ suc (m + (n + m * n))
  | sym (+-assoc m n (m * n)) -- suc (n +  m + n * m)  ≡ suc (m +  n + m * n)
  |      +-swap  n m (n * m)  -- suc (m + (n + n * m)) ≡ suc (m +  n + m * n)
  | sym (+-assoc m n (n * m)) -- suc (m +  n + n * m)  ≡ suc (m +  n + m * n)
  | *-comm m n                -- suc (m +  n + n * m)  ≡ suc (m +  n + n * m)
  = refl

0∸n≡0 : ∀ (m : ℕ) → zero ∸ m ≡ zero
0∸n≡0  zero   = refl
0∸n≡0 (suc m) rewrite 0∸n≡0 m = refl

m∸0≡m : ∀ (m : ℕ) → m ∸ zero ≡ m
m∸0≡m m = refl

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

------------------------------------------------------------------------------
-- Properties of binary representation.

-----
-- BEGIN : copied from x01-842Naturals-hc-2.agda

data Bin-ℕ : Set where
  bits : Bin-ℕ
  _x0 : Bin-ℕ → Bin-ℕ
  _x1 : Bin-ℕ → Bin-ℕ
bin-zero : Bin-ℕ
bin-zero = bits

bin-one : Bin-ℕ
bin-one = bits x1     -- 1 in binary

bin-two : Bin-ℕ
bin-two = bits x1 x0  -- 10 in binary

inc : Bin-ℕ → Bin-ℕ
inc  bits  = bits x1
inc (m x0) = m    x1
inc (m x1) = (inc m) x0

dbl : ℕ → ℕ
dbl zero = zero
dbl (suc m) = suc (suc (dbl m))

tob : ℕ → Bin-ℕ
tob  zero   = bits
tob (suc m) = inc (tob m)

fromb : Bin-ℕ → ℕ
fromb  bits  = zero
fromb (n x0) =      dbl (fromb n)
fromb (n x1) = suc (dbl (fromb n))

_bin-+_ : Bin-ℕ → Bin-ℕ → Bin-ℕ
bits   bin-+     n  =                    n
m      bin-+  bits  =            m
(m x0) bin-+ (n x0) =           (m bin-+ n) x0
(m x0) bin-+ (n x1) =      inc ((m bin-+ n) x0)
(m x1) bin-+ (n x0) =      inc ((m bin-+ n) x0)
(m x1) bin-+ (n x1) = inc (inc ((m bin-+ n) x0))

-- END : copied from x01-842Naturals-hc-2.agda
-----

-- Bin-ℕ version of dbl. Do not use tob/fromb.

dblb : Bin-ℕ → Bin-ℕ
dblb  bits  = bits
dblb (m x0) =           (dblb m) x0
dblb (m x1) = inc (inc ((dblb m) x0))

_ : dblb  bits ≡ bits
_ = refl
_ : dblb (bits       x0) ≡ bits          x0
_ = refl
_ : dblb (bits       x1) ≡ bits       x1 x0
_ = refl
_ : dblb (bits    x1 x0) ≡ bits    x1 x0 x0
_ = refl
_ : dblb (bits    x1 x1) ≡ bits    x1 x1 x0
_ = refl
_ : dblb (bits x1 x0 x0) ≡ bits x1 x0 x0 x0
_ = refl
_ : dblb (bits x1 x0 x1) ≡ bits x1 x0 x1 x0
_ = refl

from∘inc : ∀ (m : Bin-ℕ) → fromb (inc m) ≡ suc (fromb m)
from∘inc  bits  = refl
from∘inc (m x0) = refl
from∘inc (m x1) --       fromb (inc (m x1))  ≡ suc (fromb (m x1))
                --      dbl (fromb (inc m))  ≡ suc (suc (dbl (fromb m)))
  rewrite
    from∘inc m  --      dbl (suc (fromb m))  ≡ suc (suc (dbl (fromb m)))
                -- suc (suc (dbl (fromb m))) ≡ suc (suc (dbl (fromb m)))
  = refl

from∘tob : ∀ (m : ℕ) → fromb (tob m) ≡ m
from∘tob  zero   = refl
from∘tob (suc m)     -- fromb (tob (suc m)) ≡ suc m
                     -- fromb (inc (tob m)) ≡ suc m
  rewrite
    from∘inc (tob m) -- suc (fromb (tob m)) ≡ suc m
  | from∘tob m       -- suc             m   ≡ suc m
  = refl

-- ∀ (m : Bin-ℕ) → tob (fromb m) ≡ m cannot be proved.
-- Because there are multiple representations of ZERO.
-- This restriction can be proved.

to/from-corr : ∀ (b : Bin-ℕ) (n : ℕ) → b ≡ tob n → fromb b ≡ n
to/from-corr .bits  zero   refl = refl
to/from-corr .(inc (tob n)) (suc n) refl -- fromb (tob (suc n)) ≡ suc n
                                         -- fromb (inc (tob n)) ≡ suc n
  rewrite
    from∘inc (tob n)                     -- suc (fromb (tob n)) ≡ suc n
  | to/from-corr (tob n) n refl          -- suc             n   ≡ suc n
  = refl

dblb∘inc : ∀ (b : Bin-ℕ) → dblb (inc b) ≡ inc (inc (dblb b))
dblb∘inc  bits  = refl
dblb∘inc (b x0) = refl
dblb∘inc (b x1)  --       dblb (inc (b   x1)) ≡  inc (inc (dblb (b x1)))
                 --      (dblb (inc  b)  x0)  ≡ (inc (inc (dblb  b)) x0)
  rewrite
    dblb∘inc b   --  (inc (inc (dblb b)) x0)  ≡ (inc (inc (dblb  b)) x0)
  = refl

to∘dbl : ∀ (m : ℕ) → tob (dbl m) ≡ dblb (tob m)
to∘dbl  zero   = refl
to∘dbl (suc m)       --      tob (dbl  (suc m))  ≡      dblb (tob (suc m))
                     -- inc (inc (tob  (dbl m))) ≡      dblb (inc (tob m))
  rewrite
    to∘dbl m         -- inc (inc (dblb (tob m))) ≡      dblb (inc (tob m))
  | dblb∘inc (tob m) -- inc (inc (dblb (tob m))) ≡ inc (inc (dblb (tob m)))
  = refl

from∘dblb : ∀ (b : Bin-ℕ) → fromb (dblb b) ≡ dbl (fromb b)
from∘dblb  bits  = refl
from∘dblb (b x0)      --             fromb (dblb (b x0)) ≡ dbl (fromb (b x0))
                      --            dbl (fromb (dblb b)) ≡ dbl (dbl (fromb b))
  rewrite
    from∘dblb b       --            dbl  (dbl (fromb b)) ≡ dbl (dbl (fromb b))
  = refl
from∘dblb (b x1)      --             fromb (dblb (b x1)) ≡ dbl (fromb (b x1))
                      --     dbl (fromb (inc (dblb b)))  ≡ suc (suc (dbl (dbl (fromb b))))
  rewrite
    from∘inc (dblb b) --     dbl (suc (fromb (dblb b)))  ≡ suc (suc (dbl (dbl (fromb b))))
  | from∘dblb b       -- suc (suc (dbl (dbl (fromb b)))) ≡ suc (suc (dbl (dbl (fromb b))))
  = refl

-- lemma
-- second case for unary addition
--     suc m + n = suc (m + n)
-- into binary setting.
-- Induction on both m and n is needed.

bin-+-linc : ∀ (m n : Bin-ℕ) → (inc m) bin-+ n ≡ inc (m bin-+ n)
bin-+-linc bits    bits  = refl
bin-+-linc bits   (n x0) = refl
bin-+-linc bits   (n x1) = refl
bin-+-linc (m x0)  bits  = refl
bin-+-linc (m x0) (n x0) = refl
bin-+-linc (m x0) (n x1) = refl
bin-+-linc (m x1)  bits  = refl
bin-+-linc (m x1) (n x0) rewrite bin-+-linc m n = refl
bin-+-linc (m x1) (n x1) rewrite bin-+-linc m n = refl

-- theorem relates unary and binary addition
to∘+ : ∀ (m n : ℕ) → tob (m + n) ≡ tob m bin-+ tob n
to∘+  zero   n = refl
to∘+ (suc m) n                 --  tob (suc m + n)        ≡ (tob (suc m) bin-+ tob n)
                               -- inc (tob (m + n))       ≡ (inc (tob m) bin-+ tob n)
  rewrite
    bin-+-linc (tob m) (tob n) -- inc (tob (m + n))       ≡ inc (tob m bin-+ tob n)
  | to∘+ m n                   -- inc (tob m bin-+ tob n) ≡ inc (tob m bin-+ tob n)
  = refl


module _ where

open import Common.Prelude renaming (_∸_ to _-_) -- work-around for #1855

data Acc {a} {A : Set a} (_<_ : A → A → Set) (x : A) : Set a where
  acc : (∀ y → y < x → Acc _<_ y) → Acc _<_ x

data _<_ : Nat → Nat → Set where
  zero : ∀ {n} → zero < suc n
  suc  : ∀ {n m} → n < m → suc n < suc m

_≤_ : Nat → Nat → Set
n ≤ m = n < suc m

data LeqView (n : Nat) : Nat → Set where
  less : ∀ {m} → n < m → LeqView n m
  equal : LeqView n n

leqView : ∀ {n m} → n ≤ m → LeqView n m
leqView {m = zero} zero = equal
leqView {m = suc m} zero = less zero
leqView {m = zero} (suc ())
leqView {m = suc m} (suc leq) with leqView leq
leqView {._} {suc m} (suc leq) | less lt = less (suc lt)
leqView {.(suc m)} {suc m} (suc lt) | equal = equal

case_of_ : ∀ {A B : Set} → A → (A → B) → B
case x of f = f x

wfAux : ∀ {n y} → (∀ z → z < n → Acc _<_ z) → y ≤ n → Acc _<_ y
wfAux f leq with leqView leq
wfAux f leq | less lt = f _ lt
wfAux f leq | equal = acc f

wfNat : ∀ n → Acc _<_ n
wfNat zero = acc λ _ ()
wfNat (suc n) with wfNat n
... | acc f = acc (λ y lt → wfAux f lt)

≤-refl : ∀ n → n ≤ n
≤-refl zero = zero
≤-refl (suc n) = suc (≤-refl n)

wk< : ∀ {n m} → n < m → n < suc m
wk< zero = zero
wk< (suc lt) = suc (wk< lt)

less-minus : ∀ n m → (suc n - suc m) ≤ n
less-minus n zero    = ≤-refl n
less-minus zero (suc m) = zero
less-minus (suc n) (suc m) = wk< (less-minus n m)

NonZero : Nat → Set
NonZero zero = ⊥
NonZero (suc _) = ⊤

divsuc : ∀ n → Nat → Acc _<_ n → Nat
divsuc 0 _ _ = 0
divsuc (suc n) m (acc f) =
  suc (divsuc (n - m) m (f _ (less-minus n m)))

div : Nat → (m : Nat) → {_ : NonZero m} → Nat
div n zero {}
div n (suc m) = divsuc n m (wfNat n)

main : IO Unit
main = printNat (div 1000000 1000)


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

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

module Groups.Isomorphisms.Definition where

record GroupIso {m n o p : _} {A : Set m} {S : Setoid {m} {o} A} {_·A_ : A → A → A} {B : Set n} {T : Setoid {n} {p} B} {_·B_ : B → B → B} (G : Group S _·A_) (H : Group T _·B_) (f : A → B) : Set (m ⊔ n ⊔ o ⊔ p) where
  open Setoid S renaming (_∼_ to _∼G_)
  open Setoid T renaming (_∼_ to _∼H_)
  field
    groupHom : GroupHom G H f
    bij : SetoidBijection S T f

record GroupsIsomorphic {m n o p : _} {A : Set m} {S : Setoid {m} {o} A} {_·A_ : A → A → A} {B : Set n} {T : Setoid {n} {p} B} {_·B_ : B → B → B} (G : Group S _·A_) (H : Group T _·B_) : Set (m ⊔ n ⊔ o ⊔ p) where
  field
    isomorphism : A → B
    proof : GroupIso G H isomorphism


module logical_foundations.Naturals where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; _∎)

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

{-# BUILTIN NATURAL ℕ  #-}

_+_ : ℕ → ℕ → ℕ
zero + n = n
suc m + n = suc (m + n)
{-# BUILTIN NATPLUS _+_  #-}
-- zero + n = n             -- 0 + n = n
-- suc m + n = suc (m + n)  -- (1 + m) + n = 1 + (m + n)

infixl 6 _+_

_ : 2 + 3 ≡ 5
_ =
  begin
    2 + 3
  ≡⟨⟩
    (suc (suc zero)) + (suc (suc (suc zero)))
  ≡⟨⟩
    suc ((suc zero) + (suc (suc (suc zero))))
  ≡⟨⟩
    suc (suc (zero + (suc (suc (suc zero)))))
  ≡⟨⟩
    5
  ∎

_ : 2 + 3 ≡ 5
_ =
  begin
    2 + 3
  ≡⟨⟩
    suc (1 + 3)
  ≡⟨⟩
    suc (suc (0 + 3))
  ≡⟨⟩
    suc (suc 3)
  ≡⟨⟩
    5
  ∎

_ : 2 + 3 ≡ 5
_ = refl

_ : 3 + 4 ≡ 7
_ =
  begin
    3 + 4
  ≡⟨⟩
    suc (2 + 4)
  ≡⟨⟩
    suc (suc (1 + 4))
  ≡⟨⟩
    suc (suc (suc (0 + 4)))
  ≡⟨⟩
    suc (suc (suc 4))
  ≡⟨⟩
    7
  ∎

_*_ : ℕ → ℕ → ℕ
zero * n = zero            -- 0 * n = 0
suc m * n = n + (m * n)    -- (1 + m) * n = n + (m * n)
{-# BUILTIN NATTIMES _*_  #-}

infixl 7 _*_

_ : 2 * 3 ≡ 6
_ =
  begin
    2 * 3
  ≡⟨⟩
    3 + (1 * 3)
  ≡⟨⟩
    3 + (3 + (0 * 3))
  ≡⟨⟩
    3 + (3 + 0)
  ≡⟨⟩
    6
  ∎

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

infixl 8 _^_

_ : 2 ^ 3 ≡ 8
_ =
  begin
    2 ^ 3
  ≡⟨⟩
    2 * (2 ^ 2)
  ≡⟨⟩
    2 * (2 * (2 ^ 1))
  ≡⟨⟩
    2 * (2 * (2 * (2 ^ 0)))
  ≡⟨⟩
    2 * (2 * (2 * 1))
  ≡⟨⟩
    8
  ∎

_ : 3 ^ 4 ≡ 81
_ = refl

_∸_ : ℕ → ℕ → ℕ
m ∸ zero = m
zero ∸ suc n = zero
suc m ∸ suc n = m ∸ n
{-# BUILTIN NATMINUS _∸_  #-}

infixl 6 _∸_

_ =
  begin
    3 ∸ 2
  ≡⟨⟩
    2 ∸ 1
  ≡⟨⟩
    1 ∸ 0
  ≡⟨⟩
    1
  ∎

_ =
  begin
    2 ∸ 3
  ≡⟨⟩
    1 ∸ 2
  ≡⟨⟩
    0 ∸ 1
  ≡⟨⟩
    0
  ∎

data Bin : Set where
  nil : Bin
  x0_ : Bin → Bin
  x1_ : Bin → Bin

inc : Bin → Bin
inc nil = x1 nil
inc (x0 b) = x1 b
inc (x1 b) = x0 (inc b)

_ : inc (x1 x1 x0 x1 nil) ≡ x0 x0 x1 x1 nil
_ = refl

_ : inc (x1 x1 x1 x1 nil) ≡ x0 x0 x0 x0 x1 nil
_ = refl

to : ℕ → Bin
to zero = x0 nil
to (suc n) = inc (to n)

_ : to 0 ≡ x0 nil
_ = refl

_ : to 1 ≡ x1 nil
_ = refl

_ : to 2 ≡ x0 x1 nil
_ = refl

_ : to 3 ≡ x1 x1 nil
_ = refl

_ : to 14 ≡ x0 x1 x1 x1 nil
_ = refl

two = suc (suc zero)

from : Bin → ℕ
from nil = zero
from (x0 n) = two * (from n)
from (x1 n) = suc (two * (from n))

_ : from nil ≡ zero
_ = refl

_ : from (x0 nil) ≡ zero
_ = refl

_ : from (x1 nil) ≡ suc zero
_ = refl

_ : from (x0 x1 nil) ≡ two
_ = refl

_ : from (x0 x1 nil) ≡ two * suc zero
_ = refl

_ : from (x1 x1 nil) ≡ suc (suc (suc zero))
_ = refl

_ : from (x1 x1 nil) ≡ suc (two * suc zero)
_ = refl

_ : from (x0 x0 x1 nil) ≡ suc (suc (suc (suc zero)))
_ = refl

_ : from (x0 x0 x1 nil) ≡ two * (two * suc zero)
_ = refl

_ : from (x1 x0 x1 nil) ≡ suc (suc (suc (suc (suc zero))))
_ = refl

_ : from (x1 x0 x1 nil) ≡ suc (two * (two * suc zero))
_ = refl


open import Prelude

open import Relation.Binary.PropositionalEquality
open import RW.Language.RTerm using (Name)
open import RW.Strategy.PropEq
open import RW.RW (≡-strat ∷ [])

open import Data.Nat.Properties.Simple
  using (+-comm; +-right-identity; +-assoc)

module PropEqTest where

    ++-assoc : ∀{a}{A : Set a}(xs ys zs : List A) →
               (xs ++ ys) ++ zs ≡ xs ++ (ys ++ zs)
    ++-assoc [] ys zs       = refl
    ++-assoc (x ∷ xs) ys zs = tactic (by (quote ++-assoc))

    open ≡-Reasoning

    ++-assocH : ∀{a}{A : Set a}(xs ys zs : List A) →
                (xs ++ ys) ++ zs ≡ xs ++ (ys ++ zs)
    ++-assocH [] ys zs =
              begin
                ([] ++ ys) ++ zs
              ≡⟨ refl ⟩
                ys ++ zs
              ≡⟨ refl ⟩
                [] ++ (ys ++ zs)
              ∎
    ++-assocH {A = A} (x ∷ xs) ys zs =
              begin
                ((x ∷ xs) ++ ys) ++ zs
              ≡⟨ refl ⟩
                x ∷ (xs ++ ys) ++ zs
              ≡⟨ refl ⟩
                x ∷ ((xs ++ ys) ++ zs)
              ≡⟨ (tactic (by (quote ++-assocH))) ⟩
                x ∷ (xs ++ (ys ++ zs))
              ≡⟨ refl ⟩
                (x ∷ xs) ++ (ys ++ zs)
              ∎

    []-++-neutral : ∀{a}{A : Set a}(xs : List A)
                  → xs ++ [] ≡ xs
    []-++-neutral [] = refl
    []-++-neutral (x ∷ xs) = tactic (by (quote []-++-neutral))

    test1 : (x y : ℕ) → (x + y) + 0 ≡ y + (x + 0)
    test1 x y
      = begin
        (x + y) + 0
      ≡⟨ (tactic (by+ acts)) ⟩
        x + y
      ≡⟨ (tactic (by+ acts)) ⟩
        y + x
      ≡⟨ (tactic (by+ acts)) ⟩
        (y + x) + 0
      ≡⟨ (tactic (by+ acts)) ⟩
        y + (x + 0)
      ∎
      where
        acts : List Name
        acts = quote +-right-identity ∷ quote +-assoc ∷ quote +-comm ∷ []


module Nat.Unary where

open import Data.Sum
open import Function
open import Nat.Class
open import Relation.Binary.PropositionalEquality
open ≡-Reasoning

data ℕ₁ : Set where
  zero : ℕ₁
  succ : ℕ₁ → ℕ₁

ind : (P : ℕ₁ → Set) → P zero → (∀ {k} → P k → P (succ k)) → ∀ n → P n
ind P z s zero = z
ind P z s (succ n) = ind (P ∘ succ) (s z) (λ {k} → s {succ k}) n

ind′ : (P : ℕ₁ → Set) → P zero → (∀ {k} → P k → P (succ k)) → ∀ n → P n
ind′ P z s zero = z
ind′ P z s (succ n) = s (ind′ P z s n)

rec : {A : Set} → A → (A → A) → ℕ₁ → A
rec {A} z s n = ind (const A) z (λ {_} → s) n

rec-succ : ∀ {A} z (s : A → A) n → rec (s z) s n ≡ s (rec z s n)
rec-succ z s zero = refl
rec-succ z s (succ n) =
  begin
    rec (s z) s (succ n)
  ≡⟨⟩
    rec (s (s z)) s n
  ≡⟨ rec-succ (s z) s n ⟩
    s (rec (s z) s n)
  ≡⟨⟩
    s (rec z s (succ n))
  ∎

data _<_ : ℕ₁ → ℕ₁ → Set where
  z<s : ∀ {n} → zero < succ n
  s<s : ∀ {m n} → m < n → succ m < succ n

n<sn : ∀ {n} → n < succ n
n<sn {zero} = z<s
n<sn {succ n} = s<s n<sn

tighten : ∀ {m n} → m < succ n → m < n ⊎ m ≡ n
tighten {zero} {zero} z<s = inj₂ refl
tighten {zero} {succ n′} z<s = inj₁ z<s
tighten {succ m′} {succ n′} (s<s m′<sn′) with tighten m′<sn′
tighten {succ m′} {succ n′} (s<s m′<sn′) | inj₁ m′<n′ = inj₁ (s<s m′<n′)
tighten {succ m′} {succ n′} (s<s m′<sn′) | inj₂ m′≡n′ = inj₂ (cong succ m′≡n′)

SIndHyp : (ℕ₁ → Set) → ℕ₁ → Set
SIndHyp P n = ∀ {m} → m < n → P m

sind : (P : ℕ₁ → Set) → (∀ {k} → SIndHyp P k → P k) → ∀ n → P n
sind P h n = prf (succ n) {n} n<sn
  where
    lem : {p : ℕ₁} (hp : SIndHyp P p) → SIndHyp P (succ p)
    lem hp m<sp with tighten m<sp
    lem hp m<sp | inj₁ m<p = hp m<p
    lem hp m<sp | inj₂ refl = h hp

    prf : ∀ n → SIndHyp P n
    prf = ind (SIndHyp P) (λ ()) lem

_+_ : ℕ₁ → ℕ₁ → ℕ₁
m + n = rec n succ m

+-zero : ∀ {n} → zero + n ≡ n
+-zero = refl

+-succ : ∀ {m n} → succ m + n ≡ m + succ n
+-succ = refl

instance
  Nat-ℕ₁ : Nat ℕ₁
  Nat-ℕ₁ = record
    { zero = zero
    ; succ = succ
    ; ind = ind
    ; _+_ = _+_
    ; +-zero = +-zero
    ; +-succ = λ {m n} → +-succ {m} {n}
    }


module Proof-last-is-deterministic where

-- Show that last is a deterministic operation.
-- The property to show is:
--
-- l == l1 ++ [x1] and l == l2 ++ [x2]  implies x1 == x2
--
-- Here we show the simplified property
--
--  l1 ++ [x1] == l2 ++ [x2]  implies x1 == x2
--
-- The initial property can be derived from this one by by symmetry
-- and transitivity of Boolean equality.

open import bool
open import bool-thms
open import bool-thms2
open import list
open import nat
open import eq

-- Auxiliary statements: an empty list cannot be equal to a list with
-- an element at the end:
nonemptylast :  ∀ (l : 𝕃 ℕ)(x : ℕ) → =𝕃 _=ℕ_ [] (l ++ [ x ]) ≡ ff
nonemptylast [] x = refl
nonemptylast (z :: l) x = refl

nonemptylastr :  ∀ (l : 𝕃 ℕ)(x : ℕ) → =𝕃 _=ℕ_ (l ++ [ x ]) [] ≡ ff
nonemptylastr [] x = refl
nonemptylastr (z :: l) x = refl

-- Main property formulated as Boolean implication:
lasteqp : ∀ (l1 l2 : 𝕃 ℕ)(x1 x2 : ℕ)
            → (=𝕃 _=ℕ_ (l1 ++ [ x1 ]) (l2 ++ [ x2 ])) imp (x1 =ℕ x2) ≡ tt
lasteqp [] [] x1 x2 rewrite &&-tt (x1 =ℕ x2) | imp-same (x1 =ℕ x2) = refl
lasteqp [] (x :: l2) x1 x2
  rewrite nonemptylast l2 x2 | &&-ff (x1 =ℕ x) = refl
lasteqp (x :: l1) [] x1 x2
  rewrite nonemptylastr l1 x1 | &&-ff (x =ℕ x2) = refl
lasteqp (z1 :: l1) (z2 :: l2) x1 x2 with (z1 =ℕ z2)
lasteqp (z1 :: l1) (z2 :: l2) x1 x2 | tt
  rewrite lasteqp l1 l2 x1 x2 = refl
lasteqp (z1 :: l1) (z2 :: l2) x1 x2 | ff = refl

-- Main property formulated as propositional implication:
lasteq : ∀ (l1 l2 : 𝕃 ℕ)(x1 x2 : ℕ)
            → =𝕃 _=ℕ_ (l1 ++ [ x1 ]) (l2 ++ [ x2 ]) ≡ tt
            → x1 =ℕ x2 ≡ tt
lasteq l1 l2 x1 x2 p = imp-mp (lasteqp l1 l2 x1 x2) p


module PiInSet where

Rel : Set -> Set1
Rel A = A -> A -> Set

Reflexive : {A : Set} -> Rel A -> Set
Reflexive {A} _R_ = forall x -> x R x

Symmetric : {A : Set} -> Rel A -> Set
Symmetric {A} _R_ = forall x y -> x R y -> y R x

data True : Set where
  tt : True

data False : Set where

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

_==_ : Rel Nat
zero  == zero  = True
zero  == suc _ = False
suc _ == zero  = False
suc n == suc m = n == m

refl== : Reflexive _==_
refl==  zero   = tt
refl== (suc n) = refl== n

sym== : Symmetric _==_
sym==  zero    zero   tt = tt
sym==  zero   (suc _) ()
sym== (suc _)  zero   ()
sym== (suc n) (suc m) n==m = sym== n m n==m


{-# OPTIONS -v tc.conv.irr:50 #-}
-- {-# OPTIONS -v tc.lhs.unify:50 #-}
module IndexInference where

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

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

infixr 40 _::_

-- The length of the vector can be inferred from the pattern.
foo : Vec Nat _ -> Nat
foo (a :: b :: c :: []) = c

-- Andreas, 2012-09-13 an example with irrelevant components in index

pred : Nat → Nat
pred (zero ) = zero
pred (suc n) = n

data ⊥ : Set where
record ⊤ : Set where

NonZero : Nat → Set
NonZero zero    = ⊥
NonZero (suc n) = ⊤

data Fin (n : Nat) : Set where
  zero : .(NonZero n) → Fin n
  suc  : .(NonZero n) → Fin (pred n) → Fin n

data SubVec (A : Set)(n : Nat) : Fin n → Set where
  []   : .{p : NonZero n} → SubVec A n (zero p)
  _::_ : .{p : NonZero n}{k : Fin (pred n)} → A → SubVec A (pred n) k → SubVec A n (suc p k)

-- The length of the vector can be inferred from the pattern.
bar : {A : Set} → SubVec A (suc (suc (suc zero))) _ → A
bar (a :: []) = a


{-# OPTIONS --termination-depth=2 #-}

module TerminationWithTwoConstructors where

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

f : Nat -> Nat
f zero = zero
f (suc zero) = zero
f (suc (suc n)) with zero
... | m = f (suc n)

{- internal represenation

f : Nat -> Nat
f zero = zero
f (suc zero) = zero
f (suc (suc n)) = faux n zero

faux : Nat -> Nat -> Nat
faux n m = f (suc n)

-}

{- this type checks with --termination-depth >= 2
calls:

 f -> f_with (-2)
 f_with -> f (+1)

-}


open import Agda.Primitive
open import Empty
open import Logic
open import Boolean

module Peano where

  data ℕ : Set where
    zero : ℕ
    succ : ℕ → ℕ
  {-# BUILTIN NATURAL ℕ #-}

  pred : ℕ → ℕ
  pred zero = zero
  pred (succ x) = x

  _+_ : ℕ → ℕ → ℕ
  zero + m = m
  (succ n) + m = succ (n + m)
  {-# BUILTIN NATPLUS _+_ #-}

  _∘_ : ℕ → ℕ → ℕ
  zero ∘ _ = zero
  (succ n) ∘ m = (n ∘ m) + m
  {-# BUILTIN NATTIMES _∘_ #-}

  data _≡_ (x : ℕ) : ℕ → Set where
    refl : x ≡ x

  data _≢_ : ℕ → ℕ → Set where
    z≢s : ∀ {n} → zero ≢ succ n
    s≢z : ∀ {n} → succ n ≢ zero
    s≢s : ∀ {m n} → m ≢ n → succ m ≢ succ n

  data Equal? (m n : ℕ) : Set where
    yes : m ≡ n → Equal? m n
    no : m ≢ n → Equal? m n

  _≟_ : (m n : ℕ) → Equal? m n
  _≟_ zero zero = yes refl
  _≟_ zero (succ _) = no z≢s
  _≟_ (succ _) zero = no s≢z
  _≟_ (succ m) (succ n) with m ≟ n
  _≟_ (succ m) (succ .m) | yes refl = yes refl
  _≟_ (succ m) (succ n) | no p = no (s≢s p)

  equality-disjoint : (m n : ℕ) → m ≡ n → m ≢ n → ⊥
  equality-disjoint zero zero refl ()
  equality-disjoint zero (succ _) () z≢s
  equality-disjoint (succ _) zero () s≢z
  equality-disjoint (succ m) (succ .m) refl (s≢s e) = equality-disjoint m m refl e

  private
    -- to make the last `equality-disjoint` match clearer, verify that s≢s can be nested
    test-s≢s : (succ (succ (succ zero))) ≢ (succ (succ zero))
    test-s≢s = s≢s (s≢s s≢z)

  data Ordering : Rel ℕ where
    less : ∀ m k → Ordering m (succ (m + k))
    equal : ∀ m → Ordering m m
    greater : ∀ m k → Ordering (succ (m + k)) m

  compare : ∀ m n → Ordering m n
  compare zero zero = equal zero
  compare (succ m) zero = greater zero m
  compare zero (succ n) = less zero n
  compare (succ m) (succ n) with compare m n
  compare (succ .m) (succ .(succ m + k)) | less m k = less (succ m) k
  compare (succ .m) (succ .m) | equal m = equal (succ m)
  compare (succ .(succ m + k)) (succ .m) | greater m k = greater (succ m) k

  infix 4 _≤_ _<_ _≥_ _>_ _≰_ _≮_ _≱_ _≯_

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

  _<_ : Rel ℕ
  m < n = succ m ≤ n

  _≥_ : Rel ℕ
  m ≥ n = n ≤ m

  _>_ : Rel ℕ
  m > n = n < m

  _≰_ : Rel ℕ
  a ≰ b = ¬ (a ≤ b)

  _≮_ : Rel ℕ
  a ≮ b = ¬ (a < b)

  _≱_ : Rel ℕ
  a ≱ b = ¬ (a ≥ b)

  _≯_ : Rel ℕ
  a ≯ b = ¬ (a > b)

  data _even : ℕ → Set where
    ZERO : zero even
    STEP : ∀ {x} → x even → succ (succ x) even

  private
    proof₁ : succ(succ(succ(succ(zero)))) even
    proof₁ = STEP (STEP ZERO)

    proof₂ : (A : Set) → A → A
    proof₂ _ ν = ν

    proof'₂ : ℕ → ℕ
    proof'₂ = proof₂ ℕ


{-# OPTIONS --without-K #-}
module hw1 where

open import Level using (_⊔_)
open import Function using (id)
open import Data.Nat using (ℕ; suc; _+_; _*_)
open import Data.Empty using (⊥)
open import Data.Sum using (_⊎_; inj₁; inj₂)

import Level

infix 4 _≡_

recℕ : ∀ {ℓ} → (C : Set ℓ) → C → (ℕ → C → C) → ℕ → C
recℕ C z f 0 = z
recℕ C z f (suc n) = f n (recℕ C z f n)

indℕ : ∀ {ℓ}
         → (C : ℕ → Set ℓ)
         → C 0
         → ((n : ℕ) → C n → C (suc n))
         → (n : ℕ)
         → C n
indℕ C z f 0 = z
indℕ C z f (suc n) = f n (indℕ C z f n)

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

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

rec≡ : {A : Set} →
       (R : A → A → Set) {reflexiveR : {a : A} → R a a} →
       ({x y : A} (p : x ≡ y) → R x y)
rec≡ R {reflR} (refl y) = reflR {y}

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

------------------------------------------------------------------------------
-- Exercise 1.1
-- show h ∘ (g ∘ f) = (h ∘ g) ∘ f
------------------------------------------------------------------------------

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

compose≡ : {A B C D : Set}
            → (f : A → B)
            → (g : B → C)
            → (h : C → D)
            → h ∘ (g ∘ f) ≡ (h ∘ g) ∘ f
compose≡ = λ f g h → refl (λ a → (h (g (f a))))

------------------------------------------------------------------------------
-- Exercise 1.2
-- Derive the recursion principle for products recA×B
-- using only the projections, and verify that the definitional
-- equalities are valid. Do the same for Σ-types.
------------------------------------------------------------------------------
---------------------------------------------------
-- Product Types
data _×_ {ℓ₁ ℓ₂} (A : Set ℓ₁) (B : Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
  pair : A → B → A × B

fst : ∀ {ℓ₁ ℓ₂} {A : Set ℓ₁} {B : Set ℓ₂} → A × B → A
fst (pair a _) = a

snd : ∀ {ℓ₁ ℓ₂} {A : Set ℓ₁} {B : Set ℓ₂}  -> A × B -> B
snd (pair _ b) = b

rec× : ∀ {ℓ} {A B : Set ℓ}
       → (C : Set ℓ)
       → (A → B → C)
       → A × B → C
rec× c f = λ p -> (f (fst p) (snd p))

fstofab≡a : ∀ {A B : Set}
              (a : A)
              → (b : B)
              → fst (pair a b) ≡ a
fstofab≡a {A} {B} = λ a b → refl a

sndofab≡b : ∀ {A B : Set}
              (a : A)
              → (b : B)
              → snd (pair a b) ≡ b
sndofab≡b {A} {B} = λ a b → refl b

uniq× : ∀ {ℓ₁ ℓ₂}
          {A : Set ℓ₁}
          {B : Set ℓ₂}
          (p : A × B)
          → (pair (fst p) (snd p)) ≡ p
uniq× (pair a b) = refl (pair a b)

rec×g≡g : ∀ {A B C : Set}
            (g : A → B → C)
            (a : A)
            → (b : B)
            → rec× C g (pair a b) ≡ g a b
rec×g≡g {A} {B} {C} = λ g a b → refl (g a b)

recfst : ∀ (A B : Set)
           → fst {B = B} ≡ rec× A (λ a b → a)
recfst A B = refl fst

---------------------------------------------------
-- Sigma Types
data Σ {ℓ₁ ℓ₂} (A : Set ℓ₁)
               (B : A → Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂)
     where dpair : (a : A) → (B a) → Σ A B

dfst : ∀ {A : Set} {B : A → Set} → Σ A B → A
dfst (dpair a _) = a

dsnd : ∀ {A : Set} {B : A → Set} → (p : Σ A B) → (B (dfst p))
dsnd (dpair _ b) = b

dfstofab≡a : ∀ {A : Set}
               {B : A → Set}
               (a : A)
               (b : B a) →
               dfst {B = B} (dpair a b) ≡ a
dfstofab≡a {A} {B} = λ a b → refl a

dsndofab≡a : ∀ {A : Set}
               {B : A → Set}
               (a : A)
               (b : B a) →
               dsnd {B = B} (dpair a b) ≡ b
dsndofab≡a {A} {B} = λ a b → refl b

uniqΣ : ∀ {A : Set}
          {B : A → Set}
          (p : Σ A B)
          → (dpair (dfst p) (dsnd p)) ≡ p
uniqΣ (dpair a b) = refl (dpair a b)

------------------------------------------------------------------------------
-- Exercise 1.3
-- Derive the induction principle for products indA×B,
-- using only the projections and the propositional uniqueness
-- principle uniqA×B. Verify that the definitional equalities are
-- valid. Generalize uniqA×B to Σ-types, and do the same for Σ-types.
------------------------------------------------------------------------------

ind× : ∀ {ℓ} {A : Set ℓ} {B : Set ℓ}
         → (C : (A × B) → Set ℓ)
         → ((a : A) (b : B) → (C (pair a b)))
         → (p : (A × B)) → (C p)
ind× = λ C f → λ p → subst {C = C}
                           (uniq× p)
                           (f (fst p) (snd p))

indΣ' : ∀  {A : Set} {B : A → Set} → (C : Σ A B → Set) →
        ((a : A) → (b : B a) → C (dpair a b)) → (p : Σ A B) → C p
indΣ' C g s = subst {C = C}
                    (uniqΣ s)
                    (g (dfst s) (dsnd s))

------------------------------------------------------------------------------
--- Exercise 1.4 Given the function iter, derive a function having the
--- type of the recursor recN. Show that the defining equations of the
--- recursor hold propositionally for this function, using the
--- induction principle for Nats.
------------------------------------------------------------------------------

iter : ∀ {ℓ} {C : Set ℓ} → C → (C → C) → ℕ → C
iter c₀ c₊ 0 = c₀
iter c₀ c₊ (suc n) = c₊ (iter c₀ c₊ n)

recℕ' : ∀ {ℓ} → (C : Set ℓ) → C → (ℕ → C → C) → ℕ → C
recℕ' C c₀ f n =
  snd (iter (pair 0 c₀)
            (λ nc →
                  (pair (suc (fst nc))
                        (f (fst nc) (snd nc))))
            n)

-- quick def and sanity check of fact via recℕ
fact = recℕ ℕ 1 (λ n nfact → (suc n) * nfact)
fact1 : fact 0 ≡ 1
fact1 = refl 1
fact5 : fact 5 ≡ 120
fact5 = refl 120

-- quick def and sanity check of fact via recℕ'
fact' = recℕ' ℕ 1 (λ n nfact → (suc n) * nfact)
fact'1 : fact' 0 ≡ 1
fact'1 = refl 1
fact'5 : fact' 5 ≡ 120
fact'5 = refl 120

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

-- this _is_ valid but I haven't done enough Agda
-- to see how to prove this. I proved it in the Coq HoTT library...
-- https://github.com/andmkent/HoTT/blob/5f9faf5ef4ea21db249d6ad45bcee0adf97f8f9d/contrib/HoTTBookExercises.v#L124
postulate
  punt1 : ∀ {ℓ} (C : Set ℓ) →
           (c₀ : C) →
           (f : (ℕ → C → C)) →
           (n : ℕ) →
           recℕ C c₀ f (suc n) ≡ recℕ' C c₀ f (suc n)

recℕ≡recℕ' : ∀ {ℓ} (C : Set ℓ) →
           (c₀ : C) →
           (f : (ℕ → C → C)) →
           ((n : ℕ) → recℕ C c₀ f n ≡ recℕ' C c₀ f n)

recℕ≡recℕ' {ℓ} C c₀ f n =
  indℕ {ℓ = ℓ}
       (λ n → (((recℕ  {ℓ = ℓ} C c₀ f) n)
                ≡
               ((recℕ' {ℓ = ℓ} C c₀ f) n)))
       (refl c₀)
       (λ n IH → (punt1 C c₀ f n))
       n

------------------------------------------------------------------------------
--- Exercise 1.5 Show that if we define A + B Σ(x:2) rec2(U, A, B,
--- x), then we can give a definition of indA+B for which the
--- definitional equalities stated in §1.7 hold.
------------------------------------------------------------------------------

data 𝟚 : Set where
  true  : 𝟚
  false : 𝟚

rec𝟚 : ∀ {ℓ} → {C : Set ℓ} → C → C → 𝟚 → C
rec𝟚 th el false = el
rec𝟚 th el true = th

if_then_else_ : ∀ {ℓ} {C : Set ℓ} → 𝟚 → C → C → C
if b then X else Y = rec𝟚 X Y b

ind𝟚 : ∀ {ℓ} → {C : 𝟚 → Set ℓ} → C true → C false → (b : 𝟚) → C b
ind𝟚 th el false = el
ind𝟚 th el true = th

bsum : ∀ (A : Set) → (B : Set) → Set
bsum  A B = Σ 𝟚 (rec𝟚 A B)

injbs1 : ∀ (A : Set) → (B : Set) → A → bsum A B
injbs1 A B a =  dpair true a

injbs2 : ∀ (A : Set) → (B : Set) → B → bsum A B
injbs2 A B b =  dpair false b

recΣ : ∀ {ℓ₁ ℓ₂ ℓ₃} → {A : Set ℓ₁} {B : A → Set ℓ₂} → (C : Set ℓ₃) →
       ((a : A) → B a → C) → Σ A B → C
recΣ C g (dpair a b) = g a b

indΣ : ∀ {ℓ₁ ℓ₂ ℓ₃} → {A : Set ℓ₁} {B : A → Set ℓ₂} → (C : Σ A B → Set ℓ₃) →
        ((a : A) → (b : B a) → C (dpair a b)) → (p : Σ A B) → C p
indΣ C g (dpair a b) = g a b

indbsum : (A : Set) (B : Set) (C : (bsum A B → Set))
           → ((a : A) → (C (injbs1 A B a)))
           → ((b : B) → (C (injbs2 A B b)))
           → (a+b : bsum A B) → (C a+b)
indbsum A B C ca cb = indΣ C (ind𝟚 ca cb)
-- where ind𝟚's C = (λ b →  (t : rec𝟚 A B b) → C (dpair b t))

indbs1 : ∀ {A B : Set} (P : (bsum A B) → Set)
         → (fa : (a : A) → P (injbs1 A B a))
         → (fb : (b : B) → P (injbs2 A B b))
         → (a : A)
         → indbsum A B P fa fb (injbs1 A B a) ≡ fa a
indbs1 P fa fb x = refl (fa x)

indbs2 : ∀ {A B : Set} (P : (bsum A B) → Set)
         → (fa : (a : A) → P (injbs1 A B a))
         → (fb : (b : B) → P (injbs2 A B b))
         → (b : B)
         → indbsum A B P fa fb (injbs2 A B b) ≡ fb b
indbs2 P fa fb x = refl (fb x)

rec⊎ : ∀ {ℓ₁ ℓ₂ ℓ₃} → {A : Set ℓ₁} {B : Set ℓ₂} →
       (C : Set ℓ₃) → (A → C) → (B → C) → (A ⊎ B → C)
rec⊎ C f g (inj₁ a) = f a
rec⊎ C f g (inj₂ b) = g b

------------------------------------------------------------------------------
--- Exercise 1.10
-- Show that the Ackermann function ack : ℕ → ℕ → ℕ is definable using
-- only recℕ satisfying the following equations:
-- ack(0, m) = succ(m)  -> ack(0) = suc
-- ack(succ(n), 0) = ack(n, 1)  -> ack (suc n) =
-- ack(succ(n), succ(m)) = ack(n, ack(succ(n), m)).

ack : ℕ → ℕ → ℕ
ack = recℕ (ℕ → ℕ)
           suc
           (λ n ackn → recℕ ℕ (ackn 1) (λ m res → (ackn res)))

acktest00 : ack 0 0 ≡ 1
acktest00 = refl 1

acktest01 : ack 0 1 ≡ 2
acktest01 = refl 2

acktest10 : ack 1 0 ≡ 2
acktest10 = refl 2

acktest11 : ack 1 1 ≡ 3
acktest11 = refl 3

acktest22 : ack 2 2 ≡ 7
acktest22 = refl 7

------------------------------------------------------------------------------
--- Exercise 1.11
-- Show that for any type A, we have ¬¬¬A → ¬A.

¬ : Set → Set
¬ P = P → ⊥

ex11 : ∀ (P : Set) → ¬ (¬ (¬ P)) → ¬ P
ex11 P = λ nnnP → λ P → nnnP (λ nP → nP P)

------------------------------------------------------------------------------
-- Exercise 1.12
-- Using the propositions as types interpretation, derive the following tautologies.
-- (i) If A, then (if B then A).
-- (ii) If A, then not (not A).
-- (iii) If (not A or not B), then not (A and B).

ex12i : ∀ (A : Set) → A → (Set → A)
ex12i = λ A a _ → a

ex12ii : ∀ (A : Set) → A → (¬ (¬ A))
ex12ii = λ A a → λ nA → nA a

ex12iii : ∀ (A B : Set) → (¬ (A ⊎ B)) → (¬ (A × B))
ex12iii = λ A B → λ nAorB → λ AandB → nAorB (inj₁ (fst AandB))

------------------------------------------------------------------------------
-- Exercise 1.13
-- Using propositions-as-types, derive the double negation of the principle of ex-
-- cluded middle, i.e. prove not (not (P or not P)).

ex13 : ∀ (P : Set) → (¬ (¬ (P ⊎ (¬ P))))
ex13 = λ P nPorPnot → nPorPnot (inj₂ (λ P → nPorPnot (inj₁ P)))

------------------------------------------------------------------------------
-- Exercise 1.16
-- Show that addition of natural numbers is commutative.

ex16 : ∀ (a b c : ℕ) → a + (b + c) ≡ (a + b) + c
ex16 = indℕ (λ a → (b c : ℕ) → a + (b + c) ≡ a + b + c)
            (λ b c → refl (b + c))
            (λ n IHn b c → cong suc (IHn b c))


module Preliminary where

  open import Prelude public

  ∃_ : ∀ {a b} {A : Set a} (B : A → Set b) → Set (a ⊔ b)
  ∃_ = Σ _

  data IsYes {a} {P : Set a} : Dec P → Set a where
    yes : (p : P) → IsYes (yes p)

  getProof : ∀ {a} {P : Set a} → {d : Dec P} → IsYes d → P
  getProof (yes p) = p

  add-zero : ∀ n → n ≡ n +N 0
  add-zero zero = refl
  add-zero (suc n) = cong suc (add-zero n)

  _≢_ : ∀ {ℓ} {A : Set ℓ} → A → A → Set ℓ
  a ≢ b = ¬ (a ≡ b)


------------------------------------------------------------------------------
-- Well-founded induction on natural numbers
------------------------------------------------------------------------------

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

-- Adapted from
-- http://www.iis.sinica.edu.tw/~scm/2008/well-founded-recursion-and-accessibility/
-- and the Agda standard library 0.8.1.

module FOT.FOTC.Data.Nat.Induction.Acc.WF-I where

open import FOTC.Base

open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Type

------------------------------------------------------------------------------
-- The accessibility predicate: x is accessible if everything which is
-- smaller than x is also accessible (inductively).
data AccLT : D → Set where
 accLT : ∀ {x} → N x → (∀ {y} → N y → y < x → AccLT y) → AccLT x

accFoldLT : {P : D → Set} →
            (∀ {x} → N x → (∀ {y} → N y → y < x → P y) → P x) →
            ∀ {x} → N x → AccLT x → P x
accFoldLT f Nx (accLT _ h) = f Nx (λ Ny y<x → accFoldLT f Ny (h Ny y<x))

-- The accessibility predicate encodes what it means to be
-- well-founded; if all elements are accessible, then LT is
-- well-founded.
WellFoundedLT : Set
WellFoundedLT = ∀ {x} → N x → AccLT x

WellFoundedInductionLT : {P : D → Set} →
                         WellFoundedLT →
                         (∀ {x} → N x → (∀ {y} → N y → y < x → P y) → P x) →
                         ∀ {x} → N x → P x
WellFoundedInductionLT wf f Nx = accFoldLT f Nx (wf Nx)


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Relation.Unary.Subtype where

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

open import Cubical.Relation.Unary
open import Cubical.Data.Nat

private
  variable
    a ℓ ℓ′ : Level
    A : Type a

Subtype : (P : Pred A ℓ) → Type _
Subtype {A = A} P = Σ[ x ∈ A ] x ∈ P

module _ (P : Pred A ℓ) where

  isOfHLevelSubtype : ∀ n → isOfHLevel (suc n) A → isOfHLevel (suc n) (Subtype P)
  isOfHLevelSubtype n hA = isOfHLevelΣ (suc n) hA (λ _ → isProp→isOfHLevelSuc n (isProp[ P ] _))

  isSetSubtype : isSet A → isSet (Subtype P)
  isSetSubtype = isOfHLevelSubtype 1

inclusion : (P : Pred A ℓ) (Q : Pred A ℓ′) → P ⊆ Q → Subtype P → Subtype Q
inclusion P Q P⊆Q = λ (x , Px) → (x , P⊆Q Px)

⇔-equiv : (P : Pred A ℓ) (Q : Pred A ℓ′) → P ⇔ Q → Subtype P ≃ Subtype Q
⇔-equiv P Q (P⊆Q , Q⊆P) = isoToEquiv (iso (inclusion P Q P⊆Q) (inclusion Q P Q⊆P)
                                            (λ (x , Qx) → cong (x ,_) (Q x .snd _ _))
                                            (λ (x , Px) → cong (x ,_) (P x .snd _ _))
                                       )


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

open import BTree {A}
open import BTree.Equality {A}

data _⋗_ : BTree → BTree → Set where
  ⋗lf : (x : A)
                → (node x leaf leaf) ⋗ leaf
  ⋗nd : {l r l' r' : BTree}
                → (x x' : A)
                → l ≃ r
                → l' ≃ r'
                → l ⋗ l'
                → (node x l r) ⋗ (node x' l' r')

mutual
  data _⋘_ : BTree → BTree → Set where
    lf⋘ : leaf ⋘ leaf
    ll⋘ : {l r l' r' : BTree}
                  → (x x' : A)
                  → (l⋘r : l ⋘ r)
                  → (l'≃r' : l' ≃ r')
                  → r ≃ l'
                  → (node x l r) ⋘ (node x' l' r')
    lr⋘ : {l r l' r' : BTree}
                  → (x x' : A)
                  → (l⋙r : l ⋙ r)
                  → (l'≃r' : l' ≃ r')
                  → l ⋗ l'
                  → (node x l r) ⋘ (node x' l' r')

  data _⋙_ : BTree → BTree → Set where
    ⋙lf : (x : A)
                  → (node x leaf leaf) ⋙ leaf
    ⋙rl : {l r l' r' : BTree}
                  → (x x' : A)
                  → (l≃r : l ≃ r)
                  → (l'⋘r' : l' ⋘ r')
                  → l ⋗ r'
                  → (node x l r) ⋙ (node x' l' r')
    ⋙rr : {l r l' r' : BTree}
                  → (x x' : A)
                  → (l≃r : l ≃ r)
                  → (l'⋙r' : l' ⋙ r')
                  →  l ≃ l'
                  → (node x l r) ⋙ (node x' l' r')

data Complete : BTree → Set where
  leaf : Complete leaf
  left : {l r : BTree}
                 (x : A)
                 → Complete l
                 → Complete r
                 → l ⋘ r
                 → Complete (node x l r)
  right : {l r : BTree}
                 (x : A)
                 → Complete l
                 → Complete r
                 → l ⋙ r
                 → Complete (node x l r)


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

open import Cubical.Foundations.Prelude

open import Cubical.Data.Nat renaming(_+_ to _+n_; _·_ to _·n_)

open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.Instances.Int

open import Cubical.Algebra.Polynomials.Multivariate.Base
open import Cubical.Algebra.Polynomials.Multivariate.Properties

private variable
  ℓ : Level

-----------------------------------------------------------------------------
-- General Nth polynome

module _
  (A : CommRing ℓ)
  (n : ℕ)
  where

  open CommRingStr
  open RingTheory (CommRing→Ring A)
  open Nth-Poly-structure A n

  PolyCommRing : CommRing ℓ
  fst PolyCommRing = Poly A n
  0r (snd PolyCommRing) = 0P
  1r (snd PolyCommRing) = 1P
  _+_ (snd PolyCommRing) = _poly+_
  _·_ (snd PolyCommRing) = _poly*_
  - snd PolyCommRing = polyInv
  isCommRing (snd PolyCommRing) = makeIsCommRing
                                  trunc
                                  poly+Assoc
                                  poly+IdR
                                  poly+InvR
                                  poly+Comm
                                  poly*Assoc
                                  poly*IdR
                                  poly*DistR
                                  poly*Comm

-----------------------------------------------------------------------------
-- Notation and syntax in the case 1,2,3 and ℤ

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

  A[X1,···,Xn] : CommRing ℓ
  A[X1,···,Xn] = PolyCommRing Ar n

  A[x1,···,xn] : Type ℓ
  A[x1,···,xn] = fst (A[X1,···,Xn])


module Logic.Predicate where

open import Functional
import      Lvl
open import Logic
open import Logic.Propositional
open import Type
open import Type.Properties.Inhabited

------------------------------------------
-- Existential quantification (Existance, Exists)

module _ {ℓ₁}{ℓ₂} where
  record ∃ {Obj : Type{ℓ₁}} (Pred : Obj → Stmt{ℓ₂}) : Stmt{ℓ₁ Lvl.⊔ ℓ₂} where
    eta-equality
    constructor [∃]-intro
    field
      witness   : Obj
      ⦃ proof ⦄ : Pred(witness)

  [∃]-witness : ∀{Obj}{Pred} → ∃{Obj}(Pred) → Obj
  [∃]-witness([∃]-intro(x) ⦃ _ ⦄ ) = x

  [∃]-proof : ∀{Obj}{Pred} → (e : ∃{Obj}(Pred)) → Pred([∃]-witness(e))
  [∃]-proof([∃]-intro(_) ⦃ proof ⦄ ) = proof

  [∃]-elim : ∀{ℓ₃}{Obj}{Pred}{Z : Stmt{ℓ₃}} → (∀{x : Obj} → Pred(x) → Z) → (∃{Obj} Pred) → Z
  [∃]-elim (f) ([∃]-intro(_) ⦃ proof ⦄) = f(proof)

  syntax ∃{T}(λ x → y) = ∃❪ x ꞉ T ❫․ y

  {- TODO: This would allow the syntax: ∃ₗ x ↦ P(x)
    ∃ₗ_ = ∃
    infixl 1 ∃ₗ_
  -}

private variable ℓ : Lvl.Level
private variable Obj X Y Z W : Type{ℓ}
private variable Pred P Q R : X → Type{ℓ}

[∃]-map-proof : (∀{x} → P(x) → Q(x)) → ((∃ P) → (∃ Q))
[∃]-map-proof (f) ([∃]-intro(x) ⦃ proof ⦄) = [∃]-intro(x) ⦃ f(proof) ⦄

[∃]-map : (f : X → X) → (∀{x} → P(x) → Q(f(x))) → ((∃ P) → (∃ Q))
[∃]-map f p ([∃]-intro(x) ⦃ proof ⦄) = [∃]-intro(f(x)) ⦃ p(proof) ⦄

[∃]-map₂ : (f : X → Y → Z) → (∀{x y} → P(x) → Q(y) → R(f x y)) → ((∃ P) → (∃ Q) → (∃ R))
[∃]-map₂ f p ([∃]-intro(x) ⦃ proof₁ ⦄) ([∃]-intro(y) ⦃ proof₂ ⦄) = [∃]-intro(f x y) ⦃ p proof₁ proof₂ ⦄

[∃]-map-proof-dependent : (ep : ∃ P) → (P(∃.witness ep) → Q(∃.witness ep)) → (∃ Q)
[∃]-map-proof-dependent ([∃]-intro(x) ⦃ proof ⦄) f = [∃]-intro(x) ⦃ f(proof) ⦄

------------------------------------------
-- Universal quantification (Forall, All)

∀ₗ : (Pred : Obj → Stmt{ℓ}) → Stmt
∀ₗ (Pred) = (∀{x} → Pred(x))

[∀]-intro : ((a : Obj) → Pred(a)) → ∀ₗ(x ↦ Pred(x))
[∀]-intro p{a} = p(a)

[∀]-elim : ∀ₗ(x ↦ Pred(x)) → (a : Obj) → Pred(a)
[∀]-elim p(a) = p{a}

-- Eliminates universal quantification for a non-empty domain using a witnessed existence which proves that the domain is non-empty.
[∀ₑ]-elim : ⦃ _ : ◊ Obj ⦄ → ∀{P : Obj → Stmt{ℓ}} → ∀ₗ(x ↦ P(x)) → P([◊]-existence)
[∀ₑ]-elim {Obj = Obj} ⦃ proof ⦄ {P} apx = [∀]-elim {Obj = Obj}{P} apx(◊.existence(proof))

syntax ∀ₗ{T}(λ x → y) = ∀❪ x ꞉ T ❫․ y

∀⁰ : (Pred : Stmt{ℓ}) → Stmt
∀⁰ = id

∀¹ : (Pred : X → Stmt{ℓ}) → Stmt
∀¹ (Pred) = ∀⁰(∀ₗ ∘₀ Pred)
-- ∀¹ (Pred) = (∀{x} → Pred(x))

∀² : (Pred : X → Y → Stmt{ℓ}) → Stmt
∀² (Pred) = ∀¹(∀ₗ ∘₁ Pred)
-- ∀² (Pred) = (∀{x}{y} → Pred(x)(y))

∀³ : (Pred : X → Y → Z → Stmt{ℓ}) → Stmt
∀³ (Pred) = ∀²(∀ₗ ∘₂ Pred)
-- ∀³ (Pred) = (∀{x}{y}{z} → Pred(x)(y)(z))

∀⁴ : (Pred : X → Y → Z → W → Stmt{ℓ}) → Stmt
∀⁴ (Pred) = ∀³(∀ₗ ∘₃ Pred)
-- ∀⁴ (Pred) = (∀{x}{y}{z}{w} → Pred(x)(y)(z)(w))


module Oscar.Relation where

open import Oscar.Level

_⟨_⟩→_ : ∀ {a} {A : Set a} {b} → A → (A → Set b) → A → Set b
m ⟨ B ⟩→ n = B m → B n

Transitive : ∀ {a} {A : Set a} {b} (B : A → A → Set b) → Set (a ⊔ b)
Transitive B = ∀ {y z} → B y z → ∀ {x} → B x y → B x z

module _ {𝔬} {⋆ : Set 𝔬} {𝔪} {_↦_ : ⋆ → ⋆ → Set 𝔪} (_∙_ : Transitive _↦_) {𝔮} (_≞_ : ∀ {x} {y} → x ↦ y → x ↦ y → Set 𝔮) where

  Extensional : Set (𝔬 ⊔ 𝔪 ⊔ 𝔮)
  Extensional =
    ∀ {x y} {f₁ f₂ : x ↦ y}
    → f₁ ≞ f₂ → ∀ {z} {g₁ g₂ : y ↦ z}
    → g₁ ≞ g₂
    → (g₁ ∙ f₁) ≞ (g₂ ∙ f₂)

  Associative : Set (𝔬 ⊔ 𝔪 ⊔ 𝔮)
  Associative =
    ∀ {w x}
      (f : w ↦ x)
    {y}
      (g : x ↦ y)
    {z}
      (h : y ↦ z)
    → ((h ∙ g) ∙ f) ≞ (h ∙ (g ∙ f))


module Logic.Structure.Monoid where

import Logic.Equivalence
import Logic.Operations as Operations

open Logic.Equivalence using (Equivalence; module Equivalence)
open Operations.Param

data Monoid (A : Set)(Eq : Equivalence A) : Set where
  monoid :
    (z   : A)
    (_+_ : A -> A -> A)
    (leftId : LeftIdentity Eq z _+_)
    (rightId : RightIdentity Eq z _+_)
    (assoc : Associative Eq _+_) ->
    Monoid A Eq

-- There should be a simpler way of doing this. Local definitions to data declarations?

module Projections where

  zero : {A : Set}{Eq : Equivalence A} -> Monoid A Eq -> A
  zero (monoid z _ _ _ _) = z

  plus : {A : Set}{Eq : Equivalence A} -> Monoid A Eq -> A -> A -> A
  plus (monoid _ p _ _ _) = p

  leftId : {A : Set}{Eq : Equivalence A}(Mon : Monoid A Eq) -> LeftIdentity Eq (zero Mon) (plus Mon)
  leftId (monoid _ _ li _ _) = li

  rightId : {A : Set}{Eq : Equivalence A}(Mon : Monoid A Eq) -> RightIdentity Eq (zero Mon) (plus Mon)
  rightId (monoid _ _ _ ri _) = ri

  assoc : {A : Set}{Eq : Equivalence A}(Mon : Monoid A Eq) -> Associative Eq (plus Mon)
  assoc (monoid _ _ _ _ a) = a

module Monoid {A : Set}{Eq : Equivalence A}(Mon : Monoid A Eq) where

  zero    = Projections.zero Mon
  _+_     = Projections.plus Mon
  leftId  = Projections.leftId Mon
  rightId = Projections.rightId Mon
  assoc   = Projections.assoc Mon


module Induct where

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

{- The downward closure of a set. -}
data downset : (ℕ → Set) → ℕ → Set where
  downset-lit : {φ : ℕ → Set} → {x : ℕ} → φ x → downset φ x
  downset-pred : {φ : ℕ → Set} → {x : ℕ} → downset φ (suc x) → downset φ x

{- Example 1: The downset of a downset is itself. Simple induction. -}
downset-downset : ∀ {φ} → ∀ {x} → downset (downset φ) x → downset φ x
downset-downset (downset-lit p) = p
downset-downset (downset-pred p) = downset-pred (downset-downset p)

{- Example 2: Downsets are downward closed. Needs lemmata. -}
lemma-larger : ∀ x y → x ≤ y → (suc x ≤ y) ⊎ (x ≡ y)
lemma-larger 0 0 z≤n = inj₂ refl
lemma-larger 0 (suc y) z≤n = inj₁ (s≤s z≤n)
lemma-larger (suc x) (suc y) (s≤s x-leq-y) with lemma-larger x y x-leq-y
lemma-larger (suc x) (suc y) (s≤s x-leq-y) | inj₁ sx-leq-y = inj₁ (s≤s sx-leq-y)
lemma-larger (suc x) (suc y) (s≤s x-leq-y) | inj₂ x-eq-y = inj₂ (cong suc x-eq-y)

downset-zero : ∀ {φ} → ∀ y → downset φ y → downset φ zero
downset-zero zero d-phi-y = d-phi-y
downset-zero (suc y) d-phi-y = downset-zero y (downset-pred d-phi-y)

downset-downward-closed : ∀ {φ} → ∀ x y → downset φ y → x ≤ y → downset φ x
downset-downward-closed 0 y d-phi-y x-leq-y = downset-zero y d-phi-y
downset-downward-closed
  (suc x) (suc y) d-phi-y (s≤s x-leq-y) with lemma-larger x y x-leq-y
... | inj₁ sx-leq-y = downset-downward-closed (suc x) y (downset-pred d-phi-y) sx-leq-y
... | inj₂ refl = d-phi-y


-- Andreas, 2012-09-15
module InstanceArgumentsDontDiscardCandidateUponUnsolvedConstraints where

import Common.Level

data ⊥ : Set where
record ⊤ : Set where

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

_≤_ : Nat → Nat → Set
zero    ≤ m       = ⊤
(suc n) ≤ zero    = ⊥
(suc n) ≤ (suc m) = n ≤ m

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

lookup : {A : Set}{n : Nat} (m : Nat) {{m≤n : suc m ≤ n}} → Vec A n → A
lookup m {{()}} []
lookup zero     (a ∷ as) = a
lookup (suc m)  (a ∷ as) = lookup m as
-- the instance in the recursive call is only found
-- if the candiate  m≤n : suc m ≤ _n  is kept
-- until the blocking _n has been solved


open import x1-Base

module x3-PropositionalEquality where

data _≡_ {X} : Rel X where
  refl : ∀ {x} → x ≡ x
{-
What propositional equality means.

DEFINITIONAL EQUALITY

To decide if two types/terms are "the same", it reduces them to their normal form,
then compares them syntactically, plus some additional laws.

Every term terminates, so refl : ((λ x → x) 1) ≡ 1 is acceptable

PROPOSITIONAL EQUALITY
User-level equality expressed by the inductive family defined.

Having a propositional equality in scope does not imply definitional equality for the
related terms, unless the propositional equality is a closed term.
That is the reason ≡ is not used from the beginning.
Instead, chose to parametrise the equality relation:
- sometimes propositional equality does not work, for example when working with functions.

In general there might be propositional equalities in scope
 that do not necessarily hold or involve abstracted variables, think of λ (p : 3 ≡ 1) → ....
-}
-- prove ≡ is an equivalence relation

sym : ∀ {X} {x y : X} → x ≡ y → y ≡ x
sym refl = refl

trans : ∀ {X} {x y z : X} → x ≡ y → y ≡ z → x ≡ z
trans refl refl = refl

equivalence : ∀ {X} → Equivalence {X} _≡_
equivalence = record { refl = refl; sym = sym; trans = trans }

-- useful
cong : ∀ {X} {x y : X} → (f : X → X) → x ≡ y → f x ≡ f y
cong _ refl = refl
{-
Above uses pattern matching in a new way:

Since value of indices of ≡ depends on constructors,
matching on constructor refines context with new information.

E.g., sym matching refl will unify y and x,
turning them into the same variable in the context for the body of sym,
therefore letting refl be invoked again.

DEPENDENT PATTERN MATCHING
Pattern matching in Agda can change the context AND constraint possible constructors
 of other parameters, if they are a type with indices and those indices have been refined.
-}


{-# OPTIONS --type-in-type #-} -- enables cheating
                               -- 2:10 : why

-- https://www.youtube.com/watch?v=2LxtHeZlaVw

module HC-Lec3 where

open import HC-Lec1
open import HC-Lec2

postulate
  -- equal inputs go to equal outputs
  -- (whereas structural equality would require equal implementations)
  extensionality : {S : Set} {T : S -> Set}
                -- 24:00 : make f and g implicit : https://www.youtube.com/watch?v=RCRddhYegzI
                -> {f g : (x : S) -> T x}
                -> ((x : S) -> f x == g x)
                ->             f   == g

-- 2:45

record Category : Set where
  field
    -- things
    Obj  : Set
    _~>_ : Obj -> Obj -> Set

    -- operations
    id~>  : {T     : Obj} -> T ~> T
    _>~>_ : {R S T : Obj} -> R ~> S -> S ~> T -> R ~> T

    -- laws
    law-id~>>~> : {S T : Obj} -> (f : S ~> T) -> (id~> >~> f)    == f
    law->~>id~> : {S T : Obj} -> (f : S ~> T) -> (f    >~> id~>) == f
    law->~>>~>  : {Q R S T : Obj}
               -> (f : Q ~> R) -> (g : R ~> S) -> (h : S ~> T)
               -> ((f >~> g) >~> h) == (f >~> (g >~> h))

-- 7:50

SET : Category
SET = record
        { Obj         = Set
        ; _~>_        = λ S T -> S -> T
        ; id~>        = id
        ; _>~>_       = _>>_
        ; law-id~>>~> = refl
        ; law->~>id~> = refl
        ; law->~>>~>  = λ f g h → refl (λ x → h (g (f x)))
        }

-- 11:50
-- A PREORDER is a category where there is at most one arrow between any two objects.
-- Therefore arrows are unique.

-- all proofs of '>=' are equivalent
unique->= : (m n : Nat) (p q : m >= n) -> p == q
unique->=      m   zero   p q = refl <>
unique->= (suc m) (suc n) p q = unique->= m n p q

NAT->= : Category
NAT->= = record
           { Obj         = Nat
           ; _~>_        = _>=_
           ; id~>        = λ {n : Nat} → refl->= n
           ; _>~>_       = λ {r s t : Nat} r>=s s>=t → trans->= r s t r>=s s>=t
           ; law-id~>>~> = λ {m n : Nat} f → unique->= m n (trans->= m m n (refl->= m) f) f
           ; law->~>id~> = λ {m n : Nat} f → unique->= m n (trans->= m n n f (refl->= n)) f
           ; law->~>>~>  = λ {q r s t : Nat} q>=r r>=s s>=t →
                            unique->= q t
                              (trans->= q s t (trans->= q r s q>=r r>=s) s>=t)
                              (trans->= q r t  q>=r (trans->= r s t r>=s s>=t))
           }

-- 22:07

-- A MONOID is a category with Obj = One.
-- The values in the monoid are the *arrows*.
-- Only one object, so arrows are the ways to get from Nat to Nat.
ONE-Nat : Category
ONE-Nat = record
            { Obj         = One
            ; _~>_        = λ _ _ -> Nat
            ; id~>        = zero
            ; _>~>_       = _+N_
            ; law-id~>>~> = refl
            ; law->~>id~> = +N-zero
            ; law->~>>~>  = assocLR-+N
            }

-- 26:05

-- Note : this is controversial : because this notion of equality is structural.
-- But equality could be defined by isomorphism instead.
-- - In this case, there could be multiple ways for something to be equal to itself.
-- - equality on Bool : True to True  and False to False
--                  or  True to False and False to True

-- structural equality is unique
eqUnique : {X : Set} {x y : X} -> (p q : x == y) -> p == q
eqUnique (refl x) (refl _) = refl (refl x)

eqUnique' : {X : Set} {x y : X} -> (p q : x == y) -> p == q
eqUnique' {_} {_} {y} p q rewrite q | p = refl (refl y)

DISCRETE : (X : Set) -> Category
DISCRETE X = record
               { Obj         = X
               ; _~>_        = _==_
               ; id~>        = λ {x} -> refl x
               ; _>~>_       = λ {(refl x) (refl .x) → refl x}
               ; law-id~>>~> = λ f     → eqUnique _ f
               ; law->~>id~> = λ f     → eqUnique _ f
               ; law->~>>~>  = λ f g h → eqUnique _ _
               }

-- 40:50

module FUNCTOR where
  open Category

  record _=>_ (C D : Category) : Set where -- functor from C to D
    field
      -- operations
      F-Obj : Obj C -> Obj D
      F-map : {S T : Obj C} -> _~>_ C S T -> _~>_ D (F-Obj S) (F-Obj T)

      -- laws
      F-map-id~> : {T : Obj C} -> F-map (id~> C {T}) == id~> D {F-Obj T}
      F-map->~>  : {R S T : Obj C} (f : _~>_ C R S) (g : _~>_ C S T)
                -> F-map (_>~>_ C f g) == _>~>_ D (F-map f) (F-map g)
open FUNCTOR

-- https://www.youtube.com/watch?v=RCRddhYegzI

-- 4:10

vMap : {n : Nat} {S T : Set} -> (S -> T) -> Vec S n -> Vec T n
vMap f       []  = []
vMap f (x :: xs) = f x :: vMap f xs

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

vMapCp : {n : Nat} {R S T : Set} {r->s : R -> S} {s->t : S -> T}
      -> (xs : Vec R n)
      -> vMap (r->s >> s->t) xs == vMap s->t (vMap r->s xs)
vMapCp []  = refl []
vMapCp {r->s = r->s} {s->t = s->t} (r :: rs)
  with vMapCp {r->s = r->s} {s->t = s->t} rs
...| xxx
  rewrite xxx
   = refl (s->t (r->s r) :: vMap s->t (vMap r->s rs))

-- vector as a functor
VEC : Nat -> SET => SET
VEC n = record
          { F-Obj      = λ X -> Vec X n
          ; F-map      = vMap
          ; F-map-id~> = extensionality vMap-id
          ; F-map->~>  = λ {R S T : Set} (r->s : R → S) (s->t : S → T)
                         → extensionality vMapCp
          }

-- 15:20

vTakeCp : {X : Set} {m n p : Nat}
       -> (m>=n : m >= n) -> (n>=p : n >= p) -> (xs : Vec X m)
       -> vTake m p (trans->= m n p m>=n n>=p)                 xs
       == vTake n p                      n>=p  (vTake m n m>=n xs)
vTakeCp {_}     {m}     {n}  {zero} m>=n n>=p _ = refl []
vTakeCp {_} {suc m} {suc n} {suc p} m>=n n>=p (x :: xs)
  rewrite vTakeCp {_} {m} {n} {p} m>=n n>=p xs
  = refl (x :: vTake n p n>=p (vTake m n m>=n xs))

-- another way to think of vector as a functor
VTAKE : Set -> NAT->= => SET
VTAKE X = record
            { F-Obj      = Vec X  -- something that turns a number into a SET
            ; F-map      = λ {m} {n} m>=n xs -> vTake m n m>=n xs
            ; F-map-id~> = extensionality (vTakeIdFact _)
            ; F-map->~>  = λ r>=s s>=t → extensionality (vTakeCp r>=s s>=t)
            }

-- 24:53

ADD : Nat -> NAT->= => NAT->=
ADD d = record
          { F-Obj = (d +N_) -- function that adds d to another number
          ; F-map = λ {s} {t} -> f-map' d s t
          -- two proofs of >= are equal to each other
          ; F-map-id~> = λ {T : Nat} -> unique->= (d +N T)
                                                  (d +N T)
                                                  (f-map' d T T (refl->= T))
                                                  (refl->= (d +N T))
          ; F-map->~> = λ {R} {S} {T}f g →
                                       unique->= (d +N R)
                                                 (d +N T)
                                                 (f-map' d R T (trans->= R S T f g))
                                                 (trans->= (d +N R) (d +N S) (d +N T)
                                                           (f-map' d R S f)
                                                           (f-map' d S T g))
          }
 where
  f-map' : ∀ d s t
         → (NAT->= Category.~>       s)        t
         → (NAT->= Category.~> (d +N s)) (d +N t)
  f-map'  zero   s t s>=t =              s>=t
  f-map' (suc d) s t s>=t = f-map' d s t s>=t

-- 41:00

CATEGORY : Category
CATEGORY = record
             { Obj         = Category
             ; _~>_        = _=>_
             ; id~>        = λ {T} -> record
                                        { F-Obj      = id
                                        ; F-map      = id
                                        ; F-map-id~> = refl (Category.id~> T)
                                        ; F-map->~>  = λ f g → refl ((T Category.>~> f) g)
                                        }
             ; _>~>_       = λ r=>s s=>t → record
                                             { F-Obj      = F-Obj r=>s >> F-Obj s=>t
                                             ; F-map      = F-map r=>s >> F-map s=>t
                                             ; F-map-id~> = {!!}
                                             ; F-map->~>  = λ f g → {!!}
                                             }
             ; law-id~>>~> = {!!}
             ; law->~>id~> = {!!}
             ; law->~>>~>  = {!!}
             }
             where open _=>_

-- 45:35
-- Category where Obj are FUNCTORS


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

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

module Groups.Subgroups.Examples {a b : _} {A : Set a} {S : Setoid {a} {b} A} {_+_ : A → A → A} (G : Group S _+_) where

open import Groups.Subgroups.Definition G
open import Groups.Lemmas G

open Group G
open Setoid S
open Equivalence eq

trivialSubgroupPred : A → Set b
trivialSubgroupPred a = (a ∼ 0G)

trivialSubgroup : Subgroup trivialSubgroupPred
Subgroup.isSubset trivialSubgroup x=y x=0 = transitive (symmetric x=y) x=0
Subgroup.closedUnderPlus trivialSubgroup x=0 y=0 = transitive (+WellDefined x=0 y=0) identLeft
Subgroup.containsIdentity trivialSubgroup = reflexive
Subgroup.closedUnderInverse trivialSubgroup x=0 = transitive (inverseWellDefined x=0) invIdent

improperSubgroupPred : A → Set
improperSubgroupPred a = True

improperSubgroup : Subgroup improperSubgroupPred
Subgroup.isSubset improperSubgroup _ _ = record {}
Subgroup.closedUnderPlus improperSubgroup _ _ = record {}
Subgroup.containsIdentity improperSubgroup = record {}
Subgroup.closedUnderInverse improperSubgroup _ = record {}


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

open import BTree {A}
open import BTree.Equality {A}
open import BTree.Equality.Properties {A}
open import BTree.Complete.Alternative {A}
open import Data.Sum renaming (_⊎_ to _∨_)

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

lemma-≃-⋘ : {l r : BTree} → l ≃ r → l ⋘ r
lemma-≃-⋘ ≃lf = lf⋘
lemma-≃-⋘ (≃nd x x' l≃r l≃l' l'≃r') = ll⋘ x x' (lemma-≃-⋘ l≃r) l'≃r' (trans≃ (symm≃ l≃r) l≃l')

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


module Nat where

data Rel (A : Set) : Set1 where
  rel : (A -> A -> Set) -> Rel A

_is_than_ : {A : Set} -> A -> Rel A -> A -> Set
x is rel f than y = f x y

data Acc {A : Set} (less : Rel A) (x : A) : Set where
  acc : ((y : A) -> x is less than y -> Acc less y) -> Acc less x

data WO {A : Set} (less : Rel A) : Set where
  wo : ((x : A) -> Acc less x) -> WO less

data False : Set where
data True : Set where
  tt : True

data Nat : Set where
  Z : Nat
  S : Nat -> Nat

data ∏ {A : Set} (f : A -> Set) : Set where
  ∏I : ((z : A) -> f z) -> ∏ f

ltNat : Nat -> Nat -> Set
ltNat Z Z = False
ltNat Z (S n) = True
ltNat (S m) (S n) = ltNat m n
ltNat (S m) Z = False

ltNatRel : Rel Nat
ltNatRel = rel ltNat

postulate woltNat : WO ltNatRel

idN : Nat -> Nat
idN x = x

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

down1 : Nat -> Nat
down1 Z = Z
down1 (S n) = down1 n

-- measure down1 = (woNat, id)
-- For a function f : (x:A) -> B(x)
-- f(p) = ... f(x1) ... f(x2)
-- measure_set : Set
-- measure_rel : Rel measure_set
-- measure_ord : WO measure_rel
-- measure_fun : A -> measure_set
-- For j-th call of f in i-th clause of f:
-- measure_i_j_hint : xj is measure_rel than p

data Measure : Set1 where
  μ : {M : Set} -> {rel : Rel M} -> (ord : WO rel) -> Measure

{-
down1_measure_set : Set
down1_measure_set = Nat
down1_measure_rel : Rel down1_measure_set
down1_measure_rel = ltNatRel
down1_measure_ord : WO measure_rel
down1_measure_ord = woltNat
-}
down1-measure : Measure
down1-measure = μ woltNat

down1-2-1-hint : (n : Nat) -> n is ltNatRel than (S n)
down1-2-1-hint Z = tt
down1-2-1-hint (S n) = down1-2-1-hint n

{-
down2 : Nat -> Nat
down2 Z = Z
down2 (S n) = down2 (idN n)

down3 : Nat -> Nat
down3 Z = Z
down3 (S n) = down3 (id n)

plus : Nat -> Nat -> Nat
plus Z n = n
plus (S m) n = S(plus m n)
-}


-- binary search trees (not balanced)

open import bool
open import bool-thms2
open import eq
open import maybe
open import product
open import product-thms
open import bool-relations using (transitive ; total)

module bst (A : Set) (_≤A_ : A → A → 𝔹)
           (≤A-trans : transitive _≤A_)
           (≤A-total : total _≤A_) where

open import bool-relations _≤A_ hiding (transitive ; total)
open import minmax _≤A_ ≤A-trans ≤A-total

data bst : A → A → Set where
  bst-leaf : ∀ {l u : A} → l ≤A u ≡ tt → bst l u
  bst-node : ∀ {l l' u' u : A}(d : A) →
               bst l' d → bst d u' →
               l ≤A l' ≡ tt → u' ≤A u ≡ tt →
               bst l u

-- find a node which is isomorphic (_=A_) to d and return it; or else return nothing
bst-search : ∀{l u : A}(d : A) → bst l u → maybe (Σ A (λ d' → d iso𝔹 d' ≡ tt))
bst-search d (bst-leaf _) = nothing
bst-search d (bst-node d' L R _ _) with keep (d ≤A d')
bst-search d (bst-node d' L R _ _) | tt , p1 with keep (d' ≤A d)
bst-search d (bst-node d' L R _ _) | tt , p1 | tt , p2 = just (d' , iso𝔹-intro p1 p2)
bst-search d (bst-node d' L R _ _) | tt , p1 | ff , p2 = bst-search d L
bst-search d (bst-node d' L R _ _) | ff , p1 = bst-search d R

bst-dec-lb : ∀ {l l' u' : A} → bst l' u' → l ≤A l' ≡ tt → bst l u'
bst-dec-lb (bst-leaf p) q = bst-leaf (≤A-trans q p)
bst-dec-lb (bst-node d L R p1 p2) q = bst-node d L R (≤A-trans q p1) p2

bst-inc-ub : ∀ {l' u' u : A} → bst l' u' → u' ≤A u ≡ tt → bst l' u
bst-inc-ub (bst-leaf p) q = bst-leaf (≤A-trans p q)
bst-inc-ub (bst-node d L R p1 p2) q = bst-node d L R p1 (≤A-trans p2 q)

bst-insert : ∀{l u : A}(d : A) → bst l u → bst (min d l) (max d u)
bst-insert d (bst-leaf p) = bst-node d (bst-leaf ≤A-refl) (bst-leaf ≤A-refl) min-≤1 max-≤1
bst-insert d (bst-node d' L R p1 p2) with keep (d ≤A d')
bst-insert d (bst-node d' L R p1 p2) | tt , p with bst-insert d L
bst-insert d (bst-node d' L R p1 p2) | tt , p | L' rewrite p =
  bst-node d' L' (bst-inc-ub R (≤A-trans p2 max-≤2)) (min2-mono p1) ≤A-refl
bst-insert d (bst-node d' L R p1 p2) | ff , p with bst-insert d R
bst-insert d (bst-node d' L R p1 p2) | ff , p | R' rewrite p =
  bst-node d' (bst-dec-lb L p1) R' min-≤2 (max2-mono p2)


module Numeral.Matrix.Relations where

open import Data.Tuple as Tuple using (_,_)
open import Logic.Predicate
open import Logic
open import Numeral.Matrix hiding (module SquareMatrix)
open import Numeral.Matrix.OverField
import      Relator.Equals as Eq
open import Structure.Operator.Field
open import Structure.Operator.Monoid
open import Structure.Operator.Properties
open import Structure.Setoid
open import Syntax.Function
open import Type

module SquareMatrix {ℓ ℓₑ} {d} {T : Type{ℓ}} ⦃ equiv : Equiv{ℓₑ}(T) ⦄ where
  record Symmetric(M : SquareMatrix(d)(T)) : Stmt{ℓₑ} where
    constructor intro
    field proof : ∀{x y} → Matrix.proj M(x , y) ≡ Matrix.proj M(y , x)

  module _ {_▫_ : T → T → T} ⦃ monoid : Monoid(_▫_) ⦄ where
    record Diagonal(M : SquareMatrix(d)(T)) : Stmt{ℓₑ} where
      constructor intro
      field proof : ∀{x y} → (x Eq.≢ y) → (Matrix.proj M(x , y) ≡ Monoid.id monoid)

    record Scalar (s : T) (M : SquareMatrix(d)(T)) : Stmt{ℓₑ} where
      constructor intro
      field
        ⦃ diagonal ⦄ : Diagonal(M)
        proof : ∀{x} → (Matrix.proj M(x , x) ≡ s)

  module _ {_+ₛ_ _⋅ₛ_ : T → T → T} ⦃ field-structure : Field(_+ₛ_)(_⋅ₛ_) ⦄ where
    record Similar(A B : SquareMatrix(d)(T)) : Stmt{ℓₑ} where
      constructor intro
      field proof : ∃(P ↦ ∃{Obj = Invertible(P)}(inver ↦ B ≡ inv(P) ⨯ A ⨯ P))

    record Diagonalizable(M : SquareMatrix(d)(T)) : Stmt{ℓₑ} where
      constructor intro
      field proof : ∃(P ↦ ∃{Obj = Invertible(P)}(inver ↦ Diagonal(inv(P) ⨯ M ⨯ P)))