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

open import Categories.Category

module Categories.Diagram.Duality {o ℓ e} (C : Category o ℓ e) where

open Category C

open import Level
open import Function using (_$_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl)

open import Categories.Functor
open import Categories.Functor.Bifunctor
open import Categories.NaturalTransformation.Dinatural

open import Categories.Object.Initial
open import Categories.Object.Terminal
open import Categories.Object.Duality
open import Categories.Diagram.Equalizer op
open import Categories.Diagram.Coequalizer C
open import Categories.Diagram.Pullback op
open import Categories.Diagram.Pushout C
open import Categories.Diagram.Cone as Cone
open import Categories.Diagram.Cocone as Cocone
open import Categories.Diagram.End as End
open import Categories.Diagram.Coend as Coend
open import Categories.Diagram.Limit as Limit
open import Categories.Diagram.Colimit as Colimit
open import Categories.Category.Construction.Cocones using (Cocones)

private
  variable
    o′ ℓ′ e′ : Level
    D J : Category o′ ℓ′ e′
    A B : Obj
    f g : A ⇒ B

Coequalizer⇒coEqualizer : Coequalizer f g → Equalizer f g
Coequalizer⇒coEqualizer coe = record
  { arr       = arr
  ; equality  = equality
  ; equalize  = coequalize
  ; universal = universal
  ; unique    = unique
  }
  where open Coequalizer coe

coEqualizer⇒Coequalizer : Equalizer f g → Coequalizer f g
coEqualizer⇒Coequalizer e = record
  { arr        = arr
  ; equality   = equality
  ; coequalize = equalize
  ; universal  = universal
  ; unique     = unique
  }
  where open Equalizer e

coPullback⇒Pushout : Pullback f g → Pushout f g
coPullback⇒Pushout p = record
  { i₁              = p₁
  ; i₂              = p₂
  ; commute         = commute
  ; universal       = universal
  ; unique          = unique
  ; universal∘i₁≈h₁ = p₁∘universal≈h₁
  ; universal∘i₂≈h₂ = p₂∘universal≈h₂
  }
  where open Pullback p

Pushout⇒coPullback : Pushout f g → Pullback f g
Pushout⇒coPullback p = record
  { p₁              = i₁
  ; p₂              = i₂
  ; isPullback = record
    { commute         = commute
    ; universal       = universal
    ; unique          = unique
    ; p₁∘universal≈h₁ = universal∘i₁≈h₁
    ; p₂∘universal≈h₂ = universal∘i₂≈h₂
    }
  }
  where open Pushout p

module _ {F : Functor J C} where
  open Functor F renaming (op to Fop)

  coApex⇒Coapex : ∀ X → Apex Fop X → Coapex F X
  coApex⇒Coapex X apex = record
      { ψ       = ψ
      ; commute = commute
      }
    where open Cone.Apex apex

  coCone⇒Cocone : Cone Fop → Cocone F
  coCone⇒Cocone c = record
    { coapex = coApex⇒Coapex _ apex
    }
    where open Cone.Cone c

  Coapex⇒coApex : ∀ X → Coapex F X → Apex Fop X
  Coapex⇒coApex X coapex = record
      { ψ       = ψ
      ; commute = commute
      }
    where open Cocone.Coapex coapex

  Cocone⇒coCone : Cocone F → Cone Fop
  Cocone⇒coCone c = record
    { apex = Coapex⇒coApex _ coapex
    }
    where open Cocone.Cocone c

  coCone⇒⇒Cocone⇒ : ∀ {K K′} → Cone⇒ Fop K K′ → Cocone⇒ F (coCone⇒Cocone K′) (coCone⇒Cocone K)
  coCone⇒⇒Cocone⇒ f = record
    { arr     = arr
    ; commute = commute
    }
    where open Cone⇒ f

  Cocone⇒⇒coCone⇒ : ∀ {K K′} → Cocone⇒ F K K′ → Cone⇒ Fop (Cocone⇒coCone K′) (Cocone⇒coCone K)
  Cocone⇒⇒coCone⇒ f = record
    { arr     = arr
    ; commute = commute
    }
    where open Cocone⇒ f

  coLimit⇒Colimit : Limit Fop → Colimit F
  coLimit⇒Colimit lim = record
    { initial = op⊤⇒⊥ (Cocones F) $ record
      { ⊤             = coCone⇒Cocone ⊤
      ; ⊤-is-terminal = record
        { !        = coCone⇒⇒Cocone⇒ !
        ; !-unique = λ f → !-unique (Cocone⇒⇒coCone⇒ f)
        }
      }
    }
    where open Limit.Limit lim
          open Terminal terminal

  Colimit⇒coLimit : Colimit F → Limit Fop
  Colimit⇒coLimit colim = record
    { terminal = record
      { ⊤             = Cocone⇒coCone ⊥
      ; ⊤-is-terminal = record
        { !        = Cocone⇒⇒coCone⇒ !
        ; !-unique = λ f → !-unique (coCone⇒⇒Cocone⇒ f)
        }
      }
    }
    where open Colimit.Colimit colim
          open Initial initial

module _ {F : Bifunctor (Category.op D) D C} where
  open HomReasoning
  open Functor F renaming (op to Fop)

  coWedge⇒Cowedge : Wedge Fop → Cowedge F
  coWedge⇒Cowedge W = record
    { E         = E
    ; dinatural = DinaturalTransformation.op dinatural
    }
    where open Wedge W

  Cowedge⇒coWedge : Cowedge F → Wedge Fop
  Cowedge⇒coWedge W = record
    { E         = E
    ; dinatural = DinaturalTransformation.op dinatural
    }
    where open Cowedge W

  coEnd⇒Coend : End Fop → Coend F
  coEnd⇒Coend e = record
    { cowedge   = coWedge⇒Cowedge wedge
    ; factor    = λ W → factor (Cowedge⇒coWedge W)
    ; universal = universal
    ; unique    = unique
    }
    where open End.End e

  Coend⇒coEnd : Coend F → End Fop
  Coend⇒coEnd e = record
    { wedge     = Cowedge⇒coWedge cowedge
    ; factor    = λ W → factor (coWedge⇒Cowedge W)
    ; universal = universal
    ; unique    = unique
    }
    where open Coend.Coend e

module DiagramDualityConversionProperties where
  private
    Coequalizer⇔coEqualizer : ∀ (coequalizer : Coequalizer f g) →
      coEqualizer⇒Coequalizer (Coequalizer⇒coEqualizer coequalizer) ≡ coequalizer
    Coequalizer⇔coEqualizer _ = refl

    coPullback⇔Pushout : ∀ (coPullback : Pullback f g) →
      Pushout⇒coPullback (coPullback⇒Pushout coPullback) ≡ coPullback
    coPullback⇔Pushout _ = refl

    module _ {F : Functor J C} where
      open Functor F renaming (op to Fop)

      coApex⇔Coapex : ∀ X → (coApex : Apex Fop X) →
                        Coapex⇒coApex X (coApex⇒Coapex X coApex) ≡ coApex
      coApex⇔Coapex _ _ = refl

      coCone⇔Cocone : ∀ (coCone : Cone Fop) →
                        Cocone⇒coCone (coCone⇒Cocone coCone) ≡ coCone
      coCone⇔Cocone _ = refl

      coCone⇒⇔Cocone⇒ : ∀ {K K′} → (coCone⇒ : Cone⇒ Fop K K′) →
                        Cocone⇒⇒coCone⇒ (coCone⇒⇒Cocone⇒ coCone⇒) ≡ coCone⇒
      coCone⇒⇔Cocone⇒ _ = refl

      coLimit⇔Colimit : ∀ (coLimit : Limit Fop) →
                        Colimit⇒coLimit (coLimit⇒Colimit coLimit) ≡ coLimit
      coLimit⇔Colimit _ = refl

    module _ {F : Bifunctor (Category.op D) D C} where
      open Functor F renaming (op to Fop)

      coWedge⇔Cowedge : ∀ (coWedge : Wedge Fop) →
                        Cowedge⇒coWedge (coWedge⇒Cowedge coWedge) ≡ coWedge
      coWedge⇔Cowedge _ = refl

      coEnd⇔Coend : ∀ (coEnd : End Fop) →
                    Coend⇒coEnd (coEnd⇒Coend coEnd) ≡ coEnd
      coEnd⇔Coend _ = refl


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

open import HoTT
open import cohomology.Exactness

module cohomology.ExactPairIso where

{- An exact sequence 0 → G → H → 0 implies that G == H -}

module _ {i} {G H K L : Group i} {φ : H →ᴳ K}
  (ex : is-exact-seq (G ⟨ cst-hom ⟩→ H ⟨ φ ⟩→ K ⟨ cst-hom ⟩→ L ⊣|)) where

  private
    inj : (h₁ h₂ : Group.El H) → GroupHom.f φ h₁ == GroupHom.f φ h₂ → h₁ == h₂
    inj = zero-kernel-injective φ
      (λ h p → Trunc-rec (Group.El-level H _ _) (λ s → ! (snd s))
                 (ktoi (exact-get ex 0) h p))

  exact-pair-iso : H == K
  exact-pair-iso = surj-inj-= φ inj (λ k → ktoi (exact-get ex 1) k idp)

module _ {i} {G H K J : Group i} {φ : G →ᴳ H} {ψ : H →ᴳ K}
  {χ : K →ᴳ J} (p : G == 0ᴳ) (q : J == 0ᴳ)
  (ex : is-exact-seq (G ⟨ φ ⟩→ H ⟨ ψ ⟩→ K ⟨ χ ⟩→ J ⊣|)) where

  private
    ex₁ : is-exact-seq (0ᴳ ⟨ cst-hom ⟩→ H ⟨ ψ ⟩→ K ⟨ χ ⟩→ J ⊣|)
    ex₁ = transport
      (λ {(G' , φ') → is-exact-seq (G' ⟨ φ' ⟩→ H ⟨ ψ ⟩→ K ⟨ χ ⟩→ J ⊣|)})
      (pair= p (prop-has-all-paths-↓ {B = λ L → L →ᴳ H}
                 (raise-level -2 0ᴳ-hom-out-level)))
      ex

    ex₂ : is-exact-seq (0ᴳ ⟨ cst-hom ⟩→ H ⟨ ψ ⟩→ K ⟨ cst-hom ⟩→ 0ᴳ ⊣|)
    ex₂ = transport
      (λ {(J' , χ') → is-exact-seq (0ᴳ ⟨ cst-hom ⟩→ H ⟨ ψ ⟩→ K ⟨ χ' ⟩→ J' ⊣|)})
      (pair= q (prop-has-all-paths-↓ {B = λ L → K →ᴳ L}
                 (raise-level _ 0ᴳ-hom-in-level)))
      ex₁

  exact-pair-path-iso : H == K
  exact-pair-path-iso = exact-pair-iso ex₂


------------------------------------------------------------------------
-- The Agda standard library
--
-- Example showing how to define an indexed container
------------------------------------------------------------------------

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

module README.Data.Container.Indexed where

open import Data.Unit
open import Data.Empty
open import Data.Nat.Base
open import Data.Product
open import Function
open import Data.W.Indexed
open import Data.Container.Indexed
open import Data.Container.Indexed.WithK

module _ {a} (A : Set a) where

------------------------------------------------------------------------
-- Vector as an indexed container

-- An indexed container is defined by three things:
-- 1. Commands the user can emit
-- 2. Responses the indexed container returns to these commands
-- 3. Update of the index based on the command and the response issued.

-- For a vector, commands are constructors, responses are the number of subvectors
-- (0 if the vector is empty, 1 otherwise) and the update corresponds to setting the
-- size of the tail (if it exists). We can formalize these ideas like so:

-- Depending on the size of the vector, we may have reached the end already (nil)
-- or we may specify what the head should be (cons). This is the type of commands.

  data VecC : ℕ → Set a where
    nil  :           VecC zero
    cons : ∀ n → A → VecC (suc n)

  Vec : Container ℕ ℕ a _
  Command Vec = VecC

-- We then treat each command independently, specifying both the response and the
-- next index based on that response.

-- In the nil case, the response is the empty type: there won't be any tail. As
-- a consequence, the next index won't be needed (and we can rely on the fact the
-- user will never be able to call it).

  Response Vec nil = ⊥
  next     Vec nil = λ ()

-- In the cons case, the response is the unit type: there is exactly one tail. The
-- next index is the predecessor of the current one. It is handily handed over to
-- use by `cons`.

  -- cons
  Response Vec (cons n a) = ⊤
  next     Vec (cons n a) = λ _ → n

-- Finally we can define the type of Vector as the least fixed point of Vec.

  Vector : ℕ → Set a
  Vector = μ Vec

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

-- We can recover the usual constructors by using `sup` to enter the fixpoint
-- and then using the appropriate pairing of a command & a handler for the
-- response.

-- For [], the response is ⊥ which makes it easy to conclude.

  [] : Vector A 0
  [] = sup (nil , λ ())

-- For _∷_, the response is ⊤ so we need to pass a tail. We give the one we took
-- as an argument.

  infixr 3 _∷_
  _∷_ : ∀ {n} → A → Vector A n → Vector A (suc n)
  x ∷ xs = sup (cons _ x , λ _ → xs)

-- We can now use these constructors to build up vectors:

1⋯3 : Vector ℕ 3
1⋯3 = 1 ∷ 2 ∷ 3 ∷ []

-- Horrible thing to check the definition of _∈_ is not buggy.
-- Not sure whether we can say anything interesting about it in the case of Vector...

open import Relation.Binary.HeterogeneousEquality

_ : _∈_ {C = Vec ℕ} {X = Vector ℕ} 1⋯3 (⟦ Vec ℕ ⟧ (Vector ℕ) 4 ∋ cons _ 0 , λ _ → 1⋯3)
_ = _ , refl


{- Example by Guillaume Brunerie, 17-11-2015 -}

{-# OPTIONS --rewriting --cubical-compatible #-}

open import Agda.Primitive

postulate
  _↦_ : ∀ {i} {A : Set i} → A → A → Set i
  idr : ∀ {i} {A : Set i} {a : A} → a ↦ a
{-# BUILTIN REWRITE _↦_ #-}

infixr 3 _==_

data _==_ {i} {A : Set i} (a : A) : A → Set i where
  idp : a == a

PathOver : ∀ {i j} {A : Set i} (B : A → Set j)
  {x y : A} (p : x == y) (u : B x) (v : B y) → Set j
PathOver B idp u v = (u == v)

syntax PathOver B p u v =
  u == v [ B ↓ p ]

postulate
  PathOver-rewr : ∀ {i j} {A : Set i} {B : Set j} {x y : A} (p : x == y) (u v : B) →
    (PathOver (λ _ → B) p u v) ↦ (u == v)
  {-# REWRITE PathOver-rewr #-}

ap : ∀ {i j} {A : Set i} {B : A → Set j} (f : (a : A) → B a) {x y : A}
  → (p : x == y) → PathOver B p (f x) (f y)
ap f idp = idp

module _ {i j k} {A : Set i} {B : Set j} {C : Set k} (g : B → C) (f : A → B) where

  ∘ap : {x y : A} (p : x == y) →
    ap g (ap f p) ↦ ap (λ x → g (f x)) p
  ∘ap idp = idr
  {-# REWRITE ∘ap #-}

  ap∘ : {x y : A} (p : x == y) →
    ap (λ x → g (f x)) p ↦ ap g (ap f p)
  ap∘ p = idr


module Conway where

open import Data.Bool renaming (_≟_ to _B≟_)
open import Data.Nat renaming (_+_ to _ℕ+_ ; _*_ to _ℕ*_ ; _≟_ to _ℕ≟_)
open import Data.Integer
  renaming (_+_ to _ℤ+_ ; _*_ to _ℤ*_ ; -_ to ℤ-_ ; _≟_ to _ℤ≟_)
open import Data.Rational renaming (_≟_ to _ℚ≟_)
open import Relation.Nullary.Core
open import Relation.Nullary.Decidable

open import Rat -- operations on rationals

------------------------------------------------------------------------------
-- Universe of games

data U : Set where
  ZERO  : U
  ONE   : U
  PLUS  : U → U → U
  TIMES : U → U → U
  NEG   : U → U
  RECIP : U → U

-- Conversion from the rationals to the universe of games

n2U : ℕ → U
n2U 0       = ZERO
n2U (suc n) = PLUS ONE (n2U n)

z2U : ℤ → U
z2U -[1+ n ] = NEG (n2U (ℕ.suc n))
z2U (+ n)    = n2U n

q2U : ℚ → U
q2U p = TIMES (z2U (ℚ.numerator p)) (RECIP (n2U (ℕ.suc (ℚ.denominator-1 p))))

-- Conversion from the universe of games to the rationals

-- use meadows?

mutual

  u2q : U → ℚ
  u2q ZERO        = 0ℚ
  u2q ONE         = 1ℚ
  u2q (PLUS g h)  = (u2q g) + (u2q h)
  u2q (TIMES g h) = (u2q g) * (u2q h)
  u2q (NEG g)     = - (u2q g)
  u2q (RECIP g)   = 1/_ (u2q g) {{!!}}
-- need to know that | numerator (u2q g) | is not 0

  u2q≢0 : (u : U) → {u≢0 : False (u2q u ℚ≟ 0ℚ)} → ℚ
  u2q≢0 u {u≢0} with (u2q u ℚ≟ 0ℚ)
  u2q≢0 u {()} | yes _
  u2q≢0 u {_}  | no _ = u2q u

------------------------------------------------------------------------------
-- Small tests

private

  test₁ = q2U (- (+ 1 ÷ 3))
  {--
    TIMES
      (NEG (PLUS ONE ZERO))
      (RECIP (PLUS ONE (PLUS ONE (PLUS ONE ZERO))))
  --}

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


------------------------------------------------------------------------------
-- Inductive PA properties
------------------------------------------------------------------------------

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

module PA.Inductive.PropertiesATP where

open import PA.Inductive.Base

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

+-rightIdentity : ∀ n → n + zero ≡ n
+-rightIdentity zero     = refl
+-rightIdentity (succ n) = prf (+-rightIdentity n)
  where postulate prf : n + zero ≡ n → succ n + zero ≡ succ n
        -- TODO (21 November 2014). See Apia issue 16
        -- {-# ATP prove prf #-}

x+Sy≡S[x+y] : ∀ m n → m + succ n ≡ succ (m + n)
x+Sy≡S[x+y] zero     _ = refl
x+Sy≡S[x+y] (succ m) n = prf (x+Sy≡S[x+y] m n)
  where postulate prf : m + succ n ≡ succ (m + n) →
                        succ m + succ n ≡ succ (succ m + n)
        -- TODO (21 November 2014). See Apia issue 16
        -- {-# ATP prove prf #-}

+-comm : ∀ m n → m + n ≡ n + m
+-comm zero     n = sym (+-rightIdentity n)
+-comm (succ m) n = prf (+-comm m n)
  where postulate prf : m + n ≡ n + m → succ m + n ≡ n + succ m
        -- TODO (21 November 2014). See Apia issue 16
        -- {-# ATP prove prf x+Sy≡S[x+y] #-}


{-# OPTIONS --universe-polymorphism #-}
module Categories.Categories where

open import Level

open import Categories.Category
open import Categories.Functor

Categories : ∀ o ℓ e → Category (suc (o ⊔ ℓ ⊔ e)) (o ⊔ ℓ ⊔ e) (o ⊔ ℓ ⊔ e)
Categories o ℓ e = record
  { Obj = Category o ℓ e
  ; _⇒_ = Functor
  ; _≡_ = _≡_
  ; _∘_ = _∘_
  ; id = id
  ; assoc = λ {_} {_} {_} {_} {F} {G} {H} → assoc {F = F} {G} {H}
  ; identityˡ = λ {_} {_} {F} → identityˡ {F = F}
  ; identityʳ = λ {_} {_} {F} → identityʳ {F = F}
  ; equiv = λ {X} {Y} → equiv {C = X} {D = Y}
  ; ∘-resp-≡ = λ {_} {_} {_} {f} {h} {g} {i} → ∘-resp-≡ {F = f} {h} {g} {i}
  }

{-
module Agda where
  open import Category.Agda
  open import Category.Discrete

  D : ∀ o → Functor (Agda o) (Categories o o zero)
  D o = record
    { F₀ = Discrete
    ; F₁ = λ f → record
      { F₀ = λ x → f x
      ; F₁ = ≣-cong f
      ; identity = tt
      ; homomorphism = tt
      ; F-resp-≡ = λ _ → tt
      }
    ; identity = λ _ → refl tt
    ; homomorphism = λ _ → refl tt
    ; F-resp-≡ = F-resp-≡′
    }
    where
    F-resp-≡′ : {A B : Set o} {F G : A → B} → (∀ x → F x ≣ G x) → ∀ {X Y : A} (f : X ≣ Y) → [ Discrete B ] ≣-cong F f ∼ ≣-cong G f
    F-resp-≡′ {A} {B} {F} {G} f {x} ≣-refl = helper {F = F} {G} (f x)
      where
      helper : {F G : A → B} → {p q : B} → p ≣ q → [ Discrete B ] ≣-refl {x = p} ∼ ≣-refl {x = q}
      helper ≣-refl = refl tt

  Ob : ∀ o → Functor (Categories o o zero) (Agda o)
  Ob o = record
    { F₀ = Category.Obj
    ; F₁ = Functor.F₀
    ; identity = λ _ → ≣-refl
    ; homomorphism = λ _ → ≣-refl
    ; F-resp-≡ = λ {_} {_} {F} {G} → F-resp-≡′ {F = F} {G}
    }
    where
    F-resp-≡′ : {A B : Category o o zero} {F G : Functor A B}
              → ({X Y : Category.Obj A} (f : Category.Hom A X Y) → [ B ] Functor.F₁ F f ∼ Functor.F₁ G f)
              → ((x : Category.Obj A) → Functor.F₀ F x ≣ Functor.F₀ G x)
    F-resp-≡′ {A} {B} {F} {G} F∼G x = helper (F∼G (Category.id A {x}))
      where
      helper : ∀ {X Y} {p : Category.Hom B X X} {q : Category.Hom B Y Y} → [ B ] p ∼ q → X ≣ Y
      helper (refl q) = ≣-refl

  open import Category.Adjunction

  D⊣Ob : ∀ o → Adjunction (D o) (Ob o)
  D⊣Ob o = record
    { unit = record
      { η = λ _ x → x
      ; commute = λ _ _ → ≣-refl
      }
    ; counit = record
      { η = λ X → record
        { F₀ = λ x → x
        ; F₁ = counit-id′ {X}
        ; identity = IsEquivalence.refl (Category.equiv X)
        ; homomorphism = {!!}
        ; F-resp-≡ = {!!}
        }
      ; commute = {!!}
      }
    ; zig = {!!}
    ; zag = λ _ → ≣-refl
    }
    where
    counit-id′ : {X : Category o o zero} → {A B : Category.Obj X} → A ≣ B → Category.Hom X A B
    counit-id′ {X} ≣-refl = Category.id X
-}


module Categories.PushOuts where

open import Library
open import Categories

open Cat

record Square {a}{b}{C : Cat {a}{b}}{X Y Z}(f : Hom C Z X)(g : Hom C Z Y) : Set (a ⊔ b) where
  constructor square
  field W    : Obj C
        h    : Hom C X W
        k    : Hom C Y W
        scom : comp C h f ≅ comp C k g

record SqMap {a}{b}{C : Cat {a}{b}}{X Y Z : Obj C}{f : Hom C Z X}{g : Hom C Z Y}
             (sq sq' : Square {a}{b}{C} f g) : Set (a ⊔ b) where
  constructor sqmap
  open Square
  field sqMor     : Hom C (W sq) (W sq')
        leftTr   : comp C sqMor (h sq) ≅ h sq'
        rightTr  : comp C sqMor (k sq) ≅ k sq'
open SqMap

record PushOut {a}{b}{C : Cat {a}{b}}{X Y Z}(f : Hom C Z X)(g : Hom C Z Y) : Set (a ⊔ b) where
  constructor pushout
  field sq : Square {a}{b}{C} f g
        uniqPush : (sq' : Square f g) → Σ (SqMap sq sq')
          \ u → (u' : SqMap sq sq') → sqMor u ≅ sqMor u'


------------------------------------------------------------------------
-- The Agda standard library
--
-- Pointwise equality over lists using propositional equality
------------------------------------------------------------------------

-- Note think carefully about using this module as pointwise
-- propositional equality can usually be replaced with propositional
-- equality.

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

open import Relation.Binary

module Data.List.Relation.Binary.Equality.Propositional {a} {A : Set a} where

open import Data.List.Base
import Data.List.Relation.Binary.Equality.Setoid as SetoidEquality
open import Relation.Binary.PropositionalEquality as P using (_≡_)

------------------------------------------------------------------------
-- Re-export everything from setoid equality

open SetoidEquality (P.setoid A) public

------------------------------------------------------------------------
-- ≋ is propositional

≋⇒≡ : _≋_ ⇒ _≡_
≋⇒≡ []               = P.refl
≋⇒≡ (P.refl ∷ xs≈ys) = P.cong (_ ∷_) (≋⇒≡ xs≈ys)

≡⇒≋ : _≡_ ⇒ _≋_
≡⇒≋ P.refl = ≋-refl


-- (Γ ⇒ Δ ∪ · A) → (Γ ⇒ Δ ∪ · (A ∨ B))
-- The conclusion (A ∨ B) is the principal/major formula of the rule.
-- The premiss A is the active/minor formula of the rule
-- Γ, Δ is the side formulas of the rule.

-- Γ ⇒ Δ
-- LHS Γ is the antecedent of the sequent.
-- RHS Γ is the consequent of the sequent.


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of All predicate transformer for fresh lists
------------------------------------------------------------------------

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

module Data.List.Fresh.Relation.Unary.All.Properties where

open import Level using (Level; _⊔_; Lift)
open import Data.Empty
open import Data.Nat.Base using (ℕ; zero; suc)
open import Data.Product using (_,_)
open import Function using (_∘′_)
open import Relation.Nullary
open import Relation.Unary  as U
open import Relation.Binary as B using (Rel)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong)

open import Data.List.Fresh using (List#; []; cons; _∷#_; _#_)
open import Data.List.Fresh.Relation.Unary.All

private
  variable
    a p r : Level
    A : Set a

module _ {R : Rel A r} where

  fromAll : ∀ {x} {xs : List# A R} → All (R x) xs → x # xs
  fromAll []       = _
  fromAll (p ∷ ps) = p , fromAll ps

  toAll : ∀ {x} {xs : List# A R} → x # xs → All (R x) xs
  toAll {xs = []}      _        = []
  toAll {xs = a ∷# as} (p , ps) = p ∷ toAll ps

module _ {R : Rel A r} {P : Pred A p} where

  append⁺ : {xs ys : List# A R} {ps : All (_# ys) xs} →
            All P xs → All P ys → All P (append xs ys ps)
  append⁺ []         pys = pys
  append⁺ (px ∷ pxs) pys = px ∷ append⁺ pxs pys


module Oscar.Data.Term.AlphaConversion {𝔣} (FunctionName : Set 𝔣) where

open import Oscar.Data.Term FunctionName

open import Oscar.Class.AlphaConversion
open import Oscar.Data.Equality
open import Oscar.Data.Fin
open import Oscar.Data.Vec
open import Oscar.Function
open import Oscar.Relation

import Oscar.Data.Term.AlphaConversion.internal FunctionName as ⋆

instance AlphaConversionFinTerm : AlphaConversion Fin Term
AlphaConversion._◂_ AlphaConversionFinTerm = ⋆._◂_
AlphaConversion.◂-identity AlphaConversionFinTerm = ⋆.◂-identity
AlphaConversion.◂-associativity AlphaConversionFinTerm = ⋆.◂-associativity
AlphaConversion.◂-extensionality AlphaConversionFinTerm = ⋆.◂-extensionality

instance AlphaConversionFinTerms : ∀ {N} → AlphaConversion Fin (Terms N)
AlphaConversion._◂_ AlphaConversionFinTerms _ = ⋆._◂s_ _
AlphaConversion.◂-identity AlphaConversionFinTerms = ⋆.◂s-identity
AlphaConversion.◂-associativity AlphaConversionFinTerms _ _ = ⋆.◂s-associativity _ _
AlphaConversion.◂-extensionality AlphaConversionFinTerms f≡̇g = ⋆.◂s-extensionality f≡̇g


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

open import function.isomorphism
open import sets.nat.core
open import sets.nat.ordering.lt
open import sets.nat.ordering.leq
open import hott.level.core

<-≤-iso : ∀ {n m} → (n < m) ≅ (suc n ≤ m)
<-≤-iso = record
  { to = f
  ; from = g
  ; iso₁ = λ _ → h1⇒prop <-level _ _
  ; iso₂ = λ _ → h1⇒prop ≤-level _ _ }
  where
    f : ∀ {n m} → n < m → suc n ≤ m
    f suc< = refl-≤
    f (n<s p) = s≤s (trans-≤ suc≤ (f p))

    g : ∀ {n m} → suc n ≤ m → n < m
    g {zero} (s≤s p) = z<n
    g {suc n} (s≤s p) = ap<-suc (g p)


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

open import LogicalFormulae
open import Numbers.Naturals.Semiring -- for length
open import Lists.Lists
open import Functions.Definition
--open import Groups.Actions

module Groups.FinitePermutations where

allInsertions : {a : _} {A : Set a} (x : A) (l : List A) → List (List A)
allInsertions x [] = [ x ] :: []
allInsertions x (y :: l) = (x :: (y :: l)) :: (map (λ i → y :: i) (allInsertions x l))

permutations : {a : _} {A : Set a} (l : List A) → List (List A)
permutations [] = [ [] ]
permutations (x :: l) = flatten (map (allInsertions x) (permutations l))

factorial : ℕ → ℕ
factorial zero = 1
factorial (succ n) = (succ n) *N factorial n

factCheck : factorial 5 ≡ 120
factCheck = refl

allInsertionsLength : {a : _} {A : Set a} (x : A) (l : List A) → length (allInsertions x l) ≡ succ (length l)
allInsertionsLength x [] = refl
allInsertionsLength x (y :: l) = applyEquality succ (transitivity (equalityCommutative (lengthMap (allInsertions x l) {f = y ::_})) (allInsertionsLength x l))

allInsertionsLength' : {a : _} {A : Set a} (x : A) (l : List A) → allTrue (λ i → length i ≡ succ (length l)) (allInsertions x l)
allInsertionsLength' x [] = refl ,, record {}
allInsertionsLength' x (y :: l) with allInsertionsLength' x l
... | bl = refl ,, allTrueMap (λ i → length i ≡ succ (succ (length l))) (y ::_) (allInsertions x l) (allTrueExtension (λ z → length z ≡ succ (length l)) ((λ i → length i ≡ succ (succ (length l))) ∘ (y ::_)) (allInsertions x l) (λ {x} → λ p → applyEquality succ p) bl)

permutationsCheck : permutations (3 :: 4 :: []) ≡ (3 :: 4 :: []) :: (4 :: 3 :: []) :: []
permutationsCheck = refl

permsAllSameLength : {a : _} {A : Set a} (l : List A) → allTrue (λ i → length i ≡ length l) (permutations l)
permsAllSameLength [] = refl ,, record {}
permsAllSameLength (x :: l) with permsAllSameLength l
... | bl = allTrueFlatten (λ i → length i ≡ succ (length l)) (map (allInsertions x) (permutations l)) (allTrueMap (allTrue (λ i → length i ≡ succ (length l))) (allInsertions x) (permutations l) (allTrueExtension (λ z → length z ≡ length l) (allTrue (λ i → length i ≡ succ (length l)) ∘ allInsertions x) (permutations l) lemma bl))
  where
    lemma : {m : List _} → (lenM=lenL : length m ≡ length l) → allTrue (λ i → length i ≡ succ (length l)) (allInsertions x m)
    lemma {m} lm=ll rewrite equalityCommutative lm=ll = allInsertionsLength' x m

allTrueEqual : {a b : _} {A : Set a} {B : Set b} (f : A → B) (equalTo : B) (l : List A) → allTrue (λ i → f i ≡ equalTo) l → map f l ≡ replicate (length l) equalTo
allTrueEqual f equalTo [] pr = refl
allTrueEqual f equalTo (x :: l) (fst ,, snd) rewrite fst | allTrueEqual f equalTo l snd = refl

totalReplicate : (l len : ℕ) → fold _+N_ 0 (replicate len l) ≡ l *N len
totalReplicate l zero rewrite multiplicationNIsCommutative l 0 = refl
totalReplicate l (succ len) rewrite totalReplicate l len | multiplicationNIsCommutative l (succ len) = applyEquality (l +N_) (multiplicationNIsCommutative l len)

permsLen : {a : _} {A : Set a} (l : List A) → length (permutations l) ≡ factorial (length l)
permsLen [] = refl
permsLen (x :: l) = transitivity (lengthFlatten (map (allInsertions x) (permutations l))) (transitivity (applyEquality (fold _+N_ 0) (mapMap (permutations l))) (transitivity (applyEquality (fold _+N_ 0) (mapExtension (permutations l) (length ∘ allInsertions x) (succ ∘ length) λ {y} → allInsertionsLength x y)) (transitivity (applyEquality (fold _+N_ 0) lemma) (transitivity (totalReplicate (succ (length l)) (length (permutations l))) ans))))
  where
    lemma : map (λ a → succ (length a)) (permutations l) ≡ replicate (length (permutations l)) (succ (length l))
    lemma = allTrueEqual (λ a → succ (length a)) (succ (length l)) (permutations l) (allTrueExtension (λ z → length z ≡ length l) (λ i → succ (length i) ≡ succ (length l)) (permutations l) (λ pr → applyEquality succ pr) (permsAllSameLength l))
    ans : length (permutations l) +N length l *N length (permutations l) ≡ factorial (length l) +N length l *N factorial (length l)
    ans rewrite permsLen l = refl

--act : GroupAction (symmetricGroup (symmetricSetoid (reflSetoid (FinSet n))))
--act = ?

{- TODO: show that symmetricGroup acts in the obvious way on permutations FinSet
listElements : {a : _} {A : Set a} (l : List A) → Set
listElements [] = False
listElements (x :: l) = True || listElements l

listElement : {a : _} {A : Set a} {l : List A} (elt : listElements l) → A
listElement {l = []} ()
listElement {l = x :: l} (inl record {}) = x
listElement {l = x :: l} (inr elt) = listElement {l = l} elt

backwardRange : ℕ → List ℕ
backwardRange zero = []
backwardRange (succ n) = n :: backwardRange n

backwardRangeLength : {n : ℕ} → length (backwardRange n) ≡ n
backwardRangeLength {zero} = refl
backwardRangeLength {succ n} rewrite backwardRangeLength {n} = refl

applyPermutation : {n : ℕ} → (f : FinSet n → FinSet n) → listElements (permutations (backwardRange n)) → listElements (permutations (backwardRange n))
applyPermutation {zero} f (inl record {}) = inl (record {})
applyPermutation {zero} f (inr ())
applyPermutation {succ n} f elt = {!!}

finitePermutations : (n : ℕ) → SetoidToSet (symmetricSetoid (reflSetoid (FinSet n))) (listElements (permutations (backwardRange n)))
SetoidToSet.project (finitePermutations n) (sym {f} fBij) = {!!}
SetoidToSet.wellDefined (finitePermutations n) = {!!}
SetoidToSet.surj (finitePermutations n) = {!!}
SetoidToSet.inj (finitePermutations n) = {!!}

-}


{-# OPTIONS --safe #-}
module Cubical.Data.Nat.Divisibility where

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

open import Cubical.Data.Sigma
open import Cubical.Data.Sum
open import Cubical.Data.Empty as ⊥

open import Cubical.HITs.PropositionalTruncation as PropTrunc

open import Cubical.Data.Nat.Base
open import Cubical.Data.Nat.Properties
open import Cubical.Data.Nat.Order

open import Cubical.Relation.Nullary

private
  variable
    l m n : ℕ

_∣_ : ℕ → ℕ → Type₀
m ∣ n = ∃[ c ∈ ℕ ] c · m ≡ n

isProp∣ : isProp (m ∣ n)
isProp∣ = squash

prediv : ℕ → ℕ → Type₀
prediv m n = Σ[ c ∈ ℕ ] c · m ≡ n

-- an alternate definition of m ∣ n that doesn't use truncation

_∣'_ : ℕ → ℕ → Type₀
zero  ∣' n = zero ≡ n
suc m ∣' n = Σ[ c ∈ ℕ ] c · suc m ≡ n

isProp∣' : isProp (m ∣' n)
isProp∣' {zero} {n} = isSetℕ _ _
isProp∣' {suc m} {n} (c₁ , p₁) (c₂ , p₂) =
  Σ≡Prop (λ _ → isSetℕ _ _) (inj-·sm {c₁} {m} {c₂} (p₁ ∙ sym p₂))

∣≃∣' : (m ∣ n) ≃ (m ∣' n)
∣≃∣' {zero} = propBiimpl→Equiv isProp∣ isProp∣'
                              (PropTrunc.rec (isSetℕ _ _) λ { (c , p) → 0≡m·0 c ∙ p })
                              (λ p → ∣ zero , p ∣)
∣≃∣' {suc m} = propTruncIdempotent≃ isProp∣'

∣-untrunc : m ∣ n → Σ[ c ∈ ℕ ] c · m ≡ n
∣-untrunc {zero} p = zero , equivFun ∣≃∣' p
∣-untrunc {suc m} = equivFun ∣≃∣'

-- basic properties of ∣

∣-refl : m ≡ n → m ∣ n
∣-refl p = ∣ 1 , +-zero _ ∙ p ∣

∣-trans : l ∣ m → m ∣ n → l ∣ n
∣-trans = PropTrunc.map2 λ {
  (c₁ , p) (c₂ , q) → (c₂ · c₁ , sym (·-assoc c₂ c₁ _) ∙ cong (c₂ ·_) p ∙ q) }

∣-left : ∀ k → m ∣ (m · k)
∣-left k = ∣ k , ·-comm k _ ∣

∣-right : ∀ k → m ∣ (k · m)
∣-right k = ∣ k , refl ∣

∣-cancelʳ : ∀ k → (m · suc k) ∣ (n · suc k) → m ∣ n
∣-cancelʳ k = PropTrunc.map λ {
  (c , p) → (c , inj-·sm (sym (·-assoc c _ (suc k)) ∙ p)) }

∣-multʳ : ∀ k → m ∣ n → (m · k) ∣ (n · k)
∣-multʳ k = PropTrunc.map λ {
  (c , p) → (c , ·-assoc c _ k ∙ cong (_· k) p) }

∣-zeroˡ : zero ∣ m → zero ≡ m
∣-zeroˡ = equivFun ∣≃∣'

∣-zeroʳ : ∀ m → m ∣ zero
∣-zeroʳ m = ∣ zero , refl ∣

∣-oneˡ : ∀ m → 1 ∣ m
∣-oneˡ m = ∣ m , ·-identityʳ m ∣

-- if n > 0, then the constant c (s.t. c · m ≡ n) is also > 0
∣s-untrunc : m ∣ suc n → Σ[ c ∈ ℕ ] (suc c) · m ≡ suc n
∣s-untrunc ∣p∣ with ∣-untrunc ∣p∣
... | (zero  , p) = ⊥.rec (znots p)
... | (suc c , p) = (c , p)

m∣sn→m≤sn : m ∣ suc n → m ≤ suc n
m∣sn→m≤sn {m} {n} = f ∘ ∣s-untrunc
  where f : Σ[ c ∈ ℕ ] (suc c) · m ≡ suc n → Σ[ c ∈ ℕ ] c + m ≡ suc n
        f (c , p) = (c · m) , (+-comm (c · m) m ∙ p)

m∣n→m≤n : {m n : ℕ} → ¬ n ≡ 0 → m ∣ n → m ≤ n
m∣n→m≤n {m = m} {n = n} p q =
  let n≡sd = suc-predℕ _ p
      m≤sd = m∣sn→m≤sn (subst (λ a → m ∣ a) n≡sd q)
  in  subst (λ a → m ≤ a) (sym n≡sd) m≤sd

m∣sn→z<m : m ∣ suc n → zero < m
m∣sn→z<m {zero} p = ⊥.rec (znots (∣-zeroˡ p))
m∣sn→z<m {suc m} p = suc-≤-suc zero-≤

antisym∣ : ∀ {m n} → m ∣ n → n ∣ m → m ≡ n
antisym∣ {zero} {n} z∣n _ = ∣-zeroˡ z∣n
antisym∣ {m} {zero} _ z∣m = sym (∣-zeroˡ z∣m)
antisym∣ {suc m} {suc n} p q = ≤-antisym (m∣sn→m≤sn p) (m∣sn→m≤sn q)

-- Inequality for strict divisibility

stDivIneq : ¬ m ≡ 0 → ¬ m ∣ n → l ∣ m → l ∣ n → l < m
stDivIneq {m = 0} p = ⊥.rec (p refl)
stDivIneq {n = 0} _ q = ⊥.rec (q (∣-zeroʳ _))
stDivIneq {m = suc m} {n = n} {l = l} _ q h h' =
  case (≤-split (m∣sn→m≤sn h))
  return (λ _ → l < suc m) of
    λ { (inl r) → r
      ; (inr r) → ⊥.rec (q (subst (λ a → a ∣ n) r h')) }


------------------------------------------------------------------------
-- Properties of combinatorial functions
------------------------------------------------------------------------

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

module Math.Combinatorics.Function.Properties where

-- agda-stdlib
open import Data.Empty using (⊥-elim)
open import Data.List as List
open import Data.List.Properties
import Data.List.All as All
import Data.List.All.Properties as Allₚ
open import Data.Maybe hiding (zipWith)
open import Data.Nat
open import Data.Nat.Properties
open import Data.Nat.DivMod
open import Data.Product
open import Data.Sum
open import Data.Unit using (tt)
open import Function
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Relation.Nullary.Decidable

-- agda-combinatorics
open import Math.Combinatorics.Function
import Math.Combinatorics.Function.Properties.Lemma as Lemma

-- agda-misc
open import Math.NumberTheory.Product.Nat

open ≤-Reasoning

-- TODO: replace with Data.Nat.Predicate
private
  _≠0 : ℕ → Set
  n ≠0 = False (n ≟ 0)

------------------------------------------------------------------------
-- Properties of _!

private
  zero-product : ∀ m n → m * n ≡ 0 → m ≡ 0 ⊎ n ≡ 0
  zero-product 0       n m*n≡0 = inj₁ refl
  zero-product (suc m) 0 m*n≡0 = inj₂ refl

-- factorial never be 0
n!≢0 : ∀ n → (n !) ≢ 0
n!≢0 0      ()
n!≢0 (suc n) [1+n]!≡0 with zero-product (suc n) (n !) [1+n]!≡0
... | inj₂ n!≡0 = n!≢0 n n!≡0

-- TODO: use Data.Nat.Predicate
False[n!≟0] : ∀ n → False (n ! ≟ 0)
False[n!≟0] n = fromWitnessFalse (n!≢0 n)

1≤n! : ∀ n → 1 ≤ n !
1≤n! 0       = ≤-refl
1≤n! (suc n) = ≤-trans (1≤n! n) (m≤m+n (n !) _)

2≤[2+n]! : ∀ n → 2 ≤ (2 + n) !
2≤[2+n]! 0       = ≤-refl
2≤[2+n]! (suc n) = ≤-trans (2≤[2+n]! n) (m≤m+n ((2 + n) !) _)

!-mono-< : ∀ {m n} → suc m < n → suc m ! < n !
!-mono-< {0}     {suc zero}    (s≤s ())
!-mono-< {0}     {suc (suc n)} _           = 2≤[2+n]! n
!-mono-< {suc m} {suc n}       (s≤s 1+m<n) =
  *-mono-< (s≤s 1+m<n) (!-mono-< 1+m<n)

!-mono-<-≢0 : ∀ {m n} {wit : m ≠0} → m < n → m ! < n !
!-mono-<-≢0 {suc m} {n} {tt} m<n = !-mono-< m<n

!-mono-<-≤ : ∀ {m n} → m < n → m ! ≤ n !
!-mono-<-≤ {0}     {n} _     = 1≤n! n
!-mono-<-≤ {suc m} {n} 1+m<n = <⇒≤ $ !-mono-< 1+m<n

!-mono-≤ : ∀ {m n} → m ≤ n → m ! ≤ n !
!-mono-≤ {m} {n} m≤n with Lemma.≤⇒≡∨< m≤n
... | inj₁ refl = ≤-refl {m !}
... | inj₂ m<n  = !-mono-<-≤ m<n

!-injective : ∀ {m n} {m≢0 : m ≠0} {n≢0 : n ≠0} → m ! ≡ n ! → m ≡ n
!-injective {m} {n} {m≢0} {n≢0} m!≡n! with <-cmp m n
... | tri< m<n _   _   = ⊥-elim $ <⇒≢ (!-mono-<-≢0 {m} {n} {m≢0} m<n) m!≡n!
... | tri≈ _   m≡n _   = m≡n
... | tri> _   _   n<m = ⊥-elim $ <⇒≢ (!-mono-<-≢0 {n} {m} {n≢0} n<m) (sym m!≡n!)

n!≤n^n : ∀ n → n ! ≤ n ^ n
n!≤n^n 0       = ≤-refl
n!≤n^n (suc n) = begin
  suc n * n !       ≤⟨ *-monoʳ-≤ (suc n) (n!≤n^n n) ⟩
  suc n * n ^ n     ≤⟨ *-monoʳ-≤ (suc n) (Lemma.^-monoˡ-≤ n (≤-step ≤-refl)) ⟩
  suc n * suc n ^ n ∎

2^n≤[1+n]! : ∀ n → 2 ^ n ≤ (suc n) !
2^n≤[1+n]! 0       = ≤-refl
2^n≤[1+n]! (suc n) = begin
  2 ^ suc n               ≡⟨⟩
  2 * 2 ^ n               ≤⟨ *-mono-≤ (m≤m+n 2 n) (2^n≤[1+n]! n) ⟩
  suc (suc n) * (suc n) ! ≡⟨⟩
  (suc (suc n)) !         ∎

[1+n]!/[1+n]≡n! : ∀ n → (suc n) ! / suc n ≡ n !
[1+n]!/[1+n]≡n! n =
  sym $ Lemma.m*n≡o⇒m≡o/n (n !) (suc n) ((suc n) !) tt (*-comm (n !) (suc n))

-- relation with product
n!≡Π[k<n]suc : ∀ n → n ! ≡ Π< n suc
n!≡Π[k<n]suc zero    = refl
n!≡Π[k<n]suc (suc n) = begin-equality
  suc n * n !      ≡⟨ *-comm (suc n) (n !) ⟩
  n ! * suc n      ≡⟨ cong (_* suc n) $ n!≡Π[k<n]suc n ⟩
  Π< n suc * suc n ∎

------------------------------------------------------------------------
-- Properties of P

P[n,1]≡n : ∀ n → P n 1 ≡ n
P[n,1]≡n 0       = refl
P[n,1]≡n (suc n) = *-identityʳ (suc n)

P[n,n]≡n! : ∀ n → P n n ≡ n !
P[n,n]≡n! 0       = refl
P[n,n]≡n! (suc n) = cong (suc n *_) (P[n,n]≡n! n)

n<k⇒P[n,k]≡0 : ∀ {n k} → n < k → P n k ≡ 0
n<k⇒P[n,k]≡0 {0}     {suc k} n<k       = refl
n<k⇒P[n,k]≡0 {suc n} {suc k} (s≤s n<k) = begin-equality
  P (suc n) (suc k) ≡⟨⟩
  suc n * P n k     ≡⟨ cong (suc n *_) $ n<k⇒P[n,k]≡0 n<k ⟩
  suc n * 0         ≡⟨ *-zeroʳ (suc n) ⟩
  0                 ∎

-- m = 3; n = 4; o = 2
-- 7 6 5 4 3 = 7 6 5 * 4 3
P-split : ∀ m n o → P (m + n) (m + o) ≡ P (m + n) m * P n o
P-split 0       n o = sym $ +-identityʳ (P n o)
P-split (suc m) n o = begin-equality
  P (suc m + n) (suc m + o)           ≡⟨⟩
  (suc m + n) * P (m + n) (m + o)     ≡⟨ cong ((suc m + n) *_) $ P-split m n o ⟩
  (suc m + n) * (P (m + n) m * P n o) ≡⟨ sym $ *-assoc (suc m + n) (P (m + n) m) (P n o) ⟩
  P (suc m + n) (suc m) * P n o       ∎

-- proved by P-split, P[n,n]≡n!
-- m = 5; n =
-- 9 8 7 6 5
--         * 4 3 2 1 =
-- 9 8 7 6 5 4 3 2 1
P[m+n,m]*n!≡[m+n]! : ∀ m n → P (m + n) m * n ! ≡ (m + n) !
P[m+n,m]*n!≡[m+n]! m n = begin-equality
  P (m + n) m * n !   ≡⟨ cong (P (m + n) m *_) $ sym $ P[n,n]≡n! n ⟩
  P (m + n) m * P n n ≡⟨ sym $ P-split m n n ⟩
  P (m + n) (m + n)   ≡⟨ P[n,n]≡n! (m + n) ⟩
  (m + n) !           ∎

-- proved by P[m+n,m]*n!≡[m+n]!
P[m+n,n]*m!≡[m+n]! : ∀ m n → P (m + n) n * m ! ≡ (m + n) !
P[m+n,n]*m!≡[m+n]! m n = begin-equality
  P (m + n) n * m ! ≡⟨ cong (λ v → P v n * m !) $ +-comm m n ⟩
  P (n + m) n * m ! ≡⟨ P[m+n,m]*n!≡[m+n]! n m ⟩
  (n + m) !         ≡⟨ cong (_!) $ +-comm n m ⟩
  (m + n) !         ∎

-- proved by P[m+n,m]*n!≡[m+n]!
P[n,k]*[n∸k]!≡n! : ∀ {n k} → k ≤ n → P n k * (n ∸ k) ! ≡ n !
P[n,k]*[n∸k]!≡n! {n} {k} k≤n = begin-equality
  P n k * m !       ≡⟨ cong (λ v → P v k * m !) $ sym $ k+m≡n ⟩
  P (k + m) k * m ! ≡⟨ P[m+n,m]*n!≡[m+n]! k m ⟩
  (k + m) !         ≡⟨ cong (_!) $ k+m≡n ⟩
  n !               ∎
  where
  m = n ∸ k
  k+m≡n : k + m ≡ n
  k+m≡n = m+[n∸m]≡n k≤n

-- proved by P[n,k]*[n∸k]!≡n!
P[n,k]≡n!/[n∸k]! : ∀ {n k} → k ≤ n → P n k ≡ _div_ (n !) ((n ∸ k) !) {False[n!≟0] (n ∸ k)}
P[n,k]≡n!/[n∸k]! {n} {k} k≤n = Lemma.m*n≡o⇒m≡o/n (P n k) ((n ∸ k) !) (n !)
                               (False[n!≟0] (n ∸ k)) (P[n,k]*[n∸k]!≡n! k≤n)

P[m,m∸n]*n!≡m! : ∀ {m n} → n ≤ m → P m (m ∸ n) * n ! ≡ m !
P[m,m∸n]*n!≡m! {m} {n} n≤m = begin-equality
  P m o * n !       ≡⟨ sym $ cong (λ v → P v o * n !) o+n≡m ⟩
  P (o + n) o * n ! ≡⟨ P[m+n,m]*n!≡[m+n]! o n ⟩
  (o + n) !         ≡⟨ cong (_!) o+n≡m ⟩
  m !               ∎
  where
  o = m ∸ n
  o+n≡m : o + n ≡ m
  o+n≡m = m∸n+n≡m n≤m

m!/n!≡P[m,m∸n] : ∀ {m n} → n ≤ m → (m ! / n !) {False[n!≟0] n} ≡ P m (m ∸ n)
m!/n!≡P[m,m∸n] {m} {n} n≤m =
  sym $ Lemma.m≡n*o⇒n≡m/o (m !) (P m (m ∸ n)) (n !) (False[n!≟0] n)
                          (sym $ P[m,m∸n]*n!≡m! n≤m)

-- proved by P-split
-- m = 2; n = 7
-- 9 8 7 = 9 8 * 7
P[m+n,1+m]≡P[m+n,m]*n : ∀ m n → P (m + n) (suc m) ≡ P (m + n) m * n
P[m+n,1+m]≡P[m+n,m]*n m n = begin-equality
  P (m + n) (1 + m)   ≡⟨ cong (P (m + n)) $ +-comm 1 m ⟩
  P (m + n) (m + 1)   ≡⟨ P-split m n 1 ⟩
  P (m + n) m * P n 1 ≡⟨ cong (P (m + n) m *_) $ P[n,1]≡n n ⟩
  P (m + n) m * n     ∎

P[m+n,1+n]≡P[m+n,n]*m : ∀ m n → P (m + n) (suc n) ≡ P (m + n) n * m
P[m+n,1+n]≡P[m+n,n]*m m n = begin-equality
  P (m + n) (suc n) ≡⟨ cong (λ v → P v (suc n)) $ +-comm m n ⟩
  P (n + m) (suc n) ≡⟨ P[m+n,1+m]≡P[m+n,m]*n n m ⟩
  P (n + m) n * m   ≡⟨ cong (λ v → P v n * m) $ +-comm n m ⟩
  P (m + n) n * m   ∎

P[n,1+k]≡P[n,k]*[n∸k] : ∀ n k → P n (suc k) ≡ P n k * (n ∸ k)
P[n,1+k]≡P[n,k]*[n∸k] n k with Lemma.lemma₃ k n
... | inj₁ (m , n≡m+k) = begin-equality
  P n (suc k)       ≡⟨ cong (λ v → P v (suc k)) n≡m+k ⟩
  P (m + k) (suc k) ≡⟨ P[m+n,1+n]≡P[m+n,n]*m m k ⟩
  P (m + k) k * m   ≡⟨ sym $ cong₂ _*_ (cong (λ v → P v k) n≡m+k) n∸k≡m ⟩
  P n k * (n ∸ k)   ∎
  where
  n∸k≡m = Lemma.m≡n+o⇒m∸o≡n n m k n≡m+k
... | inj₂ n<k = begin-equality
  P n (suc k)     ≡⟨ n<k⇒P[n,k]≡0 (≤-step n<k) ⟩
  0               ≡⟨ sym $ *-zeroʳ (P n k) ⟩
  P n k * 0       ≡⟨ sym $ cong (P n k *_) $ m≤n⇒m∸n≡0 (<⇒≤ n<k) ⟩
  P n k * (n ∸ k) ∎

P[n,1+k]≡[n∸k]*P[n,k] : ∀ n k → P n (suc k) ≡ (n ∸ k) * P n k
P[n,1+k]≡[n∸k]*P[n,k] n k = trans (P[n,1+k]≡P[n,k]*[n∸k] n k) (*-comm (P n k) (n ∸ k))

-- proved by P[n,1+k]≡[n∸k]*P[n,k] and n<k⇒P[n,k]≡0
P[1+n,1+k]≡[1+k]*P[n,k]+P[n,1+k] : ∀ n k → P (suc n) (suc k) ≡ suc k * P n k + P n (suc k)
P[1+n,1+k]≡[1+k]*P[n,k]+P[n,1+k] n k with k ≤? n
... | yes k≤n = begin-equality
  suc n * P n k                   ≡⟨ cong (_* P n k) 1+n≡[1+k]+[n∸k] ⟩
  (suc k + (n ∸ k)) * P n k       ≡⟨ *-distribʳ-+ (P n k) (suc k) (n ∸ k) ⟩
  suc k * P n k + (n ∸ k) * P n k ≡⟨ sym $ cong (suc k * P n k +_) $ P[n,1+k]≡[n∸k]*P[n,k] n k ⟩
  suc k * P n k + P n (suc k)     ∎
  where
  1+n≡[1+k]+[n∸k] : suc n ≡ suc k + (n ∸ k)
  1+n≡[1+k]+[n∸k] = sym $ begin-equality
    suc k + (n ∸ k)   ≡⟨ +-assoc 1 k (n ∸ k) ⟩
    suc (k + (n ∸ k)) ≡⟨ cong suc $ m+[n∸m]≡n k≤n ⟩
    suc n             ∎
... | no k≰n = begin-equality
  P (suc n) (suc k)           ≡⟨ n<k⇒P[n,k]≡0 (s≤s n<k) ⟩
  0 + 0                       ≡⟨ sym $ cong (_+ 0) $ *-zeroʳ (suc k) ⟩
  suc k * 0 + 0               ≡⟨ sym $ cong₂ _+_ (cong (suc k *_) (n<k⇒P[n,k]≡0 n<k)) (n<k⇒P[n,k]≡0 (≤-step n<k)) ⟩
  suc k * P n k + P n (suc k) ∎
  where
  n<k : n < k
  n<k = ≰⇒> k≰n

P[1+n,n]≡[1+n]! : ∀ n → P (suc n) n ≡ (suc n) !
P[1+n,n]≡[1+n]! n = begin-equality
  P (1 + n) n       ≡⟨ cong (λ v → P v n) (+-comm 1 n) ⟩
  P (n + 1) n       ≡⟨ sym $ *-identityʳ (P (n + 1) n) ⟩
  P (n + 1) n * 1   ≡⟨ sym $ P-split n 1 1 ⟩
  P (n + 1) (n + 1) ≡⟨ P[n,n]≡n! (n + 1) ⟩
  (n + 1) !         ≡⟨ cong (_!) (+-comm n 1) ⟩
  (1 + n) !         ∎

P-split-∸-alternative : ∀ {m n o} → o ≤ m → o ≤ n → P m n ≡ P m o * P (m ∸ o) (n ∸ o)
P-split-∸-alternative {m} {n} {o} o≤m o≤n = begin-equality
  P m n               ≡⟨ sym $ cong₂ P o+p≡m o+q≡n ⟩
  P (o + p) (o + q)   ≡⟨ P-split o p q ⟩
  P (o + p) o * P p q ≡⟨ cong (λ v → P v o * P p q) o+p≡m ⟩
  P m o * P p q       ∎
  where
  p = m ∸ o
  q = n ∸ o
  o+p≡m : o + p ≡ m
  o+p≡m = m+[n∸m]≡n o≤m
  o+q≡n : o + q ≡ n
  o+q≡n = m+[n∸m]≡n o≤n

P-split-∸ : ∀ m n o → P m (n + o) ≡ P m n * P (m ∸ n) o
P-split-∸ m n o with n ≤? m
... | yes n≤m = begin-equality
  P m (n + o)         ≡⟨ sym $ cong (λ v → P v (n + o)) n+p≡m ⟩
  P (n + p) (n + o)   ≡⟨ P-split n p o ⟩
  P (n + p) n * P p o ≡⟨ cong (λ v → P v n * P p o) n+p≡m ⟩
  P m n * P p o       ∎
  where
  p = m ∸ n
  n+p≡m : n + p ≡ m
  n+p≡m = m+[n∸m]≡n n≤m
... | no  n≰m = begin-equality
  P m (n + o)         ≡⟨ n<k⇒P[n,k]≡0 m<n+o ⟩
  0                   ≡⟨⟩
  0 * P (m ∸ n) o     ≡⟨ sym $ cong (_* P (m ∸ n) o) $ n<k⇒P[n,k]≡0 m<n ⟩
  P m n * P (m ∸ n) o ∎
  where
  m<n : m < n
  m<n = ≰⇒> n≰m
  m<n+o : m < n + o
  m<n+o = begin-strict
    m     <⟨ m<n ⟩
    n     ≤⟨ m≤m+n n o ⟩
    n + o ∎

P-slide-∸ : ∀ m n o → P m n * P (m ∸ n) o ≡ P m o * P (m ∸ o) n
P-slide-∸ m n o = begin-equality
  P m n * P (m ∸ n) o ≡⟨ sym $ P-split-∸ m n o ⟩
  P m (n + o)         ≡⟨ cong (P m) $ +-comm n o ⟩
  P m (o + n)         ≡⟨ P-split-∸ m o n ⟩
  P m o * P (m ∸ o) n ∎

-- Properties of P and order relation
k≤n⇒1≤P[n,k] : ∀ {n k} → k ≤ n → 1 ≤ P n k
k≤n⇒1≤P[n,k] {n}        {.0}       z≤n               = ≤-refl
k≤n⇒1≤P[n,k] {.(suc n)} {.(suc k)} (s≤s {k} {n} k≤n) = begin
  1             ≤⟨ s≤s z≤n ⟩
  suc n         ≡⟨ sym $ *-identityʳ (suc n) ⟩
  suc n * 1     ≤⟨ *-monoʳ-≤ (suc n) (k≤n⇒1≤P[n,k] {n} {k} k≤n) ⟩
  suc n * P n k ∎

k≤n⇒P[n,k]≢0 : ∀ {n k} → k ≤ n → P n k ≢ 0
k≤n⇒P[n,k]≢0 {n} {k} k≤n = Lemma.1≤n⇒n≢0 $ k≤n⇒1≤P[n,k] k≤n

P[n,k]≡0⇒n<k : ∀ {n k} → P n k ≡ 0 → n < k
P[n,k]≡0⇒n<k {n} {k} P[n,k]≡0 with n <? k
... | yes n<k = n<k
... | no  n≮k = ⊥-elim $ k≤n⇒P[n,k]≢0 k≤n P[n,k]≡0
  where
  k≤n = ≮⇒≥ n≮k

P[n,k]≢0⇒k≤n : ∀ {n k} → P n k ≢ 0 → k ≤ n
P[n,k]≢0⇒k≤n {n} {k} P[n,k]≢0 with k ≤? n
... | yes k≤n = k≤n
... | no  k≰n = ⊥-elim $ P[n,k]≢0 $ n<k⇒P[n,k]≡0 n<k
  where
  n<k : n < k
  n<k = ≰⇒> k≰n

P[n,k]≤n^k : ∀ n k → P n k ≤ n ^ k
P[n,k]≤n^k n       0       = ≤-refl
P[n,k]≤n^k 0       (suc k) = ≤-refl
P[n,k]≤n^k (suc n) (suc k) = begin
  suc n * P n k     ≤⟨ *-monoʳ-≤ (suc n) $ P[n,k]≤n^k n k ⟩
  suc n * n ^ k     ≤⟨ *-monoʳ-≤ (suc n) $ Lemma.^-monoˡ-≤ k $ ≤-step ≤-refl ⟩
  suc n * suc n ^ k ≡⟨⟩
  suc n ^ suc k     ∎

k≤n⇒k!≤P[n,k] : ∀ {n k} → k ≤ n → k ! ≤ P n k
k≤n⇒k!≤P[n,k] {n}     {0}     k≤n       = ≤-refl
k≤n⇒k!≤P[n,k] {suc n} {suc k} (s≤s k≤n) = begin
  suc k * k !   ≤⟨ *-mono-≤ (s≤s k≤n) (k≤n⇒k!≤P[n,k] k≤n) ⟩
  suc n * P n k ∎

P[n,k]≤n! : ∀ n k → P n k ≤ n !
P[n,k]≤n! n       0       = 1≤n! n
P[n,k]≤n! 0       (suc k) = ≤-step ≤-refl
P[n,k]≤n! (suc n) (suc k) = begin
  suc n * P n k ≤⟨ *-monoʳ-≤ (suc n) (P[n,k]≤n! n k) ⟩
  suc n * n !   ∎

P-monoʳ-< : ∀ {n k r} → 2 ≤ n → r < n → k < r → P n k < P n r
P-monoʳ-< {suc zero}          {k} {r} (s≤s ()) r<n k<r
P-monoʳ-< {n@(suc (suc n-2))} {k} {r} 2≤n      r<n k<r =
  *-cancelʳ-< (P n k) (P n r) $ begin-strict
  P n k * (n ∸ k) ! ≡⟨ P[n,k]*[n∸k]!≡n! k≤n ⟩
  n !               ≡⟨ sym $ P[n,k]*[n∸k]!≡n! r≤n ⟩
  P n r * (n ∸ r) ! <⟨ Lemma.*-monoʳ-<′ (P n r) (fromWitnessFalse P[n,r]≢0)
                     (!-mono-<-≢0 {n ∸ r} {n ∸ k} {fromWitnessFalse n∸r≢0} n∸r<n∸k) ⟩
  P n r * (n ∸ k) ! ∎
  where
  k≤r = <⇒≤ k<r
  r≤n = <⇒≤ r<n
  k≤n = ≤-trans k≤r r≤n

  P[n,r]≢0 : P n r ≢ 0
  P[n,r]≢0 = k≤n⇒P[n,k]≢0 {n} {r} r≤n

  n∸r≢0 : n ∸ r ≢ 0
  n∸r≢0 = Lemma.m<n⇒n∸m≢0 r<n

  n∸r<n∸k : n ∸ r < n ∸ k
  n∸r<n∸k = ∸-monoʳ-< k<r r≤n

P-monoʳ-≤ : ∀ {n k r} → r ≤ n → k ≤ r → P n k ≤ P n r
P-monoʳ-≤ {zero}        {zero}        {zero}        r≤n       k≤r = ≤-refl
P-monoʳ-≤ {zero}        {suc k}       {r}           r≤n       k≤r = z≤n
P-monoʳ-≤ {suc zero}    {zero}        {zero}        r≤n       k≤r = ≤-refl
P-monoʳ-≤ {suc zero}    {zero}        {suc zero}    r≤n       k≤r = ≤-refl
P-monoʳ-≤ {suc zero}    {zero}        {suc (suc r)} (s≤s ())  k≤r
P-monoʳ-≤ {suc zero}    {suc zero}    {suc zero}    (s≤s r≤n) k≤r = ≤-refl
P-monoʳ-≤ {suc zero}    {suc (suc k)} {suc r}       r≤n       k≤r = z≤n
P-monoʳ-≤ {suc (suc n)} {k} {r} r≤2+n k≤r with Lemma.≤⇒≡∨< r≤2+n
P-monoʳ-≤ {suc (suc n)} {k} {r} r≤2+n k≤r | inj₁ r≡2+n = begin
  P (2 + n) k ≡⟨ cong (λ v → P v k) (sym r≡2+n) ⟩
  P r k       ≤⟨ P[n,k]≤n! r k ⟩
  r !         ≡⟨ sym $ P[n,n]≡n! r ⟩
  P r r       ≡⟨ cong (λ v → P v r) $ r≡2+n ⟩
  P (2 + n) r ∎
P-monoʳ-≤ {suc (suc n)} {k} {r} r≤2+n k≤r | inj₂ r<2+n with Lemma.≤⇒≡∨< k≤r
P-monoʳ-≤ {suc (suc n)} {k} {r} r≤2+n k≤r | inj₂ r<2+n | inj₁ k≡r = begin
  P (2 + n) k ≡⟨ cong (P (2 + n)) k≡r ⟩
  P (2 + n) r ∎
P-monoʳ-≤ {suc (suc n)} {k} {r} r≤2+n k≤r | inj₂ r<2+n | inj₂ k<r =
   <⇒≤ (P-monoʳ-< {2 + n} {k} {r} (s≤s (s≤s z≤n)) r<2+n k<r)

P-monoˡ-< : ∀ {m n k} {wit : k ≠0} → k ≤ m → m < n → P m k < P n k
P-monoˡ-< {suc m} {suc n} {1}             {wit} k≤m (s≤s m<n) = s≤s $ begin-strict
  m * 1 ≡⟨ *-identityʳ m ⟩
  m     <⟨ m<n ⟩
  n     ≡⟨ sym $ *-identityʳ n ⟩
  n * 1 ∎
P-monoˡ-< {suc m} {suc n} {suc (suc k-1)} {wit} (s≤s k≤m) (s≤s m<n) = begin-strict
  suc m * P m (suc k-1) <⟨ *-mono-< (s≤s m<n) (P-monoˡ-< {m} {n} {suc k-1} {tt} k≤m m<n) ⟩
  suc n * P n (suc k-1) ∎

P-monoˡ-≤ : ∀ {m n} k → m ≤ n → P m k ≤ P n k
P-monoˡ-≤ {m} {n} 0         m≤n = ≤-refl
P-monoˡ-≤ {m} {n} k@(suc _) m≤n with k ≤? m
P-monoˡ-≤ {m} {n} k@(suc _) m≤n | yes k≤m with Lemma.≤⇒≡∨< m≤n
P-monoˡ-≤ {m} {n} k@(suc _) m≤n | yes k≤m | inj₁ refl = ≤-refl
P-monoˡ-≤ {m} {n} k@(suc _) m≤n | yes k≤m | inj₂ m<n  = <⇒≤ $ P-monoˡ-< k≤m m<n
P-monoˡ-≤ {m} {n} k@(suc _) m≤n | no k≰m = begin
  P m k ≡⟨ n<k⇒P[n,k]≡0 (≰⇒> k≰m) ⟩
  0     ≤⟨ z≤n ⟩
  P n k ∎

P[n,k]≡product[take[k,downFrom[1+n]]] :
  ∀ {n k} → k ≤ n → P n k ≡ product (take k (downFrom (suc n)))
P[n,k]≡product[take[k,downFrom[1+n]]] {n}     {zero}  k≤n       = refl
P[n,k]≡product[take[k,downFrom[1+n]]] {suc n} {suc k} (s≤s k≤n) = begin-equality
  suc n * P n k ≡⟨ cong (suc n *_) $ P[n,k]≡product[take[k,downFrom[1+n]]] k≤n ⟩
  suc n * product (take k (downFrom (suc n))) ∎

------------------------------------------------------------------------
-- Properties of CRec

-- proved by induction and P[1+n,1+k]≡[1+k]*P[n,k]+P[n,1+k]
CRec[n,k]*k!≡P[n,k] : ∀ n k → CRec n k * k ! ≡ P n k
CRec[n,k]*k!≡P[n,k] n       0       = refl
CRec[n,k]*k!≡P[n,k] 0       (suc k) = refl
CRec[n,k]*k!≡P[n,k] (suc n) (suc k) = begin-equality
  CRec (suc n) (suc k) * suc k !                      ≡⟨⟩
  (CRec n k + CRec n (suc k)) * (suc k * k !)         ≡⟨ Lemma.lemma₅ (CRec n k) (CRec n (suc k)) (suc k) (k !) ⟩
  suc k * (CRec n k * k !) + CRec n (suc k) * suc k ! ≡⟨ cong₂ _+_ (cong (suc k *_) $ CRec[n,k]*k!≡P[n,k] n k) (CRec[n,k]*k!≡P[n,k] n (suc k)) ⟩
  suc k * P n k + P n (suc k)                         ≡⟨ sym $ P[1+n,1+k]≡[1+k]*P[n,k]+P[n,1+k] n k ⟩
  P (suc n) (suc k)                                   ∎

[1+k]*CRec[1+n,1+k]≡[1+n]*CRec[n,k] : ∀ n k → suc k * CRec (suc n) (suc k) ≡ suc n * CRec n k
[1+k]*CRec[1+n,1+k]≡[1+n]*CRec[n,k] n k = Lemma.*-cancelʳ-≡′
    (suc k * CRec (suc n) (suc k)) (suc n * CRec n k) (False[n!≟0] k) $ begin-equality
  suc k * CRec (suc n) (suc k) * k ! ≡⟨ sym $ Lemma.lemma₈ (CRec (suc n) (suc k)) (suc k) (k !) ⟩
  CRec (suc n) (suc k) * (suc k) !   ≡⟨ CRec[n,k]*k!≡P[n,k] (suc n) (suc k) ⟩
  P (suc n) (suc k)                  ≡⟨⟩
  suc n * P n k                      ≡⟨ sym $ cong (suc n *_) $ CRec[n,k]*k!≡P[n,k] n k ⟩
  suc n * (CRec n k * k !)           ≡⟨ sym $ *-assoc (suc n) (CRec n k) (k !) ⟩
  suc n * CRec n k * k !             ∎

CRec[1+n,1+k]≡[CRec[n,k]*[1+n]]/[1+k] : ∀ n k →
  CRec (suc n) (suc k) ≡ (CRec n k * suc n) / suc k
CRec[1+n,1+k]≡[CRec[n,k]*[1+n]]/[1+k] n k = Lemma.m*n≡o⇒m≡o/n
  (CRec (suc n) (suc k)) (suc k) (CRec n k * suc n) tt ( begin-equality
    CRec (suc n) (suc k) * suc k ≡⟨ *-comm (CRec (suc n) (suc k)) (suc k) ⟩
    suc k * CRec (suc n) (suc k) ≡⟨ [1+k]*CRec[1+n,1+k]≡[1+n]*CRec[n,k] n k ⟩
    suc n * CRec n k             ≡⟨ *-comm (suc n) (CRec n k) ⟩
    CRec n k * suc n             ∎ )

------------------------------------------------------------------------
-- Properties of C

C[n,k]≡CRec[n,k] : ∀ n k → C n k ≡ CRec n k
C[n,k]≡CRec[n,k] n        zero   = refl
C[n,k]≡CRec[n,k] zero    (suc k) = refl
C[n,k]≡CRec[n,k] (suc n) (suc k) = begin-equality
  (C n k * suc n) / suc k
    ≡⟨ cong (λ v → (v * suc n) / suc k) $ C[n,k]≡CRec[n,k] n k ⟩
  (CRec n k * suc n) / suc k
    ≡⟨ sym $ CRec[1+n,1+k]≡[CRec[n,k]*[1+n]]/[1+k] n k ⟩
  CRec (suc n) (suc k)
    ∎

-- TODO prove directly
-- P n k ∣ k !
C[n,k]*k!≡P[n,k] : ∀ n k → C n k * k ! ≡ P n k
C[n,k]*k!≡P[n,k] n k = trans (cong (_* k !) (C[n,k]≡CRec[n,k] n k)) (CRec[n,k]*k!≡P[n,k] n k)

C[n,k]≡P[n,k]/k! : ∀ n k → C n k ≡ _div_ (P n k) (k !) {False[n!≟0] k}
C[n,k]≡P[n,k]/k! n k = Lemma.m*n≡o⇒m≡o/n _ _ _ (False[n!≟0] k) (C[n,k]*k!≡P[n,k] n k)

C[1+n,1+k]≡C[n,k]+C[n,1+k] : ∀ n k → C (suc n) (suc k) ≡ C n k + C n (suc k)
C[1+n,1+k]≡C[n,k]+C[n,1+k] n k = begin-equality
  C (suc n) (suc k)         ≡⟨ C[n,k]≡CRec[n,k] (suc n) (suc k) ⟩
  CRec n k + CRec n (suc k) ≡⟨ sym $ cong₂ _+_ (C[n,k]≡CRec[n,k] n k) (C[n,k]≡CRec[n,k] n (suc k)) ⟩
  C n k + C n (suc k)       ∎

n<k⇒C[n,k]≡0 : ∀ {n k} → n < k → C n k ≡ 0
n<k⇒C[n,k]≡0 {n} {k} n<k = begin-equality
  C n k                           ≡⟨ C[n,k]≡P[n,k]/k! n k ⟩
  (P n k div k !) {False[n!≟0] k} ≡⟨ cong (λ v → (v div k !) {False[n!≟0] k}) $ n<k⇒P[n,k]≡0 n<k ⟩
  (0 div k !) {False[n!≟0] k}     ≡⟨ 0/n≡0 (k !) ⟩
  0                               ∎

C[n,1]≡n : ∀ n → C n 1 ≡ n
C[n,1]≡n n = begin-equality
  C n 1       ≡⟨ C[n,k]≡P[n,k]/k! n 1 ⟩
  P n 1 div 1 ≡⟨ n/1≡n (P n 1) ⟩
  P n 1       ≡⟨ P[n,1]≡n n ⟩
  n           ∎

C[n,n]≡1 : ∀ n → C n n ≡ 1
C[n,n]≡1 n = begin-equality
  C n n                           ≡⟨ C[n,k]≡P[n,k]/k! n n ⟩
  (P n n div n !) {False[n!≟0] n} ≡⟨ cong (λ v → (v div n !) {False[n!≟0] n}) $ P[n,n]≡n! n ⟩
  (n ! div n !) {False[n!≟0] n}   ≡⟨ n/n≡1 (n !) ⟩
  1                               ∎

-- proved by C[n,k]*k!≡P[n,k] and P[m+n,n]*m!≡[m+n]!
C[m+n,n]*m!*n!≡[m+n]! : ∀ m n → C (m + n) n * m ! * n ! ≡ (m + n) !
C[m+n,n]*m!*n!≡[m+n]! m n = begin-equality
  C (m + n) n * m ! * n ! ≡⟨ Lemma.lemma₇ (C (m + n) n) (m !) (n !) ⟩
  C (m + n) n * n ! * m ! ≡⟨ cong (_* m !) $ C[n,k]*k!≡P[n,k] (m + n) n ⟩
  P (m + n) n * m !       ≡⟨ P[m+n,n]*m!≡[m+n]! m n ⟩
  (m + n) !               ∎

-- proved by C[m+n,n]*m!*n!≡[m+n]!
C[m+n,m]*m!*n!≡[m+n]! : ∀ m n → C (m + n) m * m ! * n ! ≡ (m + n) !
C[m+n,m]*m!*n!≡[m+n]! m n = begin-equality
  C (m + n) m * m ! * n ! ≡⟨ Lemma.lemma₇ (C (m + n) m) (m !) (n !) ⟩
  C (m + n) m * n ! * m ! ≡⟨ cong (λ v → C v m * n ! * m !) $ +-comm m n ⟩
  C (n + m) m * n ! * m ! ≡⟨ C[m+n,n]*m!*n!≡[m+n]! n m ⟩
  (n + m) !               ≡⟨ cong (_!) $ +-comm n m ⟩
  (m + n) !               ∎

C[n,k]*[n∸k]!*k!≡n! : ∀ {n k} → k ≤ n → C n k * (n ∸ k) ! * k ! ≡ n !
C[n,k]*[n∸k]!*k!≡n! {n} {k} k≤n = begin-equality
  C n k * m ! * k !       ≡⟨ cong (λ v → C v k * m ! * k !) $ sym $ m+k≡n ⟩
  C (m + k) k * m ! * k ! ≡⟨ C[m+n,n]*m!*n!≡[m+n]! m k ⟩
  (m + k) !               ≡⟨ cong (_!) $ m+k≡n ⟩
  n !                     ∎
  where
  m = n ∸ k
  m+k≡n : m + k ≡ n
  m+k≡n = m∸n+n≡m k≤n

private
  False[m!*n!≟0] : ∀ m n → False ((m ! * n !) ≟ 0)
  False[m!*n!≟0] m n = fromWitnessFalse $ Lemma.*-pres-≢0 (n!≢0 m) (n!≢0 n)

C[n,k]≡n!/[[n∸k]!*k!] : ∀ {n k} → k ≤ n →
  C n k ≡ _div_ (n !) ((n ∸ k) ! * k !) {False[m!*n!≟0] (n ∸ k) k}
C[n,k]≡n!/[[n∸k]!*k!] {n} {k} k≤n = Lemma.m*n≡o⇒m≡o/n
  (C n k) ((n ∸ k) ! * k !) (n !) (False[m!*n!≟0] (n ∸ k) k) $ begin-equality
    C n k * ((n ∸ k) ! * k !) ≡⟨ sym $ *-assoc (C n k) ((n ∸ k) !) (k !) ⟩
    C n k * (n ∸ k) ! * k !   ≡⟨ C[n,k]*[n∸k]!*k!≡n! k≤n ⟩
    n !                       ∎

-- proved by C[m+n,n]*m!*n!≡[m+n]! and C[m+n,m]*m!*n!≡[m+n]!
C-inv : ∀ m n → C (m + n) n ≡ C (m + n) m
C-inv m n =
  Lemma.*-cancelʳ-≡′ (C (m + n) n) (C (m + n) m) (False[n!≟0] m) $
  Lemma.*-cancelʳ-≡′ (C (m + n) n * m !) (C (m + n) m * m !) (False[n!≟0] n) $ begin-equality
    C (m + n) n * m ! * n ! ≡⟨ C[m+n,n]*m!*n!≡[m+n]! m n ⟩
    (m + n) !               ≡⟨ sym $ C[m+n,m]*m!*n!≡[m+n]! m n ⟩
    C (m + n) m * m ! * n ! ∎

C-inv-∸ : ∀ {n k} → k ≤ n → C n k ≡ C n (n ∸ k)
C-inv-∸ {n} {k} k≤n = begin-equality
  C n k       ≡⟨ cong (λ v → C v k) $ sym $ m+k≡n ⟩
  C (m + k) k ≡⟨ C-inv m k ⟩
  C (m + k) m ≡⟨ cong (λ v → C v m) $ m+k≡n ⟩
  C n (n ∸ k) ∎
  where
  m = n ∸ k
  m+k≡n : m + k ≡ n
  m+k≡n = m∸n+n≡m k≤n

C[m+n,1+n]*[1+n]≡C[m+n,n]*m : ∀ m n → C (m + n) (suc n) * suc n ≡ C (m + n) n * m
C[m+n,1+n]*[1+n]≡C[m+n,n]*m m n =
  Lemma.*-cancelʳ-≡′ (C (m + n) (suc n) * suc n) (C (m + n) n * m) (False[n!≟0] n) $ begin-equality
  C (m + n) (suc n) * suc n * n ! ≡⟨ *-assoc (C (m + n) (suc n)) (suc n) (n !) ⟩
  C (m + n) (suc n) * suc n !     ≡⟨ C[n,k]*k!≡P[n,k] (m + n) (suc n) ⟩
  P (m + n) (suc n)               ≡⟨ P[m+n,1+n]≡P[m+n,n]*m m n ⟩
  P (m + n) n * m                 ≡⟨ cong (_* m) $ sym $ C[n,k]*k!≡P[n,k] (m + n) n ⟩
  (C (m + n) n * n !) * m         ≡⟨ Lemma.lemma₇ (C (m + n) n) (n !) m ⟩
  C (m + n) n * m * n !           ∎

C[n,1+k]*[1+k]≡C[n,k]*[n∸k] : ∀ n k → C n (suc k) * suc k ≡ C n k * (n ∸ k)
C[n,1+k]*[1+k]≡C[n,k]*[n∸k] n k with k ≤? n
... | yes k≤n = begin-equality
  C n (suc k) * suc k       ≡⟨ cong (λ v → C v (suc k) * suc k) (sym m+k≡n) ⟩
  C (m + k) (suc k) * suc k ≡⟨ C[m+n,1+n]*[1+n]≡C[m+n,n]*m m k ⟩
  C (m + k) k * m           ≡⟨ cong (λ v → C v k * m) m+k≡n ⟩
  C n k * (n ∸ k)           ∎
  where
  m = n ∸ k
  m+k≡n : m + k ≡ n
  m+k≡n = m∸n+n≡m k≤n
... | no k≰n  = begin-equality
  C n (suc k) * suc k ≡⟨ cong (_* suc k) $ n<k⇒C[n,k]≡0 (≤-step n<k) ⟩
  0 * suc k           ≡⟨⟩
  0                   ≡⟨ sym $ *-zeroʳ (C n k) ⟩
  C n k * 0           ≡⟨ cong (C n k *_) $ sym $ m≤n⇒m∸n≡0 (<⇒≤ n<k) ⟩
  C n k * (n ∸ k)     ∎
  where
  n<k = ≰⇒> k≰n

-- -- C n k = ((n + 1 - k) / k) * C n (k - 1)
C[n,1+k]≡[C[n,k]*[n∸k]]/[1+k] : ∀ n k → C n (1 + k) ≡ (C n k * (n ∸ k)) / (1 + k)
C[n,1+k]≡[C[n,k]*[n∸k]]/[1+k] n k =
  Lemma.m*n≡o⇒m≡o/n (C n (suc k)) (suc k) (C n k * (n ∸ k)) tt (C[n,1+k]*[1+k]≡C[n,k]*[n∸k] n k)

-- C n k ≡ (n / k) * C (n - 1) (k - 1)
-- proved by C[n,k]*k!≡P[n,k]
[1+k]*C[1+n,1+k]≡[1+n]*C[n,k] : ∀ n k → suc k * C (suc n) (suc k) ≡ suc n * C n k
[1+k]*C[1+n,1+k]≡[1+n]*C[n,k] n k = Lemma.*-cancelʳ-≡′
    (suc k * C (suc n) (suc k)) (suc n * C n k) (False[n!≟0] k) $ begin-equality
  suc k * C (suc n) (suc k) * k ! ≡⟨ sym $ Lemma.lemma₈ (C (suc n) (suc k)) (suc k) (k !) ⟩
  C (suc n) (suc k) * (suc k) !   ≡⟨ C[n,k]*k!≡P[n,k] (suc n) (suc k) ⟩
  P (suc n) (suc k)               ≡⟨⟩
  suc n * P n k                   ≡⟨ cong (suc n *_) $ sym $ C[n,k]*k!≡P[n,k] n k ⟩
  suc n * (C n k * k !)           ≡⟨ sym $ *-assoc (suc n) (C n k) (k !) ⟩
  suc n * C n k * k !             ∎

-- Multiply both sides by n ! * o !
C[m,n]*C[m∸n,o]≡C[m,o]*C[m∸o,n] : ∀ m n o → C m n * C (m ∸ n) o ≡ C m o * C (m ∸ o) n
C[m,n]*C[m∸n,o]≡C[m,o]*C[m∸o,n] m n o =
  Lemma.*-cancelʳ-≡′ (C m n * C (m ∸ n) o) (C m o * C (m ∸ o) n) (False[n!≟0] n) $
  Lemma.*-cancelʳ-≡′ (C m n * C (m ∸ n) o * n !) (C m o * C (m ∸ o) n * n !) (False[n!≟0] o) $ begin-equality
  C m n * C (m ∸ n) o * n ! * o !     ≡⟨ Lemma.lemma₁₀ (C m n) (C (m ∸ n) o) (n !) (o !) ⟩
  (C m n * n !) * (C (m ∸ n) o * o !) ≡⟨ cong₂ _*_ (C[n,k]*k!≡P[n,k] m n) (C[n,k]*k!≡P[n,k] (m ∸ n) o) ⟩
  P m n * P (m ∸ n) o                 ≡⟨ P-slide-∸ m n o ⟩
  P m o * P (m ∸ o) n                 ≡⟨ sym $ cong₂ _*_ (C[n,k]*k!≡P[n,k] m o) (C[n,k]*k!≡P[n,k] (m ∸ o) n) ⟩
  (C m o * o !) * (C (m ∸ o) n * n !) ≡⟨ Lemma.lemma₁₁ (C m o) (o !) (C (m ∸ o) n) (n !) ⟩
  C m o * C (m ∸ o) n * n ! * o !     ∎

k≤n⇒1≤C[n,k] : ∀ {n k} → k ≤ n → 1 ≤ C n k
k≤n⇒1≤C[n,k] {n} {k} k≤n = Lemma.*-cancelʳ-≤′ 1 (C n k) (False[n!≟0] k) $ begin
  1 * k !     ≡⟨ *-identityˡ (k !) ⟩
  k !         ≤⟨ k≤n⇒k!≤P[n,k] k≤n ⟩
  P n k       ≡⟨ sym $ C[n,k]*k!≡P[n,k] n k ⟩
  C n k * k ! ∎

k≤n⇒C[n,k]≢0 : ∀ {n k} → k ≤ n → C n k ≢ 0
k≤n⇒C[n,k]≢0 {n} {k} k≤n = Lemma.1≤n⇒n≢0 $ k≤n⇒1≤C[n,k] k≤n

C[n,k]≡0⇒n<k : ∀ {n k} → C n k ≡ 0 → n < k
C[n,k]≡0⇒n<k {n} {k} C[n,k]≡0 with n <? k
... | yes n<k = n<k
... | no  n≮k = ⊥-elim $ k≤n⇒C[n,k]≢0 (≮⇒≥ n≮k) C[n,k]≡0

C[n,k]≢0⇒k≤n : ∀ {n k} → C n k ≢ 0 → k ≤ n
C[n,k]≢0⇒k≤n {n} {k} C[n,k]≢0 with k ≤? n
... | yes k≤n = k≤n
... | no  k≰n = ⊥-elim $ C[n,k]≢0 $ n<k⇒C[n,k]≡0 (≰⇒> k≰n)

------------------------------------------------------------------------
-- Properties of double factorial

!!-! : ∀ n → suc n !! * n !! ≡ suc n !
!!-! 0             = refl
!!-! 1             = refl
!!-! (suc (suc n)) = begin-equality
  (3 + n) !! * (2 + n) !!                 ≡⟨⟩
  (3 + n) * (1 + n) !! * ((2 + n) * n !!) ≡⟨ Lemma.lemma₉ (3 + n) (suc n !!) (2 + n) (n !!) ⟩
  (3 + n) * (2 + n) * ((1 + n) !! * n !!) ≡⟨ cong ((3 + n) * (2 + n) *_) $ !!-! n ⟩
  (3 + n) * (2 + n) * (suc n !)           ≡⟨ *-assoc (3 + n) (2 + n) (suc n !) ⟩
  (3 + n) !                               ∎

[2*n]!!≡n!*2^n : ∀ n → (2 * n) !! ≡ n ! * 2 ^ n
[2*n]!!≡n!*2^n 0       = refl
[2*n]!!≡n!*2^n (suc n) = begin-equality
  (2 * (1 + n)) !!              ≡⟨ cong (_!!) $ *-distribˡ-+ 2 1 n ⟩
  (2 + (2 * n)) !!              ≡⟨⟩
  (2 + 2 * n) * (2 * n) !!      ≡⟨ cong ((2 + 2 * n) *_) $ [2*n]!!≡n!*2^n n ⟩
  (2 + 2 * n) * (n ! * 2 ^ n)   ≡⟨ cong (_* (n ! * 2 ^ n)) $ trans (sym $ *-distribˡ-+ 2 1 n) (*-comm 2 (suc n)) ⟩
  (suc n * 2) * ((n !) * 2 ^ n) ≡⟨ Lemma.lemma₉ (suc n) 2 (n !) (2 ^ n) ⟩
  (suc n) ! * 2 ^ suc n         ∎

------------------------------------------------------------------------
-- Properties of unsigned Stirling number of the first kind

n<k⇒S1[n,k]≡0 : ∀ {n k} → n < k → S1 n k ≡ 0
n<k⇒S1[n,k]≡0 {0}     {.(suc _)} (s≤s n<k)         = refl
n<k⇒S1[n,k]≡0 {suc n} {.(suc m)} (s≤s {_} {m} n<k) = begin-equality
  n * S1 n (suc m) + S1 n m ≡⟨ cong₂ _+_ (cong (n *_) $ n<k⇒S1[n,k]≡0 (≤-step n<k)) (n<k⇒S1[n,k]≡0 n<k) ⟩
  n * 0 + 0                 ≡⟨ cong (_+ 0) $ *-zeroʳ n ⟩
  0                         ∎

S1[1+n,1]≡n! : ∀ n → S1 (suc n) 1 ≡ n !
S1[1+n,1]≡n! 0       = refl
S1[1+n,1]≡n! (suc n) = begin-equality
  suc n * S1 (suc n) 1 + S1 (suc n) 0 ≡⟨⟩
  suc n * S1 (suc n) 1 + 0            ≡⟨ +-identityʳ (suc n * S1 (suc n) 1) ⟩
  suc n * S1 (suc n) 1                ≡⟨ cong (suc n *_) $ S1[1+n,1]≡n! n ⟩
  (suc n) !                           ∎

S1[n,n]≡1 : ∀ n → S1 n n ≡ 1
S1[n,n]≡1 0       = refl
S1[n,n]≡1 (suc n) = begin-equality
  n * S1 n (suc n) + S1 n n ≡⟨ cong₂ _+_ (cong (n *_) $ n<k⇒S1[n,k]≡0 {n} {suc n} ≤-refl) (S1[n,n]≡1 n) ⟩
  n * 0 + 1                 ≡⟨ cong (_+ 1) $ *-zeroʳ n ⟩
  1                         ∎

S1[1+n,n]≡C[1+n,2] : ∀ n → S1 (suc n) n ≡ C (suc n) 2
S1[1+n,n]≡C[1+n,2] 0       = refl
S1[1+n,n]≡C[1+n,2] (suc n) = begin-equality
  S1 (suc (suc n)) (suc n)                  ≡⟨⟩
  suc n * S1 (suc n) (suc n) + S1 (suc n) n ≡⟨ cong₂ _+_ (cong (suc n *_) (S1[n,n]≡1 (suc n))) (S1[1+n,n]≡C[1+n,2] n) ⟩
  suc n * 1 + C (suc n) 2                   ≡⟨ cong (_+ C (suc n) 2) lemma ⟩
  C (suc n) 1 + C (suc n) 2                 ≡⟨ sym $ C[1+n,1+k]≡C[n,k]+C[n,1+k] (suc n) 1 ⟩
  C (suc (suc n)) 2                         ∎
  where
  lemma : suc n * 1 ≡ C (suc n) 1
  lemma = trans (*-identityʳ (suc n)) (sym $ C[n,1]≡n (suc n))

------------------------------------------------------------------------
-- Properties of Stirling number of the second kind

n<k⇒S2[n,k]≡0 : ∀ {n k} → n < k → S2 n k ≡ 0
n<k⇒S2[n,k]≡0 {.0}       {.(suc _)}       (s≤s z≤n)               = refl
n<k⇒S2[n,k]≡0 {.(suc _)} {.(suc (suc _))} (s≤s (s≤s {n} {k} n≤k)) = begin-equality
  S2 (suc n) (suc (suc k))      ≡⟨⟩
  2+k * S2 n 2+k + S2 n (suc k) ≡⟨ cong₂ _+_ (cong (2+k *_) (n<k⇒S2[n,k]≡0 n<2+k)) (n<k⇒S2[n,k]≡0 n<1+k) ⟩
  2+k * 0 + 0                   ≡⟨ cong (_+ 0) $ *-zeroʳ 2+k ⟩
  0                             ∎
  where
  2+k = suc (suc k)
  n<1+k : n < 1 + k
  n<1+k = s≤s n≤k
  n<2+k : n < 2 + k
  n<2+k = ≤-step n<1+k

S2[n,n]≡1 : ∀ n → S2 n n ≡ 1
S2[n,n]≡1 0       = refl
S2[n,n]≡1 (suc n) = begin-equality
  suc n * S2 n (suc n) + S2 n n ≡⟨ cong₂ _+_ (cong (suc n *_) (n<k⇒S2[n,k]≡0 {n} {suc n} ≤-refl)) (S2[n,n]≡1 n) ⟩
  suc n * 0 + 1                 ≡⟨ cong (_+ 1) $ *-zeroʳ (suc n) ⟩
  1                             ∎

S2[1+n,n]≡C[n,2] : ∀ n → S2 (suc n) n ≡ C (suc n) 2
S2[1+n,n]≡C[n,2] 0       = refl
S2[1+n,n]≡C[n,2] (suc n) = begin-equality
  S2 (suc (suc n)) (suc n)                  ≡⟨⟩
  suc n * S2 (suc n) (suc n) + S2 (suc n) n ≡⟨ cong₂ _+_ (cong (suc n *_) (S2[n,n]≡1 (suc n))) (S2[1+n,n]≡C[n,2] n) ⟩
  suc n * 1 + C (suc n) 2                   ≡⟨ cong (_+ C (suc n) 2) lemma ⟩
  C (suc n) 1 + C (suc n) 2                 ≡⟨ sym $ C[1+n,1+k]≡C[n,k]+C[n,1+k] (suc n) 1 ⟩
  C (suc (suc n)) 2                         ∎
  where
  lemma : suc n * 1 ≡ C (suc n) 1
  lemma = trans (*-identityʳ (suc n)) (sym $ C[n,1]≡n (suc n))

{-
1+S2[1+n,2]≡2^n : ∀ n → 1 + S2 (suc n) 2 ≡ 2 ^ n
1+S2[1+n,2]≡2^n 0       = refl
1+S2[1+n,2]≡2^n (suc n) = {!   !}
-}

------------------------------------------------------------------------
-- Properties of Lah number

L[0,k]≡0 : ∀ k → L 0 k ≡ 0
L[0,k]≡0 0       = refl
L[0,k]≡0 (suc k) = refl

L-rec : ∀ n k → L (suc n) (suc (suc k)) ≡ (n + suc (suc k)) * L n (suc (suc k)) + L n (suc k)
L-rec 0       k = sym $ trans (+-identityʳ (k * 0)) (*-zeroʳ k)
L-rec (suc n) k = refl

{-
n<k⇒L[n,k]≡0 : ∀ {n k} → n < k → L n k ≡ 0
n<k⇒Lnk≡0 (s≤s z≤n)       = refl
n<k⇒Lnk≡0 {.(suc (suc n))} {.(suc (suc k))} (s≤s (s≤s {suc n} {k} n<k)) = begin
  (L (suc n) (suc (suc k)) + (n + suc (suc k)) * L (suc n) (suc (suc k))) + L (suc n) (suc k)
    ≡⟨ cong₂ _+_ (cong₂ _+_ lemma (cong ((n + suc (suc k)) *_) {!   !}))n<k⇒L[n,k]≡0 (s≤s n<k)) ⟩
  0 + (n + suc (suc k)) * 0 + 0 ≡⟨ {!   !} ⟩
  0 ∎
  where
  lemma : L (suc n) (suc (suc k)) ≡ 0
  lemma = n<k⇒L[n,k]≡0 (≤-step (s≤s n<k))

L[n,n] : ∀ n → L (suc n) (suc n) ≡ 1
L[n,n] 0       = refl
L[n,n] (suc n) = {!   !}
-}
------------------------------------------------------------------------
-- Properties of Pochhammer symbol

Poch[0,1+k]≡0 : ∀ k → Poch 0 (suc k) ≡ 0
Poch[0,1+k]≡0 k = refl

Poch[n,1]≡n : ∀ n → Poch n 1 ≡ n
Poch[n,1]≡n 0       = refl
Poch[n,1]≡n (suc n) = cong suc (*-identityʳ n)

Poch[1+n,k]*n!≡[n+k]! : ∀ n k → Poch (suc n) k * n ! ≡ (n + k) !
Poch[1+n,k]*n!≡[n+k]! n 0       = trans (+-identityʳ (n !)) (sym $ cong (_!) $ +-identityʳ n)
Poch[1+n,k]*n!≡[n+k]! n (suc k) = begin-equality
  Poch (suc n) (suc k) * n !         ≡⟨⟩
  suc n * Poch (suc (suc n)) k * n ! ≡⟨ Lemma.lemma₁₂ (suc n) (Poch (suc (suc n)) k) (n !) ⟩
  Poch (suc (suc n)) k * (suc n) !   ≡⟨ Poch[1+n,k]*n!≡[n+k]! (suc n) k ⟩
  (suc n + k) !                      ≡⟨ sym $ cong (_!) $ +-suc n k ⟩
  (n + suc k) !                      ∎

Poch[1,k]≡k! : ∀ k → Poch 1 k ≡ k !
Poch[1,k]≡k! k = Lemma.*-cancelʳ-≡′ (Poch 1 k) (k !) {1 !} tt $ begin-equality
  Poch 1 k * 1 ! ≡⟨ Poch[1+n,k]*n!≡[n+k]! 0 k ⟩
  k !            ≡⟨ sym $ *-identityʳ (k !) ⟩
  k ! * 1 !      ∎

Poch[1+m,n]≡P[m+n,n] : ∀ m n → Poch (suc m) n ≡ P (m + n) n
Poch[1+m,n]≡P[m+n,n] m n =
 Lemma.*-cancelʳ-≡′ (Poch (suc m) n) (P (m + n) n) (False[n!≟0] m) $ begin-equality
  Poch (suc m) n * m ! ≡⟨ Poch[1+n,k]*n!≡[n+k]! m n ⟩
  (m + n) !            ≡⟨ sym $ P[m+n,n]*m!≡[m+n]! m n ⟩
  P (m + n) n * m !    ∎

Poch[n,k]≡P[n+k∸1,n] : ∀ n k → Poch n k ≡ P (n + k ∸ 1) k
Poch[n,k]≡P[n+k∸1,n] 0       0       = refl
Poch[n,k]≡P[n+k∸1,n] 0       (suc k) = sym $ n<k⇒P[n,k]≡0 {k} {suc k} ≤-refl
Poch[n,k]≡P[n+k∸1,n] (suc n) k       = begin-equality
  Poch (suc n) k      ≡⟨ Poch[1+m,n]≡P[m+n,n] n k ⟩
  P (n + k) k         ≡⟨⟩
  P (suc n + k ∸ 1) k ∎

Poch[1+[n∸k],k]≡P[n,k] : ∀ {n k} → k ≤ n → Poch (suc (n ∸ k)) k ≡ P n k
Poch[1+[n∸k],k]≡P[n,k] {n} {k} k≤n = begin-equality
  Poch (suc m) k ≡⟨ Poch[1+m,n]≡P[m+n,n] m k ⟩
  P (m + k) k    ≡⟨ cong (λ v → P v k) m+k≡n ⟩
  P n k          ∎
  where
  m = n ∸ k
  m+k≡n = m∸n+n≡m k≤n

Poch-split : ∀ m n o → Poch m (n + o) ≡ Poch m n * Poch (m + n) o
Poch-split m 0       o = begin-equality
  Poch m o           ≡⟨ sym $ cong (λ v → Poch v o) $ +-identityʳ m ⟩
  Poch (m + 0) o     ≡⟨ sym $ +-identityʳ (Poch (m + 0) o) ⟩
  Poch (m + 0) o + 0 ∎
Poch-split m (suc n) o = begin-equality
  m * Poch (suc m) (n + o)                  ≡⟨ cong (m *_) $ Poch-split (suc m) n o ⟩
  m * (Poch (suc m) n * Poch (suc m + n) o) ≡⟨ sym $ *-assoc m (Poch (suc m) n) (Poch (suc m + n) o) ⟩
  m * Poch (suc m) n * Poch (suc m + n) o   ≡⟨ cong (λ v → m * Poch (suc m) n * Poch v o) $ sym $ +-suc m n ⟩
  m * Poch (suc m) n * Poch (m + suc n) o   ∎

o≤n⇒Poch[m,n]≡Poch[m+o,n∸o]*Poch[m,o] :
  ∀ m {n o} → o ≤ n → Poch m n ≡ Poch (m + o) (n ∸ o) * Poch m o
o≤n⇒Poch[m,n]≡Poch[m+o,n∸o]*Poch[m,o] m {n} {o} o≤n = begin-equality
  Poch m n                  ≡⟨ cong (Poch m) n≡o+p ⟩
  Poch m (o + p)            ≡⟨ Poch-split m o p ⟩
  Poch m o * Poch (m + o) p ≡⟨ *-comm (Poch m o) (Poch (m + o) p) ⟩
  Poch (m + o) p * Poch m o ∎
  where
  p = n ∸ o
  n≡o+p : n ≡ o + (n ∸ o)
  n≡o+p = trans (sym $ m∸n+n≡m o≤n) (+-comm (n ∸ o) o)

1≤n⇒1≤Poch[n,k] : ∀ {n} k → 1 ≤ n → 1 ≤ Poch n k
1≤n⇒1≤Poch[n,k] {n} zero    1≤n = ≤-refl
1≤n⇒1≤Poch[n,k] {n} (suc k) 1≤n = begin
  1 * 1              ≤⟨ *-mono-≤ 1≤n (1≤n⇒1≤Poch[n,k] k (≤-step 1≤n)) ⟩
  n * Poch (suc n) k ∎

------------------------------------------------------------------------
-- Properties of Central binomial coefficient

private
  2*n≡n+n : ∀ n → 2 * n ≡ n + n
  2*n≡n+n n = cong (n +_) $ +-identityʳ n

CB[n]*n!*n!≡[2*n]! : ∀ n → CB n * n ! * n ! ≡ (2 * n) !
CB[n]*n!*n!≡[2*n]! n = begin-equality
  C (2 * n) n * n ! * n ! ≡⟨ cong (λ v → C v n * n ! * n !) $ 2*n≡n+n n ⟩
  C (n + n) n * n ! * n ! ≡⟨ C[m+n,m]*m!*n!≡[m+n]! n n ⟩
  (n + n) !               ≡⟨ cong (_!) $ sym $ 2*n≡n+n n ⟩
  (2 * n) !               ∎

CB[1+n]*[1+n]≡2*[1+2*n]*CB[n] : ∀ n → CB (suc n) * suc n ≡ 2 * (1 + 2 * n) * CB n
CB[1+n]*[1+n]≡2*[1+2*n]*CB[n] n = Lemma.*-cancelʳ-≡′
  (CB (1 + n) * suc n)
  (2 * (1 + 2 * n) * CB n)
  {o = suc n * n ! * n !}
  (fromWitnessFalse (Lemma.*-pres-≢0
    (Lemma.*-pres-≢0 {a = suc n} (λ ()) ((n!≢0 n))) (n!≢0 n)))
  ( begin-equality
      CB (suc n) * suc n * (suc n * n ! * n !)
        ≡⟨ Lemma.lemma₁₄ (CB (1 + n)) (suc n) (n !) ⟩
      CB (suc n) * (suc n) ! * (suc n) !
        ≡⟨ CB[n]*n!*n!≡[2*n]! (suc n) ⟩
      (2 * suc n) !
        ≡⟨ cong (_!) $ *-distribˡ-+ 2 1 n ⟩
      (2 + 2 * n) !
        ≡⟨⟩
      (2 + 2 * n) * ((1 + 2 * n) * (2 * n) !)
        ≡⟨ sym $ *-assoc (2 + 2 * n) (1 + 2 * n) ((2 * n) !) ⟩
      (2 + 2 * n) * (1 + 2 * n) * (2 * n) !
        ≡⟨ cong (_* (2 * n) !) $ Lemma.lemma₁₅ n ⟩
      2 * (1 + 2 * n) * (1 + n) * (2 * n) !
        ≡⟨ sym $ cong (2 * (1 + 2 * n) * (1 + n) *_) $ CB[n]*n!*n!≡[2*n]! n ⟩
      2 * (1 + 2 * n) * (1 + n) * (CB n * n ! * n !)
        ≡⟨ Lemma.lemma₁₆ (2 * (1 + 2 * n)) (1 + n) (CB n) (n !) ⟩
      2 * (1 + 2 * n) * CB n * (suc n * n ! * n !)
        ∎ )

------------------------------------------------------------------------
-- Properties of Catalan number
-- Catelan n = C (2 * n) n / suc n

private
  C[2*n,1+n]*[1+n]≡CB[n]*n : ∀ n → C (2 * n) (1 + n) * (1 + n) ≡ CB n * n
  C[2*n,1+n]*[1+n]≡CB[n]*n n = begin-equality
    C (2 * n) (1 + n) * (1 + n) ≡⟨ cong (λ v → C v (1 + n) * (1 + n)) $ 2*n≡n+n n ⟩
    C (n + n) (1 + n) * (1 + n) ≡⟨ C[m+n,1+n]*[1+n]≡C[m+n,n]*m n n ⟩
    C (n + n) n * n             ≡⟨ cong (λ v → C v n * n) $ sym $ 2*n≡n+n n ⟩
    C (2 * n) n * n             ∎

  [C[2*n,n]∸C[2*n,1+n]]*[1+n]≡CB[n] : ∀ n → (C (2 * n) n ∸ C (2 * n) (1 + n)) * (1 + n) ≡ CB n
  [C[2*n,n]∸C[2*n,1+n]]*[1+n]≡CB[n] n = begin-equality
    (C (2 * n) n ∸ C (2 * n) (1 + n)) * (1 + n)
      ≡⟨ *-distribʳ-∸  (1 + n) (C (2 * n) n) (C (2 * n) (1 + n)) ⟩
    C (2 * n) n * (1 + n) ∸ C (2 * n) (1 + n) * (1 + n)
      ≡⟨ cong (C (2 * n) n * (1 + n) ∸_) $ C[2*n,1+n]*[1+n]≡CB[n]*n n ⟩
    C (2 * n) n * (1 + n) ∸ C (2 * n) n * n
      ≡⟨ sym $ *-distribˡ-∸ (C (2 * n) n) (1 + n) n ⟩
    C (2 * n) n * (suc n ∸ n) ≡⟨ cong (C (2 * n) n *_) $ m+n∸n≡m 1 n ⟩
    C (2 * n) n * 1           ≡⟨ *-identityʳ (C (2 * n) n) ⟩
    C (2 * n) n               ∎

Catalan[n]≡C[2*n,n]∸C[2*n,1+n] : ∀ n → Catalan n ≡ C (2 * n) n ∸ C (2 * n) (1 + n)
Catalan[n]≡C[2*n,n]∸C[2*n,1+n] n =
  sym $ Lemma.m*n≡o⇒m≡o/n (C (2 * n) n ∸ C (2 * n) (1 + n)) (suc n)
    (C (2 * n) n) tt ([C[2*n,n]∸C[2*n,1+n]]*[1+n]≡CB[n] n)

Catalan[n]*[1+n]≡CB[n] : ∀ n → Catalan n * (1 + n) ≡ CB n
Catalan[n]*[1+n]≡CB[n] n = begin-equality
  Catalan n * (1 + n)                         ≡⟨ cong (_* (1 + n)) $ Catalan[n]≡C[2*n,n]∸C[2*n,1+n] n ⟩
  (C (2 * n) n ∸ C (2 * n) (1 + n)) * (1 + n) ≡⟨ [C[2*n,n]∸C[2*n,1+n]]*[1+n]≡CB[n] n ⟩
  C (2 * n) n                                 ∎

[1+n]!*n!*Catalan[n]≡[2*n]! : ∀ n → (1 + n) ! * n ! * Catalan n ≡ (2 * n) !
[1+n]!*n!*Catalan[n]≡[2*n]! n = begin-equality
  (suc n * n !) * n ! * Catalan n ≡⟨ Lemma.lemma₁₃ (suc n) (n !) (Catalan n) ⟩
  (Catalan n * suc n) * n ! * n ! ≡⟨ cong (λ v → v * n ! * n !) $ Catalan[n]*[1+n]≡CB[n] n ⟩
  CB n * n ! * n !                ≡⟨ CB[n]*n!*n!≡[2*n]! n ⟩
  (2 * n) !                       ∎

Catalan[n]≡[2*n]!/[[1+n]!*n!] :
  ∀ n → Catalan n ≡ _/_ ((2 * n) !) ((1 + n) ! * n !) {False[m!*n!≟0] (1 + n) n}
Catalan[n]≡[2*n]!/[[1+n]!*n!] n =
  Lemma.m*n≡o⇒m≡o/n (Catalan n) ((1 + n) ! * n !) ((2 * n) !) (False[m!*n!≟0] (1 + n) n)
    ( begin-equality
        Catalan n * (suc n ! * n !) ≡⟨ *-comm (Catalan n) (suc n ! * n !) ⟩
        suc n ! * n ! * Catalan n   ≡⟨ [1+n]!*n!*Catalan[n]≡[2*n]! n ⟩
        (2 * n) !                   ∎ )

Catalan[1+n]*[2+n]≡2*[1+2*n]*Catalan[n] : ∀ n →
  Catalan (suc n) * (2 + n) ≡ 2 * (1 + 2 * n) * Catalan n
Catalan[1+n]*[2+n]≡2*[1+2*n]*Catalan[n] n = Lemma.*-cancelʳ-≡′
  (Catalan (suc n) * (2 + n)) (2 * (1 + 2 * n) * Catalan n) {o = suc n} tt
  (begin-equality
    Catalan (suc n) * (2 + n) * suc n
      ≡⟨ cong (_* suc n) $ Catalan[n]*[1+n]≡CB[n] (suc n) ⟩
    CB (suc n) * suc n ≡⟨ CB[1+n]*[1+n]≡2*[1+2*n]*CB[n] n ⟩
    2 * (1 + 2 * n) * CB n
      ≡⟨ sym $ cong (2 * (1 + 2 * n) *_) $ Catalan[n]*[1+n]≡CB[n] n ⟩
    2 * (1 + 2 * n) * (Catalan n * suc n)
      ≡⟨ sym $ *-assoc (2 * (1 + 2 * n)) (Catalan n) (suc n) ⟩
    2 * (1 + 2 * n) * Catalan n * suc n
      ∎ )

{- TODO
Catalan[1+n]≡Σ[i≤n][Catalan[i]*Catalan[n∸i]] :
  ∀ n → Catalan (suc n) ≡ Σ[ i ≤ n ] (Catalan i * Catalan (n ∸ i))
Catalan[1+n]≡Σ[i≤n][Catalan[i]*Catalan[n∸i]] n = ?
-}

------------------------------------------------------------------
-- Properties of Multinomial coefficient

product[map[!,xs]]*Multinomial[xs]≡sum[xs]! :
  ∀ xs → product (List.map _! xs) * Multinomial xs ≡ sum xs !
product[map[!,xs]]*Multinomial[xs]≡sum[xs]! []           = refl
product[map[!,xs]]*Multinomial[xs]≡sum[xs]! xxs@(x ∷ xs) = begin-equality
  x ! * product (List.map _! xs) * (C (sum xxs) x * Multinomial xs)
    ≡⟨ Lemma.lemma₁₇ (x !) (product (List.map _! xs)) (C (sum xxs) x) (Multinomial xs) ⟩
  C (x + sum xs) x * x ! * (product (List.map _! xs) * Multinomial xs)
    ≡⟨ cong (C (sum xxs) x * x ! *_) $ product[map[!,xs]]*Multinomial[xs]≡sum[xs]! xs ⟩
  C (x + sum xs) x * x ! * sum xs !
    ≡⟨ C[m+n,m]*m!*n!≡[m+n]! x (sum xs) ⟩
  (x + sum xs) !
   ∎

private
  1≢0 : 1 ≢ 0
  1≢0 ()

  product[map[!,xs]]≢0 : ∀ xs → product (List.map _! xs) ≢ 0
  product[map[!,xs]]≢0 xs =
    foldr-preservesᵇ {P = λ x → x ≢ 0} Lemma.*-pres-≢0
                     1≢0 (Allₚ.map⁺ {f = _!} $ All.universal n!≢0 xs)

Multinomial[xs]≡sum[xs]!/product[map[!,xs]] :
  ∀ xs → Multinomial xs ≡ _/_ (sum xs !) (product (List.map _! xs))
    {fromWitnessFalse (product[map[!,xs]]≢0 xs)}
Multinomial[xs]≡sum[xs]!/product[map[!,xs]] xs = Lemma.m*n≡o⇒m≡o/n
  (Multinomial xs) (product (List.map _! xs)) (sum xs !)
  (fromWitnessFalse (product[map[!,xs]]≢0 xs)) (begin-equality
    Multinomial xs * product (List.map _! xs)
      ≡⟨ *-comm (Multinomial xs) (product (List.map _! xs)) ⟩
    product (List.map _! xs) * Multinomial xs
      ≡⟨ product[map[!,xs]]*Multinomial[xs]≡sum[xs]! xs ⟩
    sum xs !
      ∎ )

Multinomial[[x]]≡1 : ∀ x → Multinomial (x ∷ []) ≡ 1
Multinomial[[x]]≡1 x = begin-equality
  C (x + 0) x * 1 ≡⟨ *-identityʳ (C (x + 0) x) ⟩
  C (x + 0) x     ≡⟨ cong (λ v → C v x) $ +-identityʳ x ⟩
  C x x           ≡⟨ C[n,n]≡1 x ⟩
  1               ∎

Multinomial[[m,n]]≡C[m+n,m] : ∀ m n → Multinomial (m ∷ n ∷ []) ≡ C (m + n) m
Multinomial[[m,n]]≡C[m+n,m] m n = begin-equality
  C (m + (n + 0)) m * Multinomial (n ∷ [])
    ≡⟨ cong (C (m + (n + 0)) m *_) $ Multinomial[[x]]≡1 n ⟩
  C (m + (n + 0)) m * 1
    ≡⟨ *-identityʳ (C (m + (n + 0)) m) ⟩
  C (m + (n + 0)) m
    ≡⟨ cong (λ v → C (m + v) m) $ +-identityʳ n ⟩
  C (m + n) m
    ∎

------------------------------------------------------------------------
-- Properties of Pascal's triangle

module _ {a} {A : Set a} where
  length-gpascal : ∀ f (v0 v1 : A) n → length (gpascal f v0 v1 n) ≡ suc n
  length-gpascal f v0 v1 zero    = refl
  length-gpascal f v0 v1 (suc n) = begin-equality
    length (zipWith f (v0 ∷ ps) (ps ∷ʳ v0))
      ≡⟨ length-zipWith f (v0 ∷ ps) (ps ∷ʳ v0) ⟩
    length (v0 ∷ ps) ⊓ length (ps ∷ʳ v0)
      ≡⟨ cong (suc (length ps) ⊓_) $ length-++ ps ⟩
    suc (length ps) ⊓ (length ps + 1)
      ≡⟨ cong₂ (λ u v → suc u ⊓ v) hyp (trans (cong (_+ 1) hyp) (+-comm (suc n) 1)) ⟩
    suc (suc n) ⊓ (suc (suc n))
      ≡⟨ ⊓-idem (suc (suc n)) ⟩
    suc (suc n)
      ∎
    where
    ps = gpascal f v0 v1 n
    hyp = length-gpascal f v0 v1 n


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

-- The 'original' version of Lawvere Theory, based on
-- Nat^op and IOO functors. Contrast with the weak version at
-- https://ncatlab.org/nlab/show/Lawvere+theory
-- Unfortunately, many results on the weak version are not in
-- the literature, so doing that development would be new research.

module Categories.Theory.Lawvere where

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

open import Categories.Category.Cartesian using (Cartesian)
open import Categories.Category.Cartesian.Bundle using (CartesianCategory)
import Categories.Category.Core as Cat
open import Categories.Category.Instance.Nat using (Nat; Natop-Cartesian)
open import Categories.Category.Instance.Setoids
open import Categories.Category.Monoidal.Instance.Setoids using (Setoids-CartesianCategory)
open import Categories.Category.Unbundled using (Category)
open import Categories.Category.Unbundled.Properties using (pack′; unpack′)
open import Categories.Functor using (Functor; _∘F_) renaming (id to idF)
open import Categories.Functor.Cartesian
open import Categories.Functor.Cartesian.Properties
open import Categories.Functor.IdentityOnObjects

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

record LawvereTheory (ℓ e : Level) : Set (suc (ℓ ⊔ e)) where
  private
    𝒩 = Cat.Category.op Nat
  field
    L : Category ℕ ℓ e
  L′ : Cat.Category 0ℓ ℓ e
  L′ = pack′ L
  field
    T : Cartesian L′
  CartT : CartesianCategory 0ℓ ℓ e
  CartT = record { U = L′ ; cartesian = T }
  field
    I : IdentityOnObjects (unpack′ 𝒩) L
    CartF : IsCartesianF Natop-Cartesian CartT (IOO⇒Functor I)

record LT-Hom (T₁ : LawvereTheory ℓ e) (T₂ : LawvereTheory ℓ′ e′) : Set (ℓ ⊔ e ⊔ ℓ′ ⊔ e′) where
  private
    module T₁ = LawvereTheory T₁
    module T₂ = LawvereTheory T₂

  field
    cartF : CartesianF T₁.CartT T₂.CartT

  module cartF = CartesianF cartF using (F)

LT-id : {A : LawvereTheory ℓ e} → LT-Hom A A
LT-id = record { cartF = idF-CartesianF _ }

LT-∘ : {A : LawvereTheory ℓ e} {B : LawvereTheory ℓ′ e′} {C : LawvereTheory ℓ″ e″} →
       LT-Hom B C → LT-Hom A B → LT-Hom A C
LT-∘ G H = record { cartF = ∘-CartesianF (cartF G) (cartF H) }
  where open LT-Hom

-- A 'Model' will be taken to be in Setoids.
record Model (LT : LawvereTheory ℓ e) : Set (ℓ ⊔ e ⊔ suc (ℓ′ ⊔ e′)) where
  private
    module LT = LawvereTheory LT
  field
    cartF : CartesianF LT.CartT (Setoids-CartesianCategory ℓ′ e′)

  module cartF = CartesianF cartF

  mod : Functor LT.L′ (Setoids ℓ′ e′)
  mod = cartF.F

-- But we can have more general models 'in' a cartesian category
record ModelsOf_In_ (LT : LawvereTheory ℓ e) (𝒞 : CartesianCategory o′ ℓ′ e′) : Set (ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) where
  private
    module LT = LawvereTheory LT using (L′; CartT)
    module CC = CartesianCategory 𝒞 using (U)
  field
    cartF : CartesianF LT.CartT 𝒞

  mod : Functor LT.L′ CC.U
  mod = CartesianF.F cartF


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

module Categories.NaturalTransformation.Hom {o ℓ e : Level} (C : Category o ℓ e) where

open import Categories.Category.Instance.Setoids
open import Categories.Functor.Hom using (module Hom; Hom[_][-,_]; Hom[_][_,-]; Hom[_][-,-])
open import Categories.NaturalTransformation using (NaturalTransformation; ntHelper) renaming (id to idN)

import Categories.Morphism.Reasoning as MR

open Category C
open HomReasoning
open MR C
open NaturalTransformation
private
  module CE = Category.Equiv C
  module C = Category C

Hom[A,C]⇒Hom[B,C] : {A B : Obj} → (A ⇒ B) → NaturalTransformation Hom[ C ][-, A ] Hom[ C ][-, B ]
Hom[A,C]⇒Hom[B,C] {A} A⇒B = ntHelper record
  { η       = λ X → record { _⟨$⟩_ = λ X⇒A → A⇒B ∘ X⇒A ; cong = ∘-resp-≈ʳ }
  ; commute = λ f {g} {h} g≈h → begin
      A⇒B ∘ id ∘ g ∘ f   ≈⟨ sym-assoc ⟩
      (A⇒B ∘ id) ∘ g ∘ f ≈⟨ id-comm ⟩∘⟨ g≈h ⟩∘⟨refl ⟩
      (id ∘ A⇒B) ∘ h ∘ f ≈⟨ pullʳ sym-assoc ⟩
      id ∘ (A⇒B ∘ h) ∘ f ∎
  }

Hom[C,A]⇒Hom[C,B] : {A B : Obj} → (B ⇒ A) → NaturalTransformation Hom[ C ][ A ,-] Hom[ C ][ B ,-]
Hom[C,A]⇒Hom[C,B] {A} B⇒A = ntHelper record
  { η = λ X → record { _⟨$⟩_ = λ A⇒X → A⇒X ∘ B⇒A ; cong = ∘-resp-≈ˡ }
  ; commute = λ f {g} {h} g≈h → begin
      (f ∘ g ∘ id) ∘ B⇒A ≈⟨ pullʳ assoc ⟩
      f ∘ g ∘ id ∘ B⇒A   ≈⟨ (refl⟩∘⟨ g≈h ⟩∘⟨ id-comm-sym) ⟩
      f ∘ h ∘ B⇒A ∘ id   ≈⟨ (refl⟩∘⟨ sym-assoc) ⟩
      f ∘ (h ∘ B⇒A) ∘ id ∎
  }


{-# OPTIONS --universe-polymorphism #-}
module Categories.Functor.Constant where

open import Categories.Category
open import Categories.Functor

Constant : ∀ {o′ ℓ′ e′} {D : Category o′ ℓ′ e′} (x : Category.Obj D) → ∀ {o ℓ e} {C : Category o ℓ e} → Functor C D
Constant {D = D} x = record
                     { F₀ = λ _ → x
                     ; F₁ = λ _ → D.id
                     ; identity = D.Equiv.refl
                     ; homomorphism = D.Equiv.sym D.identityˡ
                     ; F-resp-≡ = λ _ → D.Equiv.refl
                     }
                     where
                     module D = Category D


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

open import HIT.Interval
open import PathOperations
open import PathStructure.Id.Tr
open import Types

funext : ∀ {a b} {A : Set a} {B : A → Set b} {f g : (x : A) → B x} →
  (∀ x → f x ≡ g x) → f ≡ g
funext {A = A} {B = B} {f = f} {g = g} p = ap h seg
  where
    h : I → (x : A) → B x
    h i x = I-rec (f x) (g x) (p x) i


module deriveUtil where

open import Level renaming ( suc to succ ; zero to Zero )
open import Data.Nat
open import Data.Fin
open import Data.List

open import regex
open import automaton
open import nfa
open import logic
open NAutomaton
open Automaton
open import  Relation.Binary.PropositionalEquality hiding ( [_] )
open import Relation.Nullary

open Bool

data alpha2 : Set where
   a : alpha2
   b : alpha2

a-eq? : (x y : alpha2) → Dec (x ≡ y)
a-eq? a a = yes refl
a-eq? b b = yes refl
a-eq? a b = no (λ ())
a-eq? b a = no (λ ())

open Regex

open import finiteSet

fin-a : FiniteSet alpha2
fin-a  = record {
     finite = finite0
   ; Q←F = Q←F0
   ; F←Q = F←Q0
   ; finiso→ = finiso→0
   ; finiso← = finiso←0
 } where
     finite0 : ℕ
     finite0 = 2
     Q←F0 : Fin finite0 → alpha2
     Q←F0 zero = a
     Q←F0 (suc zero) = b
     F←Q0 : alpha2 → Fin finite0
     F←Q0 a = # 0
     F←Q0 b = # 1
     finiso→0 : (q : alpha2) → Q←F0 ( F←Q0 q ) ≡ q
     finiso→0 a = refl
     finiso→0 b = refl
     finiso←0 : (f : Fin finite0 ) → F←Q0 ( Q←F0 f ) ≡ f
     finiso←0 zero = refl
     finiso←0 (suc zero) = refl

open import derive alpha2 fin-a a-eq?
test11 = regex→automaton ( < a > & < b > )

test12 = accept test11 record { state =  < a > & < b > ; is-derived = unit } ( a ∷ b ∷ [] )
test13 = accept test11 record { state =  < a > & < b > ; is-derived = unit } ( a ∷ a ∷ [] )

test14 = regex-match ( (  < a > & < b > ) * ) ( a ∷ b ∷ a ∷ a ∷ [] )

test15 = regex-derive ( (  < a > & < b > ) * ∷ [] )
test16 = regex-derive test15
test17 : regex-derive test16 ≡ test16
test17 = refl


------------------------------------------------------------------------
-- The Agda standard library
--
-- Rational numbers
------------------------------------------------------------------------

module Data.Rational where

import Algebra
import Data.Bool.Properties as Bool
open import Function
open import Data.Integer as ℤ using (ℤ; ∣_∣; +_; -_)
open import Data.Integer.Divisibility as ℤDiv using (Coprime)
import Data.Integer.Properties as ℤ
open import Data.Nat.Divisibility as ℕDiv using (_∣_)
import Data.Nat.Coprimality as C
open import Data.Nat as ℕ using (ℕ; zero; suc)
open import Data.Sum
import Level
open import Relation.Nullary.Decidable
open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P
  using (_≡_; refl; cong; cong₂)
open P.≡-Reasoning

------------------------------------------------------------------------
-- The definition

-- Rational numbers in reduced form. Note that there is exactly one
-- representative for every rational number. (This is the reason for
-- using "True" below. If Agda had proof irrelevance, then it would
-- suffice to use "isCoprime : Coprime numerator denominator".)

record ℚ : Set where
  field
    numerator     : ℤ
    denominator-1 : ℕ
    isCoprime     : True (C.coprime? ∣ numerator ∣ (suc denominator-1))

  denominator : ℤ
  denominator = + suc denominator-1

  coprime : Coprime numerator denominator
  coprime = toWitness isCoprime

-- Constructs rational numbers. The arguments have to be in reduced
-- form.

infixl 7 _÷_

_÷_ : (numerator : ℤ) (denominator : ℕ)
      {coprime : True (C.coprime? ∣ numerator ∣ denominator)}
      {≢0 : False (ℕ._≟_ denominator 0)} →
      ℚ
(n ÷ zero) {≢0 = ()}
(n ÷ suc d) {c} =
  record { numerator = n; denominator-1 = d; isCoprime = c }

private

  -- Note that the implicit arguments do not need to be given for
  -- concrete inputs:

  0/1 : ℚ
  0/1 = + 0 ÷ 1

  -½ : ℚ
  -½ = - + 1 ÷ 2

------------------------------------------------------------------------
-- Equality

-- Equality of rational numbers.

infix 4 _≃_

_≃_ : Rel ℚ Level.zero
p ≃ q = numerator p ℤ.* denominator q ≡
        numerator q ℤ.* denominator p
  where open ℚ

-- _≃_ coincides with propositional equality.

≡⇒≃ : _≡_ ⇒ _≃_
≡⇒≃ refl = refl

≃⇒≡ : _≃_ ⇒ _≡_
≃⇒≡ {i = p} {j = q} =
  helper (numerator p) (denominator-1 p) (isCoprime p)
         (numerator q) (denominator-1 q) (isCoprime q)
  where
  open ℚ

  helper : ∀ n₁ d₁ c₁ n₂ d₂ c₂ →
           n₁ ℤ.* + suc d₂ ≡ n₂ ℤ.* + suc d₁ →
           (n₁ ÷ suc d₁) {c₁} ≡ (n₂ ÷ suc d₂) {c₂}
  helper n₁ d₁ c₁ n₂ d₂ c₂ eq
    with Poset.antisym ℕDiv.poset 1+d₁∣1+d₂ 1+d₂∣1+d₁
    where
    1+d₁∣1+d₂ : suc d₁ ∣ suc d₂
    1+d₁∣1+d₂ = ℤDiv.coprime-divisor (+ suc d₁) n₁ (+ suc d₂)
                  (C.sym $ toWitness c₁) $
                  ℕDiv.divides ∣ n₂ ∣ (begin
                    ∣ n₁ ℤ.* + suc d₂ ∣  ≡⟨ cong ∣_∣ eq ⟩
                    ∣ n₂ ℤ.* + suc d₁ ∣  ≡⟨ ℤ.abs-*-commute n₂ (+ suc d₁) ⟩
                    ∣ n₂ ∣ ℕ.* suc d₁    ∎)

    1+d₂∣1+d₁ : suc d₂ ∣ suc d₁
    1+d₂∣1+d₁ = ℤDiv.coprime-divisor (+ suc d₂) n₂ (+ suc d₁)
                  (C.sym $ toWitness c₂) $
                  ℕDiv.divides ∣ n₁ ∣ (begin
                    ∣ n₂ ℤ.* + suc d₁ ∣  ≡⟨ cong ∣_∣ (P.sym eq) ⟩
                    ∣ n₁ ℤ.* + suc d₂ ∣  ≡⟨ ℤ.abs-*-commute n₁ (+ suc d₂) ⟩
                    ∣ n₁ ∣ ℕ.* suc d₂    ∎)

  helper n₁ d c₁ n₂ .d c₂ eq | refl with ℤ.cancel-*-right
                                           n₁ n₂ (+ suc d) (λ ()) eq
  helper n  d c₁ .n .d c₂ eq | refl | refl with Bool.proof-irrelevance c₁ c₂
  helper n  d c  .n .d .c eq | refl | refl | refl = refl

------------------------------------------------------------------------
-- Equality is decidable

infix 4 _≟_

_≟_ : Decidable {A = ℚ} _≡_
p ≟ q with ℚ.numerator p ℤ.* ℚ.denominator q ℤ.≟
           ℚ.numerator q ℤ.* ℚ.denominator p
p ≟ q | yes pq≃qp = yes (≃⇒≡ pq≃qp)
p ≟ q | no ¬pq≃qp = no (¬pq≃qp ∘ ≡⇒≃)

------------------------------------------------------------------------
-- Ordering

infix 4 _≤_ _≤?_

data _≤_ : ℚ → ℚ → Set where
  *≤* : ∀ {p q} →
        ℚ.numerator p ℤ.* ℚ.denominator q ℤ.≤
        ℚ.numerator q ℤ.* ℚ.denominator p →
        p ≤ q

drop-*≤* : ∀ {p q} → p ≤ q →
           ℚ.numerator p ℤ.* ℚ.denominator q ℤ.≤
           ℚ.numerator q ℤ.* ℚ.denominator p
drop-*≤* (*≤* pq≤qp) = pq≤qp

_≤?_ : Decidable _≤_
p ≤? q with ℚ.numerator p ℤ.* ℚ.denominator q ℤ.≤?
            ℚ.numerator q ℤ.* ℚ.denominator p
p ≤? q | yes pq≤qp = yes (*≤* pq≤qp)
p ≤? q | no ¬pq≤qp = no (λ { (*≤* pq≤qp) → ¬pq≤qp pq≤qp })

decTotalOrder : DecTotalOrder _ _ _
decTotalOrder = record
  { Carrier         = ℚ
  ; _≈_             = _≡_
  ; _≤_             = _≤_
  ; isDecTotalOrder = record
      { isTotalOrder = record
          { isPartialOrder = record
              { isPreorder = record
                  { isEquivalence = P.isEquivalence
                  ; reflexive     = refl′
                  ; trans         = trans
                  }
                ; antisym = antisym
              }
          ; total = total
          }
      ; _≟_  = _≟_
      ; _≤?_ = _≤?_
      }
  }
  where
  module ℤO = DecTotalOrder ℤ.decTotalOrder

  refl′ : _≡_ ⇒ _≤_
  refl′ refl = *≤* ℤO.refl

  trans : Transitive _≤_
  trans {i = p} {j = q} {k = r} (*≤* le₁) (*≤* le₂)
    = *≤* (ℤ.cancel-*-+-right-≤ _ _ _
            (lemma
              (ℚ.numerator p) (ℚ.denominator p)
              (ℚ.numerator q) (ℚ.denominator q)
              (ℚ.numerator r) (ℚ.denominator r)
              (ℤ.*-+-right-mono (ℚ.denominator-1 r) le₁)
              (ℤ.*-+-right-mono (ℚ.denominator-1 p) le₂)))
    where
    open Algebra.CommutativeRing ℤ.commutativeRing

    lemma : ∀ n₁ d₁ n₂ d₂ n₃ d₃ →
            n₁ ℤ.* d₂ ℤ.* d₃ ℤ.≤ n₂ ℤ.* d₁ ℤ.* d₃ →
            n₂ ℤ.* d₃ ℤ.* d₁ ℤ.≤ n₃ ℤ.* d₂ ℤ.* d₁ →
            n₁ ℤ.* d₃ ℤ.* d₂ ℤ.≤ n₃ ℤ.* d₁ ℤ.* d₂
    lemma n₁ d₁ n₂ d₂ n₃ d₃
      rewrite *-assoc n₁ d₂ d₃
            | *-comm d₂ d₃
            | sym (*-assoc n₁ d₃ d₂)
            | *-assoc n₃ d₂ d₁
            | *-comm d₂ d₁
            | sym (*-assoc n₃ d₁ d₂)
            | *-assoc n₂ d₁ d₃
            | *-comm d₁ d₃
            | sym (*-assoc n₂ d₃ d₁)
            = ℤO.trans

  antisym : Antisymmetric _≡_ _≤_
  antisym (*≤* le₁) (*≤* le₂) = ≃⇒≡ (ℤO.antisym le₁ le₂)

  total : Total _≤_
  total p q =
    [ inj₁ ∘′ *≤* , inj₂ ∘′ *≤* ]′
      (ℤO.total (ℚ.numerator p ℤ.* ℚ.denominator q)
                (ℚ.numerator q ℤ.* ℚ.denominator p))


------------------------------------------------------------------------
-- Rational numbers
------------------------------------------------------------------------

module Data.Rational where

open import Data.Bool.Properties
open import Data.Function
open import Data.Integer hiding (suc) renaming (_*_ to _ℤ*_)
open import Data.Integer.Divisibility as ℤDiv using (Coprime)
import Data.Integer.Properties as ℤ
open import Data.Nat.Divisibility as ℕDiv using (_∣_)
import Data.Nat.Coprimality as C
open import Data.Nat as ℕ renaming (_*_ to _ℕ*_)
open import Relation.Nullary.Decidable
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as PropEq
open ≡-Reasoning

------------------------------------------------------------------------
-- The definition

-- Rational numbers in reduced form.

record ℚ : Set where
  field
    numerator     : ℤ
    denominator-1 : ℕ
    isCoprime     : True (C.coprime? ∣ numerator ∣ (suc denominator-1))

  denominator : ℤ
  denominator = + suc denominator-1

  coprime : Coprime numerator denominator
  coprime = witnessToTruth isCoprime

-- Constructs rational numbers. The arguments have to be in reduced
-- form.

infixl 7 _÷_

_÷_ : (numerator : ℤ) (denominator : ℕ)
      {coprime : True (C.coprime? ∣ numerator ∣ denominator)}
      {≢0 : False (ℕ._≟_ denominator 0)} →
      ℚ
(n ÷ zero) {≢0 = ()}
(n ÷ suc d) {c} =
  record { numerator = n; denominator-1 = d; isCoprime = c }

private

  -- Note that the implicit arguments do not need to be given for
  -- concrete inputs:

  0/1 : ℚ
  0/1 = + 0 ÷ 1

  -½ : ℚ
  -½ = - + 1 ÷ 2

------------------------------------------------------------------------
-- Equality

-- Equality of rational numbers.

infix 4 _≃_

_≃_ : Rel ℚ
p ≃ q = P.numerator ℤ* Q.denominator ≡
        Q.numerator ℤ* P.denominator
  where module P = ℚ p; module Q = ℚ q

-- _≃_ coincides with propositional equality.

≡⇒≃ : _≡_ ⇒ _≃_
≡⇒≃ refl = refl

≃⇒≡ : _≃_ ⇒ _≡_
≃⇒≡ {p} {q} = helper P.numerator P.denominator-1 P.isCoprime
                     Q.numerator Q.denominator-1 Q.isCoprime
  where
  module P = ℚ p; module Q = ℚ q

  helper : ∀ n₁ d₁ c₁ n₂ d₂ c₂ →
           n₁ ℤ* + suc d₂ ≡ n₂ ℤ* + suc d₁ →
           (n₁ ÷ suc d₁) {c₁} ≡ (n₂ ÷ suc d₂) {c₂}
  helper n₁ d₁ c₁ n₂ d₂ c₂ eq
    with Poset.antisym ℕDiv.poset 1+d₁∣1+d₂ 1+d₂∣1+d₁
    where
    1+d₁∣1+d₂ : suc d₁ ∣ suc d₂
    1+d₁∣1+d₂ = ℤDiv.coprime-divisor (+ suc d₁) n₁ (+ suc d₂)
                  (C.sym $ witnessToTruth c₁) $
                  ℕDiv.divides ∣ n₂ ∣ (begin
                    ∣ n₁ ℤ* + suc d₂ ∣  ≡⟨ cong ∣_∣ eq ⟩
                    ∣ n₂ ℤ* + suc d₁ ∣  ≡⟨ ℤ.abs-*-commute n₂ (+ suc d₁) ⟩
                    ∣ n₂ ∣ ℕ* suc d₁    ∎)

    1+d₂∣1+d₁ : suc d₂ ∣ suc d₁
    1+d₂∣1+d₁ = ℤDiv.coprime-divisor (+ suc d₂) n₂ (+ suc d₁)
                  (C.sym $ witnessToTruth c₂) $
                  ℕDiv.divides ∣ n₁ ∣ (begin
                    ∣ n₂ ℤ* + suc d₁ ∣  ≡⟨ cong ∣_∣ (PropEq.sym eq) ⟩
                    ∣ n₁ ℤ* + suc d₂ ∣  ≡⟨ ℤ.abs-*-commute n₁ (+ suc d₂) ⟩
                    ∣ n₁ ∣ ℕ* suc d₂    ∎)

  helper n₁ d c₁ n₂ .d c₂ eq | refl with ℤ.cancel-*-right
                                           n₁ n₂ (+ suc d) (λ ()) eq
  helper n  d c₁ .n .d c₂ eq | refl | refl with proof-irrelevance c₁ c₂
  helper n  d c  .n .d .c eq | refl | refl | refl = refl